1pub extern crate proptest;
2pub use proptest::*;
3pub mod fuzz;
4pub mod graph;
5pub mod index;
6pub mod steps;
7
8use graph_api_derive::{EdgeExt, VertexExt};
9use graph_api_lib::ElementId;
10#[allow(unused_imports)]
11use graph_api_lib::{
12 SupportsClear, SupportsEdgeAdjacentLabelIndex, SupportsEdgeHashIndex, SupportsEdgeLabelIndex,
13 SupportsEdgeRangeIndex, SupportsVertexFullTextIndex, SupportsVertexHashIndex,
14 SupportsVertexLabelIndex, SupportsVertexRangeIndex,
15};
16use std::collections::HashSet;
17use std::fmt::{Debug, Display, Formatter};
18use thiserror::Error;
19use uuid::Uuid;
20
21#[derive(Debug, Clone, VertexExt)]
22pub enum Vertex {
23 Person {
24 #[index(hash)]
25 name: String,
26 #[index(range)]
27 age: u64,
28 #[index(hash)]
29 unique_id: Uuid,
30 #[index(range)]
31 username: String,
32 #[index(full_text)]
33 biography: String,
34 },
35 Project(Project),
36 Rust,
37}
38
39#[derive(Debug, Clone)]
40pub struct Project {
41 pub name: String,
42}
43
44#[derive(Debug, Clone, EdgeExt)]
45pub enum Edge {
46 Knows { since: i32 },
47 Created,
48 Language(Language),
49}
50#[derive(Debug, Clone)]
51pub struct Language {
52 pub name: String,
53}
54pub struct Refs<Graph>
55where
56 Graph: graph_api_lib::Graph,
57{
58 pub bryn: Graph::VertexId,
59 pub julia: Graph::VertexId,
60 pub graph_api: Graph::VertexId,
61 pub rust: Graph::VertexId,
62 pub bryn_knows_julia: Graph::EdgeId,
63 pub julia_knows_bryn: Graph::EdgeId,
64 pub bryn_created_graph_api: Graph::EdgeId,
65 pub graph_api_language_rust: Graph::EdgeId,
66}
67
68pub fn populate_graph<Graph>(graph: &mut Graph) -> Refs<Graph>
69where
70 Graph: graph_api_lib::Graph<Vertex = Vertex, Edge = Edge>,
71{
72 let bryn = graph.add_vertex(Vertex::Person {
74 name: "Bryn".to_string(),
75 age: 45,
76 unique_id: Uuid::from_u128(1),
77 username: "bryn".to_string(),
78 biography: "Did some graph stuff".to_string(),
79 });
80 let julia = graph.add_vertex(Vertex::Person {
81 name: "Julia".to_string(),
82 age: 48,
83 unique_id: Uuid::from_u128(2),
84 username: "julia".to_string(),
85 biography: "Mastered the English language".to_string(),
86 });
87 let graph_api = graph.add_vertex(Vertex::Project(Project {
88 name: "GraphApi".to_string(),
89 }));
90
91 let rust = graph.add_vertex(Vertex::Rust);
92
93 let bryn_knows_julia = graph.add_edge(bryn, julia, Edge::Knows { since: 1999 });
94 let julia_knows_bryn = graph.add_edge(julia, bryn, Edge::Knows { since: 1999 });
95 let bryn_created_graph_api = graph.add_edge(bryn, graph_api, Edge::Created);
96 let graph_api_language_rust = graph.add_edge(
97 graph_api,
98 rust,
99 Edge::Language(Language {
100 name: "Rust".to_string(),
101 }),
102 );
103
104 Refs {
105 bryn,
106 julia,
107 graph_api,
108 rust,
109 bryn_knows_julia,
110 julia_knows_bryn,
111 bryn_created_graph_api,
112 graph_api_language_rust,
113 }
114}
115
116#[derive(Error, Debug)]
117pub enum TestError {
118 Mismatch {
119 missing: Vec<String>,
120 extra: Vec<String>,
121 expected: Vec<String>,
122 },
123 MoreThanOneElement {
124 expected: Vec<String>,
125 actual: Vec<String>,
126 },
127}
128
129#[macro_export]
130macro_rules! check_unsupported {
131 ($setup:expr, $name:ident, $feature:path) => {
132 #[test]
133 #[ignore] fn $name() {
135 fn check(g: impl graph_api_lib::Graph<Vertex = (), Edge = ()>) {}
136 let g = $setup;
138 check(g);
139 }
140 };
141}
142
143#[macro_export]
144macro_rules! general_test {
145 ($setup:expr, $name:ident, $path:path) => {
146 #[test]
147 fn $name() {
148 let mut g = $setup;
149 $path(&mut g);
150 }
151 };
152}
153
154#[cfg(feature = "edge-label-index")]
155#[macro_export]
156macro_rules! edge_index_label_test {
157 ($setup:expr, $name:ident, $path:path) => {
158 #[test]
159 fn $name() {
160 let mut g = $setup;
161 $path(&mut g);
162 }
163 };
164}
165#[cfg(not(feature = "edge-label-index"))]
166#[macro_export]
167macro_rules! edge_index_label_test {
168 ($setup:expr, $name:ident, $path:path) => {
169 $crate::check_unsupported!($setup, $name, SupportsEdgeLabelIndex);
170 };
171}
172
173#[cfg(feature = "vertex-label-index")]
174#[macro_export]
175macro_rules! vertex_index_label_test {
176 ($setup:expr, $name:ident, $path:path) => {
177 #[test]
178 fn $name() {
179 let mut g = $setup;
180 $path(&mut g);
181 }
182 };
183}
184
185#[cfg(not(feature = "vertex-label-index"))]
186#[macro_export]
187macro_rules! vertex_index_label_test {
188 ($setup:expr, $name:ident, $path:path) => {
189 $crate::check_unsupported!($setup, $name, SupportsVertexLabelIndex);
190 };
191}
192
193#[cfg(feature = "vertex-hash-index")]
194#[macro_export]
195macro_rules! vertex_index_hash_test {
196 ($setup:expr, $name:ident, $path:path) => {
197 #[test]
198 fn $name() {
199 let mut g = $setup;
200 $path(&mut g);
201 }
202 };
203}
204#[cfg(not(feature = "vertex-hash-index"))]
205#[macro_export]
206macro_rules! vertex_index_hash_test {
207 ($setup:expr, $name:ident, $path:path) => {
208 $crate::check_unsupported!($setup, $name, SupportsVertexHashIndex);
209 };
210}
211
212#[cfg(feature = "vertex-full-text-index")]
213#[macro_export]
214macro_rules! vertex_index_full_text_test {
215 ($setup:expr, $name:ident, $path:path) => {
216 #[test]
217 fn $name() {
218 let mut g = $setup;
219 $path(&mut g);
220 }
221 };
222}
223#[cfg(not(feature = "vertex-full-text-index"))]
224#[macro_export]
225macro_rules! vertex_index_full_text_test {
226 ($setup:expr, $name:ident, $path:path) => {
227 $crate::check_unsupported!($setup, $name, SupportsVertexFullTextIndex);
228 };
229}
230
231#[cfg(feature = "vertex-range-index")]
232#[macro_export]
233macro_rules! vertex_index_range_test {
234 ($setup:expr, $name:ident, $path:path) => {
235 #[test]
236 fn $name() {
237 let mut g = $setup;
238 $path(&mut g);
239 }
240 };
241}
242
243#[cfg(not(feature = "vertex-range-index"))]
244#[macro_export]
245macro_rules! vertex_index_range_test {
246 ($setup:expr, $name:ident, $path:path) => {
247 $crate::check_unsupported!($setup, $name, SupportsVertexRangeIndex);
248 };
249}
250
251#[macro_export]
252macro_rules! test_suite {
253 ($setup:expr) => {
254 $crate::general_test! {$setup, graph_test_add_vertex, $crate::graph::test_add_vertex}
255 $crate::general_test!{$setup, graph_test_mutate_vertex, $crate::graph::test_mutate_vertex}
256 $crate::general_test!{$setup, graph_test_remove_vertex, $crate::graph::test_remove_vertex}
257 $crate::general_test!{$setup, graph_test_add_edge, $crate::graph::test_add_edge}
258 $crate::general_test!{$setup, graph_test_mutate_edge, $crate::graph::test_mutate_edge}
259 $crate::general_test!{$setup, graph_test_remove_edge, $crate::graph::test_remove_edge}
260 $crate::general_test!{$setup, graph_test_remove_vertex_with_edges, $crate::graph::test_remove_vertex_with_edges}
261 $crate::general_test!{$setup, filter_test_vertices_filter, $crate::steps::filter::test_vertices_filter}
262 $crate::general_test!{$setup, filter_test_edges_filter, $crate::steps::filter::test_edges_filter}
263 $crate::general_test!{$setup, vertices_test_vertices_collect, $crate::steps::collect::test_vertices_collect}
264 $crate::general_test!{$setup, vertices_test_edges_collect, $crate::steps::collect::test_edges_collect}
265 $crate::general_test!{$setup, edges_test_out_edges, $crate::steps::edges::test_out_edges}
266 $crate::general_test!{$setup, edges_test_out_edges_limit, $crate::steps::edges::test_out_edges_limit}
267 $crate::general_test!{$setup, edges_test_in_edges, $crate::steps::edges::test_in_edges}
268 $crate::general_test!{$setup, edges_test_in_edges_limit, $crate::steps::edges::test_in_edges_limit}
269 $crate::general_test!{$setup, edges_test_all_edges, $crate::steps::edges::test_all_edges}
270 $crate::general_test!{$setup, edges_test_all_edges_limit, $crate::steps::edges::test_all_edges_limit}
271 $crate::general_test!{$setup, edges_test_out_edges_filtered, $crate::steps::edges::test_out_edges_filtered}
272 $crate::general_test!{$setup, edges_test_out_edges_filtered_limit, $crate::steps::edges::test_out_edges_filtered_limit}
273 $crate::general_test!{$setup, edges_test_in_edges_filtered, $crate::steps::edges::test_in_edges_filtered}
274 $crate::general_test!{$setup, edges_test_in_edges_filtered_limit, $crate::steps::edges::test_in_edges_filtered_limit}
275 $crate::general_test!{$setup, edges_test_all_edges_filtered, $crate::steps::edges::test_all_edges_filtered}
276 $crate::general_test!{$setup, edges_test_all_edges_filtered_limit, $crate::steps::edges::test_all_edges_filtered_limit}
277 $crate::general_test!{$setup, context_test_vertices_context, $crate::steps::context::test_vertices_context}
278 $crate::general_test!{$setup, vertices_test_take, $crate::steps::vertices::test_take}
279 $crate::general_test!{$setup, vertices_test_head, $crate::steps::vertices::test_head}
280 $crate::general_test!{$setup, vertices_test_tail, $crate::steps::vertices::test_tail}
281 $crate::general_test!{$setup, mutate_context_vertex, $crate::steps::mutate_context::test_vertex_mutate_context}
282 $crate::general_test!{$setup, mutate_context_edge, $crate::steps::mutate_context::test_edge_mutate_context}
283 $crate::general_test!{$setup, mutation_test_mutation, $crate::steps::mutation::test_mutation}
284 $crate::general_test!{$setup, mutation_test_edge_mutation, $crate::steps::mutation::test_edge_mutation}
285 $crate::general_test!{$setup, count_test_vertices_count, $crate::steps::count::test_vertices_count}
286 $crate::general_test!{$setup, count_test_edges_count, $crate::steps::count::test_edges_count}
287 $crate::general_test!{$setup, take_test_vertices_take, $crate::steps::take::test_vertices_take}
288 $crate::general_test!{$setup, take_test_edges_take, $crate::steps::take::test_edges_take}
289 $crate::general_test!{$setup, first_test_vertices_first, $crate::steps::first::test_vertices_first}
290 $crate::general_test!{$setup, first_test_edges_first, $crate::steps::first::test_edges_first}
291 $crate::general_test!{$setup, fold_test_vertices_fold, $crate::steps::fold::test_vertices_fold}
292 $crate::general_test!{$setup, fold_test_edges_fold, $crate::steps::fold::test_edges_fold}
293 $crate::general_test!{$setup, reduce_test_vertices_reduce, $crate::steps::reduce::test_vertices_reduce}
294 $crate::general_test!{$setup, reduce_test_edges_reduce, $crate::steps::reduce::test_edges_reduce}
295 $crate::general_test!{$setup, detour_test_vertices_detour, $crate::steps::detour::test_vertices_detour}
296 $crate::general_test!{$setup, filter_derive_test_vertices_filter, $crate::steps::filter_derive::test_vertices_filter}
297 $crate::general_test!{$setup, filter_derive_test_edges_filter, $crate::steps::filter_derive::test_edges_filter}
298 $crate::general_test!{$setup, probe_test_vertices_probe, $crate::steps::probe::test_vertices_probe}
299 $crate::general_test!{$setup, probe_test_edges_probe, $crate::steps::probe::test_edges_probe}
300 $crate::general_test!{$setup, control_flow_test_vertices_control_flow, $crate::steps::control_flow::test_vertices_control_flow}
301 $crate::general_test!{$setup, control_flow_test_edges_control_flow, $crate::steps::control_flow::test_edges_control_flow}
302 $crate::edge_index_label_test!{$setup, index_edge_label_test_index, $crate::index::edge_label::test_index}
303 $crate::edge_index_label_test!{$setup, index_edge_label_test_index_limit, $crate::index::edge_label::test_index_limit}
304 $crate::vertex_index_label_test!{$setup, index_vertex_label_test_index, $crate::index::vertex_label::test_index}
305 $crate::vertex_index_label_test!{$setup, index_vertex_label_test_index_limit, $crate::index::vertex_label::test_index_limit}
306 $crate::vertex_index_hash_test!{$setup, index_vertex_hash_test_index, $crate::index::vertex_hash::test_index}
307 $crate::vertex_index_hash_test!{$setup, index_vertex_hash_test_index_remove, $crate::index::vertex_hash::test_index_remove}
308 $crate::vertex_index_hash_test!{$setup, index_vertex_hash_test_index_update, $crate::index::vertex_hash::test_index_update}
309 $crate::vertex_index_full_text_test!{$setup, index_vertex_full_text_test_index, $crate::index::vertex_full_text::test_index}
310 $crate::vertex_index_full_text_test!{$setup, index_vertex_full_text_test_index_remove, $crate::index::vertex_full_text::test_index_remove}
311 $crate::vertex_index_full_text_test!{$setup, index_vertex_full_text_test_index_update, $crate::index::vertex_full_text::test_index_update}
312 $crate::vertex_index_range_test!{$setup, index_vertex_range_test_index, $crate::index::vertex_range::test_index}
313 $crate::vertex_index_range_test!{$setup, index_vertex_range_test_index_remove, $crate::index::vertex_range::test_index_remove}
314 $crate::vertex_index_range_test!{$setup, index_vertex_range_test_index_update, $crate::index::vertex_range::test_index_update}
315
316 $crate::proptest! {
317 #[test]
318 fn fuzz_test(operations in $crate::collection::vec($crate::fuzz::arb_graph_operation(), 0..100)) {
319 $crate::fuzz::test_fuzz($setup, operations);
320 }
321 }
322 };
323}
324
325impl Display for TestError {
326 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
327 match self {
328 TestError::Mismatch {
329 missing,
330 extra,
331 expected,
332 } => {
333 if !missing.is_empty() {
334 write!(f, "Missing elements:\n{}\n", missing.join("\n"),)?;
335 }
336 if !extra.is_empty() {
337 write!(f, "Extra elements:\n{}\n", extra.join("\n"),)?;
338 }
339 write!(f, "Expected elements:\n{}", expected.join("\n"))?;
340 }
341 TestError::MoreThanOneElement { actual, expected } => {
342 write!(
343 f,
344 "Expected one of:\n{}\nBut got:\n{}",
345 expected.join("\n"),
346 actual.join("\n")
347 )?;
348 }
349 }
350
351 Ok(())
352 }
353}
354
355pub fn assert_elements_one_of<Graph>(
356 graph: &Graph,
357 actual: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
358 expected: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
359) -> Result<(), TestError>
360where
361 Graph: graph_api_lib::Graph,
362{
363 let actual: Vec<ElementId<Graph>> = actual.into_iter().map(Into::into).collect();
364 let expected: Vec<ElementId<Graph>> = expected.into_iter().map(Into::into).collect();
365
366 let actual_strings: Vec<String> = actual.iter().map(|e| graph.dbg(*e)).collect();
368 let expected_strings: Vec<String> = expected.iter().map(|e| graph.dbg(*e)).collect();
369
370 if actual.len() != 1 {
371 return Err(TestError::MoreThanOneElement {
372 expected: expected_strings,
373 actual: actual_strings,
374 });
375 }
376
377 Ok(())
378}
379
380pub fn assert_elements_eq<Graph>(
381 graph: &Graph,
382 actual: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
383 expected: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
384) -> Result<(), TestError>
385where
386 Graph: graph_api_lib::Graph,
387{
388 let actual = actual.into_iter().map(Into::into).collect::<HashSet<_>>();
389 let expected = expected.into_iter().map(Into::into).collect::<HashSet<_>>();
390 if actual != expected {
391 let missing: Vec<String> = expected
392 .difference(&actual)
393 .map(|e| graph.dbg(*e))
394 .collect();
395 let extra: Vec<String> = actual
396 .difference(&expected)
397 .map(|e| graph.dbg(*e))
398 .collect();
399
400 let expected: Vec<String> = expected.iter().map(|e| graph.dbg(*e)).collect();
401
402 return Err(TestError::Mismatch {
403 missing,
404 extra,
405 expected,
406 });
407 }
408 Ok(())
409}
410
411#[macro_export]
412macro_rules! assert_elements_eq {
413 ($graph:expr, $actual:expr, $expected:expr) => {
414 if let Err(e) = $crate::assert_elements_eq($graph, $actual, $expected) {
415 panic!("{}", e);
416 }
417 };
418}
419
420#[macro_export]
421macro_rules! assert_elements_one_of {
422 ($graph:expr, $actual:expr, $expected:expr) => {
423 if let Err(e) = $crate::assert_elements_one_of($graph, $actual, $expected) {
424 panic!("{}", e);
425 }
426 };
427}