github.com/bhojpur/cache@v0.0.4/pkg/memory/node_test.go (about) 1 package memory 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 import ( 24 "testing" 25 "unsafe" 26 ) 27 28 // Ensure that a node can insert a key/value. 29 func TestNode_put(t *testing.T) { 30 n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}} 31 n.put([]byte("baz"), []byte("baz"), []byte("2"), 0, 0) 32 n.put([]byte("foo"), []byte("foo"), []byte("0"), 0, 0) 33 n.put([]byte("bar"), []byte("bar"), []byte("1"), 0, 0) 34 n.put([]byte("foo"), []byte("foo"), []byte("3"), 0, leafPageFlag) 35 36 if len(n.inodes) != 3 { 37 t.Fatalf("exp=3; got=%d", len(n.inodes)) 38 } 39 if k, v := n.inodes[0].key, n.inodes[0].value; string(k) != "bar" || string(v) != "1" { 40 t.Fatalf("exp=<bar,1>; got=<%s,%s>", k, v) 41 } 42 if k, v := n.inodes[1].key, n.inodes[1].value; string(k) != "baz" || string(v) != "2" { 43 t.Fatalf("exp=<baz,2>; got=<%s,%s>", k, v) 44 } 45 if k, v := n.inodes[2].key, n.inodes[2].value; string(k) != "foo" || string(v) != "3" { 46 t.Fatalf("exp=<foo,3>; got=<%s,%s>", k, v) 47 } 48 if n.inodes[2].flags != uint32(leafPageFlag) { 49 t.Fatalf("not a leaf: %d", n.inodes[2].flags) 50 } 51 } 52 53 // Ensure that a node can deserialize from a leaf page. 54 func TestNode_read_LeafPage(t *testing.T) { 55 // Create a page. 56 var buf [4096]byte 57 page := (*page)(unsafe.Pointer(&buf[0])) 58 page.flags = leafPageFlag 59 page.count = 2 60 61 // Insert 2 elements at the beginning. sizeof(leafPageElement) == 16 62 nodes := (*[3]leafPageElement)(unsafe.Pointer(&page.ptr)) 63 nodes[0] = leafPageElement{flags: 0, pos: 32, ksize: 3, vsize: 4} // pos = sizeof(leafPageElement) * 2 64 nodes[1] = leafPageElement{flags: 0, pos: 23, ksize: 10, vsize: 3} // pos = sizeof(leafPageElement) + 3 + 4 65 66 // Write data for the nodes at the end. 67 data := (*[4096]byte)(unsafe.Pointer(&nodes[2])) 68 copy(data[:], []byte("barfooz")) 69 copy(data[7:], []byte("helloworldbye")) 70 71 // Deserialize page into a leaf. 72 n := &node{} 73 n.read(page) 74 75 // Check that there are two inodes with correct data. 76 if !n.isLeaf { 77 t.Fatal("expected leaf") 78 } 79 if len(n.inodes) != 2 { 80 t.Fatalf("exp=2; got=%d", len(n.inodes)) 81 } 82 if k, v := n.inodes[0].key, n.inodes[0].value; string(k) != "bar" || string(v) != "fooz" { 83 t.Fatalf("exp=<bar,fooz>; got=<%s,%s>", k, v) 84 } 85 if k, v := n.inodes[1].key, n.inodes[1].value; string(k) != "helloworld" || string(v) != "bye" { 86 t.Fatalf("exp=<helloworld,bye>; got=<%s,%s>", k, v) 87 } 88 } 89 90 // Ensure that a node can serialize into a leaf page. 91 func TestNode_write_LeafPage(t *testing.T) { 92 // Create a node. 93 n := &node{isLeaf: true, inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 94 n.put([]byte("susy"), []byte("susy"), []byte("que"), 0, 0) 95 n.put([]byte("ricki"), []byte("ricki"), []byte("lake"), 0, 0) 96 n.put([]byte("john"), []byte("john"), []byte("johnson"), 0, 0) 97 98 // Write it to a page. 99 var buf [4096]byte 100 p := (*page)(unsafe.Pointer(&buf[0])) 101 n.write(p) 102 103 // Read the page back in. 104 n2 := &node{} 105 n2.read(p) 106 107 // Check that the two pages are the same. 108 if len(n2.inodes) != 3 { 109 t.Fatalf("exp=3; got=%d", len(n2.inodes)) 110 } 111 if k, v := n2.inodes[0].key, n2.inodes[0].value; string(k) != "john" || string(v) != "johnson" { 112 t.Fatalf("exp=<john,johnson>; got=<%s,%s>", k, v) 113 } 114 if k, v := n2.inodes[1].key, n2.inodes[1].value; string(k) != "ricki" || string(v) != "lake" { 115 t.Fatalf("exp=<ricki,lake>; got=<%s,%s>", k, v) 116 } 117 if k, v := n2.inodes[2].key, n2.inodes[2].value; string(k) != "susy" || string(v) != "que" { 118 t.Fatalf("exp=<susy,que>; got=<%s,%s>", k, v) 119 } 120 } 121 122 // Ensure that a node can split into appropriate subgroups. 123 func TestNode_split(t *testing.T) { 124 // Create a node. 125 n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 126 n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) 127 n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) 128 n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) 129 n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0, 0) 130 n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0, 0) 131 132 // Split between 2 & 3. 133 n.split(100) 134 135 var parent = n.parent 136 if len(parent.children) != 2 { 137 t.Fatalf("exp=2; got=%d", len(parent.children)) 138 } 139 if len(parent.children[0].inodes) != 2 { 140 t.Fatalf("exp=2; got=%d", len(parent.children[0].inodes)) 141 } 142 if len(parent.children[1].inodes) != 3 { 143 t.Fatalf("exp=3; got=%d", len(parent.children[1].inodes)) 144 } 145 } 146 147 // Ensure that a page with the minimum number of inodes just returns a single node. 148 func TestNode_split_MinKeys(t *testing.T) { 149 // Create a node. 150 n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 151 n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) 152 n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) 153 154 // Split. 155 n.split(20) 156 if n.parent != nil { 157 t.Fatalf("expected nil parent") 158 } 159 } 160 161 // Ensure that a node that has keys that all fit on a page just returns one leaf. 162 func TestNode_split_SinglePage(t *testing.T) { 163 // Create a node. 164 n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 165 n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) 166 n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) 167 n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) 168 n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0, 0) 169 n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0, 0) 170 171 // Split. 172 n.split(4096) 173 if n.parent != nil { 174 t.Fatalf("expected nil parent") 175 } 176 }