finding_test.odin (6772B)
1 package finding 2 3 import "core:os" 4 import "core:path/filepath" 5 import "core:strings" 6 import "core:testing" 7 8 @(test) 9 severity_round_trips :: proc(t: ^testing.T) { 10 testing.expect_value(t, parse_severity(" Must-Fix "), Severity.Must_Fix) 11 testing.expect_value(t, parse_severity("error"), Severity.Must_Fix) 12 testing.expect_value(t, parse_severity("warning"), Severity.Consider) 13 testing.expect_value(t, parse_severity("whatever"), Severity.Note) 14 testing.expect_value(t, severity_name(.Consider), "consider") 15 } 16 17 @(test) 18 to_string_says_where_and_what :: proc(t: ^testing.T) { 19 s := to_string( 20 Finding { 21 job = "tests", 22 rule = "cannot-fail", 23 severity = .Must_Fix, 24 file = "a_test.go", 25 line = 4, 26 message = "m", 27 fix = "do x", 28 }, 29 context.temp_allocator, 30 ) 31 testing.expect_value( 32 t, 33 s, 34 "must-fix: a_test.go:4: m\n → do x\n [tests/cannot-fail]", 35 ) 36 s = to_string( 37 Finding { 38 job = "namer", 39 rule = "abbreviation", 40 severity = .Note, 41 symbol = "cfg", 42 message = "m", 43 }, 44 context.temp_allocator, 45 ) 46 testing.expect_value(t, s, "note: cfg: m\n [namer/abbreviation]") 47 } 48 49 @(test) 50 sort_puts_the_serious_first :: proc(t: ^testing.T) { 51 findings := []Finding { 52 {severity = .Note, file = "b.go", line = 1}, 53 {severity = .Must_Fix, file = "b.go", line = 9}, 54 {severity = .Must_Fix, file = "a.go", line = 2}, 55 {severity = .Must_Fix, file = "a.go", line = 1}, 56 } 57 sort(findings) 58 testing.expect_value(t, findings[0].file, "a.go") 59 testing.expect_value(t, findings[0].line, 1) 60 testing.expect_value(t, findings[1].line, 2) 61 testing.expect_value(t, findings[2].line, 9) 62 testing.expect_value(t, findings[3].severity, Severity.Note) 63 testing.expect(t, must_fix(findings)) 64 testing.expect(t, !must_fix(findings[3:])) 65 } 66 67 @(test) 68 id_is_stable_and_matches_the_go_tool :: proc(t: ^testing.T) { 69 f := Finding { 70 job = "tests", 71 rule = "cannot-fail", 72 file = "x_test.go", 73 line = 4, 74 symbol = "TestX", 75 message = "m", 76 } 77 identify(&f, context.temp_allocator) 78 // sha256("tests\x00cannot-fail\x00x_test.go\x00TestX")[:12], as Go names it. 79 testing.expect_value(t, f.id, "52ef4321ce06") 80 moved := f 81 moved.line = 9 82 identify(&moved, context.temp_allocator) 83 testing.expect_value(t, moved.id, f.id) 84 reworded := f 85 reworded.message = "said another way" 86 identify(&reworded, context.temp_allocator) 87 testing.expect_value(t, reworded.id, f.id) 88 other := f 89 other.symbol = "TestY" 90 identify(&other, context.temp_allocator) 91 testing.expect(t, other.id != f.id, "two findings share one id") 92 93 a := Finding { 94 job = "claims", 95 rule = "unsupported-claim", 96 file = "x.go", 97 line = 4, 98 } 99 b := Finding { 100 job = "claims", 101 rule = "unsupported-claim", 102 file = "x.go", 103 line = 9, 104 } 105 identify(&a, context.temp_allocator) 106 identify(&b, context.temp_allocator) 107 testing.expect(t, a.id != b.id, "two comments share one id") 108 109 s1 := Finding { 110 job = "static", 111 rule = "history-coupled-file", 112 file = "a.go", 113 message = "ties a.go to b.go", 114 } 115 s2 := Finding { 116 job = "static", 117 rule = "history-coupled-file", 118 file = "a.go", 119 message = "ties a.go to c.go", 120 } 121 identify(&s1, context.temp_allocator) 122 identify(&s2, context.temp_allocator) 123 testing.expect(t, s1.id != s2.id, "two partners share one id") 124 } 125 126 @(test) 127 name_tells_twins_apart :: proc(t: ^testing.T) { 128 findings := []Finding { 129 {job = "namer", rule = "abbreviation", file = "x.go", symbol = "cfg", severity = .Note}, 130 {job = "namer", rule = "abbreviation", file = "x.go", symbol = "cfg", severity = .Note}, 131 } 132 name(findings, context.temp_allocator) 133 testing.expect(t, findings[0].id != findings[1].id) 134 testing.expect(t, strings.has_suffix(findings[1].id, "-2")) 135 testing.expect_value(t, findings[0].severity_name, "note") 136 } 137 138 write_source :: proc(t: ^testing.T, name, src: string) -> (dir: string) { 139 temp := os.temp_directory(context.temp_allocator) or_else "" 140 scratch, err := os.make_directory_temp(temp, "review-finding-*", context.temp_allocator) 141 if err != nil { 142 testing.fail_now(t, "no scratch directory") 143 } 144 path := filepath.join({scratch, name}, context.temp_allocator) or_else name 145 testing.expect(t, os.write_entire_file(path, transmute([]byte)src) == nil) 146 return scratch 147 } 148 149 @(test) 150 suppressed_reads_the_dismissal_beside_the_line :: proc(t: ^testing.T) { 151 dir := write_source( 152 t, 153 "x.go", 154 "package x\n\n// nothing here\nconst a = 1\n\n//review:" + 155 "ignore restates-a-fact the ico package owns the other one\nconst b = 6\n\nconst c = 7\n\nconst d = 8\n\nconst e = 9\n", 156 ) 157 defer os.remove_all(dir) 158 Case :: struct { 159 name: string, 160 f: Finding, 161 want: bool, 162 why: string, 163 } 164 for c in ([]Case{{"just above", {file = "x.go", line = 7, rule = "restates-a-fact"}, true, "the ico package owns the other one"}, {"on the line", {file = "x.go", line = 6, rule = "restates-a-fact"}, true, "the ico package owns the other one"}, {"distant", {file = "x.go", line = 12, rule = "restates-a-fact"}, false, ""}, {"another rule", {file = "x.go", line = 7, rule = "already-named"}, false, ""}, {"no line", {file = "x.go", rule = "restates-a-fact"}, true, "the ico package owns the other one"}, {"no file", {rule = "restates-a-fact"}, false, ""}, {"missing file", {file = "nowhere.go", line = 1, rule = "restates-a-fact"}, false, ""}}) { 165 why, ok := suppressed(c.f, dir, context.temp_allocator) 166 testing.expectf(t, ok == c.want, "%s: got %v, want %v", c.name, ok, c.want) 167 if ok { 168 testing.expectf(t, why == c.why, "%s: reason %q", c.name, why) 169 } 170 } 171 } 172 173 @(test) 174 suppressed_reads_all_and_no_reason :: proc(t: ^testing.T) { 175 dir := write_source(t, "x.go", "//review:" + "ignore all generated\nconst a = 1\n") 176 defer os.remove_all(dir) 177 why, ok := suppressed( 178 Finding{file = "x.go", line = 2, rule = "anything-at-all"}, 179 dir, 180 context.temp_allocator, 181 ) 182 testing.expect(t, ok) 183 testing.expect_value(t, why, "generated") 184 other := write_source( 185 t, 186 "y.go", 187 "// review:" + "ignore cannot-fail\nfunc TestX(t *testing.T) {}\n", 188 ) 189 defer os.remove_all(other) 190 why, ok = suppressed( 191 Finding{file = "y.go", line = 2, rule = "cannot-fail"}, 192 other, 193 context.temp_allocator, 194 ) 195 testing.expect(t, ok, "a space after the slashes still dismisses") 196 testing.expect_value(t, why, "no reason given") 197 } 198 199 @(test) 200 dismissal_needs_the_slashes :: proc(t: ^testing.T) { 201 rule, why, found := dismissal("x := 1 //review:" + "ignore no-shadow it is the loop's") 202 testing.expect(t, found) 203 testing.expect_value(t, rule, "no-shadow") 204 testing.expect_value(t, why, "it is the loop's") 205 _, _, found = dismissal("review:ignore no-shadow prose") 206 testing.expect(t, !found) 207 _, _, found = dismissal("//review:" + "ignore") 208 testing.expect(t, !found) 209 testing.expect_value(t, atoi(" 42 "), 42) 210 testing.expect_value(t, atoi("x"), 0) 211 }