use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
use camino::Utf8Path;
use dir_test::{Fixture, dir_test};
use insta::{assert_snapshot, with_settings};
use crate::{SourceFile, syntax_error::SyntaxError};
fn render_errors(sql: &str, errors: &[SyntaxError]) -> String {
let mut rendered = String::new();
let renderer = Renderer::plain().decor_style(DecorStyle::Unicode);
for syntax_error in errors {
let range = syntax_error.range();
let start: usize = range.start().into();
let end: usize = range.end().into();
let label = "syntax-error";
let snippet = Snippet::source(sql)
.fold(true)
.annotation(AnnotationKind::Primary.span(start..end));
let rendered_error = renderer
.render(&[Level::ERROR
.primary_title(syntax_error.message())
.id(label)
.element(snippet)])
.to_string();
rendered.push_str(&rendered_error);
rendered.push('\n');
}
rendered
}
#[dir_test(
dir: "$CARGO_MANIFEST_DIR/../squawk_parser/tests/data/ok",
glob: "*.sql",
)]
fn parser_ok_validation(fixture: Fixture<&str>) {
let content = fixture.content();
let absolute_fixture_path = Utf8Path::new(fixture.path());
let test_name = absolute_fixture_path
.file_name()
.and_then(|x| x.strip_suffix(".sql"))
.unwrap();
let parse = SourceFile::parse(content);
let errors = parse.errors();
assert!(
errors.is_empty(),
"parser ok test `{test_name}` has syntax validation errors:\n{}",
render_errors(content, &errors)
);
}
#[dir_test(
dir: "$CARGO_MANIFEST_DIR/../../postgres/regression_suite",
glob: "*.sql",
)]
fn regression_suite_validation(fixture: Fixture<&str>) {
let content = fixture.content();
let absolute_fixture_path = Utf8Path::new(fixture.path());
let test_name = absolute_fixture_path
.file_name()
.and_then(|x| x.strip_suffix(".sql"))
.unwrap();
if absolute_fixture_path.to_string().contains("psql") {
return;
}
let parse = SourceFile::parse(content);
let errors = parse.errors();
let mut errors = errors;
if test_name == "errors" {
assert!(
!errors.is_empty(),
"the errors.sql regression test must have validation errors"
);
return;
}
if test_name == "strings" {
errors.retain(|e| e.message() != "Comments between string literals are not allowed.");
}
assert!(
errors.is_empty(),
"regression test `{test_name}` has syntax validation errors:\n{}",
render_errors(content, &errors)
);
}
#[dir_test(
dir: "$CARGO_MANIFEST_DIR/test_data",
glob: "**/*.sql",
)]
fn syntaxtest(fixture: Fixture<&str>) {
let content = fixture.content();
let absolute_fixture_path = Utf8Path::new(fixture.path());
let input_file = absolute_fixture_path;
let test_name = absolute_fixture_path
.file_name()
.and_then(|x| x.strip_suffix(".sql"))
.unwrap();
let parent_dir = input_file.parent().and_then(|x| x.file_name()).unwrap();
let parse = SourceFile::parse(content);
let errors = parse.errors();
let mut buffer = format!("{:#?}", parse.syntax_node());
if !errors.is_empty() {
buffer.push('\n');
buffer.push_str(&render_errors(content, &errors));
}
with_settings!({
omit_expression => true,
input_file => input_file,
}, {
assert_snapshot!(format!("{}_{}", test_name, parent_dir), buffer);
});
if test_name.ends_with("_ok") {
assert_eq!(
errors.len(),
0,
"tests defined in the `syntax/test_data` ending with `_ok` can't have errors."
);
} else {
assert_ne!(
errors.len(),
0,
"tests defined in the `syntax/test_data` must have errors."
);
}
}