Skip to content

Commit 6a599b3

Browse files
authored
Merge pull request #37 from HoneyPony/test-cleanup
Test cleanup
2 parents aa33aa7 + 8af6215 commit 6a599b3

208 files changed

Lines changed: 367 additions & 136 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build/build_tests.rs

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1+
use std::collections::HashMap;
12
use std::fs::File;
23
use std::io::Write as _;
34

4-
fn generate_test(file: &mut File, path: &str, test_name: &str) -> std::io::Result<()> {
5-
writeln!(file, "#[test]")?;
6-
writeln!(file, "fn {test_name}() {{")?;
5+
struct BuiltTests {
6+
modules: HashMap<String, String>
7+
}
8+
9+
fn generate_test(bt: &mut BuiltTests, path: &str, test_name: &str) {
10+
let out = if let Some(out) = bt.modules.get_mut(path) {
11+
out
12+
}
13+
else {
14+
bt.modules.insert(path.to_string(), "".to_string());
15+
bt.modules.get_mut(path).unwrap()
16+
};
17+
18+
use std::fmt::Write;
19+
20+
writeln!(out, "\t#[test]").unwrap();
21+
writeln!(out, "\tfn {test_name}() {{").unwrap();
722

823
// We let the generated test code actually do the concat!.
924
let exe_path = format!("concat!(env!(\"CARGO_TARGET_TMPDIR\"), \"/{test_name}\")");
1025

11-
writeln!(file, "\trun_integration_test(\"tests/poni/{path}{test_name}.poni\", {exe_path});")?;
12-
writeln!(file, "}}")?;
13-
14-
Ok(())
26+
writeln!(out, "\t\trun_integration_test(\"tests/{path}{test_name}.poni\", {exe_path});").unwrap();
27+
writeln!(out, "\t}}").unwrap();
1528
}
1629

1730
pub fn generate(tests_file: &mut File) {
1831
let tests = [
1932
("binary/", "binary_doubleblock"),
2033
("binary/", "binary_bottom"),
34+
("binary/", "binary_parens"),
2135

2236
// print_nested: the last line is 345 because it should re-print each of the inner print()s.
2337
("print/", "print_nested"),
@@ -30,6 +44,9 @@ pub fn generate(tests_file: &mut File) {
3044
("print/", "print_dif_funs"),
3145
("print/", "print_inner_return"),
3246

47+
("print/", "err_print_empty"),
48+
49+
3350
("globals/", "global_block"),
3451
// TODO: Figure out precise float output format we want.
3552
("globals/", "globals_3"),
@@ -38,16 +55,29 @@ pub fn generate(tests_file: &mut File) {
3855
("globals/", "global_increment_indirect"),
3956
("globals/", "global_mutate"),
4057

58+
("globals/", "err_global_fun_redefine"),
59+
("globals/", "err_global_redefine"),
60+
("globals/", "errTODO_bad_order"),
61+
4162
("typecheck/", "print_assume_int"),
4263
("typecheck/", "promote_assumes_inside_block"),
4364
("typecheck/", "promote_to_float_arithmetic"),
4465
("typecheck/", "promote_to_float_assign"),
66+
("typecheck/", "promote_to_float_block_assign_assumeint"),
67+
("typecheck/", "promote_to_float_block_assign"),
68+
("typecheck/", "promote_to_float_block_return"),
69+
("typecheck/", "promote_to_float_return"),
4570
("typecheck/", "str_types"),
4671
("typecheck/", "return_block_return"), // Make sure this one at least compiles
47-
// TODO: Add other tests when we get function calls
72+
73+
("typecheck/", "err_try_assign_float_for_int"),
74+
("typecheck/", "err_try_return_float_for_int_short"),
75+
("typecheck/", "err_try_return_float_for_int"),
76+
4877

4978
("string/", "simple_str"),
5079
("string/", "str_of_strbuf"),
80+
("string/", "very_simple_str"),
5181

5282
("functions/", "parse_params"),
5383
("functions/", "param_trailing_comma"),
@@ -57,20 +87,29 @@ pub fn generate(tests_file: &mut File) {
5787
("functions/", "void_fun"),
5888
("functions/", "fib"),
5989

90+
("functions/", "err_assign_to_fun"),
91+
6092
("scope/", "block_shadow"),
6193

6294
("if/", "basic_if_expr_ret"),
6395
("if/", "basic_if_expr_var"),
6496
("if/", "basic_if_expr"),
6597
("if/", "basic_if"),
98+
("if/", "if_extra_parens"),
6699
("if/", "if_no_else"),
67100
("if/", "if_no_else_in_print"),
68101
("if/", "if_fun_calls"),
69102

103+
("if/", "err_if_bad_condition"),
104+
("if/", "err_if_incompat_types"),
105+
("if/", "err_if_no_else_bad_type"),
106+
70107
("comparison/", "compare_basic"),
71108
("comparison/", "compare_constants"),
72109
("comparison/", "compare_doubleblock"),
73110

111+
("lexer/", "err_unterminated_string"),
112+
74113
("logical/", "basic_and"),
75114
("logical/", "basic_or"),
76115
("logical/", "short_and"),
@@ -81,6 +120,17 @@ pub fn generate(tests_file: &mut File) {
81120
("logical/", "short_or_expr_doubleblock"),
82121
("logical/", "or_bottom"),
83122

123+
("misc/", "complex_return_in_binop"),
124+
("misc/", "err_return_in_binop"),
125+
("misc/", "err_top_level_return"),
126+
("misc/", "noerr_return_in_binop"),
127+
("misc/", "simple_var_exprs_and_infer"),
128+
("misc/", "test_init"),
129+
("misc/", "unused_expr"),
130+
131+
("parser/", "err_fun_missing_brace"),
132+
("parser/", "err_missing_expr_paren"),
133+
84134
("call/", "call_captured_rev"),
85135
("call/", "call_captured"),
86136
("call/", "call_if_simple"),
@@ -98,6 +148,7 @@ pub fn generate(tests_file: &mut File) {
98148
("call/", "uses_fun_with_class_retval"),
99149
("call/", "uses_fun_with_class_param"),
100150

151+
("classes/", "basic_class"),
101152
("classes/", "basic_new_inferred_get"),
102153
("classes/", "basic_new_inferred_get_promote"),
103154
("classes/", "basic_new_explicit_get"),
@@ -119,8 +170,24 @@ pub fn generate(tests_file: &mut File) {
119170
("classes/", "basic_new_list"),
120171
("classes/", "class_member_that_is_fun"),
121172
("classes/", "class_member_function_capture"),
173+
("classes/", "noout_data_and_fun_assign"),
174+
("classes/", "noout_data_and_fun_read"),
175+
("classes/", "noout_pure_data_complex"),
176+
("classes/", "noout_pure_data_initializers"),
177+
("classes/", "noout_pure_data"),
178+
179+
("classes/", "err_assign_to_class"),
180+
("classes/", "err_get_nonexistent_member"),
181+
("classes/", "err_new_unknown_property"),
182+
("classes/", "err_new_wrong_ty_known"),
183+
("classes/", "err_new_wrong_ty_unknown"),
184+
("classes/", "err_pure_data_wrongty"),
185+
("classes/", "err_try_to_read_class_in_initializer"),
186+
("classes/", "err_weird_var"),
122187

123188
("get/", "get_string_length"),
189+
("get/", "err_get_on_int"),
190+
("get/", "err_get_on_string"),
124191

125192
("set/", "basic_set"),
126193
("set/", "set_bottom"),
@@ -163,9 +230,21 @@ pub fn generate(tests_file: &mut File) {
163230
("array/", "array_ref_semantics"),
164231
("array/", "array_length"),
165232
("array/", "array_complicated_signature"),
233+
("array/", "err_empty_arr_and_var"),
166234
];
167235

236+
let mut bt = BuiltTests {
237+
modules: HashMap::new()
238+
};
168239
for (path, test) in tests {
169-
generate_test(tests_file, path, test).unwrap();
240+
generate_test(&mut bt, path, test);
241+
}
242+
for (k, v) in &bt.modules {
243+
// get rid of slash
244+
let substr = &k[..k.len() - 1];
245+
writeln!(tests_file, "mod r#{substr} {{").unwrap();
246+
writeln!(tests_file, "\tuse crate::run_integration_test;").unwrap();
247+
writeln!(tests_file, "{v}").unwrap();
248+
writeln!(tests_file, "}}").unwrap();
170249
}
171250
}

out

15.8 KB
Binary file not shown.

src/binder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ impl<'db> Binder<'db> {
137137
}
138138
}
139139

140+
self.db.report_error(Error::simple(
141+
format!("Unknown identifier '{}'", self.db.get(ident)),
142+
&location
143+
));
144+
140145
self.had_error = true;
141146
None
142147
}

src/db.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ pub struct Db {
110110
/// Note that these do NOT include the newlines or carriage returns. Those
111111
/// are assumed to already exist.
112112
pub test_lines: Vec<String>,
113+
/// Expected errors from the program.
114+
///
115+
/// For now, we do not expect any particular error string; if we have any
116+
/// errors in the test_errors array, we simply expect the compilation to fail.
117+
pub test_errors: Vec<String>,
113118

114119
/// Maps names of the form "scope.scope.Item" to ScopeEntries. Used to bind
115120
/// names to specific objects.
@@ -197,6 +202,7 @@ impl Db {
197202

198203
test_mode: false,
199204
test_lines: Vec::new(),
205+
test_errors: Vec::new(),
200206

201207
sig_declare_code: String::new(),
202208

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct Note {
2323
}
2424

2525
pub struct Error {
26-
main_message: String,
26+
pub main_message: String,
2727
main_location: SourceLocation,
2828

2929
is_warning: bool,

src/lexer.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ impl Lexer {
268268
enum CommentKind {
269269
None,
270270
TestLine,
271+
TestErr,
271272
}
272273

273274
let mut kind = CommentKind::None;
@@ -279,6 +280,10 @@ impl Lexer {
279280
// Start the buffer at the beginning of the line.
280281
self.buffer.clear();
281282
}
283+
if self.advance_if('?')? {
284+
kind = CommentKind::TestErr;
285+
self.buffer.clear();
286+
}
282287
}
283288

284289
while !self.at_eof {
@@ -295,14 +300,30 @@ impl Lexer {
295300
let line = self.buffer.trim();
296301
db.test_lines.push(line.to_string());
297302
}
303+
304+
CommentKind::TestErr => {
305+
let line = self.buffer.trim();
306+
db.test_errors.push(line.to_string());
307+
}
298308
}
299309

300310
Ok(())
301311
}
302312

313+
fn mk_eof(&mut self, db: &mut Db) -> std::io::Result<Token> {
314+
// When at the EOF, create a lexeme that looks like <EOF> so that
315+
// we can nicely output it.
316+
//
317+
// This also helps with a problem where before we were creating a
318+
// lexeme of \0, making it very difficult to test against it.
319+
self.buffer.clear();
320+
self.buffer.push_str("<EOF>");
321+
return self.mk_token_res(db, Tok::Eof);
322+
}
323+
303324
pub fn next_token(&mut self, db: &mut Db) -> std::io::Result<Token> {
304325
if self.at_eof {
305-
return self.mk_token_res(db, Tok::Eof);
326+
return self.mk_eof(db);
306327
}
307328

308329
let c = self.advance_past_whitespace()?;
@@ -376,7 +397,11 @@ impl Lexer {
376397
}
377398

378399
// TODO: Do we want to introduce a separate "error token" here?
379-
Tok::Eof
400+
401+
// Note that if we're in the self.at_eof == true case, we do
402+
// want to return an eof, because we really are there. (Here
403+
// we should be matching on the \0 that we generate above.)
404+
return self.mk_eof(db);
380405
}
381406
};
382407

src/main.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,41 @@ fn duration(prev: SystemTime, message: &str, duration_set: &mut Vec<&'static str
173173
}
174174

175175
fn report_errors(db: &Db) {
176+
if db.test_mode && !db.test_errors.is_empty() {
177+
if db.errors.len() != db.test_errors.len() {
178+
eprintln!("Test failure: Wrong number of errors.");
179+
exit(15);
180+
}
181+
182+
// For now, we simply expect every single error that the compiler generates.
183+
// This does mean tests are brittle, but on the other hand... we don't update
184+
// the error messages that often. And for the most part, changes to the error
185+
// messages shouldn't require huge numbers of test updates (or where they do,
186+
// the updates should be somewhat regexable).
187+
for i in 0..db.errors.len() {
188+
// TODO:
189+
// For some reason, the err_fun_missing_brace test is failing even though
190+
// the strings absolutely appear to be the same. Very strange...
191+
let want_str = &db.test_errors[i];//.trim();
192+
let got_str = &db.errors[i].main_message;//.trim();
193+
if want_str != got_str {
194+
eprintln!("Test failure: Error message mismatch (index {i}):\nExpected: [{}]\nGot: [{}]",
195+
want_str, got_str);
196+
197+
let mut j = 0;
198+
for (a, b) in want_str.chars().zip(got_str.chars()) {
199+
if a != b {
200+
eprintln!("Mismatch at index {j}: {a} vs {b}")
201+
}
202+
j += 1;
203+
}
204+
exit(15);
205+
}
206+
}
207+
208+
// The test was successful.
209+
exit(0);
210+
}
176211
for error in &db.errors {
177212
crate::error::show_error(&error, db);
178213
}
@@ -204,7 +239,7 @@ fn main() {
204239

205240
if had_error {
206241
report_errors(&db);
207-
exit(1);
242+
exit(2);
208243
}
209244

210245
let timer = duration(timer, "binding", &mut duration_set);
@@ -214,7 +249,7 @@ fn main() {
214249

215250
if had_error {
216251
report_errors(&db);
217-
exit(2);
252+
exit(3);
218253
}
219254

220255
let timer = duration(timer, "type check", &mut duration_set);
@@ -264,6 +299,13 @@ fn main() {
264299
// If we're in test mode, then we want to run the program and check its
265300
// output.
266301
if args.test_mode {
302+
if !db.test_errors.is_empty() {
303+
// In this case, we actually have an error: we expected the compilation
304+
// to result in an error, but it didn't. So, report that to the test
305+
// runner.
306+
eprintln!("Test failure: Expected an error, but compilation suceeded.");
307+
exit(15);
308+
}
267309
// We've already checked the output is an Exe, so just run it at
268310
// that path.
269311
match test_compiled(&args.output_path, &db) {

src/parser.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,16 @@ impl<'a, 'b> Parser<'a, 'b> {
477477
match self.peek_typ() {
478478
Tok::LeftBrace => self.block(),
479479

480+
Tok::LeftParen => {
481+
// Eat left paren
482+
self.advance()?;
483+
// Inner expression
484+
let inner = self.expression()?;
485+
// Expect right paren after expression
486+
expected!(self, Tok::RightParen, "')' after parenthesized expression")?;
487+
Ok(inner)
488+
}
489+
480490
Tok::Identifier => self.expr_ident(),
481491

482492
Tok::If => self.expr_if(),
@@ -547,7 +557,7 @@ impl<'a, 'b> Parser<'a, 'b> {
547557

548558
fn expr_prefix(&mut self) -> Result<Expr> {
549559
match self.peek_typ() {
550-
Tok::LeftBrace | Tok::Identifier | Tok::If | Tok::Fun => {
560+
Tok::LeftBrace | Tok::LeftParen | Tok::Identifier | Tok::If | Tok::Fun => {
551561
let location = self.start();
552562
let mut inner = self.expr_prefix_callable()?;
553563

0 commit comments

Comments
 (0)