Testsuite: makes format detection more strict

pull/370/head
Tpt 2 years ago committed by Thomas Tanon
parent 323ad73831
commit 6375481a80
  1. 2
      testsuite/rdf-tests
  2. 90
      testsuite/src/files.rs
  3. 4
      testsuite/src/manifest.rs
  4. 142
      testsuite/src/parser_evaluator.rs
  5. 18
      testsuite/src/report.rs
  6. 48
      testsuite/src/sparql_evaluator.rs

@ -1 +1 @@
Subproject commit 22cfdfda79153b6dba91844e69043c019c89fd84
Subproject commit 7f556ae7c7d94ef63997e13a173603fba80db5d7

@ -1,15 +1,16 @@
use anyhow::{anyhow, bail, Result};
use oxigraph::io::{DatasetFormat, DatasetParser, GraphFormat, GraphParser};
use oxigraph::model::{Dataset, Graph, GraphNameRef};
use oxigraph::store::Store;
use oxigraph::model::{Dataset, Graph};
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::PathBuf;
pub fn read_file(url: &str) -> Result<impl BufRead> {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push(if url.starts_with("http://w3c.github.io/rdf-tests/") {
url.replace("http://w3c.github.io/rdf-tests/", "rdf-tests/")
path.push(if url.starts_with("http://w3c.github.io/") {
url.replace("http://w3c.github.io/", "")
} else if url.starts_with("https://w3c.github.io/") {
url.replace("https://w3c.github.io/", "")
} else if url.starts_with("http://www.w3.org/2013/RDFXMLTests/") {
url.replace("http://www.w3.org/2013/RDFXMLTests/", "rdf-tests/rdf-xml/")
} else if url.starts_with("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/") {
@ -22,8 +23,6 @@ pub fn read_file(url: &str) -> Result<impl BufRead> {
"http://www.w3.org/2009/sparql/docs/tests/",
"rdf-tests/sparql11/",
)
} else if url.starts_with("https://w3c.github.io/rdf-star/") {
url.replace("https://w3c.github.io/", "")
} else if url.starts_with("https://github.com/oxigraph/oxigraph/tests/") {
url.replace(
"https://github.com/oxigraph/oxigraph/tests/",
@ -41,47 +40,7 @@ pub fn read_file_to_string(url: &str) -> Result<String> {
Ok(buf)
}
pub fn load_to_store<'a>(
url: &str,
store: &Store,
to_graph_name: impl Into<GraphNameRef<'a>>,
) -> Result<()> {
if url.ends_with(".nt") {
store.load_graph(
read_file(url)?,
GraphFormat::NTriples,
to_graph_name,
Some(url),
)?
} else if url.ends_with(".ttl") {
store.load_graph(
read_file(url)?,
GraphFormat::Turtle,
to_graph_name,
Some(url),
)?
} else if url.ends_with(".rdf") {
store.load_graph(
read_file(url)?,
GraphFormat::RdfXml,
to_graph_name,
Some(url),
)?
} else if url.ends_with(".nq") {
store.load_dataset(read_file(url)?, DatasetFormat::NQuads, Some(url))?
} else if url.ends_with(".trig") {
store.load_dataset(read_file(url)?, DatasetFormat::TriG, Some(url))?
} else {
bail!("Serialization type not found for {url}");
}
Ok(())
}
pub fn load_to_graph(url: &str, graph: &mut Graph) -> Result<()> {
let format = url
.rsplit_once('.')
.and_then(|(_, extension)| GraphFormat::from_extension(extension))
.ok_or_else(|| anyhow!("Serialization type not found for {url}"))?;
pub fn load_to_graph(url: &str, graph: &mut Graph, format: GraphFormat) -> Result<()> {
let parser = GraphParser::from_format(format).with_base_iri(url)?;
for t in parser.read_triples(read_file(url)?)? {
graph.insert(&t?);
@ -89,37 +48,34 @@ pub fn load_to_graph(url: &str, graph: &mut Graph) -> Result<()> {
Ok(())
}
pub fn load_graph(url: &str) -> Result<Graph> {
pub fn load_graph(url: &str, format: GraphFormat) -> Result<Graph> {
let mut graph = Graph::new();
load_to_graph(url, &mut graph)?;
load_to_graph(url, &mut graph, format)?;
Ok(graph)
}
pub fn load_to_dataset<'a>(
url: &str,
dataset: &mut Dataset,
to_graph_name: impl Into<GraphNameRef<'a>>,
) -> Result<()> {
let to_graph_name = to_graph_name.into();
let extension = url.rsplit_once('.').map(|(_, ext)| ext);
if let Some(format) = extension.and_then(GraphFormat::from_extension) {
let parser = GraphParser::from_format(format).with_base_iri(url)?;
for t in parser.read_triples(read_file(url)?)? {
dataset.insert(&t?.in_graph(to_graph_name));
}
} else if let Some(format) = extension.and_then(DatasetFormat::from_extension) {
pub fn guess_graph_format(url: &str) -> Result<GraphFormat> {
url.rsplit_once('.')
.and_then(|(_, extension)| GraphFormat::from_extension(extension))
.ok_or_else(|| anyhow!("Serialization type not found for {url}"))
}
pub fn load_to_dataset(url: &str, dataset: &mut Dataset, format: DatasetFormat) -> Result<()> {
let parser = DatasetParser::from_format(format).with_base_iri(url)?;
for q in parser.read_quads(read_file(url)?)? {
dataset.insert(&q?);
}
} else {
bail!("Serialization type not found for {url}")
}
Ok(())
}
pub fn load_dataset(url: &str) -> Result<Dataset> {
pub fn load_dataset(url: &str, format: DatasetFormat) -> Result<Dataset> {
let mut dataset = Dataset::new();
load_to_dataset(url, &mut dataset, GraphNameRef::DefaultGraph)?;
load_to_dataset(url, &mut dataset, format)?;
Ok(dataset)
}
pub fn guess_dataset_format(url: &str) -> Result<DatasetFormat> {
url.rsplit_once('.')
.and_then(|(_, extension)| DatasetFormat::from_extension(extension))
.ok_or_else(|| anyhow!("Serialization type not found for {url}"))
}

@ -1,4 +1,4 @@
use crate::files::load_to_graph;
use crate::files::{guess_graph_format, load_to_graph};
use crate::vocab::*;
use anyhow::{bail, Result};
use oxigraph::model::vocab::*;
@ -281,7 +281,7 @@ impl TestManifest {
return Ok(None);
};
self.graph.clear();
load_to_graph(&url, &mut self.graph)?;
load_to_graph(&url, &mut self.graph, guess_graph_format(&url)?)?;
let manifests = self
.graph

@ -1,109 +1,147 @@
use crate::evaluator::TestEvaluator;
use crate::files::load_dataset;
use crate::files::{guess_dataset_format, guess_graph_format, load_dataset, load_graph};
use crate::manifest::Test;
use crate::report::dataset_diff;
use crate::report::{dataset_diff, graph_diff};
use anyhow::{anyhow, bail, Result};
use oxigraph::io::{DatasetFormat, GraphFormat};
pub fn register_parser_tests(evaluator: &mut TestEvaluator) {
evaluator.register(
"http://www.w3.org/ns/rdftest#TestNTriplesPositiveSyntax",
evaluate_positive_syntax_test,
|t| evaluate_positive_graph_syntax_test(t, GraphFormat::NTriples),
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestNQuadsPositiveSyntax",
evaluate_positive_syntax_test,
|t| evaluate_positive_dataset_syntax_test(t, DatasetFormat::NQuads),
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTurtlePositiveSyntax",
evaluate_positive_syntax_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTrigPositiveSyntax",
evaluate_positive_syntax_test,
|t| evaluate_positive_graph_syntax_test(t, GraphFormat::Turtle),
);
evaluator.register("http://www.w3.org/ns/rdftest#TestTrigPositiveSyntax", |t| {
evaluate_positive_dataset_syntax_test(t, DatasetFormat::TriG)
});
evaluator.register(
"http://www.w3.org/ns/rdftest#TestNTriplesNegativeSyntax",
evaluate_negative_syntax_test,
|t| evaluate_negative_graph_syntax_test(t, GraphFormat::NTriples),
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestNQuadsNegativeSyntax",
evaluate_negative_syntax_test,
|t| evaluate_negative_dataset_syntax_test(t, DatasetFormat::NQuads),
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTurtleNegativeSyntax",
evaluate_negative_syntax_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTurtleNegativeEval",
evaluate_negative_syntax_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTrigNegativeSyntax",
evaluate_negative_syntax_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTrigNegativeEval",
evaluate_negative_syntax_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestXMLNegativeSyntax",
evaluate_negative_syntax_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTurtleEval",
evaluate_eval_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestTrigEval",
evaluate_eval_test,
);
evaluator.register(
"http://www.w3.org/ns/rdftest#TestXMLEval",
evaluate_eval_test,
|t| evaluate_negative_graph_syntax_test(t, GraphFormat::Turtle),
);
evaluator.register("http://www.w3.org/ns/rdftest#TestTrigNegativeSyntax", |t| {
evaluate_negative_dataset_syntax_test(t, DatasetFormat::TriG)
});
evaluator.register("http://www.w3.org/ns/rdftest#TestXMLNegativeSyntax", |t| {
evaluate_negative_graph_syntax_test(t, GraphFormat::RdfXml)
});
evaluator.register("http://www.w3.org/ns/rdftest#TestTurtleEval", |t| {
evaluate_graph_eval_test(t, GraphFormat::Turtle)
});
evaluator.register("http://www.w3.org/ns/rdftest#TestTrigEval", |t| {
evaluate_dataset_eval_test(t, DatasetFormat::TriG)
});
evaluator.register("http://www.w3.org/ns/rdftest#TestXMLEval", |t| {
evaluate_graph_eval_test(t, GraphFormat::RdfXml)
});
evaluator.register("http://www.w3.org/ns/rdftest#TestTurtleNegativeEval", |t| {
evaluate_negative_graph_syntax_test(t, GraphFormat::Turtle)
});
evaluator.register("http://www.w3.org/ns/rdftest#TestTrigNegativeEval", |t| {
evaluate_negative_dataset_syntax_test(t, DatasetFormat::TriG)
});
}
fn evaluate_positive_syntax_test(test: &Test) -> Result<()> {
fn evaluate_positive_graph_syntax_test(test: &Test, format: GraphFormat) -> Result<()> {
let action = test
.action
.as_deref()
.ok_or_else(|| anyhow!("No action found for test {test}"))?;
load_dataset(action).map_err(|e| anyhow!("Parse error: {e}"))?;
load_graph(action, format).map_err(|e| anyhow!("Parse error: {e}"))?;
Ok(())
}
fn evaluate_negative_syntax_test(test: &Test) -> Result<()> {
fn evaluate_positive_dataset_syntax_test(test: &Test, format: DatasetFormat) -> Result<()> {
let action = test
.action
.as_deref()
.ok_or_else(|| anyhow!("No action found for test {test}"))?;
match load_dataset(action) {
Ok(_) => bail!("File parsed with an error even if it should not"),
load_dataset(action, format).map_err(|e| anyhow!("Parse error: {e}"))?;
Ok(())
}
fn evaluate_negative_graph_syntax_test(test: &Test, format: GraphFormat) -> Result<()> {
let action = test
.action
.as_deref()
.ok_or_else(|| anyhow!("No action found for test {test}"))?;
match load_graph(action, format) {
Ok(_) => bail!("File parsed without errors even if it should not"),
Err(_) => Ok(()),
}
}
fn evaluate_negative_dataset_syntax_test(test: &Test, format: DatasetFormat) -> Result<()> {
let action = test
.action
.as_deref()
.ok_or_else(|| anyhow!("No action found for test {test}"))?;
match load_dataset(action, format) {
Ok(_) => bail!("File parsed without errors even if it should not"),
Err(_) => Ok(()),
}
}
fn evaluate_eval_test(test: &Test) -> Result<()> {
fn evaluate_graph_eval_test(test: &Test, format: GraphFormat) -> Result<()> {
let action = test
.action
.as_deref()
.ok_or_else(|| anyhow!("No action found for test {test}"))?;
let mut actual_graph =
load_dataset(action).map_err(|e| anyhow!("Parse error on file {action}: {e}"))?;
load_graph(action, format).map_err(|e| anyhow!("Parse error on file {action}: {e}"))?;
actual_graph.canonicalize();
if let Some(result) = &test.result {
let mut expected_graph =
load_dataset(result).map_err(|e| anyhow!("Parse error on file {action}: {e}"))?;
let results = test
.result
.as_ref()
.ok_or_else(|| anyhow!("No tests result found"))?;
let mut expected_graph = load_graph(results, guess_graph_format(results)?)
.map_err(|e| anyhow!("Parse error on file {results}: {e}"))?;
expected_graph.canonicalize();
if expected_graph == actual_graph {
Ok(())
} else {
bail!(
"The two files are not isomorphic. Diff:\n{}",
dataset_diff(&expected_graph, &actual_graph)
graph_diff(&expected_graph, &actual_graph)
)
}
}
fn evaluate_dataset_eval_test(test: &Test, format: DatasetFormat) -> 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).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_dataset_format(results)?)
.map_err(|e| anyhow!("Parse error on file {results}: {e}"))?;
expected_dataset.canonicalize();
if expected_dataset == actual_dataset {
Ok(())
} else {
bail!("No tests result found")
bail!(
"The two files are not isomorphic. Diff:\n{}",
dataset_diff(&expected_dataset, &actual_dataset)
)
}
}

@ -1,5 +1,5 @@
use anyhow::Result;
use oxigraph::model::{Dataset, NamedNode};
use oxigraph::model::{Dataset, Graph, NamedNode};
use std::fmt::Write;
use text_diff::{diff, Difference};
use time::format_description::well_known::Rfc3339;
@ -12,7 +12,7 @@ pub struct TestResult {
pub date: OffsetDateTime,
}
pub fn dataset_diff(expected: &Dataset, actual: &Dataset) -> String {
pub(super) fn dataset_diff(expected: &Dataset, actual: &Dataset) -> String {
format_diff(
&normalize_dataset_text(expected),
&normalize_dataset_text(actual),
@ -26,6 +26,20 @@ fn normalize_dataset_text(store: &Dataset) -> String {
quads.join("\n")
}
pub(super) fn graph_diff(expected: &Graph, actual: &Graph) -> String {
format_diff(
&normalize_graph_text(expected),
&normalize_graph_text(actual),
"triples",
)
}
fn normalize_graph_text(store: &Graph) -> String {
let mut triples: Vec<_> = store.iter().map(|q| q.to_string()).collect();
triples.sort();
triples.join("\n")
}
pub(super) fn format_diff(expected: &str, actual: &str, kind: &str) -> String {
let (_, changeset) = diff(expected, actual, "\n");
let mut ret = String::new();

@ -150,10 +150,10 @@ fn result_syntax_check(test: &Test, format: QueryResultsFormat) -> Result<()> {
fn evaluate_evaluation_test(test: &Test) -> Result<()> {
let store = Store::new()?;
if let Some(data) = &test.data {
load_to_store(data, &store, GraphNameRef::DefaultGraph)?;
load_dataset_to_store(data, &store)?;
}
for (name, value) in &test.graph_data {
load_to_store(value, &store, name)?;
load_graph_to_store(value, &store, name)?;
}
let query_file = test
.query
@ -172,14 +172,14 @@ fn evaluate_evaluation_test(test: &Test) -> Result<()> {
if !query.dataset().is_default_dataset() {
for graph_name in query.dataset().default_graph_graphs().unwrap_or(&[]) {
if let GraphName::NamedNode(graph_name) = graph_name {
load_to_store(graph_name.as_str(), &store, graph_name.as_ref())?;
load_graph_to_store(graph_name.as_str(), &store, graph_name.as_ref())?;
} else {
bail!("Invalid FROM in query {query} for test {test}");
}
}
for graph_name in query.dataset().available_named_graphs().unwrap_or(&[]) {
if let NamedOrBlankNode::NamedNode(graph_name) = graph_name {
load_to_store(graph_name.as_str(), &store, graph_name.as_ref())?;
load_graph_to_store(graph_name.as_str(), &store, graph_name.as_ref())?;
} else {
bail!("Invalid FROM NAMED in query {query} for test {test}");
}
@ -243,18 +243,18 @@ fn evaluate_negative_update_syntax_test(test: &Test) -> Result<()> {
fn evaluate_update_evaluation_test(test: &Test) -> Result<()> {
let store = Store::new()?;
if let Some(data) = &test.data {
load_to_store(data, &store, GraphNameRef::DefaultGraph)?;
load_dataset_to_store(data, &store)?;
}
for (name, value) in &test.graph_data {
load_to_store(value, &store, name)?;
load_graph_to_store(value, &store, name)?;
}
let result_store = Store::new()?;
if let Some(data) = &test.result {
load_to_store(data, &result_store, GraphNameRef::DefaultGraph)?;
load_dataset_to_store(data, &result_store)?;
}
for (name, value) in &test.result_graph_data {
load_to_store(value, &result_store, name)?;
load_graph_to_store(value, &result_store, name)?;
}
let update_file = test
@ -303,7 +303,7 @@ fn load_sparql_query_result(url: &str) -> Result<StaticQueryResults> {
false,
)
} else {
StaticQueryResults::from_graph(load_graph(url)?)
StaticQueryResults::from_graph(load_graph(url, guess_graph_format(url)?)?)
}
}
@ -321,7 +321,7 @@ impl StaticServiceHandler {
.map(|(name, data)| {
let name = NamedNode::new(name)?;
let store = Store::new()?;
load_to_store(data, &store, GraphNameRef::DefaultGraph)?;
load_dataset_to_store(data, &store)?;
Ok((name, store))
})
.collect::<Result<_>>()?,
@ -700,3 +700,31 @@ fn solutions_to_string(solutions: Vec<Vec<(Variable, Term)>>, ordered: bool) ->
}
lines.join("\n")
}
fn load_graph_to_store<'a>(
url: &str,
store: &Store,
to_graph_name: impl Into<GraphNameRef<'a>>,
) -> Result<()> {
store.load_graph(
read_file(url)?,
guess_graph_format(url)?,
to_graph_name,
Some(url),
)?;
Ok(())
}
fn load_dataset_to_store<'a>(url: &str, store: &Store) -> Result<()> {
if let Ok(format) = guess_dataset_format(url) {
store.load_dataset(read_file(url)?, format, Some(url))
} else {
store.load_graph(
read_file(url)?,
guess_graph_format(url)?,
GraphNameRef::DefaultGraph,
Some(url),
)
}?;
Ok(())
}

Loading…
Cancel
Save