github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/bisect/minimize/slice_test.go (about) 1 // Copyright 2023 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package minimize 5 6 import ( 7 "fmt" 8 "math" 9 "math/rand" 10 "testing" 11 "time" 12 13 "github.com/google/syzkaller/pkg/testutil" 14 "github.com/stretchr/testify/assert" 15 ) 16 17 func TestBisectSliceToZero(t *testing.T) { 18 t.Parallel() 19 array := make([]int, 100) 20 ret, err := Slice(Config[int]{ 21 Pred: func(arr []int) (bool, error) { 22 // No elements are needed. 23 return true, nil 24 }, 25 Logf: t.Logf, 26 }, array) 27 assert.NoError(t, err) 28 assert.Len(t, ret, 0) 29 } 30 31 func TestBisectSliceFull(t *testing.T) { 32 t.Parallel() 33 array := make([]int, 100) 34 ret, err := Slice(Config[int]{ 35 Pred: func(arr []int) (bool, error) { 36 // All elements are needed. 37 return false, nil 38 }, 39 Logf: t.Logf, 40 }, array) 41 assert.NoError(t, err) 42 assert.Equal(t, ret, array) 43 } 44 45 func TestBisectRandomSlice(t *testing.T) { 46 t.Parallel() 47 r := rand.New(testutil.RandSource(t)) 48 for i := 0; i < testutil.IterCount(); i++ { 49 // Create an array of random size and set the elements that must remain to non-zero values. 50 size := r.Intn(50) 51 subset := r.Intn(size + 1) 52 array := make([]int, size) 53 for _, j := range r.Perm(size)[:subset] { 54 array[j] = j + 1 55 } 56 var expect []int 57 for _, j := range array { 58 if j > 0 { 59 expect = append(expect, j) 60 } 61 } 62 predCalls := 0 63 ret, err := Slice(Config[int]{ 64 Pred: func(arr []int) (bool, error) { 65 predCalls++ 66 // All elements of the subarray must be present. 67 nonZero := 0 68 for _, x := range arr { 69 if x > 0 { 70 nonZero++ 71 } 72 } 73 return nonZero == subset, nil 74 }, 75 Logf: t.Logf, 76 }, array) 77 assert.NoError(t, err) 78 assert.EqualValues(t, expect, ret) 79 // Ensure we don't make too many predicate calls. 80 maxCalls := 3 + 2*subset*(1+int(math.Floor(math.Log2(float64(size))))) 81 assert.LessOrEqual(t, predCalls, maxCalls) 82 } 83 } 84 85 func BenchmarkSplits(b *testing.B) { 86 for _, guilty := range []int{1, 2, 3, 4} { 87 guilty := guilty 88 b.Run(fmt.Sprintf("%d_guilty", guilty), func(b *testing.B) { 89 var sum int 90 for i := 0; i < b.N; i++ { 91 sum += runMinimize(guilty) 92 } 93 b.ReportMetric(float64(sum)/float64(b.N), "remaining-elements") 94 }) 95 } 96 } 97 98 func runMinimize(guilty int) int { 99 const size = 300 100 const steps = 5 101 102 r := rand.New(rand.NewSource(time.Now().UnixNano())) 103 array := make([]int, size) 104 for _, j := range r.Perm(size)[:guilty] { 105 array[j] = 1 106 } 107 108 ret, _ := Slice(Config[int]{ 109 MaxSteps: steps, 110 Pred: func(arr []int) (bool, error) { 111 nonZero := 0 112 for _, x := range arr { 113 if x > 0 { 114 nonZero++ 115 } 116 } 117 return nonZero == guilty, nil 118 }, 119 }, array) 120 return len(ret) 121 }