Skip to content

Commit

Permalink
more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Dec 21, 2024
1 parent 94eba20 commit 1ac8eb0
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 170 deletions.
2 changes: 2 additions & 0 deletions gold_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type goldTableItem[V any] struct {
val V
}

/*
func (t *goldTable[V]) insert(pfx netip.Prefix, val V) {
pfx = pfx.Masked()
for i, ent := range *t {
Expand All @@ -30,6 +31,7 @@ func (t *goldTable[V]) insert(pfx netip.Prefix, val V) {
}
*t = append(*t, goldTableItem[V]{pfx, val})
}
*/

func (t *goldTable[V]) get(pfx netip.Prefix) (val V, ok bool) {
pfx = pfx.Masked()
Expand Down
26 changes: 26 additions & 0 deletions internal/bitset/bitset_iter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package bitset

import (
"fmt"
"slices"
"testing"
)

Expand Down Expand Up @@ -41,6 +42,31 @@ func TestAllBitSetIter(t *testing.T) {
}
}

func TestAllBitSetIterStop(t *testing.T) {
t.Parallel()

var b BitSet

for u := range uint(20) {
b = b.Set(u)
}

got := []uint{}
want := []uint{0, 1, 2, 3, 4}

// range over func
for u := range b.All() {
if u > 4 {
break
}
got = append(got, u)
}

if !slices.Equal(got, want) {
t.Fatalf("rangefunc with break condition, expected: %v, got: %v", want, got)
}
}

func TestAllBitSetCallback(t *testing.T) {
t.Parallel()
tc := []uint{0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 511}
Expand Down
58 changes: 58 additions & 0 deletions internal/bitset/bitset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,64 @@ func TestTest(t *testing.T) {
}
}

func TestFirstSet(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
set []uint
wantIdx uint
wantOk bool
}{
{
name: "null",
set: []uint{},
wantIdx: 0,
wantOk: false,
},
{
name: "zero",
set: []uint{0},
wantIdx: 0,
wantOk: true,
},
{
name: "1,5",
set: []uint{1, 5},
wantIdx: 1,
wantOk: true,
},
{
name: "5,7",
set: []uint{5, 7},
wantIdx: 5,
wantOk: true,
},
{
name: "2. word",
set: []uint{70, 777},
wantIdx: 70,
wantOk: true,
},
}

for _, tc := range testCases {
var b BitSet
for _, u := range tc.set {
b = b.Set(u)
}

idx, ok := b.FirstSet()

if ok != tc.wantOk {
t.Errorf("FirstSet, %s: got ok: %v, want: %v", tc.name, ok, tc.wantOk)
}

if idx != tc.wantIdx {
t.Errorf("FirstSet, %s: got idx: %d, want: %d", tc.name, idx, tc.wantIdx)
}
}
}

