use crate::evaluator::TestEvaluator; use crate::files::{guess_rdf_format, load_dataset, load_n3}; use crate::manifest::Test; use crate::report::dataset_diff; use anyhow::{anyhow, bail, Result}; use oxigraph::io::RdfFormat; use oxigraph::model::{BlankNode, Dataset, Quad}; use oxttl::n3::{N3Quad, N3Term}; pub fn register_parser_tests(evaluator: &mut TestEvaluator) { evaluator.register( "http://www.w3.org/ns/rdftest#TestNTriplesPositiveSyntax", |t| evaluate_positive_syntax_test(t, RdfFormat::NTriples), ); evaluator.register( "http://www.w3.org/ns/rdftest#TestNQuadsPositiveSyntax", |t| evaluate_positive_syntax_test(t, RdfFormat::NQuads), ); evaluator.register( "http://www.w3.org/ns/rdftest#TestTurtlePositiveSyntax", |t| evaluate_positive_syntax_test(t, RdfFormat::Turtle), ); evaluator.register("http://www.w3.org/ns/rdftest#TestTrigPositiveSyntax", |t| { evaluate_positive_syntax_test(t, RdfFormat::TriG) }); evaluator.register( "https://w3c.github.io/N3/tests/test.n3#TestN3PositiveSyntax", evaluate_positive_n3_syntax_test, ); evaluator.register( "http://www.w3.org/ns/rdftest#TestNTriplesNegativeSyntax", |t| evaluate_negative_syntax_test(t, RdfFormat::NTriples), ); evaluator.register( "http://www.w3.org/ns/rdftest#TestNQuadsNegativeSyntax", |t| evaluate_negative_syntax_test(t, RdfFormat::NQuads), ); evaluator.register( "http://www.w3.org/ns/rdftest#TestTurtleNegativeSyntax", |t| evaluate_negative_syntax_test(t, RdfFormat::Turtle), ); evaluator.register("http://www.w3.org/ns/rdftest#TestTrigNegativeSyntax", |t| { evaluate_negative_syntax_test(t, RdfFormat::TriG) }); evaluator.register("http://www.w3.org/ns/rdftest#TestXMLNegativeSyntax", |t| { evaluate_negative_syntax_test(t, RdfFormat::RdfXml) }); evaluator.register( "https://w3c.github.io/N3/tests/test.n3#TestN3NegativeSyntax", evaluate_negative_n3_syntax_test, ); evaluator.register("http://www.w3.org/ns/rdftest#TestTurtleEval", |t| { evaluate_eval_test(t, RdfFormat::Turtle, false) }); evaluator.register("http://www.w3.org/ns/rdftest#TestTrigEval", |t| { evaluate_eval_test(t, RdfFormat::TriG, false) }); evaluator.register("http://www.w3.org/ns/rdftest#TestXMLEval", |t| { evaluate_eval_test(t, RdfFormat::RdfXml, false) }); evaluator.register("https://w3c.github.io/N3/tests/test.n3#TestN3Eval", |t| { evaluate_n3_eval_test(t, false) }); evaluator.register("http://www.w3.org/ns/rdftest#TestTurtleNegativeEval", |t| { evaluate_negative_syntax_test(t, RdfFormat::Turtle) }); evaluator.register("http://www.w3.org/ns/rdftest#TestTrigNegativeEval", |t| { evaluate_negative_syntax_test(t, RdfFormat::TriG) }); evaluator.register( "https://w3c.github.io/rdf-canon/tests/vocab#RDFC10EvalTest", |t| evaluate_positive_syntax_test(t, RdfFormat::NQuads), //TODO: not a proper implementation! ); evaluator.register( "https://w3c.github.io/rdf-canon/tests/vocab#RDFC10NegativeEvalTest", |_| Ok(()), //TODO: not a proper implementation ); evaluator.register( "https://w3c.github.io/rdf-canon/tests/vocab#RDFC10MapTest", |_| Ok(()), //TODO: not a proper implementation ); evaluator.register( "https://github.com/oxigraph/oxigraph/tests#TestNTripleRecovery", |t| evaluate_eval_test(t, RdfFormat::NTriples, true), ); evaluator.register( "https://github.com/oxigraph/oxigraph/tests#TestTurtleRecovery", |t| evaluate_eval_test(t, RdfFormat::Turtle, true), ); evaluator.register( "https://github.com/oxigraph/oxigraph/tests#TestN3Recovery", |t| evaluate_n3_eval_test(t, true), ); } fn evaluate_positive_syntax_test(test: &Test, format: RdfFormat) -> Result<()> { let action = test .action .as_deref() .ok_or_else(|| anyhow!("No action found for test {test}"))?; load_dataset(action, format, false).map_err(|e| anyhow!("Parse error: {e}"))?; Ok(()) } fn evaluate_positive_n3_syntax_test(test: &Test) -> Result<()> { let action = test .action .as_deref() .ok_or_else(|| anyhow!("No action found for test {test}"))?; load_n3(action, false).map_err(|e| anyhow!("Parse error: {e}"))?; Ok(()) } fn evaluate_negative_syntax_test(test: &Test, format: RdfFormat) -> Result<()> { let action = test .action .as_deref() .ok_or_else(|| anyhow!("No action found for test {test}"))?; match load_dataset(action, format, false) { Ok(_) => bail!("File parsed without errors even if it should not"), Err(_) => Ok(()), } } fn evaluate_negative_n3_syntax_test(test: &Test) -> Result<()> { let action = test .action .as_deref() .ok_or_else(|| anyhow!("No action found for test {test}"))?; match load_n3(action, false) { Ok(_) => bail!("File parsed without errors even if it should not"), Err(_) => Ok(()), } } fn evaluate_eval_test(test: &Test, format: RdfFormat, ignore_errors: bool) -> Result<()> { let action = test .action .as_deref() .ok_or_else(|| anyhow!("No action found for test {test}"))?; let mut actual_dataset = load_dataset(action, format, ignore_errors) .map_err(|e| anyhow!("Parse error on file {action}: {e}"))?; actual_dataset.canonicalize(); let results = test .result .as_ref() .ok_or_else(|| anyhow!("No tests result found"))?; let mut expected_dataset = load_dataset(results, guess_rdf_format(results)?, false) .map_err(|e| anyhow!("Parse error on file {results}: {e}"))?; expected_dataset.canonicalize(); if expected_dataset == actual_dataset { Ok(()) } else { bail!( "The two files are not isomorphic. Diff:\n{}", dataset_diff(&expected_dataset, &actual_dataset) ) } } fn evaluate_n3_eval_test(test: &Test, ignore_errors: bool) -> Result<()> { let action = test .action .as_deref() .ok_or_else(|| anyhow!("No action found for test {test}"))?; let mut actual_dataset = n3_to_dataset( load_n3(action, ignore_errors).map_err(|e| anyhow!("Parse error on file {action}: {e}"))?, ); actual_dataset.canonicalize(); let results = test .result .as_ref() .ok_or_else(|| anyhow!("No tests result found"))?; let mut expected_dataset = n3_to_dataset( load_n3(results, false).map_err(|e| anyhow!("Parse error on file {results}: {e}"))?, ); expected_dataset.canonicalize(); if expected_dataset == actual_dataset { Ok(()) } else { bail!( "The two files are not isomorphic. Diff:\n{}", dataset_diff(&expected_dataset, &actual_dataset) ) } } fn n3_to_dataset(quads: Vec) -> Dataset { quads .into_iter() .filter_map(|q| { Some(Quad { subject: match q.subject { N3Term::NamedNode(n) => n.into(), N3Term::BlankNode(n) => n.into(), N3Term::Triple(n) => n.into(), N3Term::Literal(_) => return None, N3Term::Variable(v) => BlankNode::new_unchecked(v.into_string()).into(), }, predicate: match q.predicate { N3Term::NamedNode(n) => n, _ => return None, }, object: match q.object { N3Term::NamedNode(n) => n.into(), N3Term::BlankNode(n) => n.into(), N3Term::Triple(n) => n.into(), N3Term::Literal(n) => n.into(), N3Term::Variable(v) => BlankNode::new_unchecked(v.into_string()).into(), }, graph_name: q.graph_name, }) }) .collect() }