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  }