job_test.odin (7343B)
1 package job 2 3 import "core:strings" 4 import "core:testing" 5 6 import "../change" 7 import "../txt" 8 9 dyn :: proc(items: []$T) -> [dynamic]T { 10 out := make([dynamic]T, context.temp_allocator) 11 append(&out, ..items) 12 return out 13 } 14 15 @(test) 16 jobs_carry_their_criteria :: proc(t: ^testing.T) { 17 jobs := all(context.temp_allocator) 18 testing.expect_value(t, len(jobs), 5) 19 for j in jobs { 20 testing.expectf(t, len(rules(j.criteria)) >= 4, "%s cites too few rules", j.name) 21 testing.expect(t, strings.contains(j.criteria, "- `")) 22 } 23 allowed := rules(jobs[1].criteria) 24 testing.expect(t, allowed["cannot-fail"], "the tests criteria name cannot-fail") 25 picked, err := chosen("tests, hygiene", context.temp_allocator) 26 testing.expect_value(t, err, "") 27 testing.expect_value(t, len(picked), 2) 28 _, err = chosen("nothing", context.temp_allocator) 29 testing.expect(t, strings.contains(err, "no job called")) 30 } 31 32 @(test) 33 subjects_render_the_part_each_job_reads :: proc(t: ^testing.T) { 34 context.allocator = context.temp_allocator 35 c := change.Change { 36 message = "x: do the thing", 37 stat = " a.go | 2 +-", 38 convention = {"x: earlier"}, 39 symbols = dyn( 40 []change.Symbol { 41 { 42 name = "Parse", 43 kind = "func", 44 file = "a.go", 45 line = 3, 46 exported = true, 47 signature = "func Parse() {", 48 doc = "Parse reads.\nMore.", 49 }, 50 }, 51 ), 52 tests = dyn( 53 []change.Function { 54 { 55 name = "TestParse", 56 file = "a_test.go", 57 line = 9, 58 body = "func TestParse(t *testing.T) {\n\tt.Skip()\n\tParse()\n}", 59 skips = 10, 60 }, 61 }, 62 ), 63 comments = dyn( 64 []change.Located { 65 {text = "one", file = "a.go", line = 1, below = "x := 1"}, 66 {text = "two", file = "a.go", line = 2, below = "y := 2\nz := 3"}, 67 {text = "parse the input", file = "a.go", line = 7, below = "func parseInput() {"}, 68 }, 69 ), 70 index = { 71 { 72 name = "Parse", 73 kind = "func", 74 file = "a.go", 75 line = 3, 76 body = "func Parse() {\n\treturn\n}", 77 }, 78 }, 79 } 80 c.candidates = make(map[string][]string) 81 c.candidates["Parse"] = []string{"b.go:4: func parse() {"} 82 c.twins = make(map[string][]string) 83 c.twins["Parse"] = []string{"b.go:1: const Parse = 1 <- same value"} 84 namer := namer_subject(&c, context.temp_allocator) 85 for want in ([]string{"a.go:3 func Parse (exported)", "doc: Parse reads.", "names already in this repository: b.go:4: func parse() {"}) { 86 testing.expectf( 87 t, 88 strings.contains(namer, want), 89 "%q missing from namer:\n%s", 90 want, 91 namer, 92 ) 93 } 94 dup := duplication_subject(&c, context.temp_allocator) 95 for want in ([]string{"Judge every pair on this list.", " b.go:1: const Parse = 1\n", "NEW a.go:3 func Parse", " b.go:4: func parse() {"}) { 96 testing.expectf( 97 t, 98 strings.contains(dup, want), 99 "%q missing from duplication:\n%s", 100 want, 101 dup, 102 ) 103 } 104 tests := tests_subject(&c, context.temp_allocator) 105 for want in ([]string{"--- a_test.go:9 TestParse (skips itself at line 10)", "Functions the tests call", "--- a.go:3 Parse\nfunc Parse() {"}) { 106 testing.expectf( 107 t, 108 strings.contains(tests, want), 109 "%q missing from tests:\n%s", 110 want, 111 tests, 112 ) 113 } 114 claims := claims_subject(&c, context.temp_allocator) 115 for want in ([]string{"a.go:1 one\na.go:2 two\n code: y := 2\n code: z := 3\n"}) { 116 testing.expectf( 117 t, 118 strings.contains(claims, want), 119 "%q missing from claims:\n%s", 120 want, 121 claims, 122 ) 123 } 124 testing.expect( 125 t, 126 !strings.contains(claims, "parse the input"), 127 "a restating comment is not a claim", 128 ) 129 hygiene := hygiene_subject(&c, context.temp_allocator) 130 for want in ([]string{"Commit message:\n\nx: do the thing", "Files changed:\n a.go | 2 +-", " x: earlier"}) { 131 testing.expectf( 132 t, 133 strings.contains(hygiene, want), 134 "%q missing from hygiene:\n%s", 135 want, 136 hygiene, 137 ) 138 } 139 empty := change.Change{} 140 testing.expect_value(t, namer_subject(&empty, context.temp_allocator), "") 141 testing.expect_value(t, hygiene_subject(&empty, context.temp_allocator), "") 142 } 143 144 @(test) 145 answers_are_decoded_and_held_to_the_criteria :: proc(t: ^testing.T) { 146 allowed := rules("- `cannot-fail` x\n- `other` y\n", context.temp_allocator) 147 raw, found := txt.object( 148 "Sure! {\"findings\":[{\"rule\":\"cannot-fail\",\"severity\":\"must-fix\",\"file\":\"a_test.go\",\"line\":4,\"symbol\":\"TestX\",\"message\":\"m\",\"fix\":\"f\"},{\"rule\":\"invented\",\"severity\":\"note\",\"message\":\"n\"}]} thanks", 149 ) 150 testing.expect(t, found) 151 got, ok := decode(raw, "tests", allowed, context.temp_allocator) 152 testing.expect(t, ok) 153 testing.expect_value(t, len(got), 1) 154 if len(got) == 1 { 155 testing.expect_value(t, got[0].job, "tests") 156 testing.expect_value(t, got[0].rule, "cannot-fail") 157 testing.expect_value(t, got[0].line, 4) 158 testing.expect_value(t, got[0].severity_name, "must-fix") 159 } 160 testing.expect(t, readable(`{"findings":[]}`)) 161 testing.expect(t, !readable("no json here")) 162 _, none := txt.object("prose") 163 testing.expect(t, !none) 164 testing.expect_value(t, len(calls("f(); g.h(x); if (y) {", context.temp_allocator)), 2) 165 } 166 167 @(test) 168 parts_cut_a_large_subject_by_file :: proc(t: ^testing.T) { 169 context.allocator = context.temp_allocator 170 c := change.Change { 171 files = {"a.go", "b.go", "c.md", "d.go"}, 172 } 173 c.symbols = make([dynamic]change.Symbol) 174 long := strings.repeat("x", 9000) 175 for name in ([]string{"a.go", "b.go", "d.go"}) { 176 append( 177 &c.symbols, 178 change.Symbol{name = "S", kind = "func", file = name, line = 1, signature = long}, 179 ) 180 } 181 j := all(context.temp_allocator)[2] 182 pieces := parts(j, &c, context.temp_allocator) 183 testing.expect_value(t, len(pieces), 3) 184 if len(pieces) == 3 { 185 testing.expect_value(t, len(pieces[0].symbols), 1) 186 testing.expect_value(t, pieces[0].files[0], "a.go") 187 testing.expect_value(t, pieces[2].files[0], "d.go") 188 } 189 small := change.Change { 190 files = {"a.go"}, 191 } 192 small.symbols = make([dynamic]change.Symbol) 193 append( 194 &small.symbols, 195 change.Symbol{name = "S", kind = "func", file = "a.go", line = 1, signature = "short"}, 196 ) 197 testing.expect_value(t, len(parts(j, &small, context.temp_allocator)), 1) 198 // A file the job reads nothing from is in no part: the tests job 199 // reads a test file, the namer does not. 200 tests_only := change.Change{files = {"a.go", "a_test.go"}} 201 tests_only.symbols = make([dynamic]change.Symbol) 202 append(&tests_only.symbols, change.Symbol{name = "S", kind = "func", file = "a.go", line = 1, signature = strings.repeat("x", 17000)}) 203 tests_only.tests = make([dynamic]change.Function) 204 append(&tests_only.tests, change.Function{name = "TestS", file = "a_test.go", line = 1, body = strings.repeat("y", 17000)}) 205 namer_pieces := parts(all(context.temp_allocator)[2], &tests_only, context.temp_allocator) 206 testing.expect_value(t, len(namer_pieces), 1) 207 if len(namer_pieces) == 1 { 208 testing.expect_value(t, len(namer_pieces[0].files), 1) 209 testing.expect_value(t, namer_pieces[0].files[0], "a.go") 210 } 211 for piece in namer_pieces { 212 testing.expect(t, strings.trim_space(all(context.temp_allocator)[2].subject(piece, context.temp_allocator)) != "", "an empty part is never asked") 213 } 214 tests_pieces := parts(all(context.temp_allocator)[1], &tests_only, context.temp_allocator) 215 testing.expect_value(t, len(tests_pieces), 1) 216 if len(tests_pieces) == 1 { 217 testing.expect_value(t, tests_pieces[0].files[0], "a_test.go") 218 } 219 }