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