234 lines
4.9 KiB
Go
234 lines
4.9 KiB
Go
package ipalloc
|
|
|
|
import (
|
|
"log"
|
|
"net/netip"
|
|
"testing"
|
|
)
|
|
|
|
func TestSplitPrefix(t *testing.T) {
|
|
tests := []struct{ in, lo, hi string }{
|
|
{"0.0.0.0/0", "0.0.0.0/1", "128.0.0.0/1"},
|
|
{"0.0.0.0/29", "0.0.0.0/30", "0.0.0.4/30"},
|
|
{"0.0.0.0/31", "0.0.0.0/32", "0.0.0.1/32"},
|
|
{"0.0.0.1/0", "0.0.0.0/1", "128.0.0.0/1"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
lo, hi := splitPrefix(netip.MustParsePrefix(test.in))
|
|
if lo != netip.MustParsePrefix(test.lo) || hi != netip.MustParsePrefix(test.hi) {
|
|
t.Fatal(test.in, lo, hi)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTreeAllocSmall(t *testing.T) {
|
|
tests := []struct {
|
|
bits int
|
|
best string
|
|
}{
|
|
{32, "0.0.0.0/32"},
|
|
{32, "0.0.0.1/32"},
|
|
{31, "0.0.0.2/31"},
|
|
{32, "0.0.0.4/32"},
|
|
}
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/28")}
|
|
|
|
for _, test := range tests {
|
|
//t.Log("searching for", test.bits)
|
|
best := root.Alloc(test.bits)
|
|
if best == nil {
|
|
log.Print(root.Dot())
|
|
t.Fatal(root, test.bits)
|
|
}
|
|
//t.Log(test, "->", best.prefix)
|
|
if best.prefix != netip.MustParsePrefix(test.best) {
|
|
log.Print(root.Dot())
|
|
t.Fatal(root, test.bits, best.prefix.String())
|
|
}
|
|
}
|
|
}
|
|
func TestTreeAlloc(t *testing.T) {
|
|
tests := []struct {
|
|
bits int
|
|
best string
|
|
}{
|
|
{32, "0.0.0.0/32"},
|
|
{32, "0.0.0.1/32"},
|
|
{1, "128.0.0.0/1"},
|
|
{2, "64.0.0.0/2"},
|
|
{31, "0.0.0.2/31"},
|
|
{32, "0.0.0.4/32"},
|
|
}
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/0")}
|
|
|
|
for _, test := range tests {
|
|
best := root.Alloc(test.bits)
|
|
if best == nil {
|
|
t.Fatal(root, test.bits)
|
|
}
|
|
if best.prefix != netip.MustParsePrefix(test.best) {
|
|
log.Print(root.Dot())
|
|
t.Fatal(root, test.bits, best.prefix.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTreeDealloc(t *testing.T) {
|
|
tests := []struct {
|
|
bits int
|
|
best string
|
|
}{
|
|
{32, "0.0.0.0/32"},
|
|
{32, "0.0.0.1/32"},
|
|
{1, "128.0.0.0/1"},
|
|
{2, "64.0.0.0/2"},
|
|
{31, "0.0.0.2/31"},
|
|
{32, "0.0.0.4/32"},
|
|
}
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/0")}
|
|
|
|
for _, test := range tests {
|
|
best := root.Alloc(test.bits)
|
|
if best == nil {
|
|
t.Fatal(root, test.bits)
|
|
}
|
|
if best.prefix != netip.MustParsePrefix(test.best) {
|
|
t.Fatal(root, test.bits, best.prefix.String())
|
|
}
|
|
}
|
|
|
|
deallocs := []string{
|
|
"0.0.0.0/32",
|
|
"0.0.0.1/32",
|
|
"128.0.0.0/1",
|
|
"64.0.0.0/2",
|
|
"0.0.0.2/31",
|
|
"0.0.0.4/32",
|
|
}
|
|
|
|
for _, test := range deallocs {
|
|
if err := root.Dealloc(netip.MustParsePrefix(test)); err != nil {
|
|
t.Fatal(test, err)
|
|
}
|
|
}
|
|
|
|
if !root.Leaf() {
|
|
root.Walk(func(t *tree) bool {
|
|
log.Println(t.prefix, t.Leaf())
|
|
return true
|
|
})
|
|
t.Fatalf("root not empty: %#v", root)
|
|
}
|
|
|
|
first := root.Alloc(2).prefix
|
|
root.Alloc(2)
|
|
root.Dealloc(first)
|
|
if first != root.Alloc(2).prefix {
|
|
root.Walk(func(t *tree) bool {
|
|
log.Println(t.prefix, t.Leaf())
|
|
return true
|
|
})
|
|
t.Fatal(first)
|
|
}
|
|
}
|
|
func TestFindBestPrefix(t *testing.T) {
|
|
tests := []struct {
|
|
bits int
|
|
best string
|
|
}{
|
|
{32, "0.0.0.0/32"},
|
|
{32, "0.0.0.1/32"},
|
|
{1, "128.0.0.0/1"},
|
|
{2, "64.0.0.0/2"},
|
|
{31, "0.0.0.2/31"},
|
|
{32, "0.0.0.4/32"},
|
|
}
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/0")}
|
|
|
|
for _, test := range tests {
|
|
best := root.Alloc(test.bits)
|
|
if best == nil {
|
|
t.Fatal(root, test.bits)
|
|
}
|
|
if best.prefix != netip.MustParsePrefix(test.best) {
|
|
t.Fatal(root, test.bits, best.prefix.String())
|
|
}
|
|
}
|
|
|
|
best := [][2]string{
|
|
{"128.0.0.0/32", "128.0.0.0/1"},
|
|
{"128.0.0.1/32", "128.0.0.0/1"},
|
|
}
|
|
|
|
for _, test := range best {
|
|
best := root.FindBestPrefix(netip.MustParsePrefix(test[0]))
|
|
want := netip.MustParsePrefix(test[1])
|
|
|
|
if best.prefix != want {
|
|
t.Fatal(best, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInsert(t *testing.T) {
|
|
tests := []string{
|
|
"0.0.0.1/32",
|
|
"0.0.0.0/32",
|
|
"128.0.0.0/1",
|
|
"64.0.0.0/2",
|
|
"0.0.0.4/32",
|
|
"0.0.0.2/31",
|
|
}
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/0")}
|
|
|
|
for _, test := range tests {
|
|
p := netip.MustParsePrefix(test)
|
|
insert := root.Insert(p)
|
|
if insert == nil {
|
|
t.Fatal(root, test)
|
|
}
|
|
match := root.Find(p)
|
|
if match.prefix != p {
|
|
t.Fatal("unable to find", p, "in", root)
|
|
}
|
|
}
|
|
|
|
for _, test := range tests {
|
|
p := netip.MustParsePrefix(test)
|
|
insert := root.Insert(p)
|
|
if insert != nil {
|
|
t.Fatal(root, test)
|
|
}
|
|
}
|
|
}
|
|
|
|
func expectAlloc(t *testing.T, root *tree, bits int, expect string) {
|
|
if alloc := root.Alloc(bits); alloc.prefix != netip.MustParsePrefix(expect) {
|
|
t.Logf("allocating %d bits failed: got %s, want %s", bits, alloc.prefix, expect)
|
|
t.Log("graph:\n", root.Dot())
|
|
t.Fatal()
|
|
|
|
}
|
|
}
|
|
func TestAllocDealloc(t *testing.T) {
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/30")}
|
|
expectAlloc(t, root, 31, "0.0.0.0/31")
|
|
expectAlloc(t, root, 32, "0.0.0.2/32")
|
|
if err := root.Dealloc(netip.MustParsePrefix("0.0.0.0/31")); err != nil {
|
|
t.Fatalf("%s", err)
|
|
}
|
|
expectAlloc(t, root, 32, "0.0.0.3/32")
|
|
expectAlloc(t, root, 32, "0.0.0.0/32")
|
|
expectAlloc(t, root, 32, "0.0.0.1/32")
|
|
}
|
|
|
|
func BenchmarkFill(b *testing.B) {
|
|
root := &tree{prefix: netip.MustParsePrefix("0.0.0.0/16")}
|
|
for {
|
|
if alloc := root.Alloc(32); alloc == nil {
|
|
break
|
|
}
|
|
}
|
|
}
|