frontend_test.odin (7406B)
1 package frontend 2 3 import "core:os" 4 import "core:path/filepath" 5 import "core:testing" 6 7 write_temp :: proc(t: ^testing.T, name, src: string) -> string { 8 dir, err := os.temp_directory(context.temp_allocator) 9 testing.expect(t, err == nil, "temp directory") 10 path, join_err := filepath.join({dir, name}, context.temp_allocator) 11 testing.expect(t, join_err == nil, "join") 12 testing.expect(t, os.write_entire_file(path, transmute([]byte)src) == nil, "write fixture") 13 return path 14 } 15 16 @(test) 17 go_sidecar_lists_declarations :: proc(t: ^testing.T) { 18 if !installed(.Go) { 19 testing.fail_now( 20 t, 21 "review-go is not on the path: go build -o ~/go/bin/review-go ./sidecar/gofront", 22 ) 23 } 24 path := write_temp( 25 t, 26 "review_frontend_fixture.go", 27 `package fixture 28 29 import "fmt" 30 31 // Limit bounds the work. 32 const Limit = 3 33 34 type Point struct{ X int } 35 36 func helper() { fmt.Println(Limit) } 37 38 func TestHelper(t *testing.T) { helper() } 39 `, 40 ) 41 defer os.remove(path) 42 43 out, err := scan(.Go, {path}, context.temp_allocator) 44 testing.expect_value(t, err, Scan_Error.None) 45 testing.expect_value(t, len(out.files), 1) 46 file := out.files[0] 47 testing.expect_value(t, file.name, path) 48 testing.expect_value(t, file.pkg, "fixture") 49 testing.expect_value(t, file.error, "") 50 testing.expect_value(t, len(file.imports), 1) 51 testing.expect_value(t, file.imports[0], "fmt") 52 53 by_name := make(map[string]Decl, context.temp_allocator) 54 for decl in file.decls { 55 by_name[decl.name] = decl 56 } 57 testing.expect_value(t, len(by_name), 5) 58 testing.expect_value(t, by_name["Limit"].kind, "const") 59 testing.expect_value(t, by_name["Limit"].line, 6) 60 testing.expect_value(t, by_name["Limit"].doc, "Limit bounds the work.") 61 testing.expect_value(t, by_name["Limit"].exported, true) 62 testing.expect_value(t, by_name["Point"].kind, "type") 63 testing.expect_value(t, by_name["X"].kind, "field") 64 testing.expect_value(t, by_name["helper"].exported, false) 65 testing.expect_value(t, by_name["helper"].text, "func helper() { fmt.Println(Limit) }") 66 testing.expect_value(t, by_name["TestHelper"].test, true) 67 68 found := tests(file, context.temp_allocator) 69 testing.expect_value(t, len(found), 1) 70 testing.expect_value(t, found[0].name, "TestHelper") 71 testing.expect_value(t, len(file.comments), 1) 72 testing.expect_value(t, file.comments[0].line, 5) 73 } 74 75 @(test) 76 go_sidecar_reports_a_file_it_cannot_parse :: proc(t: ^testing.T) { 77 if !installed(.Go) { 78 testing.fail_now(t, "review-go is not on the path") 79 } 80 path := write_temp(t, "review_frontend_broken.go", "package x\nfunc {\n") 81 defer os.remove(path) 82 out, err := scan(.Go, {path}, context.temp_allocator) 83 testing.expect_value(t, err, Scan_Error.None) 84 testing.expect_value(t, len(out.files), 1) 85 testing.expect(t, len(out.files[0].error) > 0, "a parse error is reported") 86 testing.expect_value(t, len(out.files[0].decls), 0) 87 } 88 89 @(test) 90 scan_of_nothing_asks_nothing :: proc(t: ^testing.T) { 91 out, err := scan(.Go, {}, context.temp_allocator) 92 testing.expect_value(t, err, Scan_Error.None) 93 testing.expect_value(t, len(out.files), 0) 94 } 95 96 write_fixture :: proc(t: ^testing.T, name, src: string) -> string { 97 return write_temp(t, name, src) 98 } 99 100 @(test) 101 ast_grep_reads_typescript :: proc(t: ^testing.T) { 102 if !installed(.Script) { 103 testing.fail_now(t, "ast-grep is not on the path") 104 } 105 path := write_fixture( 106 t, 107 "review_grep_fixture.ts", 108 `import { x } from "y"; 109 110 // Limit bounds the work. 111 export const limit: number = 3; 112 const hidden = 4; 113 export type Pair = [number, number]; 114 interface Shape { size: number } 115 export function read(a: string): number { 116 const inner = 1; 117 return inner; 118 } 119 export default function main() {} 120 describe('icons', () => { 121 it('decodes the largest', () => { expect(size).toBe(1); }); 122 test.skip("reads a binary", () => {}); 123 }); 124 `, 125 ) 126 defer os.remove(path) 127 out, err := scan(.Script, {path}, context.temp_allocator) 128 testing.expect_value(t, err, Scan_Error.None) 129 testing.expect_value(t, len(out.files), 1) 130 by := make(map[string]Decl, context.temp_allocator) 131 for d in out.files[0].decls { 132 by[d.name] = d 133 } 134 testing.expect_value(t, len(by), 8) 135 testing.expect_value(t, by["limit"].kind, "value") 136 testing.expect(t, by["limit"].exported) 137 testing.expect_value(t, by["limit"].doc, "Limit bounds the work.") 138 testing.expect_value(t, by["limit"].line, 4) 139 testing.expect(t, !by["hidden"].exported) 140 testing.expect_value(t, by["Pair"].kind, "type") 141 testing.expect_value(t, by["Shape"].kind, "type") 142 testing.expect_value(t, by["read"].kind, "func") 143 testing.expect_value(t, by["read"].end_line, 11) 144 testing.expect_value(t, by["main"].kind, "func") 145 testing.expect(t, by["decodes the largest"].test) 146 testing.expect(t, by["reads a binary"].test) 147 _, nested := by["inner"] 148 testing.expect(t, !nested, "a declaration inside a body is not top level") 149 } 150 151 @(test) 152 ast_grep_reads_python_and_rust :: proc(t: ^testing.T) { 153 if !installed(.Python) { 154 testing.fail_now(t, "ast-grep is not on the path") 155 } 156 py := write_fixture( 157 t, 158 "review_grep_fixture.py", 159 `"""Icons.""" 160 MAX_ICONS = 12 161 _hidden: int = 4 162 163 # Reads the icons a file holds. 164 def read_icons(src): 165 inner = 1 166 def nested(): 167 return inner 168 return [] 169 170 class Reader: 171 size = 0 172 def method(self): 173 return 1 174 175 def test_smoke(): 176 read_icons("y") 177 `, 178 ) 179 defer os.remove(py) 180 out, err := scan(.Python, {py}, context.temp_allocator) 181 testing.expect_value(t, err, Scan_Error.None) 182 by := make(map[string]Decl, context.temp_allocator) 183 for d in out.files[0].decls { 184 by[d.name] = d 185 } 186 testing.expect_value(t, len(by), 5) 187 testing.expect_value(t, by["MAX_ICONS"].kind, "value") 188 testing.expect(t, by["MAX_ICONS"].exported) 189 testing.expect(t, !by["_hidden"].exported) 190 testing.expect_value(t, by["read_icons"].kind, "func") 191 testing.expect_value(t, by["read_icons"].doc, "Reads the icons a file holds.") 192 testing.expect_value(t, by["Reader"].kind, "type") 193 testing.expect(t, by["test_smoke"].test) 194 testing.expect_value(t, by["test_smoke"].kind, "func") 195 196 rs := write_fixture( 197 t, 198 "review_grep_fixture.rs", 199 `pub const LIMIT: usize = 3; 200 static mut COUNT: i32 = 0; 201 202 /// A shape. 203 pub struct Shape { size: usize } 204 205 impl Shape { 206 pub fn area(&self) -> usize { self.size } 207 fn hidden(&self) -> usize { 0 } 208 } 209 210 #[test] 211 fn reads_shapes() { 212 assert_eq!(count(), 1); 213 } 214 `, 215 ) 216 defer os.remove(rs) 217 out, err = scan(.Rust, {rs}, context.temp_allocator) 218 testing.expect_value(t, err, Scan_Error.None) 219 clear(&by) 220 for d in out.files[0].decls { 221 by[d.name] = d 222 } 223 testing.expect_value(t, len(by), 6) 224 testing.expect(t, by["LIMIT"].exported) 225 testing.expect(t, !by["COUNT"].exported) 226 testing.expect_value(t, by["Shape"].kind, "type") 227 testing.expect_value(t, by["Shape"].doc, "A shape.") 228 testing.expect(t, by["area"].exported) 229 testing.expect(t, !by["hidden"].exported) 230 testing.expect(t, by["reads_shapes"].test) 231 testing.expect_value(t, by["reads_shapes"].end_line, 15) 232 } 233 234 @(test) 235 doc_above_reads_the_block :: proc(t: ^testing.T) { 236 lines := []string{"x", "/** Two", " * lines */", "// and one", "const a = 1"} 237 testing.expect_value(t, doc_above(lines, 5, context.temp_allocator), "Two lines and one") 238 testing.expect_value(t, doc_above(lines, 1, context.temp_allocator), "") 239 testing.expect_value(t, scratch_ext("a/b.mjs"), ".js") 240 testing.expect_value(t, grammar_of("a.tsx"), "tsx") 241 testing.expect(t, typed("export type $N = $$$B")) 242 testing.expect(t, !typed("export const $N = $$$V")) 243 }