use lazy_static::lazy_static;
use std::fmt::Debug;
use super::*;
use crate::ns::*;
use crate::source::*;
use crate::term::*;
pub type StaticTerm = SimpleTerm<'static>;
#[allow(missing_docs)]
mod ns {
use super::*;
pub const NS: Namespace<&str> = Namespace::new_unchecked_const("http://example.org/");
lazy_static! {
pub static ref C1: NsTerm<'static> = NS.get("C1").unwrap();
pub static ref C2: NsTerm<'static> = NS.get("C2").unwrap();
pub static ref P1: NsTerm<'static> = NS.get("p1").unwrap();
pub static ref P2: NsTerm<'static> = NS.get("p2").unwrap();
pub static ref I1A: NsTerm<'static> = NS.get("I1A").unwrap();
pub static ref I1B: NsTerm<'static> = NS.get("I1B").unwrap();
pub static ref I2A: NsTerm<'static> = NS.get("I2A").unwrap();
pub static ref I2B: NsTerm<'static> = NS.get("I2B").unwrap();
}
pub const B1: BnodeId<&str> = BnodeId::new_unchecked_const("1");
pub const B2: BnodeId<&str> = BnodeId::new_unchecked_const("2");
pub const B3: BnodeId<&str> = BnodeId::new_unchecked_const("3");
pub const EN: LanguageTag<&str> = LanguageTag::new_unchecked_const("en");
pub const V1: VarName<&str> = VarName::new_unchecked_const("v1");
pub const V2: VarName<&str> = VarName::new_unchecked_const("v2");
pub const V3: VarName<&str> = VarName::new_unchecked_const("v3");
}
pub use ns::*;
pub fn no_triple() -> impl TripleSource {
let v = Vec::<[StaticTerm; 3]>::new();
v.into_iter().into_source()
}
pub const SOME_TRIPLES_COUNT: usize = 18;
pub fn some_triples() -> impl TripleSource {
let v = vec![
[*C1, rdf::type_, rdfs::Class],
[*C2, rdf::type_, rdfs::Class],
[*C2, rdf::type_, rdfs::Resource],
[*C2, rdfs::subClassOf, *C1],
[*C2, rdfs::subClassOf, rdfs::Resource],
[*P1, rdf::type_, rdf::Property],
[*P1, rdfs::domain, *C1],
[*P1, rdfs::range, *C2],
[*P2, rdf::type_, rdf::Property],
[*P2, rdfs::domain, *C2],
[*P2, rdfs::range, *C2],
[*I1A, rdf::type_, *C1],
[*I1B, rdf::type_, *C1],
[*I2A, rdf::type_, *C2],
[*I2B, rdf::type_, *C2],
[*I1A, *P1, *I2A],
[*I1B, *P1, *I2B],
[*I2A, *P2, *I2B],
];
assert_eq!(v.len(), SOME_TRIPLES_COUNT);
v.into_iter().into_source()
}
pub(crate) fn t<T: Term>(t: T) -> StaticTerm {
t.into_term()
}
pub(crate) fn tt<T1: Term, T2: Term, T3: Term>(s: T1, p: T2, o: T3) -> StaticTerm {
StaticTerm::from_triple([t(s), t(p), t(o)])
}
pub const NODE_TYPES_COUNT: usize = 5;
pub fn strict_node_types_triples() -> impl TripleSource {
let v = vec![
[t(rdf::type_), t(rdf::type_), t(rdf::Property)],
[t(B1), t(rdf::value), t("lit1")],
[t(B2), t(rdf::value), t(B1)],
[t(B2), t(rdf::value), t("lit2")],
[t(B2), t(rdf::value), t("lit2" * EN)],
];
assert_eq!(v.len(), NODE_TYPES_COUNT);
v.into_iter().into_source()
}
pub fn generalized_node_types_triples() -> impl TripleSource {
let v = vec![
[t(rdf::type_), t(rdf::type_), t(rdf::Property)],
[t(B1), t(B2), t(B1)],
[t("lit2"), t("lit1"), t("lit1")],
[t(V1), t(V1), t(V2)],
[
tt(V1, B1, "lit1"),
tt(B2, "lit2", V2),
tt("lit2" * EN, B1, tt(V2, rdf::value, rdf::type_)),
],
];
assert_eq!(v.len(), NODE_TYPES_COUNT);
v.into_iter().into_source()
}
pub fn dump_graph<G: Graph>(g: &G)
where
for<'x> GTerm<'x, G>: 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, term: &U)
where
I: IntoIterator<Item = &'a T>,
T: Term + 'a,
U: Term,
{
assert!(collection.into_iter().any(|t| term.eq(t.borrow_term())))
}
#[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 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 handle_duplicate() -> 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,
&rdf::type_,
&rdfs::Class
)? || !$is_set);
if $is_set {
assert_eq!(g.triples().count(), 1);
} else {
assert!(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);
Ok(())
}
#[test]
fn 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 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 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::{SimpleTerm, Term, TermKind};
use $crate::term::matcher::Any;
use std::collections::HashSet;
#[allow(unused_imports)] use super::*;
#[test]
fn triples() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
for iter in [Box::new(g.triples()) as Box<dyn Iterator<Item=_>>, Box::new(g.triples_matching(Any, Any, Any))] {
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).collect();
assert_eq!(v.len(), SOME_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 triples_with_s() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching([*C2], Any, Any);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_with_p() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching(Any, [rdf::type_], Any);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_with_o() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching(Any, Any, [*C2]);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_with_sp() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching([*C2], [rdf::type_], Any);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_with_so() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching([*C2], Any, [rdfs::Resource]);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_with_po() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching(Any, [rdf::type_], [rdfs::Class]);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_with_spo() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching([*C2], [rdf::type_], [rdfs::Resource]);
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).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 triples_matching() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let iter = g.triples_matching([*C2, *P2], [rdf::type_, rdfs::domain], |t: SimpleTerm<'_>| !Term::eq(&t, rdfs::Class));
let hint = iter.size_hint();
let v: Vec<_> = iter.map(Result::unwrap).collect();
assert_eq!(v.len(), 3);
assert_consistent_hint(v.len(), hint);
assert!(Graph::contains(&v, *C2, rdf::type_, rdfs::Resource)?);
assert!(!Graph::contains(
&v,
*C2,
rdf::type_,
rdfs::Class,
)?);
Ok(())
}
#[test]
fn 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 subjects() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let subjects: HashSet<StaticTerm> = g.subjects().map(|t| t.unwrap().into_term()).collect();
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);
let g = if $is_gen {
$graph_collector(generalized_node_types_triples()).unwrap()
} else {
$graph_collector(strict_node_types_triples()).unwrap()
};
let kinds: HashSet<_> = g.subjects().map(|t| t.unwrap().kind()).collect();
assert_eq!(kinds.len(), if $is_gen {5} else {2});
assert!(kinds.contains(&TermKind::Iri));
assert!(kinds.contains(&TermKind::BlankNode));
Ok(())
}
#[test]
fn predicates() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let predicates: HashSet<StaticTerm> = g.predicates().map(|t| t.unwrap().into_term()).collect();
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);
let g = if $is_gen {
$graph_collector(generalized_node_types_triples()).unwrap()
} else {
$graph_collector(strict_node_types_triples()).unwrap()
};
let kinds: HashSet<_> = g.predicates().map(|t| t.unwrap().kind()).collect();
assert_eq!(kinds.len(), if $is_gen {5} else {1});
assert!(kinds.contains(&TermKind::Iri));
Ok(())
}
#[test]
fn objects() -> Result<(), Box<dyn std::error::Error>> {
let g: $graph_impl = $graph_collector(some_triples()).unwrap();
let objects: HashSet<StaticTerm> = g.objects().map(|t| t.unwrap().into_term()).collect();
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);
let g = if $is_gen {
$graph_collector(generalized_node_types_triples()).unwrap()
} else {
$graph_collector(strict_node_types_triples()).unwrap()
};
let kinds: HashSet<_> = g.objects().map(|t| t.unwrap().kind()).collect();
assert_eq!(kinds.len(), if $is_gen {5} else {3});
assert!(kinds.contains(&TermKind::Iri));
assert!(kinds.contains(&TermKind::BlankNode));
assert!(kinds.contains(&TermKind::Literal));
Ok(())
}
#[test]
fn 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: HashSet<StaticTerm> = g.iris().map(|t| t.unwrap().into_term()).collect();
dbg!(&iris);
assert_eq!(iris.len(), 3);
assert_contains(&iris, &rdf::Property);
assert_contains(&iris, &rdf::type_);
assert_contains(&iris, &rdf::value);
Ok(())
}
#[test]
fn 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: HashSet<StaticTerm> = g.blank_nodes().map(|t| t.unwrap().into_term()).collect();
assert_eq!(bnodes.len(), 2);
assert_contains(&bnodes, &B1);
assert_contains(&bnodes, &B2);
Ok(())
}
#[test]
fn 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()
};
dbg!(&g);
let literals: HashSet<StaticTerm> = g.literals().map(|t| t.unwrap().into_term()).collect();
assert_eq!(literals.len(), 3);
assert_contains(&literals, &"lit1");
assert_contains(&literals, &"lit2");
assert_contains(&literals, &("lit2"*EN));
Ok(())
}
#[test]
fn quoted_triples() -> Result<(), Box<dyn std::error::Error>> {
if $is_gen {
let g: $graph_impl = $graph_collector(generalized_node_types_triples()).unwrap();
let triples: HashSet<StaticTerm> = g.quoted_triples().map(|t| t.unwrap().into_term()).collect();
assert_eq!(triples.len(), 4);
let t1 = StaticTerm::from_triple([
V1.as_simple(),
B1.as_simple(),
"lit1".as_simple(),
]);
let t2 = StaticTerm::from_triple([
B2.as_simple(),
"lit2".as_simple(),
V2.as_simple(),
]);
let t3 = StaticTerm::from_triple([
V2.as_simple(),
rdf::value.as_simple(),
rdf::type_.as_simple(),
]);
let t4 = StaticTerm::from_triple([
"lit2"*EN,
B1.as_simple(),
t3.clone(),
]);
assert_contains(&triples, &t1);
assert_contains(&triples, &t2);
assert_contains(&triples, &t3);
assert_contains(&triples, &t4);
} else {
let g: $graph_impl = $graph_collector(strict_node_types_triples()).unwrap();
let triples: HashSet<StaticTerm> = g.quoted_triples().map(|t| t.unwrap().into_term()).collect();
assert_eq!(triples.len(), 0);
}
Ok(())
}
#[test]
fn variables() -> Result<(), Box<dyn std::error::Error>> {
if $is_gen {
let g: $graph_impl = $graph_collector(generalized_node_types_triples()).unwrap();
let variables: HashSet<StaticTerm> = g.variables().map(|t| t.unwrap().into_term()).collect();
assert_eq!(variables.len(), 2);
assert_contains(&variables, &V1);
assert_contains(&variables, &V2);
} else {
let g: $graph_impl = $graph_collector(strict_node_types_triples()).unwrap();
let variables: HashSet<StaticTerm> = g.variables().map(|t| t.unwrap().into_term()).collect();
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,
{}
);
};
}