|
|
|
@ -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<Term>, |
|
|
|
|
manifests_to_do: Vec<String>, |
|
|
|
|
tests_to_do: VecDeque<Term>, |
|
|
|
|
manifests_to_do: VecDeque<String>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl TestManifest { |
|
|
|
|
pub fn new<S: ToString>(manifest_urls: impl IntoIterator<Item = S>) -> 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<Test>; |
|
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Result<Test>> { |
|
|
|
|
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() |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|