diff --git a/storage/index_test.go b/storage/index_test.go index 92f179d97..782dfcefd 100644 --- a/storage/index_test.go +++ b/storage/index_test.go @@ -5,35 +5,55 @@ import ( "testing" ) -func TestIndexPutAndGet(t *testing.T) { - index := newTestTreeIndex() +func TestIndexGet(t *testing.T) { + index := newTreeIndex() + index.Put([]byte("foo"), revision{main: 2}) + index.Put([]byte("foo"), revision{main: 4}) + index.Tombstone([]byte("foo"), revision{main: 6}) - tests := []T{ - {[]byte("foo"), 0, ErrRevisionNotFound, 0}, - {[]byte("foo"), 1, nil, 1}, - {[]byte("foo"), 3, nil, 1}, - {[]byte("foo"), 5, nil, 5}, - {[]byte("foo"), 6, nil, 5}, + tests := []struct { + rev int64 - {[]byte("foo1"), 0, ErrRevisionNotFound, 0}, - {[]byte("foo1"), 1, ErrRevisionNotFound, 0}, - {[]byte("foo1"), 2, nil, 2}, - {[]byte("foo1"), 5, nil, 2}, - {[]byte("foo1"), 6, nil, 6}, - - {[]byte("foo2"), 0, ErrRevisionNotFound, 0}, - {[]byte("foo2"), 1, ErrRevisionNotFound, 0}, - {[]byte("foo2"), 3, nil, 3}, - {[]byte("foo2"), 4, nil, 4}, - {[]byte("foo2"), 6, nil, 4}, + wrev revision + wcreated revision + wver int64 + werr error + }{ + {0, revision{}, revision{}, 0, ErrRevisionNotFound}, + {1, revision{}, revision{}, 0, ErrRevisionNotFound}, + {2, revision{main: 2}, revision{main: 2}, 1, nil}, + {3, revision{main: 2}, revision{main: 2}, 1, nil}, + {4, revision{main: 4}, revision{main: 2}, 2, nil}, + {5, revision{main: 4}, revision{main: 2}, 2, nil}, + {6, revision{}, revision{}, 0, ErrRevisionNotFound}, + } + for i, tt := range tests { + rev, created, ver, err := index.Get([]byte("foo"), tt.rev) + if err != tt.werr { + t.Errorf("#%d: err = %v, want %v", i, err, tt.werr) + } + if rev != tt.wrev { + t.Errorf("#%d: rev = %+v, want %+v", i, rev, tt.wrev) + } + if created != tt.wcreated { + t.Errorf("#%d: created = %+v, want %+v", i, created, tt.wcreated) + } + if ver != tt.wver { + t.Errorf("#%d: ver = %d, want %d", i, ver, tt.wver) + } } - verify(t, index, tests) } func TestIndexRange(t *testing.T) { - atRev := int64(3) allKeys := [][]byte{[]byte("foo"), []byte("foo1"), []byte("foo2")} allRevs := []revision{{main: 1}, {main: 2}, {main: 3}} + + index := newTreeIndex() + for i := range allKeys { + index.Put(allKeys[i], allRevs[i]) + } + + atRev := int64(3) tests := []struct { key, end []byte wkeys [][]byte @@ -73,7 +93,6 @@ func TestIndexRange(t *testing.T) { }, } for i, tt := range tests { - index := newTestTreeIndex() keys, revs := index.Range(tt.key, tt.end, atRev) if !reflect.DeepEqual(keys, tt.wkeys) { t.Errorf("#%d: keys = %+v, want %+v", i, keys, tt.wkeys) @@ -85,139 +104,151 @@ func TestIndexRange(t *testing.T) { } func TestIndexTombstone(t *testing.T) { - index := newTestTreeIndex() + index := newTreeIndex() + index.Put([]byte("foo"), revision{main: 1}) - err := index.Tombstone([]byte("foo"), revision{main: 7}) + err := index.Tombstone([]byte("foo"), revision{main: 2}) if err != nil { t.Errorf("tombstone error = %v, want nil", err) } - _, _, _, err = index.Get([]byte("foo"), 7) + + _, _, _, err = index.Get([]byte("foo"), 2) if err != ErrRevisionNotFound { t.Errorf("get error = %v, want nil", err) } -} - -func TestContinuousCompact(t *testing.T) { - index := newTestTreeIndex() - - tests := []T{ - {[]byte("foo"), 0, ErrRevisionNotFound, 0}, - {[]byte("foo"), 1, nil, 1}, - {[]byte("foo"), 3, nil, 1}, - {[]byte("foo"), 5, nil, 5}, - {[]byte("foo"), 6, nil, 5}, - - {[]byte("foo1"), 0, ErrRevisionNotFound, 0}, - {[]byte("foo1"), 1, ErrRevisionNotFound, 0}, - {[]byte("foo1"), 2, nil, 2}, - {[]byte("foo1"), 5, nil, 2}, - {[]byte("foo1"), 6, nil, 6}, - - {[]byte("foo2"), 0, ErrRevisionNotFound, 0}, - {[]byte("foo2"), 1, ErrRevisionNotFound, 0}, - {[]byte("foo2"), 3, nil, 3}, - {[]byte("foo2"), 4, nil, 4}, - {[]byte("foo2"), 6, nil, 4}, - } - wa := map[revision]struct{}{ - revision{main: 1}: {}, - } - ga := index.Compact(1) - if !reflect.DeepEqual(ga, wa) { - t.Errorf("a = %v, want %v", ga, wa) - } - verify(t, index, tests) - - wa = map[revision]struct{}{ - revision{main: 1}: {}, - revision{main: 2}: {}, - } - ga = index.Compact(2) - if !reflect.DeepEqual(ga, wa) { - t.Errorf("a = %v, want %v", ga, wa) - } - verify(t, index, tests) - - wa = map[revision]struct{}{ - revision{main: 1}: {}, - revision{main: 2}: {}, - revision{main: 3}: {}, - } - ga = index.Compact(3) - if !reflect.DeepEqual(ga, wa) { - t.Errorf("a = %v, want %v", ga, wa) - } - verify(t, index, tests) - - wa = map[revision]struct{}{ - revision{main: 1}: {}, - revision{main: 2}: {}, - revision{main: 4}: {}, - } - ga = index.Compact(4) - delete(wa, revision{main: 3}) - tests[12] = T{[]byte("foo2"), 3, ErrRevisionNotFound, 0} - if !reflect.DeepEqual(wa, ga) { - t.Errorf("a = %v, want %v", ga, wa) - } - verify(t, index, tests) - - wa = map[revision]struct{}{ - revision{main: 2}: {}, - revision{main: 4}: {}, - revision{main: 5}: {}, - } - ga = index.Compact(5) - delete(wa, revision{main: 1}) - if !reflect.DeepEqual(ga, wa) { - t.Errorf("a = %v, want %v", ga, wa) - } - tests[1] = T{[]byte("foo"), 1, ErrRevisionNotFound, 0} - tests[2] = T{[]byte("foo"), 3, ErrRevisionNotFound, 0} - verify(t, index, tests) - - wa = map[revision]struct{}{ - revision{main: 4}: {}, - revision{main: 5}: {}, - revision{main: 6}: {}, - } - ga = index.Compact(6) - delete(wa, revision{main: 2}) - if !reflect.DeepEqual(ga, wa) { - t.Errorf("a = %v, want %v", ga, wa) - } - tests[7] = T{[]byte("foo1"), 2, ErrRevisionNotFound, 0} - tests[8] = T{[]byte("foo1"), 5, ErrRevisionNotFound, 0} - verify(t, index, tests) -} - -func verify(t *testing.T, index index, tests []T) { - for i, tt := range tests { - h, _, _, err := index.Get(tt.key, tt.rev) - if err != tt.werr { - t.Errorf("#%d: err = %v, want %v", i, err, tt.werr) - } - if h.main != tt.wrev { - t.Errorf("#%d: rev = %d, want %d", i, h.main, tt.wrev) - } + err = index.Tombstone([]byte("foo"), revision{main: 3}) + if err != ErrRevisionNotFound { + t.Errorf("tombstone error = %v, want %v", err, ErrRevisionNotFound) } } -type T struct { - key []byte - rev int64 +func TestIndexCompact(t *testing.T) { + maxRev := int64(20) + tests := []struct { + key []byte + remove bool + rev revision + created revision + ver int64 + }{ + {[]byte("foo"), false, revision{main: 1}, revision{main: 1}, 1}, + {[]byte("foo1"), false, revision{main: 2}, revision{main: 2}, 1}, + {[]byte("foo2"), false, revision{main: 3}, revision{main: 3}, 1}, + {[]byte("foo2"), false, revision{main: 4}, revision{main: 3}, 2}, + {[]byte("foo"), false, revision{main: 5}, revision{main: 1}, 2}, + {[]byte("foo1"), false, revision{main: 6}, revision{main: 2}, 2}, + {[]byte("foo1"), true, revision{main: 7}, revision{}, 0}, + {[]byte("foo2"), true, revision{main: 8}, revision{}, 0}, + {[]byte("foo"), true, revision{main: 9}, revision{}, 0}, + {[]byte("foo"), false, revision{10, 0}, revision{10, 0}, 1}, + {[]byte("foo1"), false, revision{10, 1}, revision{10, 1}, 1}, + } - werr error - wrev int64 -} - -func newTestTreeIndex() index { + // Continuous Compact index := newTreeIndex() - index.Put([]byte("foo"), revision{main: 1}) - index.Put([]byte("foo1"), revision{main: 2}) - index.Put([]byte("foo2"), revision{main: 3}) - index.Put([]byte("foo2"), revision{main: 4}) - index.Put([]byte("foo"), revision{main: 5}) - index.Put([]byte("foo1"), revision{main: 6}) - return index + for _, tt := range tests { + if tt.remove { + index.Tombstone(tt.key, tt.rev) + } else { + index.Put(tt.key, tt.rev) + } + } + for i := int64(1); i < maxRev; i++ { + am := index.Compact(i) + + windex := newTreeIndex() + for _, tt := range tests { + if _, ok := am[tt.rev]; ok || tt.rev.GreaterThan(revision{main: i}) { + if tt.remove { + windex.Tombstone(tt.key, tt.rev) + } else { + windex.Restore(tt.key, tt.created, tt.rev, tt.ver) + } + } + } + if !index.Equal(windex) { + t.Errorf("#%d: not equal index", i) + } + } + + // Once Compact + for i := int64(1); i < maxRev; i++ { + index := newTreeIndex() + for _, tt := range tests { + if tt.remove { + index.Tombstone(tt.key, tt.rev) + } else { + index.Put(tt.key, tt.rev) + } + } + am := index.Compact(i) + + windex := newTreeIndex() + for _, tt := range tests { + if _, ok := am[tt.rev]; ok || tt.rev.GreaterThan(revision{main: i}) { + if tt.remove { + windex.Tombstone(tt.key, tt.rev) + } else { + windex.Restore(tt.key, tt.created, tt.rev, tt.ver) + } + } + } + if !index.Equal(windex) { + t.Errorf("#%d: not equal index", i) + } + } +} + +func TestIndexRestore(t *testing.T) { + key := []byte("foo") + + tests := []struct { + created revision + modified revision + ver int64 + }{ + {revision{1, 0}, revision{1, 0}, 1}, + {revision{1, 0}, revision{1, 1}, 2}, + {revision{1, 0}, revision{2, 0}, 3}, + } + + // Continuous Restore + index := newTreeIndex() + for i, tt := range tests { + index.Restore(key, tt.created, tt.modified, tt.ver) + + modified, created, ver, err := index.Get(key, tt.modified.main) + if modified != tt.modified { + t.Errorf("#%d: modified = %v, want %v", i, modified, tt.modified) + } + if created != tt.created { + t.Errorf("#%d: created = %v, want %v", i, created, tt.created) + } + if ver != tt.ver { + t.Errorf("#%d: ver = %d, want %d", i, ver, tt.ver) + } + if err != nil { + t.Errorf("#%d: err = %v, want nil", i, err) + } + } + + // Once Restore + for i, tt := range tests { + index := newTreeIndex() + index.Restore(key, tt.created, tt.modified, tt.ver) + + modified, created, ver, err := index.Get(key, tt.modified.main) + if modified != tt.modified { + t.Errorf("#%d: modified = %v, want %v", i, modified, tt.modified) + } + if created != tt.created { + t.Errorf("#%d: created = %v, want %v", i, created, tt.created) + } + if ver != tt.ver { + t.Errorf("#%d: ver = %d, want %d", i, ver, tt.ver) + } + if err != nil { + t.Errorf("#%d: err = %v, want nil", i, err) + } + } }