github.com/alanchchen/go-ethereum@v1.6.6-0.20170601190819-6171d01b1195/swarm/storage/chunker_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 "crypto/rand" 22 "encoding/binary" 23 "fmt" 24 "io" 25 "runtime" 26 "sync" 27 "testing" 28 "time" 29 ) 30 31 /* 32 Tests TreeChunker by splitting and joining a random byte slice 33 */ 34 35 type test interface { 36 Fatalf(string, ...interface{}) 37 Logf(string, ...interface{}) 38 } 39 40 type chunkerTester struct { 41 inputs map[uint64][]byte 42 chunks map[string]*Chunk 43 t test 44 } 45 46 func (self *chunkerTester) checkChunks(t *testing.T, want int) { 47 l := len(self.chunks) 48 if l != want { 49 t.Errorf("expected %v chunks, got %v", want, l) 50 } 51 } 52 53 func (self *chunkerTester) Split(chunker Splitter, data io.Reader, size int64, chunkC chan *Chunk, swg *sync.WaitGroup, expectedError error) (key Key) { 54 // reset 55 self.chunks = make(map[string]*Chunk) 56 57 if self.inputs == nil { 58 self.inputs = make(map[uint64][]byte) 59 } 60 61 quitC := make(chan bool) 62 timeout := time.After(600 * time.Second) 63 if chunkC != nil { 64 go func() { 65 for { 66 select { 67 case <-timeout: 68 self.t.Fatalf("Join timeout error") 69 case <-quitC: 70 return 71 case chunk := <-chunkC: 72 // self.chunks = append(self.chunks, chunk) 73 self.chunks[chunk.Key.String()] = chunk 74 if chunk.wg != nil { 75 chunk.wg.Done() 76 } 77 } 78 } 79 }() 80 } 81 key, err := chunker.Split(data, size, chunkC, swg, nil) 82 if err != nil && expectedError == nil { 83 self.t.Fatalf("Split error: %v", err) 84 } else if expectedError != nil && (err == nil || err.Error() != expectedError.Error()) { 85 self.t.Fatalf("Not receiving the correct error! Expected %v, received %v", expectedError, err) 86 } 87 if chunkC != nil { 88 if swg != nil { 89 swg.Wait() 90 } 91 close(quitC) 92 } 93 return 94 } 95 96 func (self *chunkerTester) Join(chunker Chunker, key Key, c int, chunkC chan *Chunk, quitC chan bool) LazySectionReader { 97 // reset but not the chunks 98 99 reader := chunker.Join(key, chunkC) 100 101 timeout := time.After(600 * time.Second) 102 i := 0 103 go func() { 104 for { 105 select { 106 case <-timeout: 107 self.t.Fatalf("Join timeout error") 108 109 case chunk, ok := <-chunkC: 110 if !ok { 111 close(quitC) 112 return 113 } 114 // this just mocks the behaviour of a chunk store retrieval 115 stored, success := self.chunks[chunk.Key.String()] 116 if !success { 117 self.t.Fatalf("not found") 118 return 119 } 120 chunk.SData = stored.SData 121 chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 122 close(chunk.C) 123 i++ 124 } 125 } 126 }() 127 return reader 128 } 129 130 func testRandomBrokenData(splitter Splitter, n int, tester *chunkerTester) { 131 data := io.LimitReader(rand.Reader, int64(n)) 132 brokendata := brokenLimitReader(data, n, n/2) 133 134 buf := make([]byte, n) 135 _, err := brokendata.Read(buf) 136 if err == nil || err.Error() != "Broken reader" { 137 tester.t.Fatalf("Broken reader is not broken, hence broken. Returns: %v", err) 138 } 139 140 data = io.LimitReader(rand.Reader, int64(n)) 141 brokendata = brokenLimitReader(data, n, n/2) 142 143 chunkC := make(chan *Chunk, 1000) 144 swg := &sync.WaitGroup{} 145 146 key := tester.Split(splitter, brokendata, int64(n), chunkC, swg, fmt.Errorf("Broken reader")) 147 tester.t.Logf(" Key = %v\n", key) 148 } 149 150 func testRandomData(splitter Splitter, n int, tester *chunkerTester) { 151 if tester.inputs == nil { 152 tester.inputs = make(map[uint64][]byte) 153 } 154 input, found := tester.inputs[uint64(n)] 155 var data io.Reader 156 if !found { 157 data, input = testDataReaderAndSlice(n) 158 tester.inputs[uint64(n)] = input 159 } else { 160 data = io.LimitReader(bytes.NewReader(input), int64(n)) 161 } 162 163 chunkC := make(chan *Chunk, 1000) 164 swg := &sync.WaitGroup{} 165 166 key := tester.Split(splitter, data, int64(n), chunkC, swg, nil) 167 tester.t.Logf(" Key = %v\n", key) 168 169 chunkC = make(chan *Chunk, 1000) 170 quitC := make(chan bool) 171 172 chunker := NewTreeChunker(NewChunkerParams()) 173 reader := tester.Join(chunker, key, 0, chunkC, quitC) 174 output := make([]byte, n) 175 r, err := reader.Read(output) 176 if r != n || err != io.EOF { 177 tester.t.Fatalf("read error read: %v n = %v err = %v\n", r, n, err) 178 } 179 if input != nil { 180 if !bytes.Equal(output, input) { 181 tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", input, output) 182 } 183 } 184 close(chunkC) 185 <-quitC 186 } 187 188 func TestRandomData(t *testing.T) { 189 // sizes := []int{123456} 190 sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 123456, 2345678} 191 tester := &chunkerTester{t: t} 192 chunker := NewTreeChunker(NewChunkerParams()) 193 for _, s := range sizes { 194 testRandomData(chunker, s, tester) 195 } 196 pyramid := NewPyramidChunker(NewChunkerParams()) 197 for _, s := range sizes { 198 testRandomData(pyramid, s, tester) 199 } 200 } 201 202 func TestRandomBrokenData(t *testing.T) { 203 sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 123456, 2345678} 204 tester := &chunkerTester{t: t} 205 chunker := NewTreeChunker(NewChunkerParams()) 206 for _, s := range sizes { 207 testRandomBrokenData(chunker, s, tester) 208 t.Logf("done size: %v", s) 209 } 210 } 211 212 func readAll(reader LazySectionReader, result []byte) { 213 size := int64(len(result)) 214 215 var end int64 216 for pos := int64(0); pos < size; pos += 1000 { 217 if pos+1000 > size { 218 end = size 219 } else { 220 end = pos + 1000 221 } 222 reader.ReadAt(result[pos:end], pos) 223 } 224 } 225 226 func benchReadAll(reader LazySectionReader) { 227 size, _ := reader.Size(nil) 228 output := make([]byte, 1000) 229 for pos := int64(0); pos < size; pos += 1000 { 230 reader.ReadAt(output, pos) 231 } 232 } 233 234 func benchmarkJoin(n int, t *testing.B) { 235 t.ReportAllocs() 236 for i := 0; i < t.N; i++ { 237 chunker := NewTreeChunker(NewChunkerParams()) 238 tester := &chunkerTester{t: t} 239 data := testDataReader(n) 240 241 chunkC := make(chan *Chunk, 1000) 242 swg := &sync.WaitGroup{} 243 244 key := tester.Split(chunker, data, int64(n), chunkC, swg, nil) 245 // t.StartTimer() 246 chunkC = make(chan *Chunk, 1000) 247 quitC := make(chan bool) 248 reader := tester.Join(chunker, key, i, chunkC, quitC) 249 benchReadAll(reader) 250 close(chunkC) 251 <-quitC 252 // t.StopTimer() 253 } 254 stats := new(runtime.MemStats) 255 runtime.ReadMemStats(stats) 256 fmt.Println(stats.Sys) 257 } 258 259 func benchmarkSplitTree(n int, t *testing.B) { 260 t.ReportAllocs() 261 for i := 0; i < t.N; i++ { 262 chunker := NewTreeChunker(NewChunkerParams()) 263 tester := &chunkerTester{t: t} 264 data := testDataReader(n) 265 tester.Split(chunker, data, int64(n), nil, nil, nil) 266 } 267 stats := new(runtime.MemStats) 268 runtime.ReadMemStats(stats) 269 fmt.Println(stats.Sys) 270 } 271 272 func benchmarkSplitPyramid(n int, t *testing.B) { 273 t.ReportAllocs() 274 for i := 0; i < t.N; i++ { 275 splitter := NewPyramidChunker(NewChunkerParams()) 276 tester := &chunkerTester{t: t} 277 data := testDataReader(n) 278 tester.Split(splitter, data, int64(n), nil, nil, nil) 279 } 280 stats := new(runtime.MemStats) 281 runtime.ReadMemStats(stats) 282 fmt.Println(stats.Sys) 283 } 284 285 func BenchmarkJoin_2(t *testing.B) { benchmarkJoin(100, t) } 286 func BenchmarkJoin_3(t *testing.B) { benchmarkJoin(1000, t) } 287 func BenchmarkJoin_4(t *testing.B) { benchmarkJoin(10000, t) } 288 func BenchmarkJoin_5(t *testing.B) { benchmarkJoin(100000, t) } 289 func BenchmarkJoin_6(t *testing.B) { benchmarkJoin(1000000, t) } 290 func BenchmarkJoin_7(t *testing.B) { benchmarkJoin(10000000, t) } 291 func BenchmarkJoin_8(t *testing.B) { benchmarkJoin(100000000, t) } 292 293 func BenchmarkSplitTree_2(t *testing.B) { benchmarkSplitTree(100, t) } 294 func BenchmarkSplitTree_2h(t *testing.B) { benchmarkSplitTree(500, t) } 295 func BenchmarkSplitTree_3(t *testing.B) { benchmarkSplitTree(1000, t) } 296 func BenchmarkSplitTree_3h(t *testing.B) { benchmarkSplitTree(5000, t) } 297 func BenchmarkSplitTree_4(t *testing.B) { benchmarkSplitTree(10000, t) } 298 func BenchmarkSplitTree_4h(t *testing.B) { benchmarkSplitTree(50000, t) } 299 func BenchmarkSplitTree_5(t *testing.B) { benchmarkSplitTree(100000, t) } 300 func BenchmarkSplitTree_6(t *testing.B) { benchmarkSplitTree(1000000, t) } 301 func BenchmarkSplitTree_7(t *testing.B) { benchmarkSplitTree(10000000, t) } 302 func BenchmarkSplitTree_8(t *testing.B) { benchmarkSplitTree(100000000, t) } 303 304 func BenchmarkSplitPyramid_2(t *testing.B) { benchmarkSplitPyramid(100, t) } 305 func BenchmarkSplitPyramid_2h(t *testing.B) { benchmarkSplitPyramid(500, t) } 306 func BenchmarkSplitPyramid_3(t *testing.B) { benchmarkSplitPyramid(1000, t) } 307 func BenchmarkSplitPyramid_3h(t *testing.B) { benchmarkSplitPyramid(5000, t) } 308 func BenchmarkSplitPyramid_4(t *testing.B) { benchmarkSplitPyramid(10000, t) } 309 func BenchmarkSplitPyramid_4h(t *testing.B) { benchmarkSplitPyramid(50000, t) } 310 func BenchmarkSplitPyramid_5(t *testing.B) { benchmarkSplitPyramid(100000, t) } 311 func BenchmarkSplitPyramid_6(t *testing.B) { benchmarkSplitPyramid(1000000, t) } 312 func BenchmarkSplitPyramid_7(t *testing.B) { benchmarkSplitPyramid(10000000, t) } 313 func BenchmarkSplitPyramid_8(t *testing.B) { benchmarkSplitPyramid(100000000, t) } 314 315 // godep go test -bench ./swarm/storage -cpuprofile cpu.out -memprofile mem.out