diff --git a/bitset/bitset_mutable_set_test.go b/bitset/bitset_mutable_set_test.go deleted file mode 100644 index a4985c4..0000000 --- a/bitset/bitset_mutable_set_test.go +++ /dev/null @@ -1,188 +0,0 @@ -package bitset - -import ( - "github.com/lock14/collections/arraylist" - "github.com/lock14/collections/hashset" - "slices" - "testing" -) - -func TestBitSet_MutableSet(t *testing.T) { - t.Parallel() - b := New(WithCapacity(100)) - b.Add(10) - b.Add(20) - - if !b.Contains(10) { - t.Errorf("expected to contain 10") - } - if !b.Contains(20) { - t.Errorf("expected to contain 20") - } - - b.RemoveElement(10) - if b.Contains(10) { - t.Errorf("expected 10 to be removed") - } - - if b.Empty() { - t.Errorf("expected not to be empty") - } - - b.RemoveElement(20) - if !b.Empty() { - t.Errorf("expected to be empty") - } - - b.Add(5) - b.Add(15) - val := b.Remove() - if val != 5 && val != 15 { - t.Errorf("Remove returned unexpected value: %v", val) - } - - b.Clear() - if !b.Empty() { - t.Errorf("expected empty after Clear") - } - - b.AddAll(slices.Values([]int{1, 2, 3})) - if !b.Contains(1) || !b.Contains(2) || !b.Contains(3) { - t.Errorf("AddAll failed") - } - - other := New(WithCapacity(100)) - other.Add(2) - other.Add(3) - other.Add(4) - - b.RemoveAll(other) - if b.Contains(2) || b.Contains(3) || !b.Contains(1) { - t.Errorf("RemoveAll failed") - } - - b.Clear() - b.AddAll(slices.Values([]int{1, 2, 3})) - b.RetainAll(other) // retains 2, 3 - if b.Contains(1) || !b.Contains(2) || !b.Contains(3) { - t.Errorf("RetainAll failed") - } - - // test RetainAll when b has more words than other - b.Clear() - b.Add(100) // maxWordInUse = 2 - other.Clear() - other.Add(2) // maxWordInUse = 1 - b.RetainAll(other) - if b.Contains(100) { - t.Errorf("RetainAll did not clear upper words") - } - - b.Clear() - b.AddAll(slices.Values([]int{2, 3})) - other.Clear() - other.AddAll(slices.Values([]int{2, 3, 4})) - if b.ContainsAll(other) { - t.Errorf("expected not to contain all") - } - - // test ContainsAll when other has more words than b - other.Clear() - other.AddAll(slices.Values([]int{2, 3, 100})) - if b.ContainsAll(other) { - t.Errorf("expected not to contain all") - } - // test ContainsAll when other has more words than b but empty upper words - other.Clear() - other.Add(2) - other.ensureSize(5) - other.maxWordInUse = 6 - if !b.ContainsAll(other) { - t.Errorf("expected to contain all even with empty upper words") - } - - other.Clear() - other.AddAll(slices.Values([]int{2, 3})) - if !other.ContainsAll(b) { - t.Errorf("ContainsAll failed") - } - - collected := slices.Collect(b.All()) - if !slices.Equal(collected, []int{2, 3}) { - t.Errorf("All iterator failed") - } - - // Test generic Collection branch (using arraylist.Wrap) - list := arraylist.Wrap([]int{2, 3, 4}) - b.Clear() - b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) - b.RemoveAll(list) - if b.Contains(2) || b.Contains(3) || b.Contains(4) || !b.Contains(1) || !b.Contains(5) { - t.Errorf("RemoveAll with generic collection failed") - } - - b.Clear() - b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) - b.RetainAll(list) - if b.Contains(1) || b.Contains(5) || !b.Contains(2) || !b.Contains(3) || !b.Contains(4) { - t.Errorf("RetainAll with generic collection failed") - } - - if !b.ContainsAll(arraylist.Wrap([]int{2, 3, 4})) { - t.Errorf("ContainsAll with generic collection should be true") - } - if b.ContainsAll(arraylist.Wrap([]int{2, 3, 4, 99})) { - t.Errorf("ContainsAll with generic collection should be false") - } - - // Test generic Set branch (using hashset.New) - hset := hashset.New[int]() - hset.Add(2) - hset.Add(3) - hset.Add(4) - - b.Clear() - b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) - b.RemoveAll(hset) - if b.Contains(2) || b.Contains(3) || b.Contains(4) || !b.Contains(1) || !b.Contains(5) { - t.Errorf("RemoveAll with Set failed") - } - - b.Clear() - b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) - b.RetainAll(hset) - if b.Contains(1) || b.Contains(5) || !b.Contains(2) || !b.Contains(3) || !b.Contains(4) { - t.Errorf("RetainAll with Set failed") - } - - // Test Size() lazy evaluation - b.Clear() - b.AddAll(slices.Values([]int{1, 2, 3})) - b.FlipRange(0, 5) // sets size to -1 - if b.Size() != 2 { // triggers recomputeSize - t.Errorf("Lazy size evaluation failed: expected 2, got %v", b.Size()) - } - - // Test Remove() panic - b.Clear() - func() { - defer func() { - if r := recover(); r == nil { - t.Errorf("Remove() did not panic on empty set") - } - }() - b.Remove() - }() - - // Test Remove() panic when size is broken - b.Clear() - b.size = 1 // break size - func() { - defer func() { - if r := recover(); r == nil { - t.Errorf("Remove() did not panic on broken size") - } - }() - b.Remove() - }() -} diff --git a/bitset/bitset_navigable_set_test.go b/bitset/bitset_navigable_set_test.go deleted file mode 100644 index 0024950..0000000 --- a/bitset/bitset_navigable_set_test.go +++ /dev/null @@ -1,268 +0,0 @@ -package bitset - -import ( - "slices" - "testing" - - linkedlist "github.com/lock14/collections/linkedlist" -) - -func TestBitSet_Size(t *testing.T) { - b := New() - if b.Size() != 0 { - t.Fatalf("expected 0 got %d", b.Size()) - } - b.Add(5) - b.Add(10) - if b.Size() != 2 { - t.Fatalf("expected 2 got %d", b.Size()) - } -} - -func TestBitSet_Remove(t *testing.T) { - b := New() - b.Add(5) - val := b.Remove() - if val != 5 { - t.Fatalf("expected 5, got %d", val) - } -} - -func TestBitSet_RetainAll(t *testing.T) { - b := New() - b.Add(1) - b.Add(2) - b.Add(3) - - other := linkedlist.New[int]() - other.Add(2) - other.Add(4) - - b.RetainAll(other) - if b.Size() != 1 || !b.Contains(2) { - t.Fatalf("expected only 2 to be retained, got size %d", b.Size()) - } -} - -func TestBitSet_ContainsAll(t *testing.T) { - b := New() - b.Add(1) - b.Add(2) - - other := linkedlist.New[int]() - other.Add(1) - other.Add(2) - - if !b.ContainsAll(other) { - t.Fatal("expected contains all to be true") - } - other.Add(3) - if b.ContainsAll(other) { - t.Fatal("expected contains all to be false") - } -} - -func TestBitSet_FirstLast(t *testing.T) { - b := New() - func() { - defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic on First() when empty") - } - }() - b.First() - }() - func() { - defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic on Last() when empty") - } - }() - b.Last() - }() - b.Add(5) - b.Add(10) - if v := b.First(); v != 5 { - t.Fatalf("expected 5, got %d", v) - } - if v := b.Last(); v != 10 { - t.Fatalf("expected 10, got %d", v) - } -} - -func TestBitSet_PollFirstLast(t *testing.T) { - b := New() - func() { - defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic on PollFirst() when empty") - } - }() - b.PollFirst() - }() - func() { - defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic on PollLast() when empty") - } - }() - b.PollLast() - }() - b.Add(5) - b.Add(10) - if v := b.PollFirst(); v != 5 { - t.Fatalf("expected 5, got %d", v) - } - if b.Contains(5) { - t.Fatal("expected 5 to be removed") - } - if v := b.PollLast(); v != 10 { - t.Fatalf("expected 10, got %d", v) - } - if b.Contains(10) { - t.Fatal("expected 10 to be removed") - } -} - -func TestBitSet_AddFirstLast(t *testing.T) { - b := New() - func() { - defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic on AddFirst") - } - }() - b.AddFirst(1) - }() - func() { - defer func() { - if r := recover(); r == nil { - t.Fatal("expected panic on AddLast") - } - }() - b.AddLast(1) - }() -} - -func TestBitSet_NavigableQueries(t *testing.T) { - b := New() - - // Test empty conditions - if _, ok := b.Lower(100); ok { - t.Fatal("expected lower to be false on empty") - } - if _, ok := b.Floor(100); ok { - t.Fatal("expected floor to be false on empty") - } - if _, ok := b.Ceiling(0); ok { - t.Fatal("expected ceiling to be false on empty") - } - if _, ok := b.Higher(0); ok { - t.Fatal("expected higher to be false on empty") - } - - b.Add(5) - b.Add(10) - b.Add(15) - - if v, ok := b.Lower(10); !ok || v != 5 { - t.Fatalf("expected 5, got %d", v) - } - if _, ok := b.Lower(5); ok { - t.Fatal("expected no lower than 5") - } - if v, ok := b.Lower(100); !ok || v != 15 { - t.Fatalf("expected 15, got %d", v) - } - - if v, ok := b.Floor(10); !ok || v != 10 { - t.Fatalf("expected 10, got %d", v) - } - if v, ok := b.Floor(12); !ok || v != 10 { - t.Fatalf("expected 10, got %d", v) - } - if v, ok := b.Floor(100); !ok || v != 15 { - t.Fatalf("expected 15, got %d", v) - } - - if v, ok := b.Ceiling(10); !ok || v != 10 { - t.Fatalf("expected 10, got %d", v) - } - if v, ok := b.Ceiling(12); !ok || v != 15 { - t.Fatalf("expected 15, got %d", v) - } - if v, ok := b.Ceiling(-10); !ok || v != 5 { - t.Fatalf("expected 5, got %d", v) - } - - if v, ok := b.Higher(10); !ok || v != 15 { - t.Fatalf("expected 15, got %d", v) - } - if _, ok := b.Higher(15); ok { - t.Fatal("expected no higher than 15") - } - if v, ok := b.Higher(-10); !ok || v != 5 { - t.Fatalf("expected 5, got %d", v) - } - - b.Add(63) - b.Add(64) - b.Add(127) - b.Add(128) - b.Lower(64) - b.Lower(128) - b.Floor(63) - b.Floor(127) - b.Ceiling(64) - b.Ceiling(128) - b.Higher(63) - b.Higher(127) -} - -func TestBitSet_Iterators(t *testing.T) { - b := New() - - // Test empty iterators - if len(slices.Collect(b.Backward())) != 0 { - t.Fatal("expected empty backward") - } - if len(slices.Collect(b.From(0))) != 0 { - t.Fatal("expected empty from") - } - if len(slices.Collect(b.To(100))) != 0 { - t.Fatal("expected empty to") - } - if len(slices.Collect(b.Between(0, 100))) != 0 { - t.Fatal("expected empty between") - } - - b.Add(5) - b.Add(10) - b.Add(15) - - back := slices.Collect(b.Backward()) - if !slices.Equal(back, []int{15, 10, 5}) { - t.Fatalf("expected [15 10 5], got %v", back) - } - - from := slices.Collect(b.From(10)) - if !slices.Equal(from, []int{10, 15}) { - t.Fatalf("expected [10 15], got %v", from) - } - - to := slices.Collect(b.To(15)) - if !slices.Equal(to, []int{5, 10}) { - t.Fatalf("expected [5 10], got %v", to) - } - - between := slices.Collect(b.Between(5, 15)) - if !slices.Equal(between, []int{5, 10}) { - t.Fatalf("expected [5 10], got %v", between) - } - - b.Add(63) - b.Add(64) - slices.Collect(b.From(64)) - slices.Collect(b.To(64)) - slices.Collect(b.Between(63, 65)) -} diff --git a/bitset/bitset_test.go b/bitset/bitset_test.go index 350f7e6..08598e9 100644 --- a/bitset/bitset_test.go +++ b/bitset/bitset_test.go @@ -5,6 +5,10 @@ import ( "fmt" "slices" "testing" + + "github.com/lock14/collections/arraylist" + "github.com/lock14/collections/hashset" + linkedlist "github.com/lock14/collections/linkedlist" ) // https://oeis.org/A000040 @@ -792,3 +796,613 @@ func TestBitSet_Iterators_Bounds(t *testing.T) { break } } + +func TestBitSet_MutableSetTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_MutableSet", + run: func(t *testing.T) { + + t.Parallel() + b := New(WithCapacity(100)) + b.Add(10) + b.Add(20) + + if !b.Contains(10) { + t.Errorf("expected to contain 10") + } + if !b.Contains(20) { + t.Errorf("expected to contain 20") + } + + b.RemoveElement(10) + if b.Contains(10) { + t.Errorf("expected 10 to be removed") + } + + if b.Empty() { + t.Errorf("expected not to be empty") + } + + b.RemoveElement(20) + if !b.Empty() { + t.Errorf("expected to be empty") + } + + b.Add(5) + b.Add(15) + val := b.Remove() + if val != 5 && val != 15 { + t.Errorf("Remove returned unexpected value: %v", val) + } + + b.Clear() + if !b.Empty() { + t.Errorf("expected empty after Clear") + } + + b.AddAll(slices.Values([]int{1, 2, 3})) + if !b.Contains(1) || !b.Contains(2) || !b.Contains(3) { + t.Errorf("AddAll failed") + } + + other := New(WithCapacity(100)) + other.Add(2) + other.Add(3) + other.Add(4) + + b.RemoveAll(other) + if b.Contains(2) || b.Contains(3) || !b.Contains(1) { + t.Errorf("RemoveAll failed") + } + + b.Clear() + b.AddAll(slices.Values([]int{1, 2, 3})) + b.RetainAll(other) // retains 2, 3 + if b.Contains(1) || !b.Contains(2) || !b.Contains(3) { + t.Errorf("RetainAll failed") + } + + // test RetainAll when b has more words than other + b.Clear() + b.Add(100) // maxWordInUse = 2 + other.Clear() + other.Add(2) // maxWordInUse = 1 + b.RetainAll(other) + if b.Contains(100) { + t.Errorf("RetainAll did not clear upper words") + } + + b.Clear() + b.AddAll(slices.Values([]int{2, 3})) + other.Clear() + other.AddAll(slices.Values([]int{2, 3, 4})) + if b.ContainsAll(other) { + t.Errorf("expected not to contain all") + } + + // test ContainsAll when other has more words than b + other.Clear() + other.AddAll(slices.Values([]int{2, 3, 100})) + if b.ContainsAll(other) { + t.Errorf("expected not to contain all") + } + // test ContainsAll when other has more words than b but empty upper words + other.Clear() + other.Add(2) + other.ensureSize(5) + other.maxWordInUse = 6 + if !b.ContainsAll(other) { + t.Errorf("expected to contain all even with empty upper words") + } + + other.Clear() + other.AddAll(slices.Values([]int{2, 3})) + if !other.ContainsAll(b) { + t.Errorf("ContainsAll failed") + } + + collected := slices.Collect(b.All()) + if !slices.Equal(collected, []int{2, 3}) { + t.Errorf("All iterator failed") + } + + // Test generic Collection branch (using arraylist.Wrap) + list := arraylist.Wrap([]int{2, 3, 4}) + b.Clear() + b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) + b.RemoveAll(list) + if b.Contains(2) || b.Contains(3) || b.Contains(4) || !b.Contains(1) || !b.Contains(5) { + t.Errorf("RemoveAll with generic collection failed") + } + + b.Clear() + b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) + b.RetainAll(list) + if b.Contains(1) || b.Contains(5) || !b.Contains(2) || !b.Contains(3) || !b.Contains(4) { + t.Errorf("RetainAll with generic collection failed") + } + + if !b.ContainsAll(arraylist.Wrap([]int{2, 3, 4})) { + t.Errorf("ContainsAll with generic collection should be true") + } + if b.ContainsAll(arraylist.Wrap([]int{2, 3, 4, 99})) { + t.Errorf("ContainsAll with generic collection should be false") + } + + // Test generic Set branch (using hashset.New) + hset := hashset.New[int]() + hset.Add(2) + hset.Add(3) + hset.Add(4) + + b.Clear() + b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) + b.RemoveAll(hset) + if b.Contains(2) || b.Contains(3) || b.Contains(4) || !b.Contains(1) || !b.Contains(5) { + t.Errorf("RemoveAll with Set failed") + } + + b.Clear() + b.AddAll(slices.Values([]int{1, 2, 3, 4, 5})) + b.RetainAll(hset) + if b.Contains(1) || b.Contains(5) || !b.Contains(2) || !b.Contains(3) || !b.Contains(4) { + t.Errorf("RetainAll with Set failed") + } + + // Test Size() lazy evaluation + b.Clear() + b.AddAll(slices.Values([]int{1, 2, 3})) + b.FlipRange(0, 5) // sets size to -1 + if b.Size() != 2 { // triggers recomputeSize + t.Errorf("Lazy size evaluation failed: expected 2, got %v", b.Size()) + } + + // Test Remove() panic + b.Clear() + func() { + defer func() { + if r := recover(); r == nil { + t.Errorf("Remove() did not panic on empty set") + } + }() + b.Remove() + }() + + // Test Remove() panic when size is broken + b.Clear() + b.size = 1 // break size + func() { + defer func() { + if r := recover(); r == nil { + t.Errorf("Remove() did not panic on broken size") + } + }() + b.Remove() + }() + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_SizeTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_Size", + run: func(t *testing.T) { + + b := New() + if b.Size() != 0 { + t.Fatalf("expected 0 got %d", b.Size()) + } + b.Add(5) + b.Add(10) + if b.Size() != 2 { + t.Fatalf("expected 2 got %d", b.Size()) + } + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_RemoveTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_Remove", + run: func(t *testing.T) { + + b := New() + b.Add(5) + val := b.Remove() + if val != 5 { + t.Fatalf("expected 5, got %d", val) + } + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_RetainAllTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_RetainAll", + run: func(t *testing.T) { + + b := New() + b.Add(1) + b.Add(2) + b.Add(3) + + other := linkedlist.New[int]() + other.Add(2) + other.Add(4) + + b.RetainAll(other) + if b.Size() != 1 || !b.Contains(2) { + t.Fatalf("expected only 2 to be retained, got size %d", b.Size()) + } + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_ContainsAllTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_ContainsAll", + run: func(t *testing.T) { + + b := New() + b.Add(1) + b.Add(2) + + other := linkedlist.New[int]() + other.Add(1) + other.Add(2) + + if !b.ContainsAll(other) { + t.Fatal("expected contains all to be true") + } + other.Add(3) + if b.ContainsAll(other) { + t.Fatal("expected contains all to be false") + } + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_FirstLastTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_FirstLast", + run: func(t *testing.T) { + + b := New() + func() { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on First() when empty") + } + }() + b.First() + }() + func() { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on Last() when empty") + } + }() + b.Last() + }() + b.Add(5) + b.Add(10) + if v := b.First(); v != 5 { + t.Fatalf("expected 5, got %d", v) + } + if v := b.Last(); v != 10 { + t.Fatalf("expected 10, got %d", v) + } + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_PollFirstLastTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_PollFirstLast", + run: func(t *testing.T) { + + b := New() + func() { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on PollFirst() when empty") + } + }() + b.PollFirst() + }() + func() { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on PollLast() when empty") + } + }() + b.PollLast() + }() + b.Add(5) + b.Add(10) + if v := b.PollFirst(); v != 5 { + t.Fatalf("expected 5, got %d", v) + } + if b.Contains(5) { + t.Fatal("expected 5 to be removed") + } + if v := b.PollLast(); v != 10 { + t.Fatalf("expected 10, got %d", v) + } + if b.Contains(10) { + t.Fatal("expected 10 to be removed") + } + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_AddFirstLastTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_AddFirstLast", + run: func(t *testing.T) { + + b := New() + func() { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on AddFirst") + } + }() + b.AddFirst(1) + }() + func() { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on AddLast") + } + }() + b.AddLast(1) + }() + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_NavigableQueriesTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_NavigableQueries", + run: func(t *testing.T) { + + b := New() + + // Test empty conditions + if _, ok := b.Lower(100); ok { + t.Fatal("expected lower to be false on empty") + } + if _, ok := b.Floor(100); ok { + t.Fatal("expected floor to be false on empty") + } + if _, ok := b.Ceiling(0); ok { + t.Fatal("expected ceiling to be false on empty") + } + if _, ok := b.Higher(0); ok { + t.Fatal("expected higher to be false on empty") + } + + b.Add(5) + b.Add(10) + b.Add(15) + + if v, ok := b.Lower(10); !ok || v != 5 { + t.Fatalf("expected 5, got %d", v) + } + if _, ok := b.Lower(5); ok { + t.Fatal("expected no lower than 5") + } + if v, ok := b.Lower(100); !ok || v != 15 { + t.Fatalf("expected 15, got %d", v) + } + + if v, ok := b.Floor(10); !ok || v != 10 { + t.Fatalf("expected 10, got %d", v) + } + if v, ok := b.Floor(12); !ok || v != 10 { + t.Fatalf("expected 10, got %d", v) + } + if v, ok := b.Floor(100); !ok || v != 15 { + t.Fatalf("expected 15, got %d", v) + } + + if v, ok := b.Ceiling(10); !ok || v != 10 { + t.Fatalf("expected 10, got %d", v) + } + if v, ok := b.Ceiling(12); !ok || v != 15 { + t.Fatalf("expected 15, got %d", v) + } + if v, ok := b.Ceiling(-10); !ok || v != 5 { + t.Fatalf("expected 5, got %d", v) + } + + if v, ok := b.Higher(10); !ok || v != 15 { + t.Fatalf("expected 15, got %d", v) + } + if _, ok := b.Higher(15); ok { + t.Fatal("expected no higher than 15") + } + if v, ok := b.Higher(-10); !ok || v != 5 { + t.Fatalf("expected 5, got %d", v) + } + + b.Add(63) + b.Add(64) + b.Add(127) + b.Add(128) + b.Lower(64) + b.Lower(128) + b.Floor(63) + b.Floor(127) + b.Ceiling(64) + b.Ceiling(128) + b.Higher(63) + b.Higher(127) + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} + +func TestBitSet_IteratorsTableDriven(t *testing.T) { + cases := []struct { + name string + run func(t *testing.T) + }{ + { + name: "TestBitSet_Iterators", + run: func(t *testing.T) { + + b := New() + + // Test empty iterators + if len(slices.Collect(b.Backward())) != 0 { + t.Fatal("expected empty backward") + } + if len(slices.Collect(b.From(0))) != 0 { + t.Fatal("expected empty from") + } + if len(slices.Collect(b.To(100))) != 0 { + t.Fatal("expected empty to") + } + if len(slices.Collect(b.Between(0, 100))) != 0 { + t.Fatal("expected empty between") + } + + b.Add(5) + b.Add(10) + b.Add(15) + + back := slices.Collect(b.Backward()) + if !slices.Equal(back, []int{15, 10, 5}) { + t.Fatalf("expected [15 10 5], got %v", back) + } + + from := slices.Collect(b.From(10)) + if !slices.Equal(from, []int{10, 15}) { + t.Fatalf("expected [10 15], got %v", from) + } + + to := slices.Collect(b.To(15)) + if !slices.Equal(to, []int{5, 10}) { + t.Fatalf("expected [5 10], got %v", to) + } + + between := slices.Collect(b.Between(5, 15)) + if !slices.Equal(between, []int{5, 10}) { + t.Fatalf("expected [5 10], got %v", between) + } + + b.Add(63) + b.Add(64) + slices.Collect(b.From(64)) + slices.Collect(b.To(64)) + slices.Collect(b.Between(63, 65)) + + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, tc.run) + } +} diff --git a/treemap/leak_test.go b/treemap/leak_test.go deleted file mode 100644 index 9d608d3..0000000 --- a/treemap/leak_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package treemap - -import ( - "runtime" - "sync/atomic" - "testing" - "time" -) - -type Dummy struct { - v int -} - -func TestTreeMap_MemoryLeak(t *testing.T) { - t.Skip("Flaky test") - tm := NewOrdered[int, *Dummy]() - - var collected int32 - - // Create a closure that creates the dummy object so that there are no - // local variables holding references to it in the main test function. - func() { - d := &Dummy{v: 42} - runtime.SetFinalizer(d, func(obj *Dummy) { - atomic.AddInt32(&collected, 1) - }) - - tm.Put(1, d) - }() - - // Remove the element. If there's a memory leak (e.g., trailing element in slice - // not being zeroed out), the pointer will remain in the underlying slice capacity, - // and the object won't be collected. - tm.Remove(1) - - // Force GC and wait for finalizer to run - for i := 0; i < 50; i++ { - runtime.GC() - if atomic.LoadInt32(&collected) > 0 { - break - } - time.Sleep(10 * time.Millisecond) - } - - // Check if it was collected - if atomic.LoadInt32(&collected) == 0 { - t.Error("Memory leak detected: removed element was not garbage collected") - } -} diff --git a/treemap/treemap_test.go b/treemap/treemap_test.go index bb97bd2..00b7814 100644 --- a/treemap/treemap_test.go +++ b/treemap/treemap_test.go @@ -4,8 +4,11 @@ import ( "fmt" "iter" "math/rand" + "runtime" "slices" + "sync/atomic" "testing" + "time" ) func TestTreeMap_Operations(t *testing.T) { @@ -495,3 +498,55 @@ func TestTreeMap_Iterators_Bounds(t *testing.T) { break } } + +type Dummy struct { + v int +} + +func TestTreeMap_MemoryLeak(t *testing.T) { + cases := []struct { + name string + }{ + {"remove should not leak memory"}, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Skip("Flaky test") + tm := NewOrdered[int, *Dummy]() + + var collected int32 + + // Create a closure that creates the dummy object so that there are no + // local variables holding references to it in the main test function. + func() { + d := &Dummy{v: 42} + runtime.SetFinalizer(d, func(obj *Dummy) { + atomic.AddInt32(&collected, 1) + }) + + tm.Put(1, d) + }() + + // Remove the element. If there's a memory leak (e.g., trailing element in slice + // not being zeroed out), the pointer will remain in the underlying slice capacity, + // and the object won't be collected. + tm.Remove(1) + + // Force GC and wait for finalizer to run + for i := 0; i < 50; i++ { + runtime.GC() + if atomic.LoadInt32(&collected) > 0 { + break + } + time.Sleep(10 * time.Millisecond) + } + + // Check if it was collected + if atomic.LoadInt32(&collected) == 0 { + t.Error("Memory leak detected: removed element was not garbage collected") + } + }) + } +}