github.com/JimmyHuang454/JLS-go@v0.0.0-20230831150107-90d536585ba0/internal/fuzz/minimize_test.go (about) 1 // Copyright 2021 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 //go:build darwin || freebsd || linux || windows 6 7 package fuzz 8 9 import ( 10 "bytes" 11 "context" 12 "errors" 13 "fmt" 14 "reflect" 15 "testing" 16 "time" 17 "unicode" 18 "unicode/utf8" 19 ) 20 21 func TestMinimizeInput(t *testing.T) { 22 type testcase struct { 23 name string 24 fn func(CorpusEntry) error 25 input []any 26 expected []any 27 } 28 cases := []testcase{ 29 { 30 name: "ones_byte", 31 fn: func(e CorpusEntry) error { 32 b := e.Values[0].([]byte) 33 ones := 0 34 for _, v := range b { 35 if v == 1 { 36 ones++ 37 } 38 } 39 if ones == 3 { 40 return fmt.Errorf("bad %v", e.Values[0]) 41 } 42 return nil 43 }, 44 input: []any{[]byte{0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 45 expected: []any{[]byte{1, 1, 1}}, 46 }, 47 { 48 name: "single_bytes", 49 fn: func(e CorpusEntry) error { 50 b := e.Values[0].([]byte) 51 if len(b) < 2 { 52 return nil 53 } 54 if len(b) == 2 && b[0] == 1 && b[1] == 2 { 55 return nil 56 } 57 return fmt.Errorf("bad %v", e.Values[0]) 58 }, 59 input: []any{[]byte{1, 2, 3, 4, 5}}, 60 expected: []any{[]byte("00")}, 61 }, 62 { 63 name: "set_of_bytes", 64 fn: func(e CorpusEntry) error { 65 b := e.Values[0].([]byte) 66 if len(b) < 3 { 67 return nil 68 } 69 if bytes.Equal(b, []byte{0, 1, 2, 3, 4, 5}) || bytes.Equal(b, []byte{0, 4, 5}) { 70 return fmt.Errorf("bad %v", e.Values[0]) 71 } 72 return nil 73 }, 74 input: []any{[]byte{0, 1, 2, 3, 4, 5}}, 75 expected: []any{[]byte{0, 4, 5}}, 76 }, 77 { 78 name: "non_ascii_bytes", 79 fn: func(e CorpusEntry) error { 80 b := e.Values[0].([]byte) 81 if len(b) == 3 { 82 return fmt.Errorf("bad %v", e.Values[0]) 83 } 84 return nil 85 }, 86 input: []any{[]byte("ท")}, // ท is 3 bytes 87 expected: []any{[]byte("000")}, 88 }, 89 { 90 name: "ones_string", 91 fn: func(e CorpusEntry) error { 92 b := e.Values[0].(string) 93 ones := 0 94 for _, v := range b { 95 if v == '1' { 96 ones++ 97 } 98 } 99 if ones == 3 { 100 return fmt.Errorf("bad %v", e.Values[0]) 101 } 102 return nil 103 }, 104 input: []any{"001010001000000000000000000"}, 105 expected: []any{"111"}, 106 }, 107 { 108 name: "string_length", 109 fn: func(e CorpusEntry) error { 110 b := e.Values[0].(string) 111 if len(b) == 5 { 112 return fmt.Errorf("bad %v", e.Values[0]) 113 } 114 return nil 115 }, 116 input: []any{"zzzzz"}, 117 expected: []any{"00000"}, 118 }, 119 { 120 name: "string_with_letter", 121 fn: func(e CorpusEntry) error { 122 b := e.Values[0].(string) 123 r, _ := utf8.DecodeRune([]byte(b)) 124 if unicode.IsLetter(r) { 125 return fmt.Errorf("bad %v", e.Values[0]) 126 } 127 return nil 128 }, 129 input: []any{"ZZZZZ"}, 130 expected: []any{"A"}, 131 }, 132 } 133 134 for _, tc := range cases { 135 tc := tc 136 t.Run(tc.name, func(t *testing.T) { 137 t.Parallel() 138 ws := &workerServer{ 139 fuzzFn: func(e CorpusEntry) (time.Duration, error) { 140 return time.Second, tc.fn(e) 141 }, 142 } 143 mem := &sharedMem{region: make([]byte, 100)} // big enough to hold value and header 144 vals := tc.input 145 success, err := ws.minimizeInput(context.Background(), vals, mem, minimizeArgs{}) 146 if !success { 147 t.Errorf("minimizeInput did not succeed") 148 } 149 if err == nil { 150 t.Fatal("minimizeInput didn't provide an error") 151 } 152 if expected := fmt.Sprintf("bad %v", tc.expected[0]); err.Error() != expected { 153 t.Errorf("unexpected error: got %q, want %q", err, expected) 154 } 155 if !reflect.DeepEqual(vals, tc.expected) { 156 t.Errorf("unexpected results: got %v, want %v", vals, tc.expected) 157 } 158 }) 159 } 160 } 161 162 // TestMinimizeFlaky checks that if we're minimizing an interesting 163 // input and a flaky failure occurs, that minimization was not indicated 164 // to be successful, and the error isn't returned (since it's flaky). 165 func TestMinimizeFlaky(t *testing.T) { 166 ws := &workerServer{fuzzFn: func(e CorpusEntry) (time.Duration, error) { 167 return time.Second, errors.New("ohno") 168 }} 169 mem := &sharedMem{region: make([]byte, 100)} // big enough to hold value and header 170 vals := []any{[]byte(nil)} 171 args := minimizeArgs{KeepCoverage: make([]byte, len(coverageSnapshot))} 172 success, err := ws.minimizeInput(context.Background(), vals, mem, args) 173 if success { 174 t.Error("unexpected success") 175 } 176 if err != nil { 177 t.Errorf("unexpected error: %v", err) 178 } 179 if count := mem.header().count; count != 1 { 180 t.Errorf("count: got %d, want 1", count) 181 } 182 }