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  }