github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/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 "io/ioutil" 27 "os" 28 "sync" 29 "testing" 30 "time" 31 32 "github.com/FusionFoundation/efsn/log" 33 ch "github.com/FusionFoundation/efsn/swarm/chunk" 34 colorable "github.com/mattn/go-colorable" 35 ) 36 37 var ( 38 loglevel = flag.Int("loglevel", 3, "verbosity of logs") 39 getTimeout = 30 * time.Second 40 ) 41 42 func init() { 43 flag.Parse() 44 log.PrintOrigins(true) 45 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) 46 } 47 48 type brokenLimitedReader struct { 49 lr io.Reader 50 errAt int 51 off int 52 size int 53 } 54 55 func brokenLimitReader(data io.Reader, size int, errAt int) *brokenLimitedReader { 56 return &brokenLimitedReader{ 57 lr: data, 58 errAt: errAt, 59 size: size, 60 } 61 } 62 63 func newLDBStore(t *testing.T) (*LDBStore, func()) { 64 dir, err := ioutil.TempDir("", "bzz-storage-test") 65 if err != nil { 66 t.Fatal(err) 67 } 68 log.Trace("memstore.tempdir", "dir", dir) 69 70 ldbparams := NewLDBStoreParams(NewDefaultStoreParams(), dir) 71 db, err := NewLDBStore(ldbparams) 72 if err != nil { 73 t.Fatal(err) 74 } 75 76 cleanup := func() { 77 db.Close() 78 err := os.RemoveAll(dir) 79 if err != nil { 80 t.Fatal(err) 81 } 82 } 83 84 return db, cleanup 85 } 86 87 func mputRandomChunks(store ChunkStore, n int, chunksize int64) ([]Chunk, error) { 88 return mput(store, n, GenerateRandomChunk) 89 } 90 91 func mputChunks(store ChunkStore, chunks ...Chunk) error { 92 i := 0 93 f := func(n int64) Chunk { 94 chunk := chunks[i] 95 i++ 96 return chunk 97 } 98 _, err := mput(store, len(chunks), f) 99 return err 100 } 101 102 func mput(store ChunkStore, n int, f func(i int64) Chunk) (hs []Chunk, err error) { 103 // put to localstore and wait for stored channel 104 // does not check delivery error state 105 errc := make(chan error) 106 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 107 defer cancel() 108 for i := int64(0); i < int64(n); i++ { 109 chunk := f(ch.DefaultSize) 110 go func() { 111 select { 112 case errc <- store.Put(ctx, chunk): 113 case <-ctx.Done(): 114 } 115 }() 116 hs = append(hs, chunk) 117 } 118 119 // wait for all chunks to be stored 120 for i := 0; i < n; i++ { 121 err := <-errc 122 if err != nil { 123 return nil, err 124 } 125 } 126 return hs, nil 127 } 128 129 func mget(store ChunkStore, hs []Address, f func(h Address, chunk Chunk) error) error { 130 wg := sync.WaitGroup{} 131 wg.Add(len(hs)) 132 errc := make(chan error) 133 134 for _, k := range hs { 135 go func(h Address) { 136 defer wg.Done() 137 // TODO: write timeout with context 138 chunk, err := store.Get(context.TODO(), h) 139 if err != nil { 140 errc <- err 141 return 142 } 143 if f != nil { 144 err = f(h, chunk) 145 if err != nil { 146 errc <- err 147 return 148 } 149 } 150 }(k) 151 } 152 go func() { 153 wg.Wait() 154 close(errc) 155 }() 156 var err error 157 select { 158 case err = <-errc: 159 case <-time.NewTimer(5 * time.Second).C: 160 err = fmt.Errorf("timed out after 5 seconds") 161 } 162 return err 163 } 164 165 func testDataReader(l int) (r io.Reader) { 166 return io.LimitReader(rand.Reader, int64(l)) 167 } 168 169 func (r *brokenLimitedReader) Read(buf []byte) (int, error) { 170 if r.off+len(buf) > r.errAt { 171 return 0, fmt.Errorf("Broken reader") 172 } 173 r.off += len(buf) 174 return r.lr.Read(buf) 175 } 176 177 func testStoreRandom(m ChunkStore, n int, chunksize int64, t *testing.T) { 178 chunks, err := mputRandomChunks(m, n, chunksize) 179 if err != nil { 180 t.Fatalf("expected no error, got %v", err) 181 } 182 err = mget(m, chunkAddresses(chunks), nil) 183 if err != nil { 184 t.Fatalf("testStore failed: %v", err) 185 } 186 } 187 188 func testStoreCorrect(m ChunkStore, n int, chunksize int64, t *testing.T) { 189 chunks, err := mputRandomChunks(m, n, chunksize) 190 if err != nil { 191 t.Fatalf("expected no error, got %v", err) 192 } 193 f := func(h Address, chunk Chunk) error { 194 if !bytes.Equal(h, chunk.Address()) { 195 return fmt.Errorf("key does not match retrieved chunk Address") 196 } 197 hasher := MakeHashFunc(DefaultHash)() 198 hasher.ResetWithLength(chunk.SpanBytes()) 199 hasher.Write(chunk.Payload()) 200 exp := hasher.Sum(nil) 201 if !bytes.Equal(h, exp) { 202 return fmt.Errorf("key is not hash of chunk data") 203 } 204 return nil 205 } 206 err = mget(m, chunkAddresses(chunks), f) 207 if err != nil { 208 t.Fatalf("testStore failed: %v", err) 209 } 210 } 211 212 func benchmarkStorePut(store ChunkStore, n int, chunksize int64, b *testing.B) { 213 chunks := make([]Chunk, n) 214 i := 0 215 f := func(dataSize int64) Chunk { 216 chunk := GenerateRandomChunk(dataSize) 217 chunks[i] = chunk 218 i++ 219 return chunk 220 } 221 222 mput(store, n, f) 223 224 f = func(dataSize int64) Chunk { 225 chunk := chunks[i] 226 i++ 227 return chunk 228 } 229 230 b.ReportAllocs() 231 b.ResetTimer() 232 233 for j := 0; j < b.N; j++ { 234 i = 0 235 mput(store, n, f) 236 } 237 } 238 239 func benchmarkStoreGet(store ChunkStore, n int, chunksize int64, b *testing.B) { 240 chunks, err := mputRandomChunks(store, n, chunksize) 241 if err != nil { 242 b.Fatalf("expected no error, got %v", err) 243 } 244 b.ReportAllocs() 245 b.ResetTimer() 246 addrs := chunkAddresses(chunks) 247 for i := 0; i < b.N; i++ { 248 err := mget(store, addrs, nil) 249 if err != nil { 250 b.Fatalf("mget failed: %v", err) 251 } 252 } 253 } 254 255 // MapChunkStore is a very simple ChunkStore implementation to store chunks in a map in memory. 256 type MapChunkStore struct { 257 chunks map[string]Chunk 258 mu sync.RWMutex 259 } 260 261 func NewMapChunkStore() *MapChunkStore { 262 return &MapChunkStore{ 263 chunks: make(map[string]Chunk), 264 } 265 } 266 267 func (m *MapChunkStore) Put(_ context.Context, ch Chunk) error { 268 m.mu.Lock() 269 defer m.mu.Unlock() 270 m.chunks[ch.Address().Hex()] = ch 271 return nil 272 } 273 274 func (m *MapChunkStore) Get(_ context.Context, ref Address) (Chunk, error) { 275 m.mu.RLock() 276 defer m.mu.RUnlock() 277 chunk := m.chunks[ref.Hex()] 278 if chunk == nil { 279 return nil, ErrChunkNotFound 280 } 281 return chunk, nil 282 } 283 284 func (m *MapChunkStore) Close() { 285 } 286 287 func chunkAddresses(chunks []Chunk) []Address { 288 addrs := make([]Address, len(chunks)) 289 for i, ch := range chunks { 290 addrs[i] = ch.Address() 291 } 292 return addrs 293 }