use std::{ffi::OsStr, fmt::Write, fs, path::PathBuf};
use expect_test::expect_file;
use rowan::ast::AstNode;
use crate::{
ast::{self, HasEntry},
tokenize, Root, SyntaxKind,
};
#[test]
fn interpolation() {
let root = ast::Root::parse(include_str!("../test_data/parser/success/interpolation.nix"))
.ok()
.unwrap();
let let_in = ast::LetIn::try_from(root.expr().unwrap()).unwrap();
let set = ast::AttrSet::try_from(let_in.body().unwrap()).unwrap();
let entry = set.entries().nth(1).unwrap();
let attrpath_value = ast::AttrpathValue::try_from(entry).unwrap();
let value = ast::Str::try_from(attrpath_value.value().unwrap()).unwrap();
match &*value.normalized_parts() {
&[
ast::InterpolPart::Literal(ref s1),
ast::InterpolPart::Interpolation(_),
ast::InterpolPart::Literal(ref s2),
ast::InterpolPart::Interpolation(_),
ast::InterpolPart::Literal(ref s3)
]
if s1 == "The set's x value is: "
&& s2 == "\n\nThis line shall have no indention\n This line shall be indented by 2\n\n\n"
&& s3 == "\n" => (),
parts => panic!("did not match: {:#?}", parts)
}
}
#[test]
fn inherit() {
let root =
ast::Root::parse(include_str!("../test_data/parser/success/inherit.nix")).ok().unwrap();
let let_in = ast::LetIn::try_from(root.expr().unwrap()).unwrap();
let set = ast::AttrSet::try_from(let_in.body().unwrap()).unwrap();
let inherit = set.inherits().nth(1).unwrap();
let from = inherit.from().unwrap().expr().unwrap();
let ident: ast::Ident = ast::Ident::try_from(from).unwrap();
assert_eq!(ident.syntax().text(), "set");
let mut children = inherit.attrs();
assert_eq!(children.next().unwrap().syntax().text(), "z");
assert_eq!(children.next().unwrap().syntax().text(), "a");
assert!(children.next().is_none());
}
#[test]
fn math() {
let root = ast::Root::parse(include_str!("../test_data/parser/success/math.nix")).ok().unwrap();
let op1 = ast::BinOp::try_from(root.expr().unwrap()).unwrap();
let op2 = ast::BinOp::try_from(op1.lhs().unwrap()).unwrap();
assert_eq!(op1.operator().unwrap(), ast::BinOpKind::Add);
let lhs = ast::Literal::try_from(op2.lhs().unwrap()).unwrap();
assert_eq!(lhs.syntax().text(), "1");
let rhs = ast::BinOp::try_from(op2.rhs().unwrap()).unwrap();
assert_eq!(rhs.operator().unwrap(), ast::BinOpKind::Mul);
}
#[test]
fn t_macro() {
assert_eq!(T![@], SyntaxKind::TOKEN_AT);
assert!(matches!(SyntaxKind::TOKEN_L_PAREN, T!['(']));
}
fn dir_tests<F>(dir: &str, get_actual: F)
where
F: Fn(String) -> String,
{
let base_path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "test_data", dir].iter().collect();
let success_path = base_path.join("success");
let error_path = base_path.join("error");
let entries = success_path.read_dir().unwrap().chain(error_path.read_dir().unwrap());
for entry in entries {
let path = entry.unwrap().path();
if path.extension() != Some(OsStr::new("nix")) {
continue;
}
println!("testing: {}", path.display());
let mut code = fs::read_to_string(&path).unwrap();
if code.ends_with('\n') {
code.truncate(code.len() - 1);
}
let actual = get_actual(code);
expect_file![path.with_extension("expect")].assert_eq(&actual);
}
}
#[test]
fn parser_dir_tests() {
dir_tests("parser", |code| {
let parse = Root::parse(&code);
let mut actual = String::new();
for error in parse.errors() {
writeln!(actual, "error: {}", error).unwrap();
}
writeln!(actual, "{:#?}", parse.syntax()).unwrap();
actual
})
}
#[test]
fn tokenizer_dir_tests() {
dir_tests("tokenizer", |code| {
let mut actual = String::new();
for (kind, str) in tokenize(&code) {
writeln!(actual, "{:?}, \"{}\"", kind, str).unwrap();
}
actual
})
}