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 }