github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 TestBisectSliceWithFixed(t *testing.T) { 46 t.Parallel() 47 array := make([]int, 100) 48 for i := 0; i < 100; i++ { 49 array[i] = i 50 } 51 ret, err := SliceWithFixed(Config[int]{ 52 Pred: func(arr []int) (bool, error) { 53 // Let's ensure that there are always fixed elements. 54 values := map[int]bool{} 55 for _, v := range arr { 56 values[v] = true 57 } 58 if !values[0] || !values[20] || !values[40] { 59 t.Fatal("predicate step without fixed elements") 60 } 61 // And also require the elements 25 and 50. 62 return values[25] && values[50], nil 63 }, 64 Logf: t.Logf, 65 }, array, func(elem int) bool { 66 // Let's keep these 3 elements. 67 return elem == 0 || elem == 20 || elem == 40 68 }) 69 assert.NoError(t, err) 70 assert.Equal(t, []int{0, 20, 25, 40, 50}, ret) 71 } 72 73 func TestBisectRandomSlice(t *testing.T) { 74 t.Parallel() 75 r := rand.New(testutil.RandSource(t)) 76 for i := 0; i < testutil.IterCount(); i++ { 77 // Create an array of random size and set the elements that must remain to non-zero values. 78 size := r.Intn(50) 79 subset := r.Intn(size + 1) 80 array := make([]int, size) 81 for _, j := range r.Perm(size)[:subset] { 82 array[j] = j + 1 83 } 84 var expect []int 85 for _, j := range array { 86 if j > 0 { 87 expect = append(expect, j) 88 } 89 } 90 predCalls := 0 91 ret, err := Slice(Config[int]{ 92 Pred: func(arr []int) (bool, error) { 93 predCalls++ 94 // All elements of the subarray must be present. 95 nonZero := 0 96 for _, x := range arr { 97 if x > 0 { 98 nonZero++ 99 } 100 } 101 return nonZero == subset, nil 102 }, 103 Logf: t.Logf, 104 }, array) 105 assert.NoError(t, err) 106 assert.EqualValues(t, expect, ret) 107 // Ensure we don't make too many predicate calls. 108 maxCalls := 3 + 2*subset*(1+int(math.Floor(math.Log2(float64(size))))) 109 assert.LessOrEqual(t, predCalls, maxCalls) 110 } 111 } 112 113 func BenchmarkSplits(b *testing.B) { 114 for _, guilty := range []int{1, 2, 3, 4} { 115 b.Run(fmt.Sprintf("%d_guilty", guilty), func(b *testing.B) { 116 var sum int 117 for i := 0; i < b.N; i++ { 118 sum += runMinimize(guilty) 119 } 120 b.ReportMetric(float64(sum)/float64(b.N), "remaining-elements") 121 }) 122 } 123 } 124 125 func runMinimize(guilty int) int { 126 const size = 300 127 const steps = 5 128 129 r := rand.New(rand.NewSource(time.Now().UnixNano())) 130 array := make([]int, size) 131 for _, j := range r.Perm(size)[:guilty] { 132 array[j] = 1 133 } 134 135 ret, _ := Slice(Config[int]{ 136 MaxSteps: steps, 137 Pred: func(arr []int) (bool, error) { 138 nonZero := 0 139 for _, x := range arr { 140 if x > 0 { 141 nonZero++ 142 } 143 } 144 return nonZero == guilty, nil 145 }, 146 }, array) 147 return len(ret) 148 }