| @@ -13,6 +13,7 @@ import ( · 12 unmodified lines |
| 13 | "fmt" | 13 | "fmt" |
| 14 | "log" | 14 | "log" |
| 15 | "os" | 15 | "os" |
| | 16 | "path/filepath" |
| 16 | "regexp" | 17 | "regexp" |
| 17 | "runtime" | 18 | "runtime" |
| 18 | "strings" | 19 | "strings" |
| @@ -186,7 +187,7 @@ func checkTB(tb tb, deadline time.Time, prop func(*T)) { · 167 unmodified lines |
| 186 | } | 187 | } |
| 187 | | 188 | |
| 188 | start := time.Now() | 189 | start := time.Now() |
| 189 | valid, invalid, earlyExit, seed, buf, err1, err2 := doCheck(tb, flags.failfile, deadline, checks, baseSeed(), prop) | 190 | valid, invalid, earlyExit, seed, failfile, buf, err1, err2 := doCheck(tb, deadline, checks, baseSeed(), flags.failfile, true, prop) |
| 190 | dt := time.Since(start) | 191 | dt := time.Since(start) |
| 191 | | 192 | |
| 192 | if err1 == nil && err2 == nil { | 193 | if err1 == nil && err2 == nil { |
| @@ -196,20 +197,26 @@ func checkTB(tb tb, deadline time.Time, prop func(*T)) { · 3 unmodified lines |
| 196 | tb.Errorf("[rapid] only generated %v valid tests from %v total (%v)", valid, valid+invalid, dt) | 197 | tb.Errorf("[rapid] only generated %v valid tests from %v total (%v)", valid, valid+invalid, dt) |
| 197 | } | 198 | } |
| 198 | } else { | 199 | } else { |
| 199 | repr := fmt.Sprintf("-rapid.seed=%d", seed) | 200 | if failfile == "" && !flags.nofailfile { |
| 200 | if flags.failfile != "" && seed == 0 { | 201 | _, failfile = failFileName(tb.Name()) |
| 201 | repr = fmt.Sprintf("-rapid.failfile=%q", flags.failfile) | | |
| 202 | } else if !flags.nofailfile { | | |
| 203 | _, failfile := failFileName(tb.Name()) | | |
| 204 | out := captureTestOutput(tb, prop, buf) | 202 | out := captureTestOutput(tb, prop, buf) |
| 205 | err := saveFailFile(failfile, rapidVersion, out, seed, buf) | 203 | err := saveFailFile(failfile, rapidVersion, out, seed, buf) |
| 206 | if err == nil { | 204 | if err != nil { |
| 207 | repr = fmt.Sprintf("-rapid.failfile=%q (or -rapid.seed=%d)", failfile, seed) | | |
| 208 | } else { | | |
| 209 | tb.Logf("[rapid] %v", err) | 205 | tb.Logf("[rapid] %v", err) |
| | 206 | failfile = "" |
| 210 | } | 207 | } |
| 211 | } | 208 | } |
| 212 | | 209 | |
| | 210 | var repr string |
| | 211 | switch { |
| | 212 | case failfile != "" && seed != 0: |
| | 213 | repr = fmt.Sprintf("-rapid.failfile=%q (or -rapid.seed=%d)", failfile, seed) |
| | 214 | case failfile != "": |
| | 215 | repr = fmt.Sprintf("-rapid.failfile=%q", failfile) |
| | 216 | case seed != 0: |
| | 217 | repr = fmt.Sprintf("-rapid.seed=%d", seed) |
| | 218 | } |
| | 219 | |
| 213 | name := regexp.QuoteMeta(tb.Name()) | 220 | name := regexp.QuoteMeta(tb.Name()) |
| 214 | if traceback(err1) == traceback(err2) { | 221 | if traceback(err1) == traceback(err2) { |
| 215 | if err2.isStopTest() { | 222 | if err2.isStopTest() { |
| @@ -229,21 +236,29 @@ func checkTB(tb tb, deadline time.Time, prop func(*T)) { · 13 unmodified lines |
| 229 | } | 236 | } |
| 230 | } | 237 | } |
| 231 | | 238 | |
| 232 | func doCheck(tb tb, failfile string, deadline time.Time, checks int, seed uint64, prop func(*T)) (int, int, bool, uint64, []uint64, *testError, *testError) { | 239 | func doCheck(tb tb, deadline time.Time, checks int, seed uint64, failfile string, globFailFiles bool, prop func(*T)) (int, int, bool, uint64, string, []uint64, *testError, *testError) { |
| 233 | tb.Helper() | 240 | tb.Helper() |
| 234 | | 241 | |
| 235 | assertf(!tb.Failed(), "check function called with *testing.T which has already failed") | 242 | assertf(!tb.Failed(), "check function called with *testing.T which has already failed") |
| 236 | | 243 | |
| | 244 | var failfiles []string |
| 237 | if failfile != "" { | 245 | if failfile != "" { |
| | 246 | failfiles = []string{failfile} |
| | 247 | } |
| | 248 | if globFailFiles { |
| | 249 | matches, _ := filepath.Glob(failFilePattern(tb.Name())) |
| | 250 | failfiles = append(failfiles, matches...) |
| | 251 | } |
| | 252 | for _, failfile := range failfiles { |
| 238 | buf, err1, err2 := checkFailFile(tb, failfile, prop) | 253 | buf, err1, err2 := checkFailFile(tb, failfile, prop) |
| 239 | if err1 != nil || err2 != nil { | 254 | if err1 != nil || err2 != nil { |
| 240 | return 0, 0, false, 0, buf, err1, err2 | 255 | return 0, 0, false, 0, failfile, buf, err1, err2 |
| 241 | } | 256 | } |
| 242 | } | 257 | } |
| 243 | | 258 | |
| 244 | valid, invalid, earlyExit, seed, err1 := findBug(tb, deadline, checks, seed, prop) | 259 | valid, invalid, earlyExit, seed, err1 := findBug(tb, deadline, checks, seed, prop) |
| 245 | if err1 == nil { | 260 | if err1 == nil { |
| 246 | return valid, invalid, earlyExit, 0, nil, nil, nil | 261 | return valid, invalid, earlyExit, 0, "", nil, nil, nil |
| 247 | } | 262 | } |
| 248 | | 263 | |
| 249 | s := newRandomBitStream(seed, true) | 264 | s := newRandomBitStream(seed, true) |
| @@ -251,13 +266,13 @@ func doCheck(tb tb, failfile string, deadline time.Time, checks int, seed uint64 · 1 unmodified line |
| 251 | t.Logf("[rapid] trying to reproduce the failure") | 266 | t.Logf("[rapid] trying to reproduce the failure") |
| 252 | err2 := checkOnce(t, prop) | 267 | err2 := checkOnce(t, prop) |
| 253 | if !sameError(err1, err2) { | 268 | if !sameError(err1, err2) { |
| 254 | return valid, invalid, false, seed, s.data, err1, err2 | 269 | return valid, invalid, false, seed, "", s.data, err1, err2 |
| 255 | } | 270 | } |
| 256 | | 271 | |
| 257 | t.Logf("[rapid] trying to minimize the failing test case") | 272 | t.Logf("[rapid] trying to minimize the failing test case") |
| 258 | buf, err3 := shrink(tb, shrinkDeadline(deadline), s.recordedBits, err2, prop) | 273 | buf, err3 := shrink(tb, shrinkDeadline(deadline), s.recordedBits, err2, prop) |
| 259 | | 274 | |
| 260 | return valid, invalid, false, seed, buf, err2, err3 | 275 | return valid, invalid, false, seed, "", buf, err2, err3 |
| 261 | } | 276 | } |
| 262 | | 277 | |
| 263 | func checkFailFile(tb tb, failfile string, prop func(*T)) ([]uint64, *testError, *testError) { | 278 | func checkFailFile(tb tb, failfile string, prop func(*T)) ([]uint64, *testError, *testError) { |