mirror of https://github.com/tailscale/tailscale/
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
3.8 KiB
Go
204 lines
3.8 KiB
Go
6 months ago
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
package pool
|
||
|
|
||
|
import (
|
||
|
"slices"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func TestPool(t *testing.T) {
|
||
|
p := Pool[int]{}
|
||
|
|
||
|
if got, want := p.Len(), 0; got != want {
|
||
|
t.Errorf("got initial length %v; want %v", got, want)
|
||
|
}
|
||
|
|
||
|
h1 := p.Add(101)
|
||
|
h2 := p.Add(102)
|
||
|
h3 := p.Add(103)
|
||
|
h4 := p.Add(104)
|
||
|
|
||
|
if got, want := p.Len(), 4; got != want {
|
||
|
t.Errorf("got length %v; want %v", got, want)
|
||
|
}
|
||
|
|
||
|
tests := []struct {
|
||
|
h Handle[int]
|
||
|
want int
|
||
|
}{
|
||
|
{h1, 101},
|
||
|
{h2, 102},
|
||
|
{h3, 103},
|
||
|
{h4, 104},
|
||
|
}
|
||
|
for i, test := range tests {
|
||
|
got, ok := p.Peek(test.h)
|
||
|
if !ok {
|
||
|
t.Errorf("test[%d]: did not find item", i)
|
||
|
continue
|
||
|
}
|
||
|
if got != test.want {
|
||
|
t.Errorf("test[%d]: got %v; want %v", i, got, test.want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if deleted := p.Delete(h2); !deleted {
|
||
|
t.Errorf("h2 not deleted")
|
||
|
}
|
||
|
if deleted := p.Delete(h2); deleted {
|
||
|
t.Errorf("h2 should not be deleted twice")
|
||
|
}
|
||
|
if got, want := p.Len(), 3; got != want {
|
||
|
t.Errorf("got length %v; want %v", got, want)
|
||
|
}
|
||
|
if _, ok := p.Peek(h2); ok {
|
||
|
t.Errorf("h2 still in pool")
|
||
|
}
|
||
|
|
||
|
// Remove an item by handle
|
||
|
got, ok := p.Take(h4)
|
||
|
if !ok {
|
||
|
t.Errorf("h4 not found")
|
||
|
}
|
||
|
if got != 104 {
|
||
|
t.Errorf("got %v; want 104", got)
|
||
|
}
|
||
|
|
||
|
// Take doesn't work on previously-taken or deleted items.
|
||
|
if _, ok := p.Take(h4); ok {
|
||
|
t.Errorf("h4 should not be taken twice")
|
||
|
}
|
||
|
if _, ok := p.Take(h2); ok {
|
||
|
t.Errorf("h2 should not be taken after delete")
|
||
|
}
|
||
|
|
||
|
// Remove all items and return them
|
||
|
items := p.AppendTakeAll(nil)
|
||
|
want := []int{101, 103}
|
||
|
if !slices.Equal(items, want) {
|
||
|
t.Errorf("got items %v; want %v", items, want)
|
||
|
}
|
||
|
if got := p.Len(); got != 0 {
|
||
|
t.Errorf("got length %v; want 0", got)
|
||
|
}
|
||
|
|
||
|
// Insert and then clear should result in no items.
|
||
|
p.Add(105)
|
||
|
p.Clear()
|
||
|
if got := p.Len(); got != 0 {
|
||
|
t.Errorf("got length %v; want 0", got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTakeRandom(t *testing.T) {
|
||
|
p := Pool[int]{}
|
||
|
for i := 0; i < 10; i++ {
|
||
|
p.Add(i + 100)
|
||
|
}
|
||
|
|
||
|
seen := make(map[int]bool)
|
||
|
for i := 0; i < 10; i++ {
|
||
|
item, ok := p.TakeRandom()
|
||
|
if !ok {
|
||
|
t.Errorf("unexpected empty pool")
|
||
|
break
|
||
|
}
|
||
|
if seen[item] {
|
||
|
t.Errorf("got duplicate item %v", item)
|
||
|
}
|
||
|
seen[item] = true
|
||
|
}
|
||
|
|
||
|
// Verify that the pool is empty
|
||
|
if _, ok := p.TakeRandom(); ok {
|
||
|
t.Errorf("expected empty pool")
|
||
|
}
|
||
|
|
||
|
for i := 0; i < 10; i++ {
|
||
|
want := 100 + i
|
||
|
if !seen[want] {
|
||
|
t.Errorf("item %v not seen", want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if t.Failed() {
|
||
|
t.Logf("seen: %+v", seen)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkPool_AddDelete(b *testing.B) {
|
||
|
b.Run("impl=Pool", func(b *testing.B) {
|
||
|
p := Pool[int]{}
|
||
|
|
||
|
// Warm up/force an initial allocation
|
||
|
h := p.Add(0)
|
||
|
p.Delete(h)
|
||
|
|
||
|
b.ResetTimer()
|
||
|
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
h := p.Add(i)
|
||
|
p.Delete(h)
|
||
|
}
|
||
|
})
|
||
|
b.Run("impl=map", func(b *testing.B) {
|
||
|
p := make(map[int]bool)
|
||
|
|
||
|
// Force initial allocation
|
||
|
p[0] = true
|
||
|
delete(p, 0)
|
||
|
|
||
|
b.ResetTimer()
|
||
|
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
p[i] = true
|
||
|
delete(p, i)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func BenchmarkPool_TakeRandom(b *testing.B) {
|
||
|
b.Run("impl=Pool", func(b *testing.B) {
|
||
|
p := Pool[int]{}
|
||
|
|
||
|
// Insert the number of items we'll be taking, then reset the timer.
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
p.Add(i)
|
||
|
}
|
||
|
b.ResetTimer()
|
||
|
|
||
|
// Now benchmark taking all the items.
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
p.TakeRandom()
|
||
|
}
|
||
|
|
||
|
if p.Len() != 0 {
|
||
|
b.Errorf("pool not empty")
|
||
|
}
|
||
|
})
|
||
|
b.Run("impl=map", func(b *testing.B) {
|
||
|
p := make(map[int]bool)
|
||
|
|
||
|
// Insert the number of items we'll be taking, then reset the timer.
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
p[i] = true
|
||
|
}
|
||
|
b.ResetTimer()
|
||
|
|
||
|
// Now benchmark taking all the items.
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
// Taking a random item is simulated by a single map iteration.
|
||
|
for k := range p {
|
||
|
delete(p, k) // "take" the item by removing it
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(p) != 0 {
|
||
|
b.Errorf("map not empty")
|
||
|
}
|
||
|
})
|
||
|
}
|