sophia_api/graph/
test.rs

1//! Contains helper functions and macros for testing Graph implementations
2use lazy_static::lazy_static;
3use std::fmt::Debug;
4
5use super::*;
6use crate::ns::*;
7use crate::source::*;
8use crate::term::*;
9
10/// Shortcut type alias for self-sustained [`SimpleTerm`]
11pub type StaticTerm = SimpleTerm<'static>;
12
13#[allow(missing_docs)]
14mod ns {
15    use super::*;
16    pub const NS: Namespace<&str> = Namespace::new_unchecked_const("http://example.org/");
17    lazy_static! {
18        pub static ref C1: NsTerm<'static> = NS.get("C1").unwrap();
19        pub static ref C2: NsTerm<'static> = NS.get("C2").unwrap();
20        pub static ref P1: NsTerm<'static> = NS.get("p1").unwrap();
21        pub static ref P2: NsTerm<'static> = NS.get("p2").unwrap();
22        pub static ref I1A: NsTerm<'static> = NS.get("I1A").unwrap();
23        pub static ref I1B: NsTerm<'static> = NS.get("I1B").unwrap();
24        pub static ref I2A: NsTerm<'static> = NS.get("I2A").unwrap();
25        pub static ref I2B: NsTerm<'static> = NS.get("I2B").unwrap();
26    }
27    pub const B1: BnodeId<&str> = BnodeId::new_unchecked_const("1");
28    pub const B2: BnodeId<&str> = BnodeId::new_unchecked_const("2");
29    pub const B3: BnodeId<&str> = BnodeId::new_unchecked_const("3");
30    pub const EN: LanguageTag<&str> = LanguageTag::new_unchecked_const("en");
31    pub const V1: VarName<&str> = VarName::new_unchecked_const("v1");
32    pub const V2: VarName<&str> = VarName::new_unchecked_const("v2");
33    pub const V3: VarName<&str> = VarName::new_unchecked_const("v3");
34}
35pub use ns::*;
36
37/// Generates an empty triple source.
38pub fn no_triple() -> impl TripleSource {
39    let v = Vec::<[StaticTerm; 3]>::new();
40    v.into_iter().into_source()
41}
42
43/// The number of triples generated by [`some_triples`].
44pub const SOME_TRIPLES_COUNT: usize = 18;
45
46/// Generates a triple source containing a dummy ontology.
47pub fn some_triples() -> impl TripleSource {
48    let v = vec![
49        [*C1, rdf::type_, rdfs::Class],
50        [*C2, rdf::type_, rdfs::Class],
51        [*C2, rdf::type_, rdfs::Resource],
52        [*C2, rdfs::subClassOf, *C1],
53        [*C2, rdfs::subClassOf, rdfs::Resource],
54        //
55        [*P1, rdf::type_, rdf::Property],
56        [*P1, rdfs::domain, *C1],
57        [*P1, rdfs::range, *C2],
58        //
59        [*P2, rdf::type_, rdf::Property],
60        [*P2, rdfs::domain, *C2],
61        [*P2, rdfs::range, *C2],
62        //
63        [*I1A, rdf::type_, *C1],
64        [*I1B, rdf::type_, *C1],
65        [*I2A, rdf::type_, *C2],
66        [*I2B, rdf::type_, *C2],
67        [*I1A, *P1, *I2A],
68        [*I1B, *P1, *I2B],
69        [*I2A, *P2, *I2B],
70    ];
71    assert_eq!(v.len(), SOME_TRIPLES_COUNT);
72    v.into_iter().into_source()
73}
74
75/// Concert any term to a |`StaticTerm`].
76pub(crate) fn t<T: Term>(t: T) -> StaticTerm {
77    t.into_term()
78}
79
80/// Concert any triple to a |`StaticTerm`] quoted triple.
81pub(crate) fn tt<T1: Term, T2: Term, T3: Term>(s: T1, p: T2, o: T3) -> StaticTerm {
82    StaticTerm::from_triple([t(s), t(p), t(o)])
83}
84
85/// The number of triples generated by [`strict_node_types_triples`]
86/// and [`generalized_node_types_triples`].
87pub const NODE_TYPES_COUNT: usize = 5;
88
89/// Generates a triple source containing all types of nodes,
90/// in all possible positions allowed in RDF 1.1.
91pub fn strict_node_types_triples() -> impl TripleSource {
92    let v = vec![
93        [t(rdf::type_), t(rdf::type_), t(rdf::Property)],
94        [t(B1), t(rdf::value), t("lit1")],
95        [t(B2), t(rdf::value), t(B1)],
96        [t(B2), t(rdf::value), t("lit2")],
97        [t(B2), t(rdf::value), t("lit2" * EN)],
98    ];
99    assert_eq!(v.len(), NODE_TYPES_COUNT);
100    v.into_iter().into_source()
101}
102
103/// Generates a triple source containing all types of nodes,
104/// in all possible positions (generalized RDF).
105pub fn generalized_node_types_triples() -> impl TripleSource {
106    let v = vec![
107        [t(rdf::type_), t(rdf::type_), t(rdf::Property)],
108        [t(B1), t(B2), t(B1)],
109        [t("lit2"), t("lit1"), t("lit1")],
110        [t(V1), t(V1), t(V2)],
111        [
112            tt(V1, B1, "lit1"),
113            tt(B2, "lit2", V2),
114            tt("lit2" * EN, B1, tt(V2, rdf::value, rdf::type_)),
115        ],
116    ];
117    assert_eq!(v.len(), NODE_TYPES_COUNT);
118    v.into_iter().into_source()
119}
120
121/// Prints g on stdout
122pub fn dump_graph<G: Graph>(g: &G)
123where
124    for<'x> GTerm<'x, G>: Debug,
125{
126    println!("<<<<");
127    for t in g.triples() {
128        let t = t.unwrap();
129        println!("{:?}\n{:?}\n{:?}\n\n", t.s(), t.p(), t.o());
130    }
131    println!(">>>>");
132}
133
134/// Checks that `val` is in the interval represented by `hint`.
135pub fn assert_consistent_hint(val: usize, hint: (usize, Option<usize>)) {
136    assert!(hint.0 <= val, "hint {:?} not consistent with {}", hint, val);
137    assert!(
138        val <= hint.1.unwrap_or(val),
139        "hint {:?} not consistent with {}",
140        hint,
141        val
142    )
143}
144
145/// Checks that `term` is presnet in `collection`
146/// (according to [`Term::eq`]).
147pub fn assert_contains<'a, I, T, U>(collection: I, term: &U)
148where
149    I: IntoIterator<Item = &'a T>,
150    T: Term + 'a,
151    U: Term,
152{
153    assert!(collection.into_iter().any(|t| term.eq(t.borrow_term())))
154}
155
156/// Generate a test suite for an implementation of
157/// [`Graph`], [`CollectibleGraph`] and [`MutableGraph`].
158///
159/// If your type only implements [`Graph`] and [`CollectibleGraph`],
160/// you should use [`test_immutable_graph_impl`](crate::test_immutable_graph_impl) instead.
161///
162/// This macro is only available when the feature `test_macro` is enabled.
163///
164/// It accepts the following parameters:
165/// * `module_name`: the name of the module to generate (defaults to `test`);
166/// * `graph_impl`: the type to test, implementing [`Graph`], [`CollectibleGraph`] and [`MutableGraph`];
167/// * `is_set`: a boolean, indicating if `graph_impl` implements [`SetGraph`]
168///   (defaults to `true`);
169/// * `is_gen`: a boolean, indicating if `graph_impl` supports the [generalized model]
170///   (defaults to `true`);
171/// * `graph_collector`: a function used to collect triples into an instance of `graph_impl`
172///   (defaults to `graph_impl::from_triple_source`);
173/// * `mt` is used internally, do not touch it...
174///
175/// [generalized model]: crate#generalized-vs-strict-rdf-model
176#[macro_export]
177macro_rules! test_graph_impl {
178    ($graph_impl: ident) => {
179        $crate::test_graph_impl!(test, $graph_impl);
180    };
181    ($module_name: ident, $graph_impl: ident) => {
182        $crate::test_graph_impl!($module_name, $graph_impl, true);
183    };
184    ($module_name: ident, $graph_impl: ident, $is_set: expr) => {
185        $crate::test_graph_impl!($module_name, $graph_impl, $is_set, true);
186    };
187    ($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr) => {
188        $crate::test_graph_impl!($module_name, $graph_impl, $is_set, $is_gen, $graph_impl::from_triple_source);
189    };
190    ($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr, $graph_collector: path) => {
191        $crate::test_graph_impl!($module_name, $graph_impl, $is_set, $is_gen, $graph_collector, {
192            // these tests will only be performed for implementations of `MutableGraph`
193            #[test]
194            fn simple_mutations() -> Result<(), Box<dyn std::error::Error>> {
195                let mut g: $graph_impl = $graph_collector(no_triple()).unwrap();
196                assert_eq!(g.triples().count(), 0);
197                assert!(MutableGraph::insert(
198                    &mut g,
199                    &*C1,
200                    &rdf::type_,
201                    &rdfs::Class
202                )? || !$is_set);
203                assert_eq!(g.triples().count(), 1);
204                assert!(MutableGraph::insert(&mut g, &*C1, &rdfs::subClassOf, &*C2)? || !$is_set);
205                assert_eq!(g.triples().count(), 2);
206                assert!(MutableGraph::remove(
207                    &mut g,
208                    &*C1,
209                    &rdf::type_,
210                    &rdfs::Class
211                )? || !$is_set);
212                assert_eq!(g.triples().count(), 1);
213                assert!(MutableGraph::remove(&mut g, &*C1, &rdfs::subClassOf, &*C2)? || !$is_set);
214                assert_eq!(g.triples().count(), 0);
215                Ok(())
216            }
217
218            #[test]
219            fn handle_duplicate() -> Result<(), Box<dyn std::error::Error>> {
220                let mut g: $graph_impl = $graph_collector(no_triple()).unwrap();
221                assert_eq!(g.triples().count(), 0);
222                assert!(MutableGraph::insert(
223                    &mut g,
224                    &*C1,
225                    &rdf::type_,
226                    &rdfs::Class
227                )? || !$is_set);
228                assert_eq!(g.triples().count(), 1);
229                assert!(!MutableGraph::insert(
230                    &mut g,
231                    &*C1,
232                    &rdf::type_,
233                    &rdfs::Class
234                )? || !$is_set);
235                if $is_set {
236                    assert_eq!(g.triples().count(), 1);
237                } else {
238                    assert!(g.triples().count() >= 1);
239                }
240                assert!(MutableGraph::remove(
241                    &mut g,
242                    &*C1,
243                    &rdf::type_,
244                    &rdfs::Class
245                )? || !$is_set);
246                assert_eq!(g.triples().count(), 0);
247                assert!(!MutableGraph::remove(
248                    &mut g,
249                    &*C1,
250                    &rdf::type_,
251                    &rdfs::Class
252                )? || !$is_set);
253                assert_eq!(g.triples().count(), 0);
254                Ok(())
255            }
256
257            #[test]
258            fn x_all_mutations() {
259                let mut g: $graph_impl = $graph_collector(no_triple()).unwrap();
260                assert_eq!(g.triples().count(), 0);
261                let inserted = g.insert_all(some_triples()).unwrap();
262                if $is_set {
263                    assert_eq!(inserted, 18, "returned by insert_all");
264                }
265                assert_eq!(g.triples().count(), 18, "after insert_all");
266                if $is_set {
267                    let inserted = g.insert_all(some_triples()).unwrap();
268                    assert_eq!(inserted, 0, "returned by insert_all again");
269                    assert_eq!(g.triples().count(), 18, "after insert_all again");
270                }
271                let removed = g.remove_all(some_triples()).unwrap();
272                if $is_set {
273                    assert_eq!(removed, 18, "returned by remove_all");
274                }
275                assert_eq!(g.triples().count(), 0, "after remove_all");
276                if $is_set {
277                    let removed = g.remove_all(some_triples()).unwrap();
278                    assert_eq!(removed, 0, "returned by remove_all again");
279                    assert_eq!(g.triples().count(), 0, "after remove_all again");
280                }
281            }
282
283            #[test]
284            fn remove_matching() -> Result<(), Box<dyn std::error::Error>> {
285                let mut g: $graph_impl = $graph_collector(some_triples()).unwrap();
286
287                let o_matcher: &[_] = &[*C1, *C2];
288                g.remove_matching(Any, [rdf::type_], o_matcher)?;
289                assert_consistent_hint(14, g.triples().size_hint());
290                Ok(())
291            }
292
293            #[test]
294            fn retain_matching() -> Result<(), Box<dyn std::error::Error>> {
295                let mut g: $graph_impl = $graph_collector(some_triples()).unwrap();
296
297                let o_matcher: &[_] = &[*C1, *C2];
298                g.retain_matching(Any, [rdf::type_], o_matcher)?;
299                assert_consistent_hint(4, g.triples().size_hint());
300                Ok(())
301            }
302        });
303    };
304    ($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr, $graph_collector: path, { $($mt:tt)* }) => {
305        #[cfg(test)]
306        mod $module_name {
307            use $crate::graph::test::*;
308            use $crate::graph::*;
309            use $crate::ns::*;
310            use $crate::term::{SimpleTerm, Term, TermKind};
311            use $crate::term::matcher::Any;
312            use std::collections::HashSet;
313
314            #[allow(unused_imports)] // makes the macro easier to use
315            use super::*;
316
317            #[test]
318            fn triples() -> Result<(), Box<dyn std::error::Error>> {
319                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
320
321                for iter in [Box::new(g.triples()) as Box<dyn Iterator<Item=_>>, Box::new(g.triples_matching(Any, Any, Any))] {
322                    let hint = iter.size_hint();
323                    let v: Vec<_> = iter.map(Result::unwrap).collect();
324                    assert_eq!(v.len(), SOME_TRIPLES_COUNT);
325                    assert_consistent_hint(v.len(), hint);
326                    assert!(Graph::contains(&v, *C1, rdf::type_, rdfs::Class)?);
327                    assert!(!Graph::contains(&v, *P1, rdf::type_, rdfs::Class)?);
328                }
329                Ok(())
330            }
331
332            #[test]
333            fn triples_with_s() -> Result<(), Box<dyn std::error::Error>> {
334                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
335
336                let iter = g.triples_matching([*C2], Any, Any);
337                let hint = iter.size_hint();
338                let v: Vec<_> = iter.map(Result::unwrap).collect();
339                assert_eq!(v.len(), 4);
340                assert_consistent_hint(v.len(), hint);
341                assert!(Graph::contains(&v, &*C2, &rdf::type_, &rdfs::Class)?);
342                assert!(!Graph::contains(&v, &*C1, &rdf::type_, &rdfs::Class)?);
343                Ok(())
344            }
345
346            #[test]
347            fn triples_with_p() -> Result<(), Box<dyn std::error::Error>> {
348                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
349
350                let iter = g.triples_matching(Any, [rdf::type_], Any);
351                let hint = iter.size_hint();
352                let v: Vec<_> = iter.map(Result::unwrap).collect();
353                assert_eq!(v.len(), 9);
354                assert_consistent_hint(v.len(), hint);
355                assert!(Graph::contains(&v, *C2, rdf::type_, rdfs::Resource)?);
356                assert!(!Graph::contains(
357                    &v,
358                    *C2,
359                    rdfs::subClassOf,
360                    rdfs::Resource
361                )?);
362                Ok(())
363            }
364
365            #[test]
366            fn triples_with_o() -> Result<(), Box<dyn std::error::Error>> {
367                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
368
369                let iter = g.triples_matching(Any, Any, [*C2]);
370                let hint = iter.size_hint();
371                let v: Vec<_> = iter.map(Result::unwrap).collect();
372                assert_eq!(v.len(), 5);
373                assert_consistent_hint(v.len(), hint);
374                assert!(Graph::contains(&v, *P2, rdfs::domain, *C2)?);
375                assert!(!Graph::contains(&v, *P1, rdfs::domain, *C1)?);
376                Ok(())
377            }
378
379            #[test]
380            fn triples_with_sp() -> Result<(), Box<dyn std::error::Error>> {
381                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
382
383                let iter = g.triples_matching([*C2], [rdf::type_], Any);
384                let hint = iter.size_hint();
385                let v: Vec<_> = iter.map(Result::unwrap).collect();
386                assert_eq!(v.len(), 2);
387                assert_consistent_hint(v.len(), hint);
388                assert!(Graph::contains(&v, *C2, &rdf::type_, rdfs::Class)?);
389                assert!(!Graph::contains(&v, *C2, &rdfs::subClassOf, *C1)?);
390                Ok(())
391            }
392
393            #[test]
394            fn triples_with_so() -> Result<(), Box<dyn std::error::Error>> {
395                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
396
397                let iter = g.triples_matching([*C2], Any, [rdfs::Resource]);
398                let hint = iter.size_hint();
399                let v: Vec<_> = iter.map(Result::unwrap).collect();
400                assert_eq!(v.len(), 2);
401                assert_consistent_hint(v.len(), hint);
402                assert!(Graph::contains(
403                    &v,
404                    *C2,
405                    rdfs::subClassOf,
406                    rdfs::Resource
407                )?);
408                assert!(!Graph::contains(&v, *C2, rdf::type_, rdfs::Class)?);
409                Ok(())
410            }
411
412            #[test]
413            fn triples_with_po() -> Result<(), Box<dyn std::error::Error>> {
414                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
415
416                let iter = g.triples_matching(Any, [rdf::type_], [rdfs::Class]);
417                let hint = iter.size_hint();
418                let v: Vec<_> = iter.map(Result::unwrap).collect();
419                assert_eq!(v.len(), 2);
420                assert_consistent_hint(v.len(), hint);
421                assert!(Graph::contains(&v, *C2, rdf::type_, rdfs::Class)?);
422                assert!(!Graph::contains(&v, *P2, rdf::type_, rdf::Property)?);
423                Ok(())
424            }
425
426            #[test]
427            fn triples_with_spo() -> Result<(), Box<dyn std::error::Error>> {
428                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
429
430                let iter = g.triples_matching([*C2], [rdf::type_], [rdfs::Resource]);
431                let hint = iter.size_hint();
432                let v: Vec<_> = iter.map(Result::unwrap).collect();
433                assert_eq!(v.len(), 1);
434                assert_consistent_hint(v.len(), hint);
435                assert!(Graph::contains(&v, *C2, rdf::type_, rdfs::Resource)?);
436                assert!(!Graph::contains(
437                    &v,
438                    *C1,
439                    rdfs::subClassOf,
440                    rdfs::Resource
441                )?);
442                Ok(())
443            }
444
445            #[test]
446            fn triples_matching() -> Result<(), Box<dyn std::error::Error>> {
447                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
448
449                let iter = g.triples_matching([*C2, *P2], [rdf::type_, rdfs::domain], |t: SimpleTerm<'_>| !Term::eq(&t, rdfs::Class));
450                let hint = iter.size_hint();
451                let v: Vec<_> = iter.map(Result::unwrap).collect();
452                assert_eq!(v.len(), 3);
453                assert_consistent_hint(v.len(), hint);
454                assert!(Graph::contains(&v, *C2, rdf::type_, rdfs::Resource)?);
455                assert!(!Graph::contains(
456                    &v,
457                    *C2,
458                    rdf::type_,
459                    rdfs::Class,
460                )?);
461                Ok(())
462            }
463
464            #[test]
465            fn contains() -> Result<(), Box<dyn std::error::Error>> {
466                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
467
468                assert!(Graph::contains(&g, &*C2, &rdfs::subClassOf, &*C1)?);
469                assert!(!Graph::contains(&g, &*C1, &rdfs::subClassOf, &*C2)?);
470                Ok(())
471            }
472
473            #[test]
474            fn subjects() -> Result<(), Box<dyn std::error::Error>> {
475                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
476
477                let subjects: HashSet<StaticTerm> = g.subjects().map(|t| t.unwrap().into_term()).collect();
478                assert_eq!(subjects.len(), 8);
479                assert_contains(&subjects, &*C1);
480                assert_contains(&subjects, &*C2);
481                assert_contains(&subjects, &*P1);
482                assert_contains(&subjects, &*P2);
483                assert_contains(&subjects, &*I1A);
484                assert_contains(&subjects, &*I1B);
485                assert_contains(&subjects, &*I2A);
486                assert_contains(&subjects, &*I2B);
487
488                let g = if $is_gen {
489                    $graph_collector(generalized_node_types_triples()).unwrap()
490                } else {
491                    $graph_collector(strict_node_types_triples()).unwrap()
492                };
493                let kinds: HashSet<_> = g.subjects().map(|t| t.unwrap().kind()).collect();
494                assert_eq!(kinds.len(), if $is_gen {5} else {2});
495                assert!(kinds.contains(&TermKind::Iri));
496                assert!(kinds.contains(&TermKind::BlankNode));
497                Ok(())
498            }
499
500            #[test]
501            fn predicates() -> Result<(), Box<dyn std::error::Error>> {
502                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
503
504                let predicates: HashSet<StaticTerm> = g.predicates().map(|t| t.unwrap().into_term()).collect();
505                assert_eq!(predicates.len(), 6);
506                assert_contains(&predicates, &rdf::type_);
507                assert_contains(&predicates, &rdfs::subClassOf);
508                assert_contains(&predicates, &rdfs::domain);
509                assert_contains(&predicates, &rdfs::range);
510                assert_contains(&predicates, &*P1);
511                assert_contains(&predicates, &*P2);
512
513                let g = if $is_gen {
514                    $graph_collector(generalized_node_types_triples()).unwrap()
515                } else {
516                    $graph_collector(strict_node_types_triples()).unwrap()
517                };
518                let kinds: HashSet<_> = g.predicates().map(|t| t.unwrap().kind()).collect();
519                assert_eq!(kinds.len(), if $is_gen {5} else {1});
520                assert!(kinds.contains(&TermKind::Iri));
521                Ok(())
522            }
523
524            #[test]
525            fn objects() -> Result<(), Box<dyn std::error::Error>> {
526                let g: $graph_impl = $graph_collector(some_triples()).unwrap();
527
528                let objects: HashSet<StaticTerm> = g.objects().map(|t| t.unwrap().into_term()).collect();
529                assert_eq!(objects.len(), 7);
530                assert_contains(&objects, &rdf::Property);
531                assert_contains(&objects, &rdfs::Class);
532                assert_contains(&objects, &rdfs::Resource);
533                assert_contains(&objects, &*C1);
534                assert_contains(&objects, &*C2);
535                assert_contains(&objects, &*I2A);
536                assert_contains(&objects, &*I2B);
537
538                let g = if $is_gen {
539                    $graph_collector(generalized_node_types_triples()).unwrap()
540                } else {
541                    $graph_collector(strict_node_types_triples()).unwrap()
542                };
543                let kinds: HashSet<_> = g.objects().map(|t| t.unwrap().kind()).collect();
544                assert_eq!(kinds.len(), if $is_gen {5} else {3});
545                assert!(kinds.contains(&TermKind::Iri));
546                assert!(kinds.contains(&TermKind::BlankNode));
547                assert!(kinds.contains(&TermKind::Literal));
548                Ok(())
549            }
550
551            #[test]
552            fn iris() -> Result<(), Box<dyn std::error::Error>> {
553                let g = if $is_gen {
554                    $graph_collector(generalized_node_types_triples()).unwrap()
555                } else {
556                    $graph_collector(strict_node_types_triples()).unwrap()
557                };
558
559                let iris: HashSet<StaticTerm> = g.iris().map(|t| t.unwrap().into_term()).collect();
560                dbg!(&iris);
561                assert_eq!(iris.len(), 3);
562                assert_contains(&iris, &rdf::Property);
563                assert_contains(&iris, &rdf::type_);
564                assert_contains(&iris, &rdf::value);
565                Ok(())
566            }
567
568            #[test]
569            fn bnodes() -> Result<(), Box<dyn std::error::Error>> {
570                let g = if $is_gen {
571                    $graph_collector(generalized_node_types_triples()).unwrap()
572                } else {
573                    $graph_collector(strict_node_types_triples()).unwrap()
574                };
575
576                let bnodes: HashSet<StaticTerm> = g.blank_nodes().map(|t| t.unwrap().into_term()).collect();
577                assert_eq!(bnodes.len(), 2);
578                assert_contains(&bnodes, &B1);
579                assert_contains(&bnodes, &B2);
580                Ok(())
581            }
582
583            #[test]
584            fn literals() -> Result<(), Box<dyn std::error::Error>> {
585                let g = if $is_gen {
586                    $graph_collector(generalized_node_types_triples()).unwrap()
587                } else {
588                    $graph_collector(strict_node_types_triples()).unwrap()
589                };
590                dbg!(&g);
591
592                let literals: HashSet<StaticTerm> = g.literals().map(|t| t.unwrap().into_term()).collect();
593                assert_eq!(literals.len(), 3);
594                assert_contains(&literals, &"lit1");
595                assert_contains(&literals, &"lit2");
596                assert_contains(&literals, &("lit2"*EN));
597                Ok(())
598            }
599
600            #[test]
601            fn quoted_triples() -> Result<(), Box<dyn std::error::Error>> {
602                if $is_gen {
603                    let g: $graph_impl = $graph_collector(generalized_node_types_triples()).unwrap();
604
605                    let triples: HashSet<StaticTerm> = g.quoted_triples().map(|t| t.unwrap().into_term()).collect();
606                    assert_eq!(triples.len(), 4);
607                    let t1 = StaticTerm::from_triple([
608                        V1.as_simple(),
609                        B1.as_simple(),
610                        "lit1".as_simple(),
611                    ]);
612                    let t2 = StaticTerm::from_triple([
613                        B2.as_simple(),
614                        "lit2".as_simple(),
615                        V2.as_simple(),
616                    ]);
617                    let t3 = StaticTerm::from_triple([
618                        V2.as_simple(),
619                        rdf::value.as_simple(),
620                        rdf::type_.as_simple(),
621                    ]);
622                    let t4 = StaticTerm::from_triple([
623                        "lit2"*EN,
624                        B1.as_simple(),
625                        t3.clone(),
626                    ]);
627                    assert_contains(&triples, &t1);
628                    assert_contains(&triples, &t2);
629                    assert_contains(&triples, &t3);
630                    assert_contains(&triples, &t4);
631                } else {
632                    let g: $graph_impl = $graph_collector(strict_node_types_triples()).unwrap();
633
634                    let triples: HashSet<StaticTerm> = g.quoted_triples().map(|t| t.unwrap().into_term()).collect();
635                    assert_eq!(triples.len(), 0);
636                }
637                Ok(())
638            }
639
640            #[test]
641            fn variables() -> Result<(), Box<dyn std::error::Error>> {
642                if $is_gen {
643                    let g: $graph_impl = $graph_collector(generalized_node_types_triples()).unwrap();
644
645                    let variables: HashSet<StaticTerm> = g.variables().map(|t| t.unwrap().into_term()).collect();
646                    assert_eq!(variables.len(), 2);
647                    assert_contains(&variables, &V1);
648                    assert_contains(&variables, &V2);
649                } else {
650                    let g: $graph_impl = $graph_collector(strict_node_types_triples()).unwrap();
651
652                    let variables: HashSet<StaticTerm> = g.variables().map(|t| t.unwrap().into_term()).collect();
653                    assert_eq!(variables.len(), 0);
654                }
655                Ok(())
656            }
657
658            // Tests for MutableGraph only, if enabled:
659            $($mt)*
660        }
661    };
662}
663
664/// Generate a test suite for an implementation of
665/// [`Graph`] and [`CollectibleGraph`].
666///
667/// If your type also implements [`MutableGraph`],
668/// you should use [`test_graph_impl`] instead.
669///
670/// This macro is only available when the feature `test_macro` is enabled.
671///
672/// It accepts the following parameters:
673/// * `module_name`: the name of the module to generate (defaults to `test`);
674/// * `graph_impl`: the type to test, implementing [`Graph`] and [`CollectibleGraph`];
675/// * `is_set`: a boolean, indicating if `graph_impl` implements [`SetGraph`]
676///   (defaults to `true`);
677/// * `is_gen`: a boolean, indicating if `graph_impl` supports the [generalized model]
678///   (defaults to `true`);
679/// * `graph_collector`: a function used to collect triples into an instance of `graph_impl`
680///   (defaults to `graph_impl::from_triple_source`);
681///
682/// [generalized model]: crate#generalized-vs-strict-rdf-model
683#[macro_export]
684macro_rules! test_immutable_graph_impl {
685    ($graph_impl: ident) => {
686        $crate::test_immutable_graph_impl!(test, $graph_impl);
687    };
688    ($module_name: ident, $graph_impl: ident) => {
689        $crate::test_immutable_graph_impl!($module_name, $graph_impl, true);
690    };
691    ($module_name: ident, $graph_impl: ident, $is_set: expr) => {
692        $crate::test_immutable_graph_impl!($module_name, $graph_impl, $is_set, true);
693    };
694    ($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr) => {
695        $crate::test_immutable_graph_impl!(
696            $module_name,
697            $graph_impl,
698            $is_set,
699            $is_gen,
700            $graph_impl::from_triple_source
701        );
702    };
703    ($module_name: ident, $graph_impl: ident, $is_set: expr, $is_gen: expr, $graph_collector: path) => {
704        // calling test_graph_impl, but passing an empty block as mt (the mutability tests)
705        $crate::test_graph_impl!(
706            $module_name,
707            $graph_impl,
708            $is_set,
709            $is_gen,
710            $graph_collector,
711            {}
712        );
713    };
714}