|
|
@ -4,14 +4,15 @@ use rayon::prelude::*; |
|
|
|
use rudf::model::vocab::rdf; |
|
|
|
use rudf::model::vocab::rdf; |
|
|
|
use rudf::model::vocab::rdfs; |
|
|
|
use rudf::model::vocab::rdfs; |
|
|
|
use rudf::model::*; |
|
|
|
use rudf::model::*; |
|
|
|
use rudf::sparql::{PreparedQuery, QueryOptions}; |
|
|
|
use rudf::sparql::*; |
|
|
|
use rudf::sparql::{Query, QueryResult, QueryResultSyntax}; |
|
|
|
use rudf::*; |
|
|
|
use rudf::{GraphSyntax, MemoryRepository, Repository, RepositoryConnection, Result}; |
|
|
|
use std::collections::HashMap; |
|
|
|
use std::fmt; |
|
|
|
use std::fmt; |
|
|
|
use std::fs::File; |
|
|
|
use std::fs::File; |
|
|
|
use std::io::Read; |
|
|
|
use std::io::Read; |
|
|
|
use std::io::{BufRead, BufReader}; |
|
|
|
use std::io::{BufRead, BufReader}; |
|
|
|
use std::path::PathBuf; |
|
|
|
use std::path::PathBuf; |
|
|
|
|
|
|
|
use std::sync::Arc; |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn sparql_w3c_syntax_testsuite() -> Result<()> { |
|
|
|
fn sparql_w3c_syntax_testsuite() -> Result<()> { |
|
|
@ -97,6 +98,7 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/negation/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/negation/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/project-expression/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/project-expression/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/property-path/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/property-path/manifest.ttl", |
|
|
|
|
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/service/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/subquery/manifest.ttl", |
|
|
|
"http://www.w3.org/2009/sparql/docs/tests/data-sparql11/subquery/manifest.ttl", |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
@ -133,7 +135,10 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { |
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/property-path/manifest#pp35").unwrap(), |
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/property-path/manifest#pp35").unwrap(), |
|
|
|
//We write "2"^^xsd:decimal instead of "2.0"^^xsd:decimal
|
|
|
|
//We write "2"^^xsd:decimal instead of "2.0"^^xsd:decimal
|
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/aggregates/manifest#agg-err-02").unwrap(), |
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/aggregates/manifest#agg-err-02").unwrap(), |
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/aggregates/manifest#agg-avg-02").unwrap() |
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/aggregates/manifest#agg-avg-02").unwrap(), |
|
|
|
|
|
|
|
//SERVICE name from a BGP
|
|
|
|
|
|
|
|
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/service/manifest#service5").unwrap(), |
|
|
|
|
|
|
|
|
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
let tests: Result<Vec<_>> = manifest_10_urls |
|
|
|
let tests: Result<Vec<_>> = manifest_10_urls |
|
|
@ -158,39 +163,39 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { |
|
|
|
} |
|
|
|
} |
|
|
|
match repository |
|
|
|
match repository |
|
|
|
.connection()? |
|
|
|
.connection()? |
|
|
|
.prepare_query(&read_file_to_string(&test.query)?, QueryOptions::default().with_base_iri(&test.query)) |
|
|
|
.prepare_query(&read_file_to_string(&test.query)?, QueryOptions::default().with_base_iri(&test.query).with_service_handler(StaticServiceHandler::new(&test.service_data)?)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Err(error) => Err(format_err!( |
|
|
|
Err(error) => Err(format_err!( |
|
|
|
"Failure to parse query of {} with error: {}", |
|
|
|
"Failure to parse query of {} with error: {}", |
|
|
|
test, error |
|
|
|
test, error |
|
|
|
)), |
|
|
|
)), |
|
|
|
Ok(query) => match query.exec() { |
|
|
|
Ok(query) => match query.exec() { |
|
|
|
Err(error) => Err(format_err!( |
|
|
|
Err(error) => Err(format_err!( |
|
|
|
"Failure to execute query of {} with error: {}", |
|
|
|
"Failure to execute query of {} with error: {}", |
|
|
|
test, error |
|
|
|
test, error |
|
|
|
)), |
|
|
|
)), |
|
|
|
Ok(result) => { |
|
|
|
Ok(result) => { |
|
|
|
let expected_graph = |
|
|
|
let expected_graph = |
|
|
|
load_sparql_query_result_graph(test.result.as_ref().unwrap())?; |
|
|
|
load_sparql_query_result_graph(test.result.as_ref().unwrap()).map_err(|e| format_err!("Error constructing expected graph for {}: {}", test, e))?; |
|
|
|
let with_order = expected_graph |
|
|
|
let with_order = expected_graph |
|
|
|
.triples_for_predicate(&rs::INDEX) |
|
|
|
.triples_for_predicate(&rs::INDEX) |
|
|
|
.next() |
|
|
|
.next() |
|
|
|
.is_some(); |
|
|
|
.is_some(); |
|
|
|
let actual_graph = to_graph(result, with_order).map_err(|e| format_err!("Error constructing result graph for {}: {}", test, e))?; |
|
|
|
let actual_graph = to_graph(result, with_order).map_err(|e| format_err!("Error constructing result graph for {}: {}", test, e))?; |
|
|
|
if actual_graph.is_isomorphic(&expected_graph) { |
|
|
|
if actual_graph.is_isomorphic(&expected_graph) { |
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Err(format_err!("Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", |
|
|
|
Err(format_err!("Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", |
|
|
|
test, |
|
|
|
test, |
|
|
|
expected_graph, |
|
|
|
expected_graph, |
|
|
|
actual_graph, |
|
|
|
actual_graph, |
|
|
|
Query::parse(&read_file_to_string(&test.query)?, Some(&test.query)).unwrap(), |
|
|
|
Query::parse(&read_file_to_string(&test.query)?, Some(&test.query)).unwrap(), |
|
|
|
repository_to_string(&repository) |
|
|
|
repository_to_string(&repository) |
|
|
|
)) |
|
|
|
)) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else if test.kind != "NegativeSyntaxTest11" { |
|
|
|
} else if test.kind != "NegativeSyntaxTest11" { |
|
|
|
panic!("Not supported test: {}", test) |
|
|
|
panic!("Not supported test: {}", test) |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -410,6 +415,7 @@ pub struct Test { |
|
|
|
pub query: String, |
|
|
|
pub query: String, |
|
|
|
pub data: Option<String>, |
|
|
|
pub data: Option<String>, |
|
|
|
pub graph_data: Vec<String>, |
|
|
|
pub graph_data: Vec<String>, |
|
|
|
|
|
|
|
pub service_data: Vec<(String, String)>, |
|
|
|
pub result: Option<String>, |
|
|
|
pub result: Option<String>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -488,6 +494,12 @@ pub mod qt { |
|
|
|
pub static ref GRAPH_DATA: NamedNode = |
|
|
|
pub static ref GRAPH_DATA: NamedNode = |
|
|
|
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#graphData") |
|
|
|
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#graphData") |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
pub static ref SERVICE_DATA: NamedNode = |
|
|
|
|
|
|
|
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#serviceData") |
|
|
|
|
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
pub static ref ENDPOINT: NamedNode = |
|
|
|
|
|
|
|
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#endpoint") |
|
|
|
|
|
|
|
.unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -522,31 +534,53 @@ impl Iterator for TestManifest { |
|
|
|
Some(Term::Literal(c)) => Some(c.value().to_string()), |
|
|
|
Some(Term::Literal(c)) => Some(c.value().to_string()), |
|
|
|
_ => None, |
|
|
|
_ => None, |
|
|
|
}; |
|
|
|
}; |
|
|
|
let (query, data, graph_data) = match self |
|
|
|
let (query, data, graph_data, service_data) = match self |
|
|
|
.graph |
|
|
|
.graph |
|
|
|
.object_for_subject_predicate(&test_subject, &*mf::ACTION) |
|
|
|
.object_for_subject_predicate(&test_subject, &*mf::ACTION) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Some(Term::NamedNode(n)) => (n.as_str().to_string(), None, vec![]), |
|
|
|
Some(Term::NamedNode(n)) => (n.as_str().to_owned(), None, vec![], vec![]), |
|
|
|
Some(Term::BlankNode(n)) => { |
|
|
|
Some(Term::BlankNode(n)) => { |
|
|
|
let n = n.clone().into(); |
|
|
|
let n = n.clone().into(); |
|
|
|
let query = match self.graph.object_for_subject_predicate(&n, &qt::QUERY) { |
|
|
|
let query = match self.graph.object_for_subject_predicate(&n, &qt::QUERY) { |
|
|
|
Some(Term::NamedNode(q)) => q.as_str().to_string(), |
|
|
|
Some(Term::NamedNode(q)) => q.as_str().to_owned(), |
|
|
|
Some(_) => return Some(Err(format_err!("invalid query"))), |
|
|
|
Some(_) => return Some(Err(format_err!("invalid query"))), |
|
|
|
None => return Some(Err(format_err!("query not found"))), |
|
|
|
None => return Some(Err(format_err!("query not found"))), |
|
|
|
}; |
|
|
|
}; |
|
|
|
let data = match self.graph.object_for_subject_predicate(&n, &qt::DATA) { |
|
|
|
let data = match self.graph.object_for_subject_predicate(&n, &qt::DATA) { |
|
|
|
Some(Term::NamedNode(q)) => Some(q.as_str().to_string()), |
|
|
|
Some(Term::NamedNode(q)) => Some(q.as_str().to_owned()), |
|
|
|
_ => None, |
|
|
|
_ => None, |
|
|
|
}; |
|
|
|
}; |
|
|
|
let graph_data = self |
|
|
|
let graph_data = self |
|
|
|
.graph |
|
|
|
.graph |
|
|
|
.objects_for_subject_predicate(&n, &qt::GRAPH_DATA) |
|
|
|
.objects_for_subject_predicate(&n, &qt::GRAPH_DATA) |
|
|
|
.filter_map(|g| match g { |
|
|
|
.filter_map(|g| match g { |
|
|
|
Term::NamedNode(q) => Some(q.as_str().to_string()), |
|
|
|
Term::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 { |
|
|
|
|
|
|
|
Term::NamedNode(g) => Some(g.clone().into()), |
|
|
|
|
|
|
|
Term::BlankNode(g) => Some(g.clone().into()), |
|
|
|
_ => None, |
|
|
|
_ => None, |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
.filter_map(|g| { |
|
|
|
|
|
|
|
if let ( |
|
|
|
|
|
|
|
Some(Term::NamedNode(endpoint)), |
|
|
|
|
|
|
|
Some(Term::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(); |
|
|
|
.collect(); |
|
|
|
(query, data, graph_data) |
|
|
|
(query, data, graph_data, service_data) |
|
|
|
} |
|
|
|
} |
|
|
|
Some(_) => return Some(Err(format_err!("invalid action"))), |
|
|
|
Some(_) => return Some(Err(format_err!("invalid action"))), |
|
|
|
None => { |
|
|
|
None => { |
|
|
@ -560,7 +594,7 @@ impl Iterator for TestManifest { |
|
|
|
.graph |
|
|
|
.graph |
|
|
|
.object_for_subject_predicate(&test_subject, &*mf::RESULT) |
|
|
|
.object_for_subject_predicate(&test_subject, &*mf::RESULT) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Some(Term::NamedNode(n)) => Some(n.as_str().to_string()), |
|
|
|
Some(Term::NamedNode(n)) => Some(n.as_str().to_owned()), |
|
|
|
Some(_) => return Some(Err(format_err!("invalid result"))), |
|
|
|
Some(_) => return Some(Err(format_err!("invalid result"))), |
|
|
|
None => None, |
|
|
|
None => None, |
|
|
|
}; |
|
|
|
}; |
|
|
@ -572,6 +606,7 @@ impl Iterator for TestManifest { |
|
|
|
query, |
|
|
|
query, |
|
|
|
data, |
|
|
|
data, |
|
|
|
graph_data, |
|
|
|
graph_data, |
|
|
|
|
|
|
|
service_data, |
|
|
|
result, |
|
|
|
result, |
|
|
|
})) |
|
|
|
})) |
|
|
|
} |
|
|
|
} |
|
|
@ -595,7 +630,7 @@ impl Iterator for TestManifest { |
|
|
|
self.manifests_to_do.extend( |
|
|
|
self.manifests_to_do.extend( |
|
|
|
RdfListIterator::iter(&self.graph, list.clone().into()) |
|
|
|
RdfListIterator::iter(&self.graph, list.clone().into()) |
|
|
|
.filter_map(|m| match m { |
|
|
|
.filter_map(|m| match m { |
|
|
|
Term::NamedNode(nm) => Some(nm.as_str().to_string()), |
|
|
|
Term::NamedNode(nm) => Some(nm.into_string()), |
|
|
|
_ => None, |
|
|
|
_ => None, |
|
|
|
}), |
|
|
|
}), |
|
|
|
); |
|
|
|
); |
|
|
@ -670,3 +705,56 @@ impl<'a> Iterator for RdfListIterator<'a> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)] |
|
|
|
|
|
|
|
struct StaticServiceHandler { |
|
|
|
|
|
|
|
services: Arc<HashMap<NamedNode, MemoryRepository>>, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl StaticServiceHandler { |
|
|
|
|
|
|
|
fn new(services: &[(String, String)]) -> Result<Self> { |
|
|
|
|
|
|
|
Ok(Self { |
|
|
|
|
|
|
|
services: Arc::new( |
|
|
|
|
|
|
|
services |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.map(|(name, data)| { |
|
|
|
|
|
|
|
let name = NamedNode::parse(name)?; |
|
|
|
|
|
|
|
let repository = MemoryRepository::default(); |
|
|
|
|
|
|
|
load_graph_to_repository(&data, &mut repository.connection()?, None)?; |
|
|
|
|
|
|
|
Ok((name, repository)) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect::<Result<_>>()?, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl ServiceHandler for StaticServiceHandler { |
|
|
|
|
|
|
|
fn handle<'a>( |
|
|
|
|
|
|
|
&'a self, |
|
|
|
|
|
|
|
service_name: &NamedNode, |
|
|
|
|
|
|
|
graph_pattern: &'a GraphPattern, |
|
|
|
|
|
|
|
) -> Result<BindingsIterator<'a>> { |
|
|
|
|
|
|
|
if let QueryResult::Bindings(iterator) = self |
|
|
|
|
|
|
|
.services |
|
|
|
|
|
|
|
.get(service_name) |
|
|
|
|
|
|
|
.ok_or_else(|| format_err!("Service {} not found", service_name))? |
|
|
|
|
|
|
|
.connection()? |
|
|
|
|
|
|
|
.prepare_query_from_pattern( |
|
|
|
|
|
|
|
&graph_pattern, |
|
|
|
|
|
|
|
QueryOptions::default().with_service_handler(self.clone()), |
|
|
|
|
|
|
|
)? |
|
|
|
|
|
|
|
.exec()? |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
//TODO: very hugly
|
|
|
|
|
|
|
|
let (variables, iter) = iterator.destruct(); |
|
|
|
|
|
|
|
let collected = iter.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
Ok(BindingsIterator::new( |
|
|
|
|
|
|
|
variables, |
|
|
|
|
|
|
|
Box::new(collected.into_iter()), |
|
|
|
|
|
|
|
)) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Err(format_err!("Expected bindings but got another QueryResult")) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|