From 0ba0e4e399d140769412035c589d4d8683633160 Mon Sep 17 00:00:00 2001 From: Tpt Date: Tue, 14 Sep 2021 17:21:52 +0200 Subject: [PATCH] Makes testsuite parsing more robust - avoids stack overflow if there are a lot of empty tests - allows blank node test ids - runs tests in the same order they are defined --- testsuite/oxigraph-tests/sparql/manifest.ttl | 4 +- testsuite/src/manifest.rs | 447 ++++++++++--------- 2 files changed, 241 insertions(+), 210 deletions(-) diff --git a/testsuite/oxigraph-tests/sparql/manifest.ttl b/testsuite/oxigraph-tests/sparql/manifest.ttl index df586541..2506901c 100644 --- a/testsuite/oxigraph-tests/sparql/manifest.ttl +++ b/testsuite/oxigraph-tests/sparql/manifest.ttl @@ -22,10 +22,10 @@ qt:data ] ; mf:result . -:describe rdf:type mf:QueryEvaluationTest ; +:describe_where rdf:type mf:QueryEvaluationTest ; mf:name "Simple DESCRIBE request" ; mf:action - [ qt:query ; + [ qt:query ; qt:data ] ; mf:result . diff --git a/testsuite/src/manifest.rs b/testsuite/src/manifest.rs index beac945b..66931881 100644 --- a/testsuite/src/manifest.rs +++ b/testsuite/src/manifest.rs @@ -3,6 +3,7 @@ use crate::vocab::*; use anyhow::{anyhow, Result}; use oxigraph::model::vocab::*; use oxigraph::model::*; +use std::collections::VecDeque; use std::fmt; pub struct Test { @@ -50,15 +51,15 @@ impl fmt::Display for Test { pub struct TestManifest { graph: Graph, - tests_to_do: Vec, - manifests_to_do: Vec, + tests_to_do: VecDeque, + manifests_to_do: VecDeque, } impl TestManifest { pub fn new(manifest_urls: impl IntoIterator) -> Self { Self { graph: Graph::new(), - tests_to_do: Vec::new(), + tests_to_do: VecDeque::new(), manifests_to_do: manifest_urls .into_iter() .map(|url| url.to_string()) @@ -71,231 +72,261 @@ impl Iterator for TestManifest { type Item = Result; fn next(&mut self) -> Option> { - match self.tests_to_do.pop() { - Some(Term::NamedNode(test_node)) => { - let kind = match self - .graph - .object_for_subject_predicate(&test_node, rdf::TYPE) - { - Some(TermRef::NamedNode(c)) => c.into_owned(), - _ => return self.next(), //We ignore the test - }; - let name = match self - .graph - .object_for_subject_predicate(&test_node, mf::NAME) - { - Some(TermRef::Literal(c)) => Some(c.value().to_string()), - _ => None, - }; - let comment = match self - .graph - .object_for_subject_predicate(&test_node, rdfs::COMMENT) - { - Some(TermRef::Literal(c)) => Some(c.value().to_string()), - _ => None, - }; - let (action, query, update, data, graph_data, service_data) = match self - .graph - .object_for_subject_predicate(&test_node, mf::ACTION) - { - Some(TermRef::NamedNode(n)) => ( - Some(n.as_str().to_owned()), - None, - None, - None, - vec![], - vec![], - ), - Some(TermRef::BlankNode(n)) => { - let query = match self.graph.object_for_subject_predicate(n, qt::QUERY) { - Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()), - _ => None, - }; - let update = match self.graph.object_for_subject_predicate(n, ut::REQUEST) { - Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()), - _ => None, - }; - let data = match self - .graph - .object_for_subject_predicate(n, qt::DATA) - .or_else(|| self.graph.object_for_subject_predicate(n, ut::DATA)) - { - Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()), - _ => None, - }; - let graph_data = self - .graph - .objects_for_subject_predicate(n, qt::GRAPH_DATA) - .chain(self.graph.objects_for_subject_predicate(n, ut::GRAPH_DATA)) - .filter_map(|g| match g { - TermRef::NamedNode(q) => { - Some((q.into_owned(), q.as_str().to_owned())) - } - TermRef::BlankNode(node) => { - if let Some(TermRef::NamedNode(graph)) = - self.graph.object_for_subject_predicate(node, ut::GRAPH) - { - if let Some(TermRef::Literal(name)) = self - .graph - .object_for_subject_predicate(node, rdfs::LABEL) - { - Some(( - NamedNode::new(name.value()).unwrap(), - graph.as_str().to_owned(), - )) - } else { - Some((graph.into_owned(), graph.as_str().to_owned())) - } - } else { - None - } - } + loop { + return match self.tests_to_do.pop_front().map(|term| match term { + Term::NamedNode(n) => Ok(n), + Term::BlankNode(n) => Ok(NamedNode::new(format!( + "http://oxigraph.org/.well-known/genid/{}", + n.as_str() + ))?), + _ => Err(anyhow!("Invalid test identifier. Got {}", term)), + }) { + Some(Ok(test_node)) => { + if self.graph.triples_for_subject(&test_node).next().is_none() { + continue; // This test does not exists + } + let name = match self + .graph + .object_for_subject_predicate(&test_node, mf::NAME) + { + Some(TermRef::Literal(c)) => Some(c.value().to_string()), + _ => None, + }; + let kind = match self + .graph + .object_for_subject_predicate(&test_node, rdf::TYPE) + { + Some(TermRef::NamedNode(c)) => c.into_owned(), + _ => { + return Some(Err(anyhow!( + "The test {} named {} has no rdf:type", + test_node, + name.as_deref().unwrap_or("") + ))) + } + }; + let comment = match self + .graph + .object_for_subject_predicate(&test_node, rdfs::COMMENT) + { + Some(TermRef::Literal(c)) => Some(c.value().to_string()), + _ => None, + }; + let (action, query, update, data, graph_data, service_data) = match self + .graph + .object_for_subject_predicate(&test_node, mf::ACTION) + { + Some(TermRef::NamedNode(n)) => ( + Some(n.as_str().to_owned()), + None, + None, + None, + vec![], + vec![], + ), + Some(TermRef::BlankNode(n)) => { + let query = match self.graph.object_for_subject_predicate(n, qt::QUERY) + { + Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()), _ => None, - }) - .collect(); - let service_data = self - .graph - .objects_for_subject_predicate(n, qt::SERVICE_DATA) - .filter_map(|g| match g { - TermRef::NamedNode(g) => Some(g.into()), - TermRef::BlankNode(g) => Some(g.into()), + }; + let update = + match self.graph.object_for_subject_predicate(n, ut::REQUEST) { + Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()), + _ => None, + }; + let data = match self + .graph + .object_for_subject_predicate(n, qt::DATA) + .or_else(|| self.graph.object_for_subject_predicate(n, ut::DATA)) + { + Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()), _ => None, - }) - .filter_map(|g: SubjectRef<'_>| { - if let ( - Some(TermRef::NamedNode(endpoint)), - Some(TermRef::NamedNode(data)), - ) = ( - self.graph.object_for_subject_predicate(g, qt::ENDPOINT), - self.graph.object_for_subject_predicate(g, qt::DATA), - ) { - Some((endpoint.as_str().to_owned(), data.as_str().to_owned())) - } else { - None - } - }) - .collect(); - (None, query, update, data, graph_data, service_data) - } - Some(_) => return Some(Err(anyhow!("invalid action"))), - None => { - return Some(Err(anyhow!("action not found for test {}", test_node))); - } - }; - let (result, result_graph_data) = match self - .graph - .object_for_subject_predicate(&test_node, mf::RESULT) - { - Some(TermRef::NamedNode(n)) => (Some(n.as_str().to_owned()), Vec::new()), - Some(TermRef::BlankNode(n)) => ( - if let Some(TermRef::NamedNode(result)) = - self.graph.object_for_subject_predicate(n, ut::DATA) - { - Some(result.as_str().to_owned()) - } else { - None - }, - self.graph - .objects_for_subject_predicate(n, ut::GRAPH_DATA) - .filter_map(|g| match g { - TermRef::NamedNode(q) => { - Some((q.into_owned(), q.as_str().to_owned())) - } - TermRef::BlankNode(node) => { - if let Some(TermRef::NamedNode(graph)) = - self.graph.object_for_subject_predicate(node, ut::GRAPH) - { - if let Some(TermRef::Literal(name)) = self - .graph - .object_for_subject_predicate(node, rdfs::LABEL) + }; + let graph_data = self + .graph + .objects_for_subject_predicate(n, qt::GRAPH_DATA) + .chain(self.graph.objects_for_subject_predicate(n, ut::GRAPH_DATA)) + .filter_map(|g| match g { + TermRef::NamedNode(q) => { + Some((q.into_owned(), q.as_str().to_owned())) + } + TermRef::BlankNode(node) => { + if let Some(TermRef::NamedNode(graph)) = + self.graph.object_for_subject_predicate(node, ut::GRAPH) { - Some(( - NamedNode::new(name.value()).unwrap(), - graph.as_str().to_owned(), - )) + if let Some(TermRef::Literal(name)) = self + .graph + .object_for_subject_predicate(node, rdfs::LABEL) + { + Some(( + NamedNode::new(name.value()).unwrap(), + graph.as_str().to_owned(), + )) + } else { + Some(( + graph.into_owned(), + graph.as_str().to_owned(), + )) + } } else { - Some((graph.into_owned(), graph.as_str().to_owned())) + None } + } + _ => None, + }) + .collect(); + let service_data = self + .graph + .objects_for_subject_predicate(n, qt::SERVICE_DATA) + .filter_map(|g| match g { + TermRef::NamedNode(g) => Some(g.into()), + TermRef::BlankNode(g) => Some(g.into()), + _ => None, + }) + .filter_map(|g: SubjectRef<'_>| { + if let ( + Some(TermRef::NamedNode(endpoint)), + Some(TermRef::NamedNode(data)), + ) = ( + self.graph.object_for_subject_predicate(g, qt::ENDPOINT), + self.graph.object_for_subject_predicate(g, qt::DATA), + ) { + Some(( + endpoint.as_str().to_owned(), + data.as_str().to_owned(), + )) } else { None } - } - _ => None, - }) - .collect(), - ), - Some(_) => return Some(Err(anyhow!("invalid result"))), - None => (None, Vec::new()), - }; - Some(Ok(Test { - id: test_node, - kind, - name, - comment, - action, - query, - update, - data, - graph_data, - service_data, - result, - result_graph_data, - })) - } - Some(_) => self.next(), - None => { - match self.manifests_to_do.pop() { - Some(url) => { - self.graph.clear(); - if let Err(error) = load_to_graph(&url, &mut self.graph) { - return Some(Err(error)); + }) + .collect(); + (None, query, update, data, graph_data, service_data) } - - for manifest in self - .graph - .subjects_for_predicate_object(rdf::TYPE, mf::MANIFEST) - { - match self - .graph - .object_for_subject_predicate(manifest, mf::INCLUDE) + Some(_) => return Some(Err(anyhow!("invalid action"))), + None => { + return Some(Err(anyhow!("action not found for test {}", test_node))); + } + }; + let (result, result_graph_data) = match self + .graph + .object_for_subject_predicate(&test_node, mf::RESULT) + { + Some(TermRef::NamedNode(n)) => (Some(n.as_str().to_owned()), Vec::new()), + Some(TermRef::BlankNode(n)) => ( + if let Some(TermRef::NamedNode(result)) = + self.graph.object_for_subject_predicate(n, ut::DATA) { - Some(TermRef::BlankNode(list)) => { - self.manifests_to_do.extend( - RdfListIterator::iter(&self.graph, list.into()).filter_map( - |m| match m { - Term::NamedNode(nm) => Some(nm.into_string()), - _ => None, - }, - ), - ); - } - Some(_) => return Some(Err(anyhow!("invalid tests list"))), - None => (), + Some(result.as_str().to_owned()) + } else { + None + }, + self.graph + .objects_for_subject_predicate(n, ut::GRAPH_DATA) + .filter_map(|g| match g { + TermRef::NamedNode(q) => { + Some((q.into_owned(), q.as_str().to_owned())) + } + TermRef::BlankNode(node) => { + if let Some(TermRef::NamedNode(graph)) = + self.graph.object_for_subject_predicate(node, ut::GRAPH) + { + if let Some(TermRef::Literal(name)) = self + .graph + .object_for_subject_predicate(node, rdfs::LABEL) + { + Some(( + NamedNode::new(name.value()).unwrap(), + graph.as_str().to_owned(), + )) + } else { + Some(( + graph.into_owned(), + graph.as_str().to_owned(), + )) + } + } else { + None + } + } + _ => None, + }) + .collect(), + ), + Some(_) => return Some(Err(anyhow!("invalid result"))), + None => (None, Vec::new()), + }; + Some(Ok(Test { + id: test_node, + kind, + name, + comment, + action, + query, + update, + data, + graph_data, + service_data, + result, + result_graph_data, + })) + } + Some(Err(error)) => Some(Err(error)), + None => { + match self.manifests_to_do.pop_front() { + Some(url) => { + self.graph.clear(); + if let Err(error) = load_to_graph(&url, &mut self.graph) { + return Some(Err(error)); } - // New tests - match self + for manifest in self .graph - .object_for_subject_predicate(manifest, mf::ENTRIES) + .subjects_for_predicate_object(rdf::TYPE, mf::MANIFEST) { - Some(TermRef::BlankNode(list)) => { - self.tests_to_do - .extend(RdfListIterator::iter(&self.graph, list.into())); + match self + .graph + .object_for_subject_predicate(manifest, mf::INCLUDE) + { + Some(TermRef::BlankNode(list)) => { + self.manifests_to_do.extend( + RdfListIterator::iter(&self.graph, list.into()) + .filter_map(|m| match m { + Term::NamedNode(nm) => Some(nm.into_string()), + _ => None, + }), + ); + } + Some(_) => return Some(Err(anyhow!("invalid tests list"))), + None => (), } - Some(term) => { - return Some(Err(anyhow!( - "Invalid tests list. Got term {}", - term - ))); + + // New tests + match self + .graph + .object_for_subject_predicate(manifest, mf::ENTRIES) + { + Some(TermRef::BlankNode(list)) => { + self.tests_to_do.extend(RdfListIterator::iter( + &self.graph, + list.into(), + )); + } + Some(term) => { + return Some(Err(anyhow!( + "Invalid tests list. Got term {}", + term + ))); + } + None => (), } - None => (), } + continue; } + None => None, } - None => return None, } - self.next() - } + }; } } }