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