github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/swarm/storage/localstore/localstore_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package localstore 18 19 import ( 20 "bytes" 21 "fmt" 22 "io/ioutil" 23 "math/rand" 24 "os" 25 "runtime" 26 "sort" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/ethereum/go-ethereum/swarm/chunk" 32 "github.com/ethereum/go-ethereum/swarm/shed" 33 "github.com/syndtr/goleveldb/leveldb" 34 ) 35 36 func init() { 37 // Some of the tests in localstore package rely on the same ordering of 38 // items uploaded or accessed compared to the ordering of items in indexes 39 // that contain StoreTimestamp or AccessTimestamp in keys. In tests 40 // where the same order is required from the database as the order 41 // in which chunks are put or accessed, if the StoreTimestamp or 42 // AccessTimestamp are the same for two or more sequential items 43 // their order in database will be based on the chunk address value, 44 // in which case the ordering of items/chunks stored in a test slice 45 // will not be the same. To ensure the same ordering in database on such 46 // indexes on windows systems, an additional short sleep is added to 47 // the now function. 48 if runtime.GOOS == "windows" { 49 setNow(func() int64 { 50 time.Sleep(time.Microsecond) 51 return time.Now().UTC().UnixNano() 52 }) 53 } 54 } 55 56 // TestDB validates if the chunk can be uploaded and 57 // correctly retrieved. 58 func TestDB(t *testing.T) { 59 db, cleanupFunc := newTestDB(t, nil) 60 defer cleanupFunc() 61 62 chunk := generateTestRandomChunk() 63 64 err := db.NewPutter(ModePutUpload).Put(chunk) 65 if err != nil { 66 t.Fatal(err) 67 } 68 69 got, err := db.NewGetter(ModeGetRequest).Get(chunk.Address()) 70 if err != nil { 71 t.Fatal(err) 72 } 73 74 if !bytes.Equal(got.Address(), chunk.Address()) { 75 t.Errorf("got address %x, want %x", got.Address(), chunk.Address()) 76 } 77 if !bytes.Equal(got.Data(), chunk.Data()) { 78 t.Errorf("got data %x, want %x", got.Data(), chunk.Data()) 79 } 80 } 81 82 // TestDB_updateGCSem tests maxParallelUpdateGC limit. 83 // This test temporary sets the limit to a low number, 84 // makes updateGC function execution time longer by 85 // setting a custom testHookUpdateGC function with a sleep 86 // and a count current and maximal number of goroutines. 87 func TestDB_updateGCSem(t *testing.T) { 88 updateGCSleep := time.Second 89 var count int 90 var max int 91 var mu sync.Mutex 92 defer setTestHookUpdateGC(func() { 93 mu.Lock() 94 // add to the count of current goroutines 95 count++ 96 if count > max { 97 // set maximal detected numbers of goroutines 98 max = count 99 } 100 mu.Unlock() 101 102 // wait for some time to ensure multiple parallel goroutines 103 time.Sleep(updateGCSleep) 104 105 mu.Lock() 106 count-- 107 mu.Unlock() 108 })() 109 110 defer func(m int) { maxParallelUpdateGC = m }(maxParallelUpdateGC) 111 maxParallelUpdateGC = 3 112 113 db, cleanupFunc := newTestDB(t, nil) 114 defer cleanupFunc() 115 116 chunk := generateTestRandomChunk() 117 118 err := db.NewPutter(ModePutUpload).Put(chunk) 119 if err != nil { 120 t.Fatal(err) 121 } 122 123 getter := db.NewGetter(ModeGetRequest) 124 125 // get more chunks then maxParallelUpdateGC 126 // in time shorter then updateGCSleep 127 for i := 0; i < 5; i++ { 128 _, err = getter.Get(chunk.Address()) 129 if err != nil { 130 t.Fatal(err) 131 } 132 } 133 134 if max != maxParallelUpdateGC { 135 t.Errorf("got max %v, want %v", max, maxParallelUpdateGC) 136 } 137 } 138 139 // newTestDB is a helper function that constructs a 140 // temporary database and returns a cleanup function that must 141 // be called to remove the data. 142 func newTestDB(t testing.TB, o *Options) (db *DB, cleanupFunc func()) { 143 t.Helper() 144 145 dir, err := ioutil.TempDir("", "localstore-test") 146 if err != nil { 147 t.Fatal(err) 148 } 149 cleanupFunc = func() { os.RemoveAll(dir) } 150 baseKey := make([]byte, 32) 151 if _, err := rand.Read(baseKey); err != nil { 152 t.Fatal(err) 153 } 154 db, err = New(dir, baseKey, o) 155 if err != nil { 156 cleanupFunc() 157 t.Fatal(err) 158 } 159 cleanupFunc = func() { 160 err := db.Close() 161 if err != nil { 162 t.Error(err) 163 } 164 os.RemoveAll(dir) 165 } 166 return db, cleanupFunc 167 } 168 169 func init() { 170 // needed for generateTestRandomChunk 171 rand.Seed(time.Now().UnixNano()) 172 } 173 174 // generateTestRandomChunk generates a Chunk that is not 175 // valid, but it contains a random key and a random value. 176 // This function is faster then storage.generateTestRandomChunk 177 // which generates a valid chunk. 178 // Some tests in this package do not need valid chunks, just 179 // random data, and their execution time can be decreased 180 // using this function. 181 func generateTestRandomChunk() chunk.Chunk { 182 data := make([]byte, chunk.DefaultSize) 183 rand.Read(data) 184 key := make([]byte, 32) 185 rand.Read(key) 186 return chunk.NewChunk(key, data) 187 } 188 189 // TestGenerateTestRandomChunk validates that 190 // generateTestRandomChunk returns random data by comparing 191 // two generated chunks. 192 func TestGenerateTestRandomChunk(t *testing.T) { 193 c1 := generateTestRandomChunk() 194 c2 := generateTestRandomChunk() 195 addrLen := len(c1.Address()) 196 if addrLen != 32 { 197 t.Errorf("first chunk address length %v, want %v", addrLen, 32) 198 } 199 dataLen := len(c1.Data()) 200 if dataLen != chunk.DefaultSize { 201 t.Errorf("first chunk data length %v, want %v", dataLen, chunk.DefaultSize) 202 } 203 addrLen = len(c2.Address()) 204 if addrLen != 32 { 205 t.Errorf("second chunk address length %v, want %v", addrLen, 32) 206 } 207 dataLen = len(c2.Data()) 208 if dataLen != chunk.DefaultSize { 209 t.Errorf("second chunk data length %v, want %v", dataLen, chunk.DefaultSize) 210 } 211 if bytes.Equal(c1.Address(), c2.Address()) { 212 t.Error("fake chunks addresses do not differ") 213 } 214 if bytes.Equal(c1.Data(), c2.Data()) { 215 t.Error("fake chunks data bytes do not differ") 216 } 217 } 218 219 // newRetrieveIndexesTest returns a test function that validates if the right 220 // chunk values are in the retrieval indexes. 221 func newRetrieveIndexesTest(db *DB, chunk chunk.Chunk, storeTimestamp, accessTimestamp int64) func(t *testing.T) { 222 return func(t *testing.T) { 223 item, err := db.retrievalDataIndex.Get(addressToItem(chunk.Address())) 224 if err != nil { 225 t.Fatal(err) 226 } 227 validateItem(t, item, chunk.Address(), chunk.Data(), storeTimestamp, 0) 228 229 // access index should not be set 230 wantErr := leveldb.ErrNotFound 231 item, err = db.retrievalAccessIndex.Get(addressToItem(chunk.Address())) 232 if err != wantErr { 233 t.Errorf("got error %v, want %v", err, wantErr) 234 } 235 } 236 } 237 238 // newRetrieveIndexesTestWithAccess returns a test function that validates if the right 239 // chunk values are in the retrieval indexes when access time must be stored. 240 func newRetrieveIndexesTestWithAccess(db *DB, chunk chunk.Chunk, storeTimestamp, accessTimestamp int64) func(t *testing.T) { 241 return func(t *testing.T) { 242 item, err := db.retrievalDataIndex.Get(addressToItem(chunk.Address())) 243 if err != nil { 244 t.Fatal(err) 245 } 246 validateItem(t, item, chunk.Address(), chunk.Data(), storeTimestamp, 0) 247 248 if accessTimestamp > 0 { 249 item, err = db.retrievalAccessIndex.Get(addressToItem(chunk.Address())) 250 if err != nil { 251 t.Fatal(err) 252 } 253 validateItem(t, item, chunk.Address(), nil, 0, accessTimestamp) 254 } 255 } 256 } 257 258 // newPullIndexTest returns a test function that validates if the right 259 // chunk values are in the pull index. 260 func newPullIndexTest(db *DB, chunk chunk.Chunk, storeTimestamp int64, wantError error) func(t *testing.T) { 261 return func(t *testing.T) { 262 item, err := db.pullIndex.Get(shed.Item{ 263 Address: chunk.Address(), 264 StoreTimestamp: storeTimestamp, 265 }) 266 if err != wantError { 267 t.Errorf("got error %v, want %v", err, wantError) 268 } 269 if err == nil { 270 validateItem(t, item, chunk.Address(), nil, storeTimestamp, 0) 271 } 272 } 273 } 274 275 // newPushIndexTest returns a test function that validates if the right 276 // chunk values are in the push index. 277 func newPushIndexTest(db *DB, chunk chunk.Chunk, storeTimestamp int64, wantError error) func(t *testing.T) { 278 return func(t *testing.T) { 279 item, err := db.pushIndex.Get(shed.Item{ 280 Address: chunk.Address(), 281 StoreTimestamp: storeTimestamp, 282 }) 283 if err != wantError { 284 t.Errorf("got error %v, want %v", err, wantError) 285 } 286 if err == nil { 287 validateItem(t, item, chunk.Address(), nil, storeTimestamp, 0) 288 } 289 } 290 } 291 292 // newGCIndexTest returns a test function that validates if the right 293 // chunk values are in the push index. 294 func newGCIndexTest(db *DB, chunk chunk.Chunk, storeTimestamp, accessTimestamp int64) func(t *testing.T) { 295 return func(t *testing.T) { 296 item, err := db.gcIndex.Get(shed.Item{ 297 Address: chunk.Address(), 298 StoreTimestamp: storeTimestamp, 299 AccessTimestamp: accessTimestamp, 300 }) 301 if err != nil { 302 t.Fatal(err) 303 } 304 validateItem(t, item, chunk.Address(), nil, storeTimestamp, accessTimestamp) 305 } 306 } 307 308 // newItemsCountTest returns a test function that validates if 309 // an index contains expected number of key/value pairs. 310 func newItemsCountTest(i shed.Index, want int) func(t *testing.T) { 311 return func(t *testing.T) { 312 var c int 313 err := i.Iterate(func(item shed.Item) (stop bool, err error) { 314 c++ 315 return 316 }, nil) 317 if err != nil { 318 t.Fatal(err) 319 } 320 if c != want { 321 t.Errorf("got %v items in index, want %v", c, want) 322 } 323 } 324 } 325 326 // newIndexGCSizeTest retruns a test function that validates if DB.gcSize 327 // value is the same as the number of items in DB.gcIndex. 328 func newIndexGCSizeTest(db *DB) func(t *testing.T) { 329 return func(t *testing.T) { 330 var want uint64 331 err := db.gcIndex.Iterate(func(item shed.Item) (stop bool, err error) { 332 want++ 333 return 334 }, nil) 335 if err != nil { 336 t.Fatal(err) 337 } 338 got, err := db.gcSize.Get() 339 if err != nil { 340 t.Fatal(err) 341 } 342 if got != want { 343 t.Errorf("got gc size %v, want %v", got, want) 344 } 345 } 346 } 347 348 // testIndexChunk embeds storageChunk with additional data that is stored 349 // in database. It is used for index values validations. 350 type testIndexChunk struct { 351 chunk.Chunk 352 storeTimestamp int64 353 } 354 355 // testItemsOrder tests the order of chunks in the index. If sortFunc is not nil, 356 // chunks will be sorted with it before validation. 357 func testItemsOrder(t *testing.T, i shed.Index, chunks []testIndexChunk, sortFunc func(i, j int) (less bool)) { 358 newItemsCountTest(i, len(chunks))(t) 359 360 if sortFunc != nil { 361 sort.Slice(chunks, sortFunc) 362 } 363 364 var cursor int 365 err := i.Iterate(func(item shed.Item) (stop bool, err error) { 366 want := chunks[cursor].Address() 367 got := item.Address 368 if !bytes.Equal(got, want) { 369 return true, fmt.Errorf("got address %x at position %v, want %x", got, cursor, want) 370 } 371 cursor++ 372 return false, nil 373 }, nil) 374 if err != nil { 375 t.Fatal(err) 376 } 377 } 378 379 // validateItem is a helper function that checks Item values. 380 func validateItem(t *testing.T, item shed.Item, address, data []byte, storeTimestamp, accessTimestamp int64) { 381 t.Helper() 382 383 if !bytes.Equal(item.Address, address) { 384 t.Errorf("got item address %x, want %x", item.Address, address) 385 } 386 if !bytes.Equal(item.Data, data) { 387 t.Errorf("got item data %x, want %x", item.Data, data) 388 } 389 if item.StoreTimestamp != storeTimestamp { 390 t.Errorf("got item store timestamp %v, want %v", item.StoreTimestamp, storeTimestamp) 391 } 392 if item.AccessTimestamp != accessTimestamp { 393 t.Errorf("got item access timestamp %v, want %v", item.AccessTimestamp, accessTimestamp) 394 } 395 } 396 397 // setNow replaces now function and 398 // returns a function that will reset it to the 399 // value before the change. 400 func setNow(f func() int64) (reset func()) { 401 current := now 402 reset = func() { now = current } 403 now = f 404 return reset 405 } 406 407 // TestSetNow tests if setNow function changes now function 408 // correctly and if its reset function resets the original function. 409 func TestSetNow(t *testing.T) { 410 // set the current function after the test finishes 411 defer func(f func() int64) { now = f }(now) 412 413 // expected value for the unchanged function 414 var original int64 = 1 415 // expected value for the changed function 416 var changed int64 = 2 417 418 // define the original (unchanged) functions 419 now = func() int64 { 420 return original 421 } 422 423 // get the time 424 got := now() 425 426 // test if got variable is set correctly 427 if got != original { 428 t.Errorf("got now value %v, want %v", got, original) 429 } 430 431 // set the new function 432 reset := setNow(func() int64 { 433 return changed 434 }) 435 436 // get the time 437 got = now() 438 439 // test if got variable is set correctly to changed value 440 if got != changed { 441 t.Errorf("got hook value %v, want %v", got, changed) 442 } 443 444 // set the function to the original one 445 reset() 446 447 // get the time 448 got = now() 449 450 // test if got variable is set correctly to original value 451 if got != original { 452 t.Errorf("got hook value %v, want %v", got, original) 453 } 454 }