github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/swarm/storage/common_test.go (about)

     1  // Copyright 2016 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 storage
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"flag"
    24  	"fmt"
    25  	"io"
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/log"
    31  	colorable "github.com/mattn/go-colorable"
    32  )
    33  
    34  var (
    35  	loglevel = flag.Int("loglevel", 3, "verbosity of logs")
    36  )
    37  
    38  func init() {
    39  	flag.Parse()
    40  	log.PrintOrigins(true)
    41  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
    42  }
    43  
    44  type brokenLimitedReader struct {
    45  	lr    io.Reader
    46  	errAt int
    47  	off   int
    48  	size  int
    49  }
    50  
    51  func brokenLimitReader(data io.Reader, size int, errAt int) *brokenLimitedReader {
    52  	return &brokenLimitedReader{
    53  		lr:    data,
    54  		errAt: errAt,
    55  		size:  size,
    56  	}
    57  }
    58  
    59  func mputRandomChunks(store ChunkStore, processors int, n int, chunksize int64) (hs []Address) {
    60  	return mput(store, processors, n, GenerateRandomChunk)
    61  }
    62  
    63  func mput(store ChunkStore, processors int, n int, f func(i int64) *Chunk) (hs []Address) {
    64  	wg := sync.WaitGroup{}
    65  	wg.Add(processors)
    66  	c := make(chan *Chunk)
    67  	for i := 0; i < processors; i++ {
    68  		go func() {
    69  			defer wg.Done()
    70  			for chunk := range c {
    71  				wg.Add(1)
    72  				chunk := chunk
    73  				store.Put(context.TODO(), chunk)
    74  				go func() {
    75  					defer wg.Done()
    76  					<-chunk.dbStoredC
    77  				}()
    78  			}
    79  		}()
    80  	}
    81  	fa := f
    82  	if _, ok := store.(*MemStore); ok {
    83  		fa = func(i int64) *Chunk {
    84  			chunk := f(i)
    85  			chunk.markAsStored()
    86  			return chunk
    87  		}
    88  	}
    89  	for i := 0; i < n; i++ {
    90  		chunk := fa(int64(i))
    91  		hs = append(hs, chunk.Addr)
    92  		c <- chunk
    93  	}
    94  	close(c)
    95  	wg.Wait()
    96  	return hs
    97  }
    98  
    99  func mget(store ChunkStore, hs []Address, f func(h Address, chunk *Chunk) error) error {
   100  	wg := sync.WaitGroup{}
   101  	wg.Add(len(hs))
   102  	errc := make(chan error)
   103  
   104  	for _, k := range hs {
   105  		go func(h Address) {
   106  			defer wg.Done()
   107  			chunk, err := store.Get(context.TODO(), h)
   108  			if err != nil {
   109  				errc <- err
   110  				return
   111  			}
   112  			if f != nil {
   113  				err = f(h, chunk)
   114  				if err != nil {
   115  					errc <- err
   116  					return
   117  				}
   118  			}
   119  		}(k)
   120  	}
   121  	go func() {
   122  		wg.Wait()
   123  		close(errc)
   124  	}()
   125  	var err error
   126  	select {
   127  	case err = <-errc:
   128  	case <-time.NewTimer(5 * time.Second).C:
   129  		err = fmt.Errorf("timed out after 5 seconds")
   130  	}
   131  	return err
   132  }
   133  
   134  func testDataReader(l int) (r io.Reader) {
   135  	return io.LimitReader(rand.Reader, int64(l))
   136  }
   137  
   138  func (r *brokenLimitedReader) Read(buf []byte) (int, error) {
   139  	if r.off+len(buf) > r.errAt {
   140  		return 0, fmt.Errorf("Broken reader")
   141  	}
   142  	r.off += len(buf)
   143  	return r.lr.Read(buf)
   144  }
   145  
   146  func generateRandomData(l int) (r io.Reader, slice []byte) {
   147  	slice = make([]byte, l)
   148  	if _, err := rand.Read(slice); err != nil {
   149  		panic("rand error")
   150  	}
   151  	r = io.LimitReader(bytes.NewReader(slice), int64(l))
   152  	return
   153  }
   154  
   155  func testStoreRandom(m ChunkStore, processors int, n int, chunksize int64, t *testing.T) {
   156  	hs := mputRandomChunks(m, processors, n, chunksize)
   157  	err := mget(m, hs, nil)
   158  	if err != nil {
   159  		t.Fatalf("testStore failed: %v", err)
   160  	}
   161  }
   162  
   163  func testStoreCorrect(m ChunkStore, processors int, n int, chunksize int64, t *testing.T) {
   164  	hs := mputRandomChunks(m, processors, n, chunksize)
   165  	f := func(h Address, chunk *Chunk) error {
   166  		if !bytes.Equal(h, chunk.Addr) {
   167  			return fmt.Errorf("key does not match retrieved chunk Key")
   168  		}
   169  		hasher := MakeHashFunc(DefaultHash)()
   170  		hasher.ResetWithLength(chunk.SData[:8])
   171  		hasher.Write(chunk.SData[8:])
   172  		exp := hasher.Sum(nil)
   173  		if !bytes.Equal(h, exp) {
   174  			return fmt.Errorf("key is not hash of chunk data")
   175  		}
   176  		return nil
   177  	}
   178  	err := mget(m, hs, f)
   179  	if err != nil {
   180  		t.Fatalf("testStore failed: %v", err)
   181  	}
   182  }
   183  
   184  func benchmarkStorePut(store ChunkStore, processors int, n int, chunksize int64, b *testing.B) {
   185  	chunks := make([]*Chunk, n)
   186  	i := 0
   187  	f := func(dataSize int64) *Chunk {
   188  		chunk := GenerateRandomChunk(dataSize)
   189  		chunks[i] = chunk
   190  		i++
   191  		return chunk
   192  	}
   193  
   194  	mput(store, processors, n, f)
   195  
   196  	f = func(dataSize int64) *Chunk {
   197  		chunk := chunks[i]
   198  		i++
   199  		return chunk
   200  	}
   201  
   202  	b.ReportAllocs()
   203  	b.ResetTimer()
   204  
   205  	for j := 0; j < b.N; j++ {
   206  		i = 0
   207  		mput(store, processors, n, f)
   208  	}
   209  }
   210  
   211  func benchmarkStoreGet(store ChunkStore, processors int, n int, chunksize int64, b *testing.B) {
   212  	hs := mputRandomChunks(store, processors, n, chunksize)
   213  	b.ReportAllocs()
   214  	b.ResetTimer()
   215  	for i := 0; i < b.N; i++ {
   216  		err := mget(store, hs, nil)
   217  		if err != nil {
   218  			b.Fatalf("mget failed: %v", err)
   219  		}
   220  	}
   221  }