github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/lockedfile/transform_test.go (about) 1 // Copyright 2019 The Go 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 // js does not support inter-process file locking. 6 // 7 //go:build !js 8 // +build !js 9 10 package lockedfile_test 11 12 import ( 13 "bytes" 14 "encoding/binary" 15 "math/rand" 16 "path/filepath" 17 "testing" 18 "time" 19 20 "golang.org/x/tools/internal/lockedfile" 21 ) 22 23 func isPowerOf2(x int) bool { 24 return x > 0 && x&(x-1) == 0 25 } 26 27 func roundDownToPowerOf2(x int) int { 28 if x <= 0 { 29 panic("nonpositive x") 30 } 31 bit := 1 32 for x != bit { 33 x = x &^ bit 34 bit <<= 1 35 } 36 return x 37 } 38 39 func TestTransform(t *testing.T) { 40 dir, remove := mustTempDir(t) 41 defer remove() 42 path := filepath.Join(dir, "blob.bin") 43 44 const maxChunkWords = 8 << 10 45 buf := make([]byte, 2*maxChunkWords*8) 46 for i := uint64(0); i < 2*maxChunkWords; i++ { 47 binary.LittleEndian.PutUint64(buf[i*8:], i) 48 } 49 if err := lockedfile.Write(path, bytes.NewReader(buf[:8]), 0666); err != nil { 50 t.Fatal(err) 51 } 52 53 var attempts int64 = 128 54 if !testing.Short() { 55 attempts *= 16 56 } 57 const parallel = 32 58 59 var sem = make(chan bool, parallel) 60 61 for n := attempts; n > 0; n-- { 62 sem <- true 63 go func() { 64 defer func() { <-sem }() 65 66 time.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond) 67 chunkWords := roundDownToPowerOf2(rand.Intn(maxChunkWords) + 1) 68 offset := rand.Intn(chunkWords) 69 70 err := lockedfile.Transform(path, func(data []byte) (chunk []byte, err error) { 71 chunk = buf[offset*8 : (offset+chunkWords)*8] 72 73 if len(data)&^7 != len(data) { 74 t.Errorf("read %d bytes, but each write is an integer multiple of 8 bytes", len(data)) 75 return chunk, nil 76 } 77 78 words := len(data) / 8 79 if !isPowerOf2(words) { 80 t.Errorf("read %d 8-byte words, but each write is a power-of-2 number of words", words) 81 return chunk, nil 82 } 83 84 u := binary.LittleEndian.Uint64(data) 85 for i := 1; i < words; i++ { 86 next := binary.LittleEndian.Uint64(data[i*8:]) 87 if next != u+1 { 88 t.Errorf("wrote sequential integers, but read integer out of sequence at offset %d", i) 89 return chunk, nil 90 } 91 u = next 92 } 93 94 return chunk, nil 95 }) 96 97 if err != nil { 98 t.Errorf("unexpected error from Transform: %v", err) 99 } 100 }() 101 } 102 103 for n := parallel; n > 0; n-- { 104 sem <- true 105 } 106 }