| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // 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")
- }
- })
- }
|