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  }