17namespace sequoia::testing
19 namespace undirected_graph{
76 node_1_node_0_2_node_1,
80 node_1_2_node_0_2_node_0_1,
95 node_1_node_0_1_2_node_1,
100 node_2_node_node_0_2,
109 node_1_1_2_2_node_0_0_2_node_0_0_1,
117 node_3_1_node_0_2_node_1_node_0,
120 node_1_node_0_node_3_node_2,
123 node_2_node_3_node_0_node_1,
134 class EdgeStorageConfig,
135 class NodeWeightStorage
141 using edge_t =
typename graph_t::edge_init_type;
142 using edges_equivalent_t = std::initializer_list<std::initializer_list<edge_t>>;
147 auto trg{make_transition_graph(t)};
150 [&t](std::string_view description,
const graph_t& obtained,
const graph_t& prediction,
const graph_t& parent, std::size_t host, std::size_t target) {
151 t.check(equality, {description, no_source_location}, obtained, prediction);
152 if(host != target) t.check_semantics({description, no_source_location}, prediction, parent);
160 static graph_t make_and_check(
regular_test& t, std::string_view description, edges_equivalent_t init)
165 static void check_initialization_exceptions(
regular_test& t)
167 using namespace maths;
170 t.check_exception_thrown<std::out_of_range>(
"Target index of edge out of range", [](){
return graph_t{{edge_t{1}}}; });
171 t.check_exception_thrown<std::logic_error>(
"Mismatched loop", [](){
return graph_t{{edge_t{0}}}; });
174 t.check_exception_thrown<std::logic_error>(
"Mismatched partial edges", [](){
return graph_t{{edge_t{1}}, {edge_t{1}}}; });
175 t.check_exception_thrown<std::logic_error>(
"Mismatched loop", [](){
return graph_t{{edge_t{1}}, {edge_t{0}, edge_t{1}}}; });
179 static transition_graph make_transition_graph(
regular_test& t)
183 check_initialization_exceptions(t);
185 return transition_graph{
189 graph_description::empty,
192 t.check_exception_thrown<std::out_of_range>(
"cbegin_edges throws for empty graph", [&g]() {
return g.cbegin_edges(0); });
197 graph_description::empty,
200 t.check_exception_thrown<std::out_of_range>(
"cend_edges throws for empty graph", [&g]() {
return g.cend_edges(0); });
205 graph_description::empty,
208 t.check_exception_thrown<std::out_of_range>(
"crbegin_edges throws for empty graph", [&g]() {
return g.crbegin_edges(0); });
213 graph_description::empty,
216 t.check_exception_thrown<std::out_of_range>(
"crend_edges throws for empty graph", [&g]() {
return g.crend_edges(0); });
221 graph_description::empty,
224 t.check_exception_thrown<std::out_of_range>(
"cedges throws for empty graph", [&g]() {
return g.cedges(0); });
229 graph_description::empty,
232 t.check_exception_thrown<std::out_of_range>(
"swapping nodes throws for empty graph", [g{g}]()
mutable { g.swap_nodes(0, 0); });
237 graph_description::empty,
240 t.check_exception_thrown<std::out_of_range>(
"swapping edges throws for empty graph", [g{g}]()
mutable { g.swap_edges(0, 0, 0); });
245 graph_description::empty,
248 t.check_exception_thrown<std::out_of_range>(
"joining nodes throws for empty graph", [g{g}]()
mutable { g.join(0, 0); });
253 graph_description::empty,
254 t.report(
"Clear empty graph"),
261 graph_description::node,
262 t.report(
"Add node to empty graph"),
264 t.check(equality,
"Index of added node is 0", g.add_node(), 0uz);
269 graph_description::node,
270 t.report(
"insert node into empty graph"),
272 t.check(equality,
"Index of added node is 0", g.insert_node(0), 0uz);
279 graph_description::node,
282 t.check_exception_thrown<std::out_of_range>(
"cbegin_edges throws when index is out of range", [&g]() {
return g.cbegin_edges(1); });
287 graph_description::node,
290 t.check_exception_thrown<std::out_of_range>(
"cend_edges throws when index is out of range", [&g]() {
return g.cend_edges(1); });
295 graph_description::node,
298 t.check_exception_thrown<std::out_of_range>(
"crbegin_edges throws when index is out of range", [&g]() {
return g.crbegin_edges(1); });
303 graph_description::node,
306 t.check_exception_thrown<std::out_of_range>(
"crend_edges throws when index is out of range", [&g]() {
return g.crend_edges(1); });
311 graph_description::node,
314 t.check_exception_thrown<std::out_of_range>(
"cedges throws when index is out of range", [&g]() {
return g.cedges(1); });
319 graph_description::node,
322 t.check_exception_thrown<std::out_of_range>(
"swapping nodes throws if first index out of range", [g{g}]()
mutable { g.swap_nodes(1, 0); });
327 graph_description::node,
330 t.check_exception_thrown<std::out_of_range>(
"swapping nodes throws if second index out of range", [g{g}]()
mutable { g.swap_nodes(0, 1); });
335 graph_description::node,
338 t.check_exception_thrown<std::out_of_range>(
"joining nodes throws if first index out of range", [g{g}]()
mutable { g.join(1, 0); });
343 graph_description::node,
346 t.check_exception_thrown<std::out_of_range>(
"joining nodes throws if second index out of range", [g{g}]()
mutable { g.join(0, 1); });
351 graph_description::empty,
352 t.report(
"Clear graph"),
359 graph_description::empty,
360 t.report(
"Erase node to give empty graph"),
367 graph_description::node,
368 t.report(
"Attempt to erase edge past the end"),
370 g.erase_edge(g.cend_edges(0));
375 graph_description::node_0,
376 t.report(
"Add loop"),
383 graph_description::node_node,
384 t.report(
"Add second node"),
386 t.check(equality,
"Index of added node is 1", g.add_node(), 1uz);
391 graph_description::node_node,
392 t.report(
"Insert second node"),
394 t.check(equality,
"Index of added node is 0", g.insert_node(0), 0uz);
399 graph_description::node_node,
400 t.report(
"Insert second node at end"),
402 t.check(equality,
"Index of added node is 1", g.insert_node(1), 1uz);
407 graph_description::node,
408 t.report(
"Swap node with self"),
417 graph_description::empty,
418 t.report(
"Clear graph"),
425 graph_description::node,
426 t.report(
"Remove loop"),
428 g.erase_edge(g.cbegin_edges(0));
433 graph_description::node,
434 t.report(
"Remove loop via second partial edge"),
436 g.erase_edge(std::ranges::next(g.cbegin_edges(0)));
441 graph_description::node_0_0,
442 t.report(
"Add a second loop"),
449 graph_description::node_node_1,
450 t.report(
"Insert node"),
452 t.check(equality,
"Index of added node is 0", g.insert_node(0), 0uz);
457 graph_description::node_0_node,
458 t.report(
"Insert node at end"),
460 t.check(equality,
"Index of added node is 1", g.insert_node(1), 1uz);
465 graph_description::node_0,
466 t.report(
"Swap node with self"),
473 graph_description::node_0,
474 t.report(
"Swap edge with self"),
476 g.swap_edges(0, 0, 0);
481 graph_description::node_0,
482 t.report(
"Swap first partial edge with partner"),
484 g.swap_edges(0, 0, 1);
489 graph_description::node_0,
490 t.report(
"Swap second partial edge with partner"),
492 g.swap_edges(0, 1, 0);
497 graph_description::node_0,
500 t.check_exception_thrown<std::out_of_range>(
"swapping edges throws for first edge index out of range", [g{g}]()
mutable { g.swap_edges(0, 2, 0); });
505 graph_description::node_0,
508 t.check_exception_thrown<std::out_of_range>(
"swapping edges throws for second edge index out of range", [g{g}]()
mutable { g.swap_edges(0, 0, 2); });
513 graph_description::node_0,
516 t.check_exception_thrown<std::out_of_range>(
"swapping edges throws for node index out of range", [g{g}]()
mutable { g.swap_edges(1, 0, 0); });
523 graph_description::empty,
524 t.report(
"Clear graph"),
531 graph_description::node_0,
532 t.report(
"Remove first loop, via first partial edge"),
534 g.erase_edge(g.cbegin_edges(0));
539 graph_description::node_0,
540 t.report(
"Remove first loop, via second partial edge"),
542 g.erase_edge(g.cbegin_edges(0) + 1);
547 graph_description::node_0,
548 t.report(
"Remove second loop, via first partial edge"),
550 g.erase_edge(g.cbegin_edges(0) + 2);
555 graph_description::node_0,
556 t.report(
"Remove second loop, via second partial edge"),
558 g.erase_edge(g.cbegin_edges(0) + 3);
563 graph_description::node_0_0,
564 t.report(
"Swap partial edges {0,1}"),
566 g.swap_edges(0, 0, 1);
571 graph_description::node_0_0,
572 t.report(
"Swap partial edges {0,2}"),
574 g.swap_edges(0, 0, 2);
579 graph_description::node_0_0,
580 t.report(
"Swap partial edges {0,3}"),
582 g.swap_edges(0, 0, 3);
587 graph_description::node_0_0,
588 t.report(
"Swap partial edges {1,2}"),
590 g.swap_edges(0, 1, 2);
595 graph_description::node_0_0,
596 t.report(
"Swap partial edges {2,1}"),
598 g.swap_edges(0, 2, 1);
605 graph_description::empty,
606 t.report(
"Clear graph"),
613 graph_description::node,
614 t.report(
"Erase node 0"),
621 graph_description::node,
622 t.report(
"Erase node 1"),
629 graph_description::node_1_node_0,
630 t.report(
"Join {0,1}"),
637 graph_description::node_1_node_0,
638 t.report(
"Join {1,0}"),
647 graph_description::empty,
648 t.report(
"Clear graph"),
655 graph_description::node,
656 t.report(
"Erase node 0"),
663 graph_description::node,
664 t.report(
"Erase node 1"),
671 graph_description::node_0_1_node_0,
672 t.report(
"Add loop to node 0 and then swap it with link"),
675 g.swap_edges(0, 0, 2);
680 graph_description::node_1_node_0,
681 t.report(
"Swap nodes {0,1}"),
688 graph_description::node_1_node_0,
689 t.report(
"Swap nodes {1,0}"),
696 graph_description::node_1_1_node_0_0,
697 t.report(
"Join {0,1}"),
706 graph_description::empty,
707 t.report(
"Clear graph"),
714 graph_description::node,
715 t.report(
"Erase node 0"),
722 graph_description::node_0,
723 t.report(
"Erase node 1"),
730 graph_description::node_1_node_0,
731 t.report(
"Remove loop via first partial edge"),
733 g.erase_edge(g.cbegin_edges(0));
738 graph_description::node_1_node_0,
739 t.report(
"Remove loop via second partial edge"),
741 g.erase_edge(++g.cbegin_edges(0));
746 graph_description::node_0_node,
747 t.report(
"Remove edge {0,1}"),
749 g.erase_edge(g.cbegin_edges(0) + 2);
754 graph_description::node_0_node,
755 t.report(
"Remove edge {1,0}"),
757 g.erase_edge(g.cbegin_edges(1));
764 graph_description::node_0,
765 t.report(
"Erase node 0"),
772 graph_description::node,
773 t.report(
"Erase node 1"),
780 graph_description::node_node_1,
781 t.report(
"Remove edge {0,1}"),
783 g.erase_edge(g.cbegin_edges(0));
788 graph_description::node_node_1,
789 t.report(
"Remove edge {1,0}"),
791 g.erase_edge(g.cbegin_edges(1));
796 graph_description::node_1_node_0,
797 t.report(
"Remove loop via first partial edge"),
799 g.erase_edge(g.cbegin_edges(1)+1);
804 graph_description::node_1_node_0,
805 t.report(
"Remove loop via second partial edge"),
807 g.erase_edge(g.cbegin_edges(1)+2);
814 graph_description::empty,
815 t.report(
"Clear graph"),
822 graph_description::node_node_1_node,
823 t.report(
"Insert node"),
825 t.check(equality,
"Index of added node is 0", g.insert_node(0), 0uz);
830 graph_description::node_node_1,
831 t.report(
"Swap nodes"),
839 graph_description::node_node_1,
840 t.report(
"Swap nodes"),
849 graph_description::empty,
850 t.report(
"Clear graph"),
857 graph_description::node_0,
858 t.report(
"Erase node 0"),
865 graph_description::node,
866 t.report(
"Erase node 1"),
873 graph_description::node_0_node,
874 t.report(
"Swap nodes"),
883 graph_description::empty,
884 t.report(
"Clear graph"),
891 graph_description::node,
892 t.report(
"Erase node 0"),
899 graph_description::node,
900 t.report(
"Erase node 1"),
907 graph_description::node_1_node_0,
908 t.report(
"Erase node 0 zeroth link"),
910 g.erase_edge(g.cbegin_edges(0));
915 graph_description::node_1_node_0,
916 t.report(
"Erase node 0 first link"),
918 g.erase_edge(g.cbegin_edges(0) + 1);
923 graph_description::node_1_node_0,
924 t.report(
"Erase node 1 zeroth link"),
926 g.erase_edge(g.cbegin_edges(1));
931 graph_description::node_1_node_0,
932 t.report(
"Erase node 1 first link"),
934 g.erase_edge(g.cbegin_edges(1) + 1);
939 graph_description::node_1_1_node_0_0,
940 t.report(
"Swap edges"),
942 g.swap_edges(0, 0, 1);
947 graph_description::node_1_1_node_0_0,
948 t.report(
"Swap node 0 edges"),
950 g.swap_edges(0, 1, 0);
955 graph_description::node_1_1_node_0_0,
956 t.report(
"Swap node 1 edges"),
958 g.swap_edges(1, 0, 1);
965 graph_description::empty,
966 t.report(
"Clear graph"),
973 graph_description::node_node,
974 t.report(
"Erase node 0"),
981 graph_description::node_node,
982 t.report(
"Erase node 1"),
989 graph_description::node_node,
990 t.report(
"Erase node 2"),
997 graph_description::node_1_node_0_node,
998 t.report(
"Join {0,1}"),
1005 graph_description::node_node_2_node_1,
1006 t.report(
"Join {2,1}"),
1015 graph_description::empty,
1016 t.report(
"Clear graph"),
1023 graph_description::node_node,
1024 t.report(
"Erase node 0"),
1031 graph_description::node_node,
1032 t.report(
"Erase node 1"),
1039 graph_description::node_1_node_0,
1040 t.report(
"Erase node 2"),
1047 graph_description::node_node_node,
1048 t.report(
"Remove link {0,1}"),
1050 g.erase_edge(g.cbegin_edges(0));
1055 graph_description::node_node_node,
1056 t.report(
"Remove link {1,0}"),
1058 g.erase_edge(g.cbegin_edges(1));
1063 graph_description::node_1_node_0_2_node_1,
1064 t.report(
"Join {1,2}"),
1071 graph_description::node_1_node_0_2_node_1,
1072 t.report(
"Join {2,1}"),
1079 graph_description::node_node_2_node_1,
1080 t.report(
"Swap nodes {0,2}"),
1087 graph_description::node_node_2_node_1,
1088 t.report(
"Swap nodes {2,0}"),
1097 graph_description::empty,
1098 t.report(
"Clear graph"),
1105 graph_description::node_1_node_0,
1106 t.report(
"Erase node 0"),
1113 graph_description::node_node,
1114 t.report(
"Erase node 1"),
1121 graph_description::node_node,
1122 t.report(
"Erase node 2"),
1129 graph_description::node_node_node,
1130 t.report(
"Remove link {1,2}"),
1132 g.erase_edge(g.cbegin_edges(1));
1137 graph_description::node_node_node,
1138 t.report(
"Remove link {2,1}"),
1140 g.erase_edge(g.cbegin_edges(2));
1145 graph_description::node_1_node_0_2_node_1,
1146 t.report(
"Join {0,1}, the swap node 1's edge"),
1149 g.swap_edges(1, 0, 1);
1154 graph_description::node_1_node_0_node,
1155 t.report(
"Swap nodes {0,2}"),
1162 graph_description::node_1_node_0_node,
1163 t.report(
"Swap nodes {2,0}"),
1172 graph_description::empty,
1173 t.report(
"Clear graph"),
1180 graph_description::node_1_node_0,
1181 t.report(
"Erase node 0"),
1188 graph_description::node_node,
1189 t.report(
"Erase node 1"),
1196 graph_description::node_1_node_0,
1197 t.report(
"Erase node 2"),
1204 graph_description::node_1_node_0_node,
1205 t.report(
"Remove link {1,2}"),
1207 g.erase_edge(g.cbegin_edges(1) + 1);
1212 graph_description::node_1_node_0_2_node_1,
1213 t.report(
"Swap nodes {0,2} then swap node 1's edges"),
1216 g.swap_edges(1, 0, 1);
1221 graph_description::node_1_node_0_2_node_1,
1222 t.report(
"Swap nodes {2,0} then swap node 1's edges"),
1225 g.swap_edges(1, 1, 0);
1232 graph_description::empty,
1233 t.report(
"Clear graph"),
1240 graph_description::node_1_node_0,
1241 t.report(
"Erase node 0"),
1248 graph_description::node_1_node_0,
1249 t.report(
"Erase node 1"),
1256 graph_description::node_1_node_0,
1257 t.report(
"Erase node 2"),
1264 graph_description::node_1_node_0_2_node_1,
1265 t.report(
"Remove {2,0}"),
1267 g.erase_edge(g.cbegin_edges(2));
1274 graph_description::empty,
1275 t.report(
"Clear graph"),
1282 graph_description::node_0_node,
1283 t.report(
"Erase node 0"),
1290 graph_description::node_node,
1291 t.report(
"Erase node 1"),
1298 graph_description::node_node_1,
1299 t.report(
"Erase node 2"),
1308 graph_description::node_0_node,
1309 t.report(
"Erase node 0"),
1316 graph_description::node_node,
1317 t.report(
"Erase node 1"),
1324 graph_description::node_node_1_node,
1325 t.report(
"Remove {0,1}"),
1327 g.erase_edge(g.cbegin_edges(0));
1332 graph_description::node_1_node_0_1_2_node_1,
1333 t.report(
"Join {1,2}"),
1342 graph_description::node_0_1_node_0,
1343 t.report(
"Erase node 0"),
1350 graph_description::node_node,
1351 t.report(
"Erase node 1"),
1358 graph_description::node_1_node_0_1_node,
1359 t.report(
"Remove {1,2}"),
1361 g.erase_edge(g.cbegin_edges(1) + 3);
1368 graph_description::node_1_node_0_1_node,
1369 t.report(
"Swap {1,2}"),
1378 graph_description::node_1_node_0,
1379 t.report(
"Erase node 0"),
1386 graph_description::node_1_1_node_0_0,
1387 t.report(
"Erase node 1"),
1394 graph_description::node_1_1_node_0_0,
1395 t.report(
"Erase node 2"),
1402 graph_description::node_1_1_2_2_node_0_0_2_node_0_0_1,
1403 t.report(
"Swap nodes {2, 1}"),
1406 g.sort_edges(g.cbegin_edges(0), g.cend_edges(0), [](
const auto& lhs,
const auto& rhs) {
return lhs.target_node() < rhs.target_node(); });
1411 graph_description::node_1_1_2_2_node_0_0_2_node_0_0_1,
1412 t.report(
"Swap nodes {2, 1}"),
1415 g.stable_sort_edges(g.cbegin_edges(0), g.cend_edges(0), [](
const auto& lhs,
const auto& rhs) {
return lhs.target_node() < rhs.target_node(); });
1422 graph_description::node_1_node_0_node,
1423 t.report(
"Erase node 0"),
1432 graph_description::node_1_node_0_node,
1433 t.report(
"Erase node 2"),
1440 graph_description::node_2_node_3_node_0_node_1,
1441 t.report(
"Swap {2,1}"),
1448 graph_description::node_2_node_3_node_0_node_1,
1449 t.report(
"Swap {0,3}"),
1458 graph_description::node_1_node_0_node_3_node_2,
1459 t.report(
"Swap {2,1}"),
1466 graph_description::node_1_node_0_node_3_node_2,
1467 t.report(
"Swap {0,3}"),
1477 make_and_check(t, t.report(
""), {}),
1480 make_and_check(t, t.report(
""), {{}}),
1483 make_and_check(t, t.report(
""), {{edge_t{0}, edge_t{0}}}),
1486 make_and_check(t, t.report(
""), {{edge_t{0}, edge_t{0}, edge_t{0}, edge_t{0}}}),
1489 make_and_check(t, t.report(
""), {{}, {}}),
1492 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}}}),
1495 make_and_check(t, t.report(
""), {{edge_t{0}, edge_t{0}, edge_t{1}}, {edge_t{0}}}),
1498 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}, edge_t{1}, edge_t{1}}}),
1501 make_and_check(t, t.report(
""), {{edge_t{0}, edge_t{0}}, {}}),
1504 make_and_check(t, t.report(
""), {{}, {edge_t{1}, edge_t{1}}}),
1507 make_and_check(t, t.report(
""), {{edge_t{1}, edge_t{1}}, {edge_t{0}, edge_t{0}}}),
1510 make_and_check(t, t.report(
""), {{}, {}, {}}),
1513 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}}, {}}),
1516 make_and_check(t, t.report(
""), {{}, {edge_t{2}}, {edge_t{1}}}),
1519 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}, edge_t{2}}, {edge_t{1}}}),
1523 auto g{make_and_check(t, t.report(
""), {{edge_t{1}, edge_t{2}}, {edge_t{0}, edge_t{2}}, {edge_t{0}, edge_t{1}}})};
1524 t.check(equality,
"Check sorting of edges on construction", graph_t{{edge_t{2}, edge_t{1}}, {edge_t{2}, edge_t{0}}, {edge_t{1}, edge_t{0}}}, g);
1530 make_and_check(t, t.report(
""), {{}, {edge_t{1}, edge_t{1}}, {}}),
1533 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}, edge_t{1}, edge_t{1}}, {}}),
1536 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}, edge_t{1}, edge_t{1}, edge_t{2}}, {edge_t{1}}}),
1539 make_and_check(t, t.report(
""), {{edge_t{2}}, {}, {edge_t{0}, edge_t{2}, edge_t{2}}}),
1543 auto g{make_and_check(t, t.report(
""), {{edge_t{1}, edge_t{1}, edge_t{2}, edge_t{2}}, {edge_t{0}, edge_t{0}, edge_t{2}}, {edge_t{0}, edge_t{0}, edge_t{1}}})};
1545 t.report(
"Check sorting of edges on construction"),
1546 graph_t{{edge_t{2}, edge_t{1}, edge_t{2}, edge_t{1}}, {edge_t{0}, edge_t{2}, edge_t{0}}, {edge_t{1}, edge_t{0}, edge_t{0}}},
1553 make_and_check(t, t.report(
""), {{edge_t{1}, edge_t{3}}, {edge_t{0}, edge_t{2}}, {edge_t{1}}, {edge_t{0}}}),
1556 make_and_check(t, t.report(
""), {{edge_t{1}}, {edge_t{0}}, {edge_t{3}}, {edge_t{2}}}),
1559 make_and_check(t, t.report(
""), {{edge_t{2}}, {edge_t{3}}, {edge_t{0}}, {edge_t{1}}}),
graph_description
Convention: the indices following 'node' - separated by underscores - give the target node of the ass...
Definition: DynamicUndirectedGraphTestingUtilities.hpp:21
Utilities for checking regular semantics.
Facility to define tests via a graph comprising states of an object and transitions between them.
Definition: DynamicGraph.hpp:303
Definition: DynamicGraph.hpp:360
class template from which all concrete tests should derive.
Definition: FreeTestCore.hpp:144
Exposes elementary check methods, with the option to plug in arbitrary Extenders to compose functiona...
Definition: FreeCheckers.hpp:708
Definition: DynamicUndirectedGraphTestingUtilities.hpp:138
Definition: DynamicGraphTestingUtilities.hpp:173
Definition: StateTransitionUtilities.hpp:77