github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/ethdb/dbtest/testsuite.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package dbtest 19 20 import ( 21 "bytes" 22 "reflect" 23 "sort" 24 "testing" 25 26 "github.com/AigarNetwork/aigar/ethdb" 27 ) 28 29 // TestDatabaseSuite runs a suite of tests against a KeyValueStore database 30 // implementation. 31 func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) { 32 t.Run("Iterator", func(t *testing.T) { 33 tests := []struct { 34 content map[string]string 35 prefix string 36 order []string 37 }{ 38 // Empty databases should be iterable 39 {map[string]string{}, "", nil}, 40 {map[string]string{}, "non-existent-prefix", nil}, 41 42 // Single-item databases should be iterable 43 {map[string]string{"key": "val"}, "", []string{"key"}}, 44 {map[string]string{"key": "val"}, "k", []string{"key"}}, 45 {map[string]string{"key": "val"}, "l", nil}, 46 47 // Multi-item databases should be fully iterable 48 { 49 map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"}, 50 "", 51 []string{"k1", "k2", "k3", "k4", "k5"}, 52 }, 53 { 54 map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"}, 55 "k", 56 []string{"k1", "k2", "k3", "k4", "k5"}, 57 }, 58 { 59 map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"}, 60 "l", 61 nil, 62 }, 63 // Multi-item databases should be prefix-iterable 64 { 65 map[string]string{ 66 "ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3", 67 "kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3", 68 }, 69 "ka", 70 []string{"ka1", "ka2", "ka3", "ka4", "ka5"}, 71 }, 72 { 73 map[string]string{ 74 "ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3", 75 "kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3", 76 }, 77 "kc", 78 nil, 79 }, 80 } 81 for i, tt := range tests { 82 // Create the key-value data store 83 db := New() 84 for key, val := range tt.content { 85 if err := db.Put([]byte(key), []byte(val)); err != nil { 86 t.Fatalf("test %d: failed to insert item %s:%s into database: %v", i, key, val, err) 87 } 88 } 89 // Iterate over the database with the given configs and verify the results 90 it, idx := db.NewIteratorWithPrefix([]byte(tt.prefix)), 0 91 for it.Next() { 92 if len(tt.order) <= idx { 93 t.Errorf("test %d: prefix=%q more items than expected: checking idx=%d (key %q), expecting len=%d", i, tt.prefix, idx, it.Key(), len(tt.order)) 94 break 95 } 96 if !bytes.Equal(it.Key(), []byte(tt.order[idx])) { 97 t.Errorf("test %d: item %d: key mismatch: have %s, want %s", i, idx, string(it.Key()), tt.order[idx]) 98 } 99 if !bytes.Equal(it.Value(), []byte(tt.content[tt.order[idx]])) { 100 t.Errorf("test %d: item %d: value mismatch: have %s, want %s", i, idx, string(it.Value()), tt.content[tt.order[idx]]) 101 } 102 idx++ 103 } 104 if err := it.Error(); err != nil { 105 t.Errorf("test %d: iteration failed: %v", i, err) 106 } 107 if idx != len(tt.order) { 108 t.Errorf("test %d: iteration terminated prematurely: have %d, want %d", i, idx, len(tt.order)) 109 } 110 db.Close() 111 } 112 }) 113 114 t.Run("IteratorWith", func(t *testing.T) { 115 db := New() 116 defer db.Close() 117 118 keys := []string{"1", "2", "3", "4", "6", "10", "11", "12", "20", "21", "22"} 119 sort.Strings(keys) // 1, 10, 11, etc 120 121 for _, k := range keys { 122 if err := db.Put([]byte(k), nil); err != nil { 123 t.Fatal(err) 124 } 125 } 126 127 { 128 it := db.NewIterator() 129 got, want := iterateKeys(it), keys 130 if err := it.Error(); err != nil { 131 t.Fatal(err) 132 } 133 it.Release() 134 if !reflect.DeepEqual(got, want) { 135 t.Errorf("Iterator: got: %s; want: %s", got, want) 136 } 137 } 138 139 { 140 it := db.NewIteratorWithPrefix([]byte("1")) 141 got, want := iterateKeys(it), []string{"1", "10", "11", "12"} 142 if err := it.Error(); err != nil { 143 t.Fatal(err) 144 } 145 it.Release() 146 if !reflect.DeepEqual(got, want) { 147 t.Errorf("IteratorWithPrefix(1): got: %s; want: %s", got, want) 148 } 149 } 150 151 { 152 it := db.NewIteratorWithPrefix([]byte("5")) 153 got, want := iterateKeys(it), []string{} 154 if err := it.Error(); err != nil { 155 t.Fatal(err) 156 } 157 it.Release() 158 if !reflect.DeepEqual(got, want) { 159 t.Errorf("IteratorWithPrefix(1): got: %s; want: %s", got, want) 160 } 161 } 162 163 { 164 it := db.NewIteratorWithStart([]byte("2")) 165 got, want := iterateKeys(it), []string{"2", "20", "21", "22", "3", "4", "6"} 166 if err := it.Error(); err != nil { 167 t.Fatal(err) 168 } 169 it.Release() 170 if !reflect.DeepEqual(got, want) { 171 t.Errorf("IteratorWithStart(2): got: %s; want: %s", got, want) 172 } 173 } 174 175 { 176 it := db.NewIteratorWithStart([]byte("5")) 177 got, want := iterateKeys(it), []string{"6"} 178 if err := it.Error(); err != nil { 179 t.Fatal(err) 180 } 181 it.Release() 182 if !reflect.DeepEqual(got, want) { 183 t.Errorf("IteratorWithStart(2): got: %s; want: %s", got, want) 184 } 185 } 186 }) 187 188 t.Run("KeyValueOperations", func(t *testing.T) { 189 db := New() 190 defer db.Close() 191 192 key := []byte("foo") 193 194 if got, err := db.Has(key); err != nil { 195 t.Error(err) 196 } else if got { 197 t.Errorf("wrong value: %t", got) 198 } 199 200 value := []byte("hello world") 201 if err := db.Put(key, value); err != nil { 202 t.Error(err) 203 } 204 205 if got, err := db.Has(key); err != nil { 206 t.Error(err) 207 } else if !got { 208 t.Errorf("wrong value: %t", got) 209 } 210 211 if got, err := db.Get(key); err != nil { 212 t.Error(err) 213 } else if !bytes.Equal(got, value) { 214 t.Errorf("wrong value: %q", got) 215 } 216 217 if err := db.Delete(key); err != nil { 218 t.Error(err) 219 } 220 221 if got, err := db.Has(key); err != nil { 222 t.Error(err) 223 } else if got { 224 t.Errorf("wrong value: %t", got) 225 } 226 }) 227 228 t.Run("Batch", func(t *testing.T) { 229 db := New() 230 defer db.Close() 231 232 b := db.NewBatch() 233 for _, k := range []string{"1", "2", "3", "4"} { 234 if err := b.Put([]byte(k), nil); err != nil { 235 t.Fatal(err) 236 } 237 } 238 239 if has, err := db.Has([]byte("1")); err != nil { 240 t.Fatal(err) 241 } else if has { 242 t.Error("db contains element before batch write") 243 } 244 245 if err := b.Write(); err != nil { 246 t.Fatal(err) 247 } 248 249 { 250 it := db.NewIterator() 251 if got, want := iterateKeys(it), []string{"1", "2", "3", "4"}; !reflect.DeepEqual(got, want) { 252 t.Errorf("got: %s; want: %s", got, want) 253 } 254 it.Release() 255 } 256 257 b.Reset() 258 259 // Mix writes and deletes in batch 260 b.Put([]byte("5"), nil) 261 b.Delete([]byte("1")) 262 b.Put([]byte("6"), nil) 263 b.Delete([]byte("3")) 264 b.Put([]byte("3"), nil) 265 266 if err := b.Write(); err != nil { 267 t.Fatal(err) 268 } 269 270 { 271 it := db.NewIterator() 272 if got, want := iterateKeys(it), []string{"2", "3", "4", "5", "6"}; !reflect.DeepEqual(got, want) { 273 t.Errorf("got: %s; want: %s", got, want) 274 } 275 it.Release() 276 } 277 }) 278 279 t.Run("BatchReplay", func(t *testing.T) { 280 db := New() 281 defer db.Close() 282 283 want := []string{"1", "2", "3", "4"} 284 b := db.NewBatch() 285 for _, k := range want { 286 if err := b.Put([]byte(k), nil); err != nil { 287 t.Fatal(err) 288 } 289 } 290 291 b2 := db.NewBatch() 292 if err := b.Replay(b2); err != nil { 293 t.Fatal(err) 294 } 295 296 if err := b2.Replay(db); err != nil { 297 t.Fatal(err) 298 } 299 300 it := db.NewIterator() 301 if got := iterateKeys(it); !reflect.DeepEqual(got, want) { 302 t.Errorf("got: %s; want: %s", got, want) 303 } 304 it.Release() 305 }) 306 307 } 308 309 func iterateKeys(it ethdb.Iterator) []string { 310 keys := []string{} 311 for it.Next() { 312 keys = append(keys, string(it.Key())) 313 } 314 sort.Strings(keys) 315 return keys 316 }