func TestNextSet(t *testing.T) {
t.Parallel()
testCases := []struct {
Expand Down
2 changes: 0 additions & 2 deletions overlaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,6 @@ func (n *node[V]) overlapsChildrenIn(o *node[V]) bool {
// make allot table with prefixes as bitsets, bitsets are precalculated
// just union the bitsets to one bitset (allot table) for all prefixes
// in this node

// gimmick, don't allocate, can't use bitset.New()
prefixRoutes := bitset.BitSet(make([]uint64, 8))

allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes))
Expand Down
188 changes: 188 additions & 0 deletions overlaps_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//
// some tests modified from github.com/tailscale/art
// for this implementation by:
//
// Copyright (c) 2024 Karl Gaissmaier
// SPDX-License-Identifier: MIT

package bart

import (
"net/netip"
"testing"
)

func TestRegressionOverlaps(t *testing.T) {
t.Run("overlaps_divergent_children_with_parent_route_entry", func(t *testing.T) {
t.Parallel()
t1, t2 := Table[int]{}, Table[int]{}

t1.Insert(mpp("128.0.0.0/2"), 1)
t1.Insert(mpp("99.173.128.0/17"), 1)
t1.Insert(mpp("219.150.142.0/23"), 1)
t1.Insert(mpp("164.148.190.250/31"), 1)
t1.Insert(mpp("48.136.229.233/32"), 1)

t2.Insert(mpp("217.32.0.0/11"), 1)
t2.Insert(mpp("38.176.0.0/12"), 1)
t2.Insert(mpp("106.16.0.0/13"), 1)
t2.Insert(mpp("164.85.192.0/23"), 1)
t2.Insert(mpp("225.71.164.112/31"), 1)

if !t1.Overlaps(&t2) {
t.Fatal("tables unexpectedly do not overlap")
}
})

t.Run("overlaps_parent_child_comparison_with_route_in_parent", func(t *testing.T) {
t.Parallel()
t1, t2 := Table[int]{}, Table[int]{}

t1.Insert(mpp("226.0.0.0/8"), 1)
t1.Insert(mpp("81.128.0.0/9"), 1)
t1.Insert(mpp("152.0.0.0/9"), 1)
t1.Insert(mpp("151.220.0.0/16"), 1)
t1.Insert(mpp("89.162.61.0/24"), 1)

t2.Insert(mpp("54.0.0.0/9"), 1)
t2.Insert(mpp("35.89.128.0/19"), 1)
t2.Insert(mpp("72.33.53.0/24"), 1)
t2.Insert(mpp("2.233.60.32/27"), 1)
t2.Insert(mpp("152.42.142.160/28"), 1)

if !t1.Overlaps(&t2) {
t.Fatal("tables unexpectedly do not overlap")
}
})
}

func TestOverlapsCompare(t *testing.T) {
t.Parallel()

// Empirically, between 5 and 6 routes per table results in ~50%
// of random pairs overlapping. Cool example of the birthday paradox!
const numEntries = 6

seen := map[bool]int{}
for range 10_000 {
pfxs := randomPrefixes(numEntries)
fast := Table[int]{}
gold := goldTable[int](pfxs)

for _, pfx := range pfxs {
fast.Insert(pfx.pfx, pfx.val)
}

inter := randomPrefixes(numEntries)
goldInter := goldTable[int](inter)
fastInter := Table[int]{}
for _, pfx := range inter {
fastInter.Insert(pfx.pfx, pfx.val)
}

gotGold := gold.overlaps(&goldInter)
gotFast := fast.Overlaps(&fastInter)

if gotGold != gotFast {
t.Fatalf("Overlaps(...) = %v, want %v\nTable1:\n%s\nTable:\n%v",
gotFast, gotGold, fast.String(), fastInter.String())
}

seen[gotFast]++
}
}

func TestOverlapsPrefixCompare(t *testing.T) {
t.Parallel()
pfxs := randomPrefixes(100_000)

fast := Table[int]{}
gold := goldTable[int](pfxs)

for _, pfx := range pfxs {
fast.Insert(pfx.pfx, pfx.val)
}

tests := randomPrefixes(10_000)
for _, tt := range tests {
gotGold := gold.overlapsPrefix(tt.pfx)
gotFast := fast.OverlapsPrefix(tt.pfx)
if gotGold != gotFast {
t.Fatalf("overlapsPrefix(%q) = %v, want %v", tt.pfx, gotFast, gotGold)
}
}
}

func TestOverlapsChildren(t *testing.T) {
t.Parallel()
pfxs1 := []netip.Prefix{
// pfxs
mpp("10.0.0.0/8"),
mpp("11.0.0.0/8"),
mpp("12.0.0.0/8"),
mpp("13.0.0.0/8"),
mpp("14.0.0.0/8"),
// chi5dren
mpp("10.100.0.0/17"),
mpp("11.100.0.0/17"),
mpp("12.100.0.0/17"),
mpp("13.100.0.0/17"),
mpp("14.100.0.0/17"),
mpp("15.100.0.0/17"),
mpp("16.100.0.0/17"),
mpp("17.100.0.0/17"),
mpp("18.100.0.0/17"),
mpp("19.100.0.0/17"),
mpp("20.100.0.0/17"),
mpp("21.100.0.0/17"),
mpp("22.100.0.0/17"),
mpp("23.100.0.0/17"),
mpp("24.100.0.0/17"),
mpp("25.100.0.0/17"),
mpp("26.100.0.0/17"),
mpp("27.100.0.0/17"),
mpp("28.100.0.0/17"),
}
pfxs2 := []netip.Prefix{
mpp("200.0.0.0/8"),
mpp("201.0.0.0/8"),
mpp("202.0.0.0/8"),
mpp("203.0.0.0/8"),
mpp("204.0.0.0/8"),
// children
mpp("201.200.0.0/18"),
mpp("202.200.0.0/18"),
mpp("203.200.0.0/18"),
mpp("204.200.0.0/18"),
mpp("205.200.0.0/18"),
mpp("206.200.0.0/18"),
mpp("207.200.0.0/18"),
mpp("208.200.0.0/18"),
mpp("209.200.0.0/18"),
mpp("210.200.0.0/18"),
mpp("211.200.0.0/18"),
mpp("212.200.0.0/18"),
mpp("213.200.0.0/18"),
mpp("214.200.0.0/18"),
mpp("215.200.0.0/18"),
mpp("216.200.0.0/18"),
mpp("217.200.0.0/18"),
mpp("218.200.0.0/18"),
mpp("219.200.0.0/18"),
}

tbl1 := new(Table[string])
for _, pfx := range pfxs1 {
tbl1.Insert(pfx, pfx.String())
}

tbl2 := new(Table[string])
for _, pfx := range pfxs2 {
tbl2.Insert(pfx, pfx.String())
}
if tbl1.Overlaps(tbl2) {
t.Fatal("tables unexpectedly do overlap")
}
}
Loading

0 comments on commit 1ac8eb0

Please sign in to comment.