github.com/ethersphere/bee/v2@v2.2.0/pkg/file/splitter/splitter_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package splitter_test 6 7 import ( 8 "bytes" 9 "context" 10 "strconv" 11 "testing" 12 "time" 13 14 "github.com/ethersphere/bee/v2/pkg/file" 15 "github.com/ethersphere/bee/v2/pkg/file/splitter" 16 "github.com/ethersphere/bee/v2/pkg/storage/inmemchunkstore" 17 "github.com/ethersphere/bee/v2/pkg/swarm" 18 "github.com/ethersphere/bee/v2/pkg/util/testutil" 19 mockbytes "gitlab.com/nolash/go-mockbytes" 20 ) 21 22 // TestSplitIncomplete tests that the Split method returns an error if 23 // the amounts of bytes written does not match the data length passed to the method. 24 func TestSplitIncomplete(t *testing.T) { 25 t.Parallel() 26 27 testData := make([]byte, 42) 28 store := inmemchunkstore.New() 29 s := splitter.NewSimpleSplitter(store) 30 31 testDataReader := file.NewSimpleReadCloser(testData) 32 _, err := s.Split(context.Background(), testDataReader, 41, false) 33 if err == nil { 34 t.Fatalf("expected error on EOF before full length write") 35 } 36 } 37 38 // TestSplitSingleChunk hashes one single chunk and verifies 39 // that that corresponding chunk exist in the store afterwards. 40 func TestSplitSingleChunk(t *testing.T) { 41 t.Parallel() 42 43 g := mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) 44 testData, err := g.SequentialBytes(swarm.ChunkSize) 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 store := inmemchunkstore.New() 50 s := splitter.NewSimpleSplitter(store) 51 52 testDataReader := file.NewSimpleReadCloser(testData) 53 resultAddress, err := s.Split(context.Background(), testDataReader, int64(len(testData)), false) 54 if err != nil { 55 t.Fatal(err) 56 } 57 58 testHashHex := "c10090961e7682a10890c334d759a28426647141213abda93b096b892824d2ef" 59 testHashAddress := swarm.MustParseHexAddress(testHashHex) 60 if !testHashAddress.Equal(resultAddress) { 61 t.Fatalf("expected %v, got %v", testHashAddress, resultAddress) 62 } 63 64 _, err = store.Get(context.Background(), resultAddress) 65 if err != nil { 66 t.Fatal(err) 67 } 68 } 69 70 // TestSplitThreeLevels hashes enough data chunks in order to 71 // create a full chunk of intermediate hashes. 72 // It verifies that all created chunks exist in the store afterwards. 73 func TestSplitThreeLevels(t *testing.T) { 74 t.Parallel() 75 76 // edge case selected from internal/job_test.go 77 g := mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) 78 testData, err := g.SequentialBytes(swarm.ChunkSize * 128) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 store := inmemchunkstore.New() 84 s := splitter.NewSimpleSplitter(store) 85 86 testDataReader := file.NewSimpleReadCloser(testData) 87 resultAddress, err := s.Split(context.Background(), testDataReader, int64(len(testData)), false) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 testHashHex := "3047d841077898c26bbe6be652a2ec590a5d9bd7cd45d290ea42511b48753c09" 93 testHashAddress := swarm.MustParseHexAddress(testHashHex) 94 if !testHashAddress.Equal(resultAddress) { 95 t.Fatalf("expected %v, got %v", testHashAddress, resultAddress) 96 } 97 98 _, err = store.Get(context.Background(), resultAddress) 99 if err != nil { 100 t.Fatal(err) 101 } 102 103 rootChunk, err := store.Get(context.Background(), resultAddress) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 rootData := rootChunk.Data()[8:] 109 for i := 0; i < swarm.ChunkSize; i += swarm.SectionSize { 110 dataAddressBytes := rootData[i : i+swarm.SectionSize] 111 dataAddress := swarm.NewAddress(dataAddressBytes) 112 _, err := store.Get(context.Background(), dataAddress) 113 if err != nil { 114 t.Fatal(err) 115 } 116 } 117 } 118 119 // TestUnalignedSplit tests that correct hash is generated regardless of 120 // individual write sizes at the source of the data. 121 func TestUnalignedSplit(t *testing.T) { 122 t.Parallel() 123 124 var ( 125 storer = inmemchunkstore.New() 126 chunkPipe = file.NewChunkPipe() 127 ) 128 129 // test vector taken from pkg/file/testing/vector.go 130 var ( 131 dataLen int64 = swarm.ChunkSize*2 + 32 132 expectAddrHex = "61416726988f77b874435bdd89a419edc3861111884fd60e8adf54e2f299efd6" 133 g = mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) 134 ) 135 136 // generate test vector data content 137 content, err := g.SequentialBytes(int(dataLen)) 138 if err != nil { 139 t.Fatal(err) 140 } 141 142 // perform the split in a separate thread 143 sp := splitter.NewSimpleSplitter(storer) 144 ctx := context.Background() 145 doneC := make(chan swarm.Address) 146 errC := make(chan error) 147 go func() { 148 addr, err := sp.Split(ctx, chunkPipe, dataLen, false) 149 if err != nil { 150 errC <- err 151 } else { 152 doneC <- addr 153 } 154 close(doneC) 155 close(errC) 156 }() 157 158 // perform the writes in unaligned bursts 159 writeSizes := []int{swarm.ChunkSize - 40, 40 + 32, swarm.ChunkSize} 160 contentBuf := bytes.NewReader(content) 161 cursor := 0 162 for _, writeSize := range writeSizes { 163 data := make([]byte, writeSize) 164 _, err = contentBuf.Read(data) 165 if err != nil { 166 t.Fatal(err) 167 } 168 c, err := chunkPipe.Write(data) 169 if err != nil { 170 t.Fatal(err) 171 } 172 cursor += c 173 } 174 err = chunkPipe.Close() 175 if err != nil { 176 t.Fatal(err) 177 } 178 179 // read and hopefully not weep 180 timer := time.NewTimer(time.Millisecond * 100) 181 select { 182 case addr := <-doneC: 183 expectAddr := swarm.MustParseHexAddress(expectAddrHex) 184 if !expectAddr.Equal(addr) { 185 t.Fatalf("addr mismatch, expected %s, got %s", expectAddr, addr) 186 } 187 case err := <-errC: 188 t.Fatal(err) 189 case <-timer.C: 190 t.Fatal("timeout") 191 } 192 } 193 194 /* 195 go test -v -bench=. -run Bench -benchmem 196 goos: linux 197 goarch: amd64 198 pkg: github.com/ethersphere/bee/pkg/file/splitter 199 BenchmarkSplitter 200 BenchmarkSplitter/1000-bytes 201 BenchmarkSplitter/1000-bytes-4 12667 95965 ns/op 154870 B/op 367 allocs/op 202 BenchmarkSplitter/10000-bytes 203 BenchmarkSplitter/10000-bytes-4 2808 418753 ns/op 369764 B/op 1624 allocs/op 204 BenchmarkSplitter/100000-bytes 205 BenchmarkSplitter/100000-bytes-4 349 3342003 ns/op 2042891 B/op 11810 allocs/op 206 BenchmarkSplitter/1000000-bytes 207 BenchmarkSplitter/1000000-bytes-4 33 30905753 ns/op 18825910 B/op 113721 allocs/op 208 BenchmarkSplitter/10000000-bytes 209 BenchmarkSplitter/10000000-bytes-4 4 295615658 ns/op 186417904 B/op 1132527 allocs/op 210 BenchmarkSplitter/100000000-bytes 211 BenchmarkSplitter/100000000-bytes-4 1 2972826021 ns/op 1861374352 B/op 11321235 allocs/op 212 PASS 213 ok github.com/ethersphere/bee/pkg/file/splitter 22.476s 214 */ 215 216 func BenchmarkSplitter(b *testing.B) { 217 for _, count := range []int{ 218 1000, // 1k 219 10000, // 10 k 220 100000, // 100 k 221 1000000, // 1 mb 222 10000000, // 10 mb 223 100000000, // 100 mb 224 } { 225 b.Run(strconv.Itoa(count)+"-bytes", func(b *testing.B) { 226 for n := 0; n < b.N; n++ { 227 benchmarkSplitter(b, count) 228 } 229 }) 230 } 231 } 232 233 func benchmarkSplitter(b *testing.B, count int) { 234 b.Helper() 235 236 b.StopTimer() 237 238 data := testutil.RandBytes(b, count) 239 m := inmemchunkstore.New() 240 s := splitter.NewSimpleSplitter(m) 241 242 testDataReader := file.NewSimpleReadCloser(data) 243 b.StartTimer() 244 245 _, err := s.Split(context.Background(), testDataReader, int64(len(data)), false) 246 if err != nil { 247 b.Fatal(err) 248 } 249 }