use std::fmt::Debug;
use crate::graph::*;
use crate::ns::*;
use crate::term::test::TestTerm;
use crate::term::CopiableTerm;
use crate::triple::stream::*;
use crate::triple::streaming_mode::{TripleStreamingMode, UnsafeTriple};
use crate::triple::*;
use lazy_static::lazy_static;
type StaticTerm = TestTerm<&'static str>;
type BoxTerm = TestTerm<Box<str>>;
pub const NS: &str = "http://example.org/";
lazy_static! {
pub static ref C1: StaticTerm = StaticTerm::iri2(NS, "C1");
pub static ref C2: StaticTerm = StaticTerm::iri2(NS, "C2");
pub static ref P1: StaticTerm = StaticTerm::iri2(NS, "p1");
pub static ref P2: StaticTerm = StaticTerm::iri2(NS, "p2");
pub static ref I1A: StaticTerm = StaticTerm::iri2(NS, "I1A");
pub static ref I1B: StaticTerm = StaticTerm::iri2(NS, "I1B");
pub static ref I2A: StaticTerm = StaticTerm::iri2(NS, "I2A");
pub static ref I2B: StaticTerm = StaticTerm::iri2(NS, "I2B");
pub static ref B1: StaticTerm = StaticTerm::bnode("1");
pub static ref B2: StaticTerm = StaticTerm::bnode("2");
pub static ref L1: StaticTerm = StaticTerm::lit_dt("lit1", xsd::string);
pub static ref L2: StaticTerm = StaticTerm::lit_dt("lit2", xsd::string);
pub static ref L2E: StaticTerm = StaticTerm::lit_lang("lit2", "en");
pub static ref V1: StaticTerm = StaticTerm::var("v1");
pub static ref V2: StaticTerm = StaticTerm::var("v2");
pub static ref V3: StaticTerm = StaticTerm::var("v3");
}
pub fn no_triple() -> impl TripleSource {
let v = Vec::<[StaticTerm; 3]>::new();
v.into_iter().into_triple_source()
}
pub fn some_triples() -> impl TripleSource {
let v: Vec<[StaticTerm; 3]> = vec![
[*C1, rdf::type_.into(), rdfs::Class.into()],
[*C2, rdf::type_.into(), rdfs::Class.into()],
[*C2, rdf::type_.into(), rdfs::Resource.into()],
[*C2, rdfs::subClassOf.into(), *C1],
[*C2, rdfs::subClassOf.into(), rdfs::Resource.into()],
[*P1, rdf::type_.into(), rdf::Property.into()],
[*P1, rdfs::domain.into(), *C1],
[*P1, rdfs::range.into(), *C2],
[*P2, rdf::type_.into(), rdf::Property.into()],
[*P2, rdfs::domain.into(), *C2],
[*P2, rdfs::range.into(), *C2],
[*I1A, rdf::type_.into(), *C1],
[*I1B, rdf::type_.into(), *C1],
[*I2A, rdf::type_.into(), *C2],
[*I2B, rdf::type_.into(), *C2],
[*I1A, *P1, *I2A],
[*I1B, *P1, *I2B],
[*I2A, *P2, *I2B],
];
v.into_iter().into_triple_source()
}
pub fn strict_node_types_triples() -> impl TripleSource {
let v: Vec<[StaticTerm; 3]> = vec![
[rdf::type_.into(), rdf::type_.into(), rdf::Property.into()],
[*B1, rdf::type_.into(), *L1],
[*B2, rdf::type_.into(), *B1],
[*B2, rdf::type_.into(), *L2],
[*B2, rdf::type_.into(), *L2E],
];
v.into_iter().into_triple_source()
}
pub fn generalized_node_types_triples() -> impl TripleSource {
let v: Vec<[StaticTerm; 3]> = vec![
[rdf::type_.into(), rdf::type_.into(), rdf::Property.into()],
[*B1, *B2, *B1],
[*L2, *L1, *L1],
[*V1, *V2, *V3],
[*B2, *V1, *L2E],
];
v.into_iter().into_triple_source()
}
pub fn as_box_t<T: Triple, E>(triple: Result<T, E>) -> [BoxTerm; 3]
where
E: Debug,
{
let triple = triple.unwrap();
[
triple.s().copied(),
triple.p().copied(),
triple.o().copied(),
]
}
#[allow(dead_code)]
pub fn dump_graph<G: Graph>(g: &G)
where
<<G::Triple as TripleStreamingMode>::UnsafeTriple as UnsafeTriple>::Term: Debug,
{
println!("<<<<");
for t in g.triples() {
let t = t.unwrap();
println!("{:?}\n{:?}\n{:?}\n\n", t.s(), t.p(), t.o());
}
println!(">>>>");
}
pub fn assert_consistent_hint(val: usize, hint: (usize, Option<usize>)) {
assert!(hint.0 <= val, "hint {:?} not consistent with {}", hint, val);
assert!(
val <= hint.1.unwrap_or(val),
"hint {:?} not consistent with {}",
hint,
val
)
}
pub fn assert_contains<'a, I, T, U>(collection: I, item: &U)
where
I: IntoIterator<Item = &'a T>,
T: 'a,
U: PartialEq<T>,
{
assert!(collection.into_iter().any(|i| item == i))
}
#[macro_export]
macro_rules! test_graph_impl {
($graph_impl: ident) => {
$crate::test_graph_impl!(test, $graph_impl);
};
($module_name: ident, $graph_impl: ident) => {
$crate::test_graph_impl!($module_name, $graph_impl, true);
};
($module_name: ident, $graph_impl: ident, $is_set: expr) => {
$crate::test_graph_impl!($module_name, $graph_impl, $is_set, true);
};
($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr) => {
$crate::test_graph_impl!($module_name, $graph_impl, $is_set, $is_gen, $graph_impl::from_triple_source);
};
($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr, $graph_collector: path) => {
$crate::test_graph_impl!($module_name, $graph_impl, $is_set, $is_gen, $graph_collector, {
#[test]
fn test_simple_mutations() -> Result<(), Box<dyn std::error::Error>> {
let mut g: $graph_impl = $graph_collector(no_triple()).unwrap();
assert_eq!(g.triples().count(), 0);
assert!(MutableGraph::insert(
&mut g,
&*C1,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
assert_eq!(g.triples().count(), 1);
assert!(MutableGraph::insert(&mut g, &*C1, &rdfs::subClassOf, &*C2)? || !$is_set);
assert_eq!(g.triples().count(), 2);
assert!(MutableGraph::remove(
&mut g,
&*C1,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
assert_eq!(g.triples().count(), 1);
assert!(MutableGraph::remove(&mut g, &*C1, &rdfs::subClassOf, &*C2)? || !$is_set);
assert_eq!(g.triples().count(), 0);
Ok(())
}
#[test]
fn test_no_duplicate() -> Result<(), Box<dyn std::error::Error>> {
if $is_set {
let mut g: $graph_impl = $graph_collector(no_triple()).unwrap();
assert_eq!(g.triples().count(), 0);
assert!(MutableGraph::insert(
&mut g,
&*C1,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
assert_eq!(g.triples().count(), 1);
assert!(!MutableGraph::insert(
&mut g,
&*C1,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
assert_eq!(g.triples().count(), 1);
assert!(MutableGraph::remove(
&mut g,
&*C1,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
assert_eq!(g.triples().count(), 0);
assert!(!MutableGraph::remove(
&mut g,
&*C1,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
assert_eq!(g.triples().count(), 0);
} else {
println!("effectively skipped, since is_set is false");
}
Ok(())
}
#[test]
fn test_x_all_mutations() {
let mut g: $graph_impl = $graph_collector(no_triple()).unwrap();
assert_eq!(g.triples().count(), 0);
let inserted = g.insert_all(some_triples()).unwrap();
if $is_set {
assert_eq!(inserted, 18, "returned by insert_all");
}
assert_eq!(g.triples().count(), 18, "after insert_all");
if $is_set {
let inserted = g.insert_all(some_triples()).unwrap();
assert_eq!(inserted, 0, "returned by insert_all again");
assert_eq!(g.triples().count(), 18, "after insert_all again");
}
let removed = g.remove_all(some_triples()).unwrap();
if $is_set {
assert_eq!(removed, 18, "returned by remove_all");
}
assert_eq!(g.triples().count(), 0, "after remove_all");
if $is_set {
let removed = g.remove_all(some_triples()).unwrap();
assert_eq!(removed, 0, "returned by remove_all again");
assert_eq!(g.triples().count(), 0, "after remove_all again");
}
}
#[test]
fn test_remove_matching() -> Result<(), Box<dyn std::error::Error>> {
let mut g: $graph_impl = $graph_collector(some_triples()).unwrap();
let o_matcher = [&*C1, &*C2];
g.remove_matching(&ANY, &rdf::type_, &o_matcher)?;
assert_consistent_hint(14, g.triples().size_hint());
Ok(())
}
#[test]
fn test_retain_matching() -> Result<(), Box<dyn std::error::Error>> {
let mut g: $graph_impl = $graph_collector(some_triples()).unwrap();
let o_matcher = [&*C1, &*C2];
g.retain_matching(&ANY, &rdf::type_, &o_matcher)?;
assert_consistent_hint(4, g.triples().size_hint());
Ok(())
}
});
};
($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr, $graph_collector: path, { $($mt:tt)* }) => {
#[cfg(test)]
mod $module_name {
use $crate::graph::test::*;
use $crate::graph::*;
use $crate::ns::*;
use $crate::term::matcher::ANY;
#[allow(unused_imports)]
use super::*;
#[test]
fn test_triples() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples();
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&ANY, &ANY, &ANY)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), g.triples().count());
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*C1, &rdf::type_, &rdfs::Class)?);
assert!(!Graph::contains(&v, &*P1, &rdf::type_, &rdfs::Class)?);
}
Ok(())
}
#[test]
fn test_triples_with_s() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_s(&*C2);
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&*C2, &ANY, &ANY)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 4);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Class)?);
assert!(!Graph::contains(&v, &*C1, &rdf::type_, &rdfs::Class)?);
}
Ok(())
}
#[test]
fn test_triples_with_p() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_p(&rdf::type_);
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&ANY, &rdf::type_, &ANY)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 9);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Resource)?);
assert!(!Graph::contains(
&v,
&*C2,
&rdfs::subClassOf,
&rdfs::Resource
)?);
}
Ok(())
}
#[test]
fn test_triples_with_o() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_o(&*C2);
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&ANY, &ANY, &*C2)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 5);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*P2, &rdfs::domain, &*C2)?);
assert!(!Graph::contains(&v, &*P1, &rdfs::domain, &*C1)?);
}
Ok(())
}
#[test]
fn test_triples_with_sp() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_sp(&*C2, &rdf::type_);
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&*C2, &rdf::type_, &ANY)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 2);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Class)?);
assert!(!Graph::contains(&v, &*C2, &rdfs::subClassOf, &*C1)?);
}
Ok(())
}
#[test]
fn test_triples_with_so() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_so(&*C2, &rdfs::Resource);
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&*C2, &ANY, &rdfs::Resource)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 2);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(
&v,
&*C2,
&rdfs::subClassOf,
&rdfs::Resource
)?);
assert!(!Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Class)?);
}
Ok(())
}
#[test]
fn test_triples_with_po() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_po(&rdf::type_, &rdfs::Class);
let hint = triples.size_hint();
for iter in vec![triples, g.triples_matching(&ANY, &rdf::type_, &rdfs::Class)] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 2);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Class)?);
assert!(!Graph::contains(&v, &*P2, &rdf::type_, &rdf::Property)?);
}
Ok(())
}
#[test]
fn test_triples_with_spo() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let triples = g.triples_with_spo(&*C2, &rdf::type_, &rdfs::Resource);
let hint = triples.size_hint();
for iter in vec![
triples,
g.triples_matching(&*C2, &rdf::type_, &rdfs::Resource),
] {
let v: Vec<_> = iter.map(as_box_t).collect();
assert_eq!(v.len(), 1);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Resource)?);
assert!(!Graph::contains(
&v,
&*C1,
&rdfs::subClassOf,
&rdfs::Resource
)?);
}
Ok(())
}
#[test]
fn test_contains() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
assert!(Graph::contains(&g, &*C2, &rdfs::subClassOf, &*C1)?);
assert!(!Graph::contains(&g, &*C1, &rdfs::subClassOf, &*C2)?);
Ok(())
}
#[test]
fn test_triples_matching() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let p_matcher = [&rdf::type_, &rdfs::domain];
let o_matcher = [&*C2, &rdfs::Class.into()];
let v: Vec<_> = g
.triples_matching(&ANY, &p_matcher, &o_matcher)
.map(as_box_t)
.collect();
assert_eq!(v.len(), 5);
assert!(Graph::contains(&v, &*C1, &rdf::type_, &rdfs::Class)?);
assert!(Graph::contains(&v, &*P2, &rdfs::domain, &*C2)?);
assert!(Graph::contains(&v, &*I2A, &rdf::type_, &*C2)?);
assert!(!Graph::contains(&v, &*P1, &rdfs::domain, &*C1)?);
assert!(!Graph::contains(&v, &*I1A, &rdf::type_, &*C1)?);
Ok(())
}
#[test]
fn test_subjects() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let subjects = g.subjects().unwrap();
assert_eq!(subjects.len(), 8);
assert_contains(&subjects, &*C1);
assert_contains(&subjects, &*C2);
assert_contains(&subjects, &*P1);
assert_contains(&subjects, &*P2);
assert_contains(&subjects, &*I1A);
assert_contains(&subjects, &*I1B);
assert_contains(&subjects, &*I2A);
assert_contains(&subjects, &*I2B);
Ok(())
}
#[test]
fn test_predicates() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let predicates = g.predicates().unwrap();
assert_eq!(predicates.len(), 6);
assert_contains(&predicates, &rdf::type_);
assert_contains(&predicates, &rdfs::subClassOf);
assert_contains(&predicates, &rdfs::domain);
assert_contains(&predicates, &rdfs::range);
assert_contains(&predicates, &*P1);
assert_contains(&predicates, &*P2);
Ok(())
}
#[test]
fn test_objects() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let objects = g.objects().unwrap();
assert_eq!(objects.len(), 7);
assert_contains(&objects, &rdf::Property);
assert_contains(&objects, &rdfs::Class);
assert_contains(&objects, &rdfs::Resource);
assert_contains(&objects, &*C1);
assert_contains(&objects, &*C2);
assert_contains(&objects, &*I2A);
assert_contains(&objects, &*I2B);
Ok(())
}
#[test]
fn test_iris() -> Result<(), Box<dyn std::error::Error>> {
let g = if $is_gen {
$graph_collector(generalized_node_types_triples()).unwrap()
} else {
$graph_collector(strict_node_types_triples()).unwrap()
};
let iris = g.iris().unwrap();
assert_eq!(iris.len(), 2);
assert_contains(&iris, &rdf::Property);
assert_contains(&iris, &rdf::type_);
Ok(())
}
#[test]
fn test_bnodes() -> Result<(), Box<dyn std::error::Error>> {
let g = if $is_gen {
$graph_collector(generalized_node_types_triples()).unwrap()
} else {
$graph_collector(strict_node_types_triples()).unwrap()
};
let bnodes = g.bnodes().unwrap();
assert_eq!(bnodes.len(), 2);
assert_contains(&bnodes, &*B1);
assert_contains(&bnodes, &*B2);
Ok(())
}
#[test]
fn test_literals() -> Result<(), Box<dyn std::error::Error>> {
let g = if $is_gen {
$graph_collector(generalized_node_types_triples()).unwrap()
} else {
$graph_collector(strict_node_types_triples()).unwrap()
};
let literals = g.literals().unwrap();
assert_eq!(literals.len(), 3);
assert_contains(&literals, &*L1);
assert_contains(&literals, &*L2);
assert_contains(&literals, &*L2E);
Ok(())
}
#[test]
fn test_variables() -> Result<(), Box<dyn std::error::Error>> {
if $is_gen {
let g: $graph_impl = $graph_collector(generalized_node_types_triples()).unwrap();
let variables = g.variables().unwrap();
assert_eq!(variables.len(), 3);
assert_contains(&variables, &*V1);
assert_contains(&variables, &*V2);
assert_contains(&variables, &*V3);
} else {
let g: $graph_impl = $graph_collector(strict_node_types_triples()).unwrap();
let variables = g.variables().unwrap();
assert_eq!(variables.len(), 0);
}
Ok(())
}
$($mt)*
}
};
}
#[macro_export]
macro_rules! test_immutable_graph_impl {
($graph_impl: ident) => {
$crate::test_immutable_graph_impl!(test, $graph_impl);
};
($module_name: ident, $graph_impl: ident) => {
$crate::test_immutable_graph_impl!($module_name, $graph_impl, true);
};
($module_name: ident, $graph_impl: ident, $is_set: expr) => {
$crate::test_immutable_graph_impl!($module_name, $graph_impl, $is_set, true);
};
($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr) => {
$crate::test_immutable_graph_impl!(
$module_name,
$graph_impl,
$is_set,
$is_gen,
$graph_impl::from_triple_source
);
};
($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr, $graph_collector: path) => {
$crate::test_graph_impl!(
$module_name,
$graph_impl,
$is_set,
$is_gen,
$graph_collector,
{}
);
};
}