github.com/codingfuture/orig-energi3@v0.8.4/swarm/storage/common_test.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package storage 19 20 import ( 21 "bytes" 22 "context" 23 "flag" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 "sync" 29 "testing" 30 "time" 31 32 "github.com/ethereum/go-ethereum/log" 33 ch "github.com/ethereum/go-ethereum/swarm/chunk" 34 "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 testing.Init() 44 flag.Parse() 45 log.PrintOrigins(true) 46 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) 47 } 48 49 type brokenLimitedReader struct { 50 lr io.Reader 51 errAt int 52 off int 53 size int 54 } 55 56 func brokenLimitReader(data io.Reader, size int, errAt int) *brokenLimitedReader { 57 return &brokenLimitedReader{ 58 lr: data, 59 errAt: errAt, 60 size: size, 61 } 62 } 63 64 func newLDBStore(t *testing.T) (*LDBStore, func()) { 65 dir, err := ioutil.TempDir("", "bzz-storage-test") 66 if err != nil { 67 t.Fatal(err) 68 } 69 log.Trace("memstore.tempdir", "dir", dir) 70 71 ldbparams := NewLDBStoreParams(NewDefaultStoreParams(), dir) 72 db, err := NewLDBStore(ldbparams) 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 cleanup := func() { 78 db.Close() 79 err := os.RemoveAll(dir) 80 if err != nil { 81 t.Fatal(err) 82 } 83 } 84 85 return db, cleanup 86 } 87 88 func mputRandomChunks(store ChunkStore, n int) ([]Chunk, error) { 89 return mput(store, n, GenerateRandomChunk) 90 } 91 92 func mput(store ChunkStore, n int, f func(i int64) Chunk) (hs []Chunk, err error) { 93 // put to localstore and wait for stored channel 94 // does not check delivery error state 95 errc := make(chan error) 96 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 97 defer cancel() 98 for i := int64(0); i < int64(n); i++ { 99 chunk := f(ch.DefaultSize) 100 go func() { 101 select { 102 case errc <- store.Put(ctx, chunk): 103 case <-ctx.Done(): 104 } 105 }() 106 hs = append(hs, chunk) 107 } 108 109 // wait for all chunks to be stored 110 for i := 0; i < n; i++ { 111 err := <-errc 112 if err != nil { 113 return nil, err 114 } 115 } 116 return hs, nil 117 } 118 119 func mget(store ChunkStore, hs []Address, f func(h Address, chunk Chunk) error) error { 120 wg := sync.WaitGroup{} 121 wg.Add(len(hs)) 122 errc := make(chan error) 123 124 for _, k := range hs { 125 go func(h Address) { 126 defer wg.Done() 127 // TODO: write timeout with context 128 chunk, err := store.Get(context.TODO(), h) 129 if err != nil { 130 errc <- err 131 return 132 } 133 if f != nil { 134 err = f(h, chunk) 135 if err != nil { 136 errc <- err 137 return 138 } 139 } 140 }(k) 141 } 142 go func() { 143 wg.Wait() 144 close(errc) 145 }() 146 var err error 147 timeout := 10 * time.Second 148 select { 149 case err = <-errc: 150 case <-time.NewTimer(timeout).C: 151 err = fmt.Errorf("timed out after %v", timeout) 152 } 153 return err 154 } 155 156 func (r *brokenLimitedReader) Read(buf []byte) (int, error) { 157 if r.off+len(buf) > r.errAt { 158 return 0, fmt.Errorf("Broken reader") 159 } 160 r.off += len(buf) 161 return r.lr.Read(buf) 162 } 163 164 func testStoreRandom(m ChunkStore, n int, t *testing.T) { 165 chunks, err := mputRandomChunks(m, n) 166 if err != nil { 167 t.Fatalf("expected no error, got %v", err) 168 } 169 err = mget(m, chunkAddresses(chunks), nil) 170 if err != nil { 171 t.Fatalf("testStore failed: %v", err) 172 } 173 } 174 175 func testStoreCorrect(m ChunkStore, n int, t *testing.T) { 176 chunks, err := mputRandomChunks(m, n) 177 if err != nil { 178 t.Fatalf("expected no error, got %v", err) 179 } 180 f := func(h Address, chunk Chunk) error { 181 if !bytes.Equal(h, chunk.Address()) { 182 return fmt.Errorf("key does not match retrieved chunk Address") 183 } 184 hasher := MakeHashFunc(DefaultHash)() 185 data := chunk.Data() 186 hasher.ResetWithLength(data[:8]) 187 hasher.Write(data[8:]) 188 exp := hasher.Sum(nil) 189 if !bytes.Equal(h, exp) { 190 return fmt.Errorf("key is not hash of chunk data") 191 } 192 return nil 193 } 194 err = mget(m, chunkAddresses(chunks), f) 195 if err != nil { 196 t.Fatalf("testStore failed: %v", err) 197 } 198 } 199 200 func benchmarkStorePut(store ChunkStore, n int, b *testing.B) { 201 chunks := make([]Chunk, n) 202 i := 0 203 f := func(dataSize int64) Chunk { 204 chunk := GenerateRandomChunk(dataSize) 205 chunks[i] = chunk 206 i++ 207 return chunk 208 } 209 210 mput(store, n, f) 211 212 f = func(dataSize int64) Chunk { 213 chunk := chunks[i] 214 i++ 215 return chunk 216 } 217 218 b.ReportAllocs() 219 b.ResetTimer() 220 221 for j := 0; j < b.N; j++ { 222 i = 0 223 mput(store, n, f) 224 } 225 } 226 227 func benchmarkStoreGet(store ChunkStore, n int, b *testing.B) { 228 chunks, err := mputRandomChunks(store, n) 229 if err != nil { 230 b.Fatalf("expected no error, got %v", err) 231 } 232 b.ReportAllocs() 233 b.ResetTimer() 234 addrs := chunkAddresses(chunks) 235 for i := 0; i < b.N; i++ { 236 err := mget(store, addrs, nil) 237 if err != nil { 238 b.Fatalf("mget failed: %v", err) 239 } 240 } 241 } 242 243 // MapChunkStore is a very simple ChunkStore implementation to store chunks in a map in memory. 244 type MapChunkStore struct { 245 chunks map[string]Chunk 246 mu sync.RWMutex 247 } 248 249 func NewMapChunkStore() *MapChunkStore { 250 return &MapChunkStore{ 251 chunks: make(map[string]Chunk), 252 } 253 } 254 255 func (m *MapChunkStore) Put(_ context.Context, ch Chunk) error { 256 m.mu.Lock() 257 defer m.mu.Unlock() 258 m.chunks[ch.Address().Hex()] = ch 259 return nil 260 } 261 262 func (m *MapChunkStore) Get(_ context.Context, ref Address) (Chunk, error) { 263 m.mu.RLock() 264 defer m.mu.RUnlock() 265 chunk := m.chunks[ref.Hex()] 266 if chunk == nil { 267 return nil, ErrChunkNotFound 268 } 269 return chunk, nil 270 } 271 272 // Need to implement Has from SyncChunkStore 273 func (m *MapChunkStore) Has(ctx context.Context, ref Address) bool { 274 m.mu.RLock() 275 defer m.mu.RUnlock() 276 277 _, has := m.chunks[ref.Hex()] 278 return has 279 } 280 281 func (m *MapChunkStore) Close() { 282 } 283 284 func chunkAddresses(chunks []Chunk) []Address { 285 addrs := make([]Address, len(chunks)) 286 for i, ch := range chunks { 287 addrs[i] = ch.Address() 288 } 289 return addrs 290 }