package bbolt import ( "testing" "unsafe" "go.etcd.io/bbolt/internal/common" ) // Ensure that a node can insert a key/value. func TestNode_put(t *testing.T) { m := &common.Meta{} m.SetPgid(1) n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{meta: m}}} n.put([]byte("baz"), []byte("baz"), []byte("2"), 0, 0) n.put([]byte("foo"), []byte("foo"), []byte("0"), 0, 0) n.put([]byte("bar"), []byte("bar"), []byte("1"), 0, 0) n.put([]byte("foo"), []byte("foo"), []byte("3"), 0, common.LeafPageFlag) if len(n.inodes) != 3 { t.Fatalf("exp=3; got=%d", len(n.inodes)) } if k, v := n.inodes[0].Key(), n.inodes[0].Value(); string(k) != "bar" || string(v) != "1" { t.Fatalf("exp=; got=<%s,%s>", k, v) } if k, v := n.inodes[1].Key(), n.inodes[1].Value(); string(k) != "baz" || string(v) != "2" { t.Fatalf("exp=; got=<%s,%s>", k, v) } if k, v := n.inodes[2].Key(), n.inodes[2].Value(); string(k) != "foo" || string(v) != "3" { t.Fatalf("exp=; got=<%s,%s>", k, v) } if n.inodes[2].Flags() != uint32(common.LeafPageFlag) { t.Fatalf("not a leaf: %d", n.inodes[2].Flags()) } } // Ensure that a node can deserialize from a leaf page. func TestNode_read_LeafPage(t *testing.T) { // Create a page. var buf [4096]byte page := (*common.Page)(unsafe.Pointer(&buf[0])) page.SetFlags(common.LeafPageFlag) page.SetCount(2) // Insert 2 elements at the beginning. sizeof(leafPageElement) == 16 nodes := page.LeafPageElements() //nodes := (*[3]leafPageElement)(unsafe.Pointer(uintptr(unsafe.Pointer(page)) + unsafe.Sizeof(*page))) nodes[0] = *common.NewLeafPageElement(0, 32, 3, 4) // pos = sizeof(leafPageElement) * 2 nodes[1] = *common.NewLeafPageElement(0, 23, 10, 3) // pos = sizeof(leafPageElement) + 3 + 4 // Write data for the nodes at the end. const s = "barfoozhelloworldbye" data := common.UnsafeByteSlice(unsafe.Pointer(uintptr(unsafe.Pointer(page))+unsafe.Sizeof(*page)+common.LeafPageElementSize*2), 0, 0, len(s)) copy(data, s) // Deserialize page into a leaf. n := &node{} n.read(page) // Check that there are two inodes with correct data. if !n.isLeaf { t.Fatal("expected leaf") } if len(n.inodes) != 2 { t.Fatalf("exp=2; got=%d", len(n.inodes)) } if k, v := n.inodes[0].Key(), n.inodes[0].Value(); string(k) != "bar" || string(v) != "fooz" { t.Fatalf("exp=; got=<%s,%s>", k, v) } if k, v := n.inodes[1].Key(), n.inodes[1].Value(); string(k) != "helloworld" || string(v) != "bye" { t.Fatalf("exp=; got=<%s,%s>", k, v) } } // Ensure that a node can serialize into a leaf page. func TestNode_write_LeafPage(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) n := &node{isLeaf: true, inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("susy"), []byte("susy"), []byte("que"), 0, 0) n.put([]byte("ricki"), []byte("ricki"), []byte("lake"), 0, 0) n.put([]byte("john"), []byte("john"), []byte("johnson"), 0, 0) // Write it to a page. var buf [4096]byte p := (*common.Page)(unsafe.Pointer(&buf[0])) n.write(p) // Read the page back in. n2 := &node{} n2.read(p) // Check that the two pages are the same. if len(n2.inodes) != 3 { t.Fatalf("exp=3; got=%d", len(n2.inodes)) } if k, v := n2.inodes[0].Key(), n2.inodes[0].Value(); string(k) != "john" || string(v) != "johnson" { t.Fatalf("exp=; got=<%s,%s>", k, v) } if k, v := n2.inodes[1].Key(), n2.inodes[1].Value(); string(k) != "ricki" || string(v) != "lake" { t.Fatalf("exp=; got=<%s,%s>", k, v) } if k, v := n2.inodes[2].Key(), n2.inodes[2].Value(); string(k) != "susy" || string(v) != "que" { t.Fatalf("exp=; got=<%s,%s>", k, v) } } // Ensure that a node can split into appropriate subgroups. func TestNode_split(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0, 0) // Split between 2 & 3. n.split(100) var parent = n.parent if len(parent.children) != 2 { t.Fatalf("exp=2; got=%d", len(parent.children)) } if len(parent.children[0].inodes) != 2 { t.Fatalf("exp=2; got=%d", len(parent.children[0].inodes)) } if len(parent.children[1].inodes) != 3 { t.Fatalf("exp=3; got=%d", len(parent.children[1].inodes)) } } // Ensure that a page with the minimum number of inodes just returns a single node. func TestNode_split_MinKeys(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) // Split. n.split(20) if n.parent != nil { t.Fatalf("expected nil parent") } } // Ensure that a node that has keys that all fit on a page just returns one leaf. func TestNode_split_SinglePage(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0, 0) // Split. n.split(4096) if n.parent != nil { t.Fatalf("expected nil parent") } }