| @@ -13,6 +13,7 @@ import ( · 12 unmodified lines |
| 13 | 13 | "fmt" |
| 14 | 14 | "log" |
| 15 | 15 | "os" |
| 16 | + "path/filepath" |
| 16 | 17 | "regexp" |
| 17 | 18 | "runtime" |
| 18 | 19 | "strings" |
| @@ -186,7 +187,7 @@ func checkTB(tb tb, deadline time.Time, prop func(*T)) { · 167 unmodified lines |
| 186 | 187 | } |
| 187 | 188 | |
| 188 | 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 | 191 | dt := time.Since(start) |
| 191 | 192 | |
| 192 | 193 | if err1 == nil && err2 == nil { |
| @@ -196,20 +197,26 @@ func checkTB(tb tb, deadline time.Time, prop func(*T)) { · 3 unmodified lines |
| 196 | 197 | tb.Errorf("[rapid] only generated %v valid tests from %v total (%v)", valid, valid+invalid, dt) |
| 197 | 198 | } |
| 198 | 199 | } else { |
| 199 | | − repr := fmt.Sprintf("-rapid.seed=%d", seed) |
| 200 | | − if flags.failfile != "" && seed == 0 { |
| 201 | | − repr = fmt.Sprintf("-rapid.failfile=%q", flags.failfile) |
| 202 | | − } else if !flags.nofailfile { |
| 203 | | − _, failfile := failFileName(tb.Name()) |
| 200 | + if failfile == "" && !flags.nofailfile { |
| 201 | + _, failfile = failFileName(tb.Name()) |
| 204 | 202 | out := captureTestOutput(tb, prop, buf) |
| 205 | 203 | err := saveFailFile(failfile, rapidVersion, out, seed, buf) |
| 206 | | − if err == nil { |
| 207 | | − repr = fmt.Sprintf("-rapid.failfile=%q (or -rapid.seed=%d)", failfile, seed) |
| 208 | | − } else { |
| 204 | + if err != nil { |
| 209 | 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 | 220 | name := regexp.QuoteMeta(tb.Name()) |
| 214 | 221 | if traceback(err1) == traceback(err2) { |
| 215 | 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 | 240 | tb.Helper() |
| 234 | 241 | |
| 235 | 242 | assertf(!tb.Failed(), "check function called with *testing.T which has already failed") |
| 236 | 243 | |
| 244 | + var failfiles []string |
| 237 | 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 | 253 | buf, err1, err2 := checkFailFile(tb, failfile, prop) |
| 239 | 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 | 259 | valid, invalid, earlyExit, seed, err1 := findBug(tb, deadline, checks, seed, prop) |
| 245 | 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 | 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 | 266 | t.Logf("[rapid] trying to reproduce the failure") |
| 252 | 267 | err2 := checkOnce(t, prop) |
| 253 | 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 | 272 | t.Logf("[rapid] trying to minimize the failing test case") |
| 258 | 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 | 278 | func checkFailFile(tb tb, failfile string, prop func(*T)) ([]uint64, *testError, *testError) { |