github.com/bhojpur/cache@v0.0.4/pkg/memory/cursor_test.go (about)

     1  package memory_test
     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  	"bytes"
    25  	"encoding/binary"
    26  	"fmt"
    27  	"log"
    28  	"os"
    29  	"reflect"
    30  	"sort"
    31  	"testing"
    32  	"testing/quick"
    33  
    34  	memcache "github.com/bhojpur/cache/pkg/memory"
    35  )
    36  
    37  // Ensure that a cursor can return a reference to the bucket that created it.
    38  func TestCursor_Bucket(t *testing.T) {
    39  	db := MustOpenDB()
    40  	defer db.MustClose()
    41  	if err := db.Update(func(tx *memcache.Tx) error {
    42  		b, err := tx.CreateBucket([]byte("widgets"))
    43  		if err != nil {
    44  			t.Fatal(err)
    45  		}
    46  		if cb := b.Cursor().Bucket(); !reflect.DeepEqual(cb, b) {
    47  			t.Fatal("cursor bucket mismatch")
    48  		}
    49  		return nil
    50  	}); err != nil {
    51  		t.Fatal(err)
    52  	}
    53  }
    54  
    55  // Ensure that a Tx cursor can seek to the appropriate keys.
    56  func TestCursor_Seek(t *testing.T) {
    57  	db := MustOpenDB()
    58  	defer db.MustClose()
    59  	if err := db.Update(func(tx *memcache.Tx) error {
    60  		b, err := tx.CreateBucket([]byte("widgets"))
    61  		if err != nil {
    62  			t.Fatal(err)
    63  		}
    64  		if err := b.Put([]byte("foo"), []byte("0001")); err != nil {
    65  			t.Fatal(err)
    66  		}
    67  		if err := b.Put([]byte("bar"), []byte("0002")); err != nil {
    68  			t.Fatal(err)
    69  		}
    70  		if err := b.Put([]byte("baz"), []byte("0003")); err != nil {
    71  			t.Fatal(err)
    72  		}
    73  
    74  		if _, err := b.CreateBucket([]byte("bkt")); err != nil {
    75  			t.Fatal(err)
    76  		}
    77  		return nil
    78  	}); err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	if err := db.View(func(tx *memcache.Tx) error {
    83  		c := tx.Bucket([]byte("widgets")).Cursor()
    84  
    85  		// Exact match should go to the key.
    86  		if k, v := c.Seek([]byte("bar")); !bytes.Equal(k, []byte("bar")) {
    87  			t.Fatalf("unexpected key: %v", k)
    88  		} else if !bytes.Equal(v, []byte("0002")) {
    89  			t.Fatalf("unexpected value: %v", v)
    90  		}
    91  
    92  		// Inexact match should go to the next key.
    93  		if k, v := c.Seek([]byte("bas")); !bytes.Equal(k, []byte("baz")) {
    94  			t.Fatalf("unexpected key: %v", k)
    95  		} else if !bytes.Equal(v, []byte("0003")) {
    96  			t.Fatalf("unexpected value: %v", v)
    97  		}
    98  
    99  		// Low key should go to the first key.
   100  		if k, v := c.Seek([]byte("")); !bytes.Equal(k, []byte("bar")) {
   101  			t.Fatalf("unexpected key: %v", k)
   102  		} else if !bytes.Equal(v, []byte("0002")) {
   103  			t.Fatalf("unexpected value: %v", v)
   104  		}
   105  
   106  		// High key should return no key.
   107  		if k, v := c.Seek([]byte("zzz")); k != nil {
   108  			t.Fatalf("expected nil key: %v", k)
   109  		} else if v != nil {
   110  			t.Fatalf("expected nil value: %v", v)
   111  		}
   112  
   113  		// Buckets should return their key but no value.
   114  		if k, v := c.Seek([]byte("bkt")); !bytes.Equal(k, []byte("bkt")) {
   115  			t.Fatalf("unexpected key: %v", k)
   116  		} else if v != nil {
   117  			t.Fatalf("expected nil value: %v", v)
   118  		}
   119  
   120  		return nil
   121  	}); err != nil {
   122  		t.Fatal(err)
   123  	}
   124  }
   125  
   126  func TestCursor_Delete(t *testing.T) {
   127  	db := MustOpenDB()
   128  	defer db.MustClose()
   129  
   130  	const count = 1000
   131  
   132  	// Insert every other key between 0 and $count.
   133  	if err := db.Update(func(tx *memcache.Tx) error {
   134  		b, err := tx.CreateBucket([]byte("widgets"))
   135  		if err != nil {
   136  			t.Fatal(err)
   137  		}
   138  		for i := 0; i < count; i += 1 {
   139  			k := make([]byte, 8)
   140  			binary.BigEndian.PutUint64(k, uint64(i))
   141  			if err := b.Put(k, make([]byte, 100)); err != nil {
   142  				t.Fatal(err)
   143  			}
   144  		}
   145  		if _, err := b.CreateBucket([]byte("sub")); err != nil {
   146  			t.Fatal(err)
   147  		}
   148  		return nil
   149  	}); err != nil {
   150  		t.Fatal(err)
   151  	}
   152  
   153  	if err := db.Update(func(tx *memcache.Tx) error {
   154  		c := tx.Bucket([]byte("widgets")).Cursor()
   155  		bound := make([]byte, 8)
   156  		binary.BigEndian.PutUint64(bound, uint64(count/2))
   157  		for key, _ := c.First(); bytes.Compare(key, bound) < 0; key, _ = c.Next() {
   158  			if err := c.Delete(); err != nil {
   159  				t.Fatal(err)
   160  			}
   161  		}
   162  
   163  		c.Seek([]byte("sub"))
   164  		if err := c.Delete(); err != memcache.ErrIncompatibleValue {
   165  			t.Fatalf("unexpected error: %s", err)
   166  		}
   167  
   168  		return nil
   169  	}); err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	if err := db.View(func(tx *memcache.Tx) error {
   174  		stats := tx.Bucket([]byte("widgets")).Stats()
   175  		if stats.KeyN != count/2+1 {
   176  			t.Fatalf("unexpected KeyN: %d", stats.KeyN)
   177  		}
   178  		return nil
   179  	}); err != nil {
   180  		t.Fatal(err)
   181  	}
   182  }
   183  
   184  // Ensure that a Tx cursor can seek to the appropriate keys when there are a
   185  // large number of keys. This test also checks that seek will always move
   186  // forward to the next key.
   187  func TestCursor_Seek_Large(t *testing.T) {
   188  	db := MustOpenDB()
   189  	defer db.MustClose()
   190  
   191  	var count = 10000
   192  
   193  	// Insert every other key between 0 and $count.
   194  	if err := db.Update(func(tx *memcache.Tx) error {
   195  		b, err := tx.CreateBucket([]byte("widgets"))
   196  		if err != nil {
   197  			t.Fatal(err)
   198  		}
   199  
   200  		for i := 0; i < count; i += 100 {
   201  			for j := i; j < i+100; j += 2 {
   202  				k := make([]byte, 8)
   203  				binary.BigEndian.PutUint64(k, uint64(j))
   204  				if err := b.Put(k, make([]byte, 100)); err != nil {
   205  					t.Fatal(err)
   206  				}
   207  			}
   208  		}
   209  		return nil
   210  	}); err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	if err := db.View(func(tx *memcache.Tx) error {
   215  		c := tx.Bucket([]byte("widgets")).Cursor()
   216  		for i := 0; i < count; i++ {
   217  			seek := make([]byte, 8)
   218  			binary.BigEndian.PutUint64(seek, uint64(i))
   219  
   220  			k, _ := c.Seek(seek)
   221  
   222  			// The last seek is beyond the end of the the range so
   223  			// it should return nil.
   224  			if i == count-1 {
   225  				if k != nil {
   226  					t.Fatal("expected nil key")
   227  				}
   228  				continue
   229  			}
   230  
   231  			// Otherwise we should seek to the exact key or the next key.
   232  			num := binary.BigEndian.Uint64(k)
   233  			if i%2 == 0 {
   234  				if num != uint64(i) {
   235  					t.Fatalf("unexpected num: %d", num)
   236  				}
   237  			} else {
   238  				if num != uint64(i+1) {
   239  					t.Fatalf("unexpected num: %d", num)
   240  				}
   241  			}
   242  		}
   243  
   244  		return nil
   245  	}); err != nil {
   246  		t.Fatal(err)
   247  	}
   248  }
   249  
   250  // Ensure that a cursor can iterate over an empty bucket without error.
   251  func TestCursor_EmptyBucket(t *testing.T) {
   252  	db := MustOpenDB()
   253  	defer db.MustClose()
   254  	if err := db.Update(func(tx *memcache.Tx) error {
   255  		_, err := tx.CreateBucket([]byte("widgets"))
   256  		return err
   257  	}); err != nil {
   258  		t.Fatal(err)
   259  	}
   260  
   261  	if err := db.View(func(tx *memcache.Tx) error {
   262  		c := tx.Bucket([]byte("widgets")).Cursor()
   263  		k, v := c.First()
   264  		if k != nil {
   265  			t.Fatalf("unexpected key: %v", k)
   266  		} else if v != nil {
   267  			t.Fatalf("unexpected value: %v", v)
   268  		}
   269  		return nil
   270  	}); err != nil {
   271  		t.Fatal(err)
   272  	}
   273  }
   274  
   275  // Ensure that a Tx cursor can reverse iterate over an empty bucket without error.
   276  func TestCursor_EmptyBucketReverse(t *testing.T) {
   277  	db := MustOpenDB()
   278  	defer db.MustClose()
   279  
   280  	if err := db.Update(func(tx *memcache.Tx) error {
   281  		_, err := tx.CreateBucket([]byte("widgets"))
   282  		return err
   283  	}); err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	if err := db.View(func(tx *memcache.Tx) error {
   287  		c := tx.Bucket([]byte("widgets")).Cursor()
   288  		k, v := c.Last()
   289  		if k != nil {
   290  			t.Fatalf("unexpected key: %v", k)
   291  		} else if v != nil {
   292  			t.Fatalf("unexpected value: %v", v)
   293  		}
   294  		return nil
   295  	}); err != nil {
   296  		t.Fatal(err)
   297  	}
   298  }
   299  
   300  // Ensure that a Tx cursor can iterate over a single root with a couple elements.
   301  func TestCursor_Iterate_Leaf(t *testing.T) {
   302  	db := MustOpenDB()
   303  	defer db.MustClose()
   304  
   305  	if err := db.Update(func(tx *memcache.Tx) error {
   306  		b, err := tx.CreateBucket([]byte("widgets"))
   307  		if err != nil {
   308  			t.Fatal(err)
   309  		}
   310  		if err := b.Put([]byte("baz"), []byte{}); err != nil {
   311  			t.Fatal(err)
   312  		}
   313  		if err := b.Put([]byte("foo"), []byte{0}); err != nil {
   314  			t.Fatal(err)
   315  		}
   316  		if err := b.Put([]byte("bar"), []byte{1}); err != nil {
   317  			t.Fatal(err)
   318  		}
   319  		return nil
   320  	}); err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	tx, err := db.Begin(false)
   324  	if err != nil {
   325  		t.Fatal(err)
   326  	}
   327  	defer func() { _ = tx.Rollback() }()
   328  
   329  	c := tx.Bucket([]byte("widgets")).Cursor()
   330  
   331  	k, v := c.First()
   332  	if !bytes.Equal(k, []byte("bar")) {
   333  		t.Fatalf("unexpected key: %v", k)
   334  	} else if !bytes.Equal(v, []byte{1}) {
   335  		t.Fatalf("unexpected value: %v", v)
   336  	}
   337  
   338  	k, v = c.Next()
   339  	if !bytes.Equal(k, []byte("baz")) {
   340  		t.Fatalf("unexpected key: %v", k)
   341  	} else if !bytes.Equal(v, []byte{}) {
   342  		t.Fatalf("unexpected value: %v", v)
   343  	}
   344  
   345  	k, v = c.Next()
   346  	if !bytes.Equal(k, []byte("foo")) {
   347  		t.Fatalf("unexpected key: %v", k)
   348  	} else if !bytes.Equal(v, []byte{0}) {
   349  		t.Fatalf("unexpected value: %v", v)
   350  	}
   351  
   352  	k, v = c.Next()
   353  	if k != nil {
   354  		t.Fatalf("expected nil key: %v", k)
   355  	} else if v != nil {
   356  		t.Fatalf("expected nil value: %v", v)
   357  	}
   358  
   359  	k, v = c.Next()
   360  	if k != nil {
   361  		t.Fatalf("expected nil key: %v", k)
   362  	} else if v != nil {
   363  		t.Fatalf("expected nil value: %v", v)
   364  	}
   365  
   366  	if err := tx.Rollback(); err != nil {
   367  		t.Fatal(err)
   368  	}
   369  }
   370  
   371  // Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements.
   372  func TestCursor_LeafRootReverse(t *testing.T) {
   373  	db := MustOpenDB()
   374  	defer db.MustClose()
   375  
   376  	if err := db.Update(func(tx *memcache.Tx) error {
   377  		b, err := tx.CreateBucket([]byte("widgets"))
   378  		if err != nil {
   379  			t.Fatal(err)
   380  		}
   381  		if err := b.Put([]byte("baz"), []byte{}); err != nil {
   382  			t.Fatal(err)
   383  		}
   384  		if err := b.Put([]byte("foo"), []byte{0}); err != nil {
   385  			t.Fatal(err)
   386  		}
   387  		if err := b.Put([]byte("bar"), []byte{1}); err != nil {
   388  			t.Fatal(err)
   389  		}
   390  		return nil
   391  	}); err != nil {
   392  		t.Fatal(err)
   393  	}
   394  	tx, err := db.Begin(false)
   395  	if err != nil {
   396  		t.Fatal(err)
   397  	}
   398  	c := tx.Bucket([]byte("widgets")).Cursor()
   399  
   400  	if k, v := c.Last(); !bytes.Equal(k, []byte("foo")) {
   401  		t.Fatalf("unexpected key: %v", k)
   402  	} else if !bytes.Equal(v, []byte{0}) {
   403  		t.Fatalf("unexpected value: %v", v)
   404  	}
   405  
   406  	if k, v := c.Prev(); !bytes.Equal(k, []byte("baz")) {
   407  		t.Fatalf("unexpected key: %v", k)
   408  	} else if !bytes.Equal(v, []byte{}) {
   409  		t.Fatalf("unexpected value: %v", v)
   410  	}
   411  
   412  	if k, v := c.Prev(); !bytes.Equal(k, []byte("bar")) {
   413  		t.Fatalf("unexpected key: %v", k)
   414  	} else if !bytes.Equal(v, []byte{1}) {
   415  		t.Fatalf("unexpected value: %v", v)
   416  	}
   417  
   418  	if k, v := c.Prev(); k != nil {
   419  		t.Fatalf("expected nil key: %v", k)
   420  	} else if v != nil {
   421  		t.Fatalf("expected nil value: %v", v)
   422  	}
   423  
   424  	if k, v := c.Prev(); k != nil {
   425  		t.Fatalf("expected nil key: %v", k)
   426  	} else if v != nil {
   427  		t.Fatalf("expected nil value: %v", v)
   428  	}
   429  
   430  	if err := tx.Rollback(); err != nil {
   431  		t.Fatal(err)
   432  	}
   433  }
   434  
   435  // Ensure that a Tx cursor can restart from the beginning.
   436  func TestCursor_Restart(t *testing.T) {
   437  	db := MustOpenDB()
   438  	defer db.MustClose()
   439  
   440  	if err := db.Update(func(tx *memcache.Tx) error {
   441  		b, err := tx.CreateBucket([]byte("widgets"))
   442  		if err != nil {
   443  			t.Fatal(err)
   444  		}
   445  		if err := b.Put([]byte("bar"), []byte{}); err != nil {
   446  			t.Fatal(err)
   447  		}
   448  		if err := b.Put([]byte("foo"), []byte{}); err != nil {
   449  			t.Fatal(err)
   450  		}
   451  		return nil
   452  	}); err != nil {
   453  		t.Fatal(err)
   454  	}
   455  
   456  	tx, err := db.Begin(false)
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  	c := tx.Bucket([]byte("widgets")).Cursor()
   461  
   462  	if k, _ := c.First(); !bytes.Equal(k, []byte("bar")) {
   463  		t.Fatalf("unexpected key: %v", k)
   464  	}
   465  	if k, _ := c.Next(); !bytes.Equal(k, []byte("foo")) {
   466  		t.Fatalf("unexpected key: %v", k)
   467  	}
   468  
   469  	if k, _ := c.First(); !bytes.Equal(k, []byte("bar")) {
   470  		t.Fatalf("unexpected key: %v", k)
   471  	}
   472  	if k, _ := c.Next(); !bytes.Equal(k, []byte("foo")) {
   473  		t.Fatalf("unexpected key: %v", k)
   474  	}
   475  
   476  	if err := tx.Rollback(); err != nil {
   477  		t.Fatal(err)
   478  	}
   479  }
   480  
   481  // Ensure that a cursor can skip over empty pages that have been deleted.
   482  func TestCursor_First_EmptyPages(t *testing.T) {
   483  	db := MustOpenDB()
   484  	defer db.MustClose()
   485  
   486  	// Create 1000 keys in the "widgets" bucket.
   487  	if err := db.Update(func(tx *memcache.Tx) error {
   488  		b, err := tx.CreateBucket([]byte("widgets"))
   489  		if err != nil {
   490  			t.Fatal(err)
   491  		}
   492  
   493  		for i := 0; i < 1000; i++ {
   494  			if err := b.Put(u64tob(uint64(i)), []byte{}); err != nil {
   495  				t.Fatal(err)
   496  			}
   497  		}
   498  
   499  		return nil
   500  	}); err != nil {
   501  		t.Fatal(err)
   502  	}
   503  
   504  	// Delete half the keys and then try to iterate.
   505  	if err := db.Update(func(tx *memcache.Tx) error {
   506  		b := tx.Bucket([]byte("widgets"))
   507  		for i := 0; i < 600; i++ {
   508  			if err := b.Delete(u64tob(uint64(i))); err != nil {
   509  				t.Fatal(err)
   510  			}
   511  		}
   512  
   513  		c := b.Cursor()
   514  		var n int
   515  		for k, _ := c.First(); k != nil; k, _ = c.Next() {
   516  			n++
   517  		}
   518  		if n != 400 {
   519  			t.Fatalf("unexpected key count: %d", n)
   520  		}
   521  
   522  		return nil
   523  	}); err != nil {
   524  		t.Fatal(err)
   525  	}
   526  }
   527  
   528  // Ensure that a Tx can iterate over all elements in a bucket.
   529  func TestCursor_QuickCheck(t *testing.T) {
   530  	f := func(items testdata) bool {
   531  		db := MustOpenDB()
   532  		defer db.MustClose()
   533  
   534  		// Bulk insert all values.
   535  		tx, err := db.Begin(true)
   536  		if err != nil {
   537  			t.Fatal(err)
   538  		}
   539  		b, err := tx.CreateBucket([]byte("widgets"))
   540  		if err != nil {
   541  			t.Fatal(err)
   542  		}
   543  		for _, item := range items {
   544  			if err := b.Put(item.Key, item.Value); err != nil {
   545  				t.Fatal(err)
   546  			}
   547  		}
   548  		if err := tx.Commit(); err != nil {
   549  			t.Fatal(err)
   550  		}
   551  
   552  		// Sort test data.
   553  		sort.Sort(items)
   554  
   555  		// Iterate over all items and check consistency.
   556  		var index = 0
   557  		tx, err = db.Begin(false)
   558  		if err != nil {
   559  			t.Fatal(err)
   560  		}
   561  
   562  		c := tx.Bucket([]byte("widgets")).Cursor()
   563  		for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() {
   564  			if !bytes.Equal(k, items[index].Key) {
   565  				t.Fatalf("unexpected key: %v", k)
   566  			} else if !bytes.Equal(v, items[index].Value) {
   567  				t.Fatalf("unexpected value: %v", v)
   568  			}
   569  			index++
   570  		}
   571  		if len(items) != index {
   572  			t.Fatalf("unexpected item count: %v, expected %v", len(items), index)
   573  		}
   574  
   575  		if err := tx.Rollback(); err != nil {
   576  			t.Fatal(err)
   577  		}
   578  
   579  		return true
   580  	}
   581  	if err := quick.Check(f, qconfig()); err != nil {
   582  		t.Error(err)
   583  	}
   584  }
   585  
   586  // Ensure that a transaction can iterate over all elements in a bucket in reverse.
   587  func TestCursor_QuickCheck_Reverse(t *testing.T) {
   588  	f := func(items testdata) bool {
   589  		db := MustOpenDB()
   590  		defer db.MustClose()
   591  
   592  		// Bulk insert all values.
   593  		tx, err := db.Begin(true)
   594  		if err != nil {
   595  			t.Fatal(err)
   596  		}
   597  		b, err := tx.CreateBucket([]byte("widgets"))
   598  		if err != nil {
   599  			t.Fatal(err)
   600  		}
   601  		for _, item := range items {
   602  			if err := b.Put(item.Key, item.Value); err != nil {
   603  				t.Fatal(err)
   604  			}
   605  		}
   606  		if err := tx.Commit(); err != nil {
   607  			t.Fatal(err)
   608  		}
   609  
   610  		// Sort test data.
   611  		sort.Sort(revtestdata(items))
   612  
   613  		// Iterate over all items and check consistency.
   614  		var index = 0
   615  		tx, err = db.Begin(false)
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  		c := tx.Bucket([]byte("widgets")).Cursor()
   620  		for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() {
   621  			if !bytes.Equal(k, items[index].Key) {
   622  				t.Fatalf("unexpected key: %v", k)
   623  			} else if !bytes.Equal(v, items[index].Value) {
   624  				t.Fatalf("unexpected value: %v", v)
   625  			}
   626  			index++
   627  		}
   628  		if len(items) != index {
   629  			t.Fatalf("unexpected item count: %v, expected %v", len(items), index)
   630  		}
   631  
   632  		if err := tx.Rollback(); err != nil {
   633  			t.Fatal(err)
   634  		}
   635  
   636  		return true
   637  	}
   638  	if err := quick.Check(f, qconfig()); err != nil {
   639  		t.Error(err)
   640  	}
   641  }
   642  
   643  // Ensure that a Tx cursor can iterate over subbuckets.
   644  func TestCursor_QuickCheck_BucketsOnly(t *testing.T) {
   645  	db := MustOpenDB()
   646  	defer db.MustClose()
   647  
   648  	if err := db.Update(func(tx *memcache.Tx) error {
   649  		b, err := tx.CreateBucket([]byte("widgets"))
   650  		if err != nil {
   651  			t.Fatal(err)
   652  		}
   653  		if _, err := b.CreateBucket([]byte("foo")); err != nil {
   654  			t.Fatal(err)
   655  		}
   656  		if _, err := b.CreateBucket([]byte("bar")); err != nil {
   657  			t.Fatal(err)
   658  		}
   659  		if _, err := b.CreateBucket([]byte("baz")); err != nil {
   660  			t.Fatal(err)
   661  		}
   662  		return nil
   663  	}); err != nil {
   664  		t.Fatal(err)
   665  	}
   666  
   667  	if err := db.View(func(tx *memcache.Tx) error {
   668  		var names []string
   669  		c := tx.Bucket([]byte("widgets")).Cursor()
   670  		for k, v := c.First(); k != nil; k, v = c.Next() {
   671  			names = append(names, string(k))
   672  			if v != nil {
   673  				t.Fatalf("unexpected value: %v", v)
   674  			}
   675  		}
   676  		if !reflect.DeepEqual(names, []string{"bar", "baz", "foo"}) {
   677  			t.Fatalf("unexpected names: %+v", names)
   678  		}
   679  		return nil
   680  	}); err != nil {
   681  		t.Fatal(err)
   682  	}
   683  }
   684  
   685  // Ensure that a Tx cursor can reverse iterate over subbuckets.
   686  func TestCursor_QuickCheck_BucketsOnly_Reverse(t *testing.T) {
   687  	db := MustOpenDB()
   688  	defer db.MustClose()
   689  
   690  	if err := db.Update(func(tx *memcache.Tx) error {
   691  		b, err := tx.CreateBucket([]byte("widgets"))
   692  		if err != nil {
   693  			t.Fatal(err)
   694  		}
   695  		if _, err := b.CreateBucket([]byte("foo")); err != nil {
   696  			t.Fatal(err)
   697  		}
   698  		if _, err := b.CreateBucket([]byte("bar")); err != nil {
   699  			t.Fatal(err)
   700  		}
   701  		if _, err := b.CreateBucket([]byte("baz")); err != nil {
   702  			t.Fatal(err)
   703  		}
   704  		return nil
   705  	}); err != nil {
   706  		t.Fatal(err)
   707  	}
   708  
   709  	if err := db.View(func(tx *memcache.Tx) error {
   710  		var names []string
   711  		c := tx.Bucket([]byte("widgets")).Cursor()
   712  		for k, v := c.Last(); k != nil; k, v = c.Prev() {
   713  			names = append(names, string(k))
   714  			if v != nil {
   715  				t.Fatalf("unexpected value: %v", v)
   716  			}
   717  		}
   718  		if !reflect.DeepEqual(names, []string{"foo", "baz", "bar"}) {
   719  			t.Fatalf("unexpected names: %+v", names)
   720  		}
   721  		return nil
   722  	}); err != nil {
   723  		t.Fatal(err)
   724  	}
   725  }
   726  
   727  func ExampleCursor() {
   728  	// Open the database.
   729  	db, err := memcache.Open(tempfile(), 0666, nil)
   730  	if err != nil {
   731  		log.Fatal(err)
   732  	}
   733  	defer os.Remove(db.Path())
   734  
   735  	// Start a read-write transaction.
   736  	if err := db.Update(func(tx *memcache.Tx) error {
   737  		// Create a new bucket.
   738  		b, err := tx.CreateBucket([]byte("animals"))
   739  		if err != nil {
   740  			return err
   741  		}
   742  
   743  		// Insert data into a bucket.
   744  		if err := b.Put([]byte("dog"), []byte("fun")); err != nil {
   745  			log.Fatal(err)
   746  		}
   747  		if err := b.Put([]byte("cat"), []byte("lame")); err != nil {
   748  			log.Fatal(err)
   749  		}
   750  		if err := b.Put([]byte("liger"), []byte("awesome")); err != nil {
   751  			log.Fatal(err)
   752  		}
   753  
   754  		// Create a cursor for iteration.
   755  		c := b.Cursor()
   756  
   757  		// Iterate over items in sorted key order. This starts from the
   758  		// first key/value pair and updates the k/v variables to the
   759  		// next key/value on each iteration.
   760  		//
   761  		// The loop finishes at the end of the cursor when a nil key is returned.
   762  		for k, v := c.First(); k != nil; k, v = c.Next() {
   763  			fmt.Printf("A %s is %s.\n", k, v)
   764  		}
   765  
   766  		return nil
   767  	}); err != nil {
   768  		log.Fatal(err)
   769  	}
   770  
   771  	if err := db.Close(); err != nil {
   772  		log.Fatal(err)
   773  	}
   774  
   775  	// Output:
   776  	// A cat is lame.
   777  	// A dog is fun.
   778  	// A liger is awesome.
   779  }
   780  
   781  func ExampleCursor_reverse() {
   782  	// Open the database.
   783  	db, err := memcache.Open(tempfile(), 0666, nil)
   784  	if err != nil {
   785  		log.Fatal(err)
   786  	}
   787  	defer os.Remove(db.Path())
   788  
   789  	// Start a read-write transaction.
   790  	if err := db.Update(func(tx *memcache.Tx) error {
   791  		// Create a new bucket.
   792  		b, err := tx.CreateBucket([]byte("animals"))
   793  		if err != nil {
   794  			return err
   795  		}
   796  
   797  		// Insert data into a bucket.
   798  		if err := b.Put([]byte("dog"), []byte("fun")); err != nil {
   799  			log.Fatal(err)
   800  		}
   801  		if err := b.Put([]byte("cat"), []byte("lame")); err != nil {
   802  			log.Fatal(err)
   803  		}
   804  		if err := b.Put([]byte("liger"), []byte("awesome")); err != nil {
   805  			log.Fatal(err)
   806  		}
   807  
   808  		// Create a cursor for iteration.
   809  		c := b.Cursor()
   810  
   811  		// Iterate over items in reverse sorted key order. This starts
   812  		// from the last key/value pair and updates the k/v variables to
   813  		// the previous key/value on each iteration.
   814  		//
   815  		// The loop finishes at the beginning of the cursor when a nil key
   816  		// is returned.
   817  		for k, v := c.Last(); k != nil; k, v = c.Prev() {
   818  			fmt.Printf("A %s is %s.\n", k, v)
   819  		}
   820  
   821  		return nil
   822  	}); err != nil {
   823  		log.Fatal(err)
   824  	}
   825  
   826  	// Close the database to release the file lock.
   827  	if err := db.Close(); err != nil {
   828  		log.Fatal(err)
   829  	}
   830  
   831  	// Output:
   832  	// A liger is awesome.
   833  	// A dog is fun.
   834  	// A cat is lame.
   835  }