slint/sixtyfps_compiler/tests/syntax_tests.rs
Simon Hausmann 732a56259f Simplify compilation call sites further
Move run_passes into the library compilation function. That way the
FileDiagnostics are created by the parser, can be passed on to the library
compilation function and after that we don't need them anymore and can
replace them with future BuildDiagnostics for example.
2020-07-16 18:53:59 +02:00

181 lines
5.1 KiB
Rust

//! This test is trying to compile all the *.60 files in the sub directories and check that compilation
//! errors are properly reported
//!
//! The compiler can have comments like this:
//! ```
//! // ^error{some_regexp}
//! ```
#[test]
fn syntax_tests() -> std::io::Result<()> {
if let Some(specific_test) =
std::env::args().skip(1).skip_while(|arg| arg.starts_with("--") || arg == "main").next()
{
let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("tests");
path.push(specific_test);
assert!(process_file(&path)?);
return Ok(());
}
let mut success = true;
for entry in std::fs::read_dir(format!("{}/tests", env!("CARGO_MANIFEST_DIR")))? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
for test_entry in path.read_dir()? {
let test_entry = test_entry?;
let path = test_entry.path();
if let Some(ext) = path.extension() {
if ext == "60" {
success &= process_file(&path)?;
}
}
}
}
}
assert!(success);
Ok(())
}
fn process_file(path: &std::path::Path) -> std::io::Result<bool> {
let source = std::fs::read_to_string(&path)?;
std::panic::catch_unwind(|| process_file_source(path, source, false)).unwrap_or_else(|err| {
println!("Panic while processing {}: {:?}", path.display(), err);
Ok(false)
})
}
fn process_file_source(
path: &std::path::Path,
source: String,
silent: bool,
) -> std::io::Result<bool> {
let (res, mut parse_diagnostics) =
sixtyfps_compilerlib::parser::parse(source.clone(), Some(path));
let mut compile_diagnostics = if !parse_diagnostics.has_error() {
let type_registry = sixtyfps_compilerlib::typeregister::TypeRegister::builtin();
let doc = sixtyfps_compilerlib::object_tree::Document::from_node(
res.into(),
&mut parse_diagnostics,
&type_registry,
);
if !parse_diagnostics.has_error() {
let compiler_config = sixtyfps_compilerlib::CompilerConfiguration::default();
sixtyfps_compilerlib::run_passes(&doc, &mut parse_diagnostics, &compiler_config);
}
parse_diagnostics
} else {
parse_diagnostics
};
let mut success = true;
// Find expected errors in the file.
let re = regex::Regex::new(r"\n *//[^\n]*(\^)error\{([^\n]*)\}\n").unwrap();
for m in re.captures_iter(&source) {
let line_begin_offset = m.get(0).unwrap().start();
let column = m.get(1).unwrap().start() - line_begin_offset;
let rx = m.get(2).unwrap().as_str();
let r = match regex::Regex::new(&rx) {
Err(e) => {
eprintln!("{:?}: Invalid regexp {:?} : {:?}", path, rx, e);
return Ok(false);
}
Ok(r) => r,
};
let offset = source[..line_begin_offset].rfind('\n').unwrap_or(0) + column;
match compile_diagnostics.inner.iter().position(|e| match e {
sixtyfps_compilerlib::diagnostics::Diagnostic::FileLoadError(_) => false,
sixtyfps_compilerlib::diagnostics::Diagnostic::CompilerDiagnostic(e) => {
e.span.offset == offset && r.is_match(&e.message)
}
}) {
Some(idx) => {
compile_diagnostics.inner.remove(idx);
}
None => {
success = false;
println!("{:?}: Error not found at offset {}: {:?}", path, offset, rx);
}
}
}
if !compile_diagnostics.inner.is_empty() {
println!("{:?}: Unexptected errors: {:#?}", path, compile_diagnostics.inner);
if !silent {
#[cfg(feature = "display-diagnostics")]
compile_diagnostics.print();
}
success = false;
}
Ok(success)
}
#[test]
/// Test that this actually fail when it should
fn self_test() -> std::io::Result<()> {
let fake_path = std::path::Path::new("fake.60");
let process = |str: &str| process_file_source(&fake_path, str.into(), true);
// this should succeed
assert!(process(
r#"
Foo := Rectangle { x: 0px; }
"#
)?);
// unless we expected an error
assert!(!process(
r#"
Foo := Rectangle { x: 0px; }
// ^error{i want an error}
"#
)?);
// An error should fail
assert!(!process(
r#"
Foo := Rectangle foo { x:0px; }
"#
)?);
// An error with the proper comment should pass
assert!(process(
r#"
Foo := Rectangle foo { x:0px; }
// ^error{expected LBrace}
"#
)?);
// But not if it is at the wrong position
assert!(!process(
r#"
Foo := Rectangle foo { x:0px; }
// ^error{expected LBrace}
"#
)?);
// or the wrong line
assert!(!process(
r#"
Foo := Rectangle foo { x:0px; }
// ^error{expected LBrace}
"#
)?);
// or the wrong message
assert!(!process(
r#"
Foo := Rectangle foo { x:0px; }
// ^error{foo_bar}
"#
)?);
Ok(())
}