fuzz_test.odin (2585B)
1 package fuzz 2 3 import "core:testing" 4 import "core:time" 5 6 // A short run on fixed seeds, so `just test` catches a regression the 7 // example-based tests would not: they compare each column while the cursor is 8 // still on its row, which is exactly when a column read that forgot to clone 9 // still looks correct. Long runs are `just fuzz`. 10 @(test) 11 properties_hold :: proc(t: ^testing.T) { 12 for seed in ([]u64{1, 3, 7, 99}) { 13 report := run({seed = seed, iterations = 600, case_timeout = 30 * time.Second}) 14 testing.expect_value(t, report.iterations, 600) 15 for f in report.failures { 16 testing.expectf( 17 t, 18 false, 19 "%s failed at iteration %d (replay: sqlite3-fuzz -seed=%d): %s", 20 f.property, 21 f.iteration, 22 f.seed, 23 f.detail, 24 ) 25 } 26 } 27 } 28 29 // A run must be a pure function of its seed, or a reported failure cannot be 30 // replayed and the harness is not worth having. The digest is what makes that 31 // checkable on a run where nothing failed, which is most of them. 32 @(test) 33 same_seed_same_run :: proc(t: ^testing.T) { 34 first := run({seed = 42, iterations = 120}) 35 second := run({seed = 42, iterations = 120}) 36 // Absolute, so two equally empty reports cannot satisfy the pairwise 37 // comparisons below. 38 testing.expect_value(t, first.iterations, 120) 39 testing.expect_value(t, second.iterations, 120) 40 testing.expect(t, first.digest != 0, "a run that generated cases has a digest") 41 testing.expect_value(t, first.seed, 42) 42 testing.expect_value(t, first.digest, second.digest) 43 testing.expect_value(t, len(first.failures), len(second.failures)) 44 for f, i in first.failures { 45 testing.expect_value(t, f.property, second.failures[i].property) 46 testing.expect_value(t, f.iteration, second.failures[i].iteration) 47 testing.expect_value(t, f.detail, second.failures[i].detail) 48 } 49 } 50 51 // A different seed must reach different cases, or the digest above would hold 52 // for any two runs and prove nothing. 53 @(test) 54 different_seed_different_run :: proc(t: ^testing.T) { 55 a := run({seed = 42, iterations = 120}) 56 b := run({seed = 43, iterations = 120}) 57 testing.expect(t, a.digest != b.digest, "two seeds must not generate the same cases") 58 } 59 60 // A run asked for no particular seed must still report the one it used, or a 61 // random run that fails cannot be turned back into a deterministic one. 62 @(test) 63 unset_seed_is_filled_in :: proc(t: ^testing.T) { 64 report := run({iterations = 6}) 65 testing.expect(t, report.seed != 0, "the seed used must come back in the report") 66 replay := run({seed = report.seed, iterations = 6}) 67 testing.expect_value(t, replay.digest, report.digest) 68 }