1 /* Copyright (c) 2017-2023. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 #include "src/3rd-party/catch.hpp"
7 #include "src/mc/explo/udpor/Configuration.hpp"
8 #include "src/mc/explo/udpor/EventSet.hpp"
9 #include "src/mc/explo/udpor/History.hpp"
10 #include "src/mc/explo/udpor/UnfoldingEvent.hpp"
12 using namespace simgrid::mc::udpor;
14 TEST_CASE("simgrid::mc::udpor::Configuration: Constructing Configurations")
16 // The following tests concern the given event structure:
25 UnfoldingEvent e2{&e1};
26 UnfoldingEvent e3{&e2};
27 UnfoldingEvent e4{&e3}, e5{&e3};
29 SECTION("Creating a configuration without events")
32 REQUIRE(C.get_events().empty());
33 REQUIRE(C.get_latest_event() == nullptr);
36 SECTION("Creating a configuration with events")
38 // 5 choose 0 = 1 test
39 REQUIRE_NOTHROW(Configuration({&e1}));
41 // 5 choose 1 = 5 tests
42 REQUIRE_THROWS_AS(Configuration({&e2}), std::invalid_argument);
43 REQUIRE_THROWS_AS(Configuration({&e3}), std::invalid_argument);
44 REQUIRE_THROWS_AS(Configuration({&e4}), std::invalid_argument);
45 REQUIRE_THROWS_AS(Configuration({&e5}), std::invalid_argument);
47 // 5 choose 2 = 10 tests
48 REQUIRE_NOTHROW(Configuration({&e1, &e2}));
49 REQUIRE_THROWS_AS(Configuration({&e1, &e3}), std::invalid_argument);
50 REQUIRE_THROWS_AS(Configuration({&e1, &e4}), std::invalid_argument);
51 REQUIRE_THROWS_AS(Configuration({&e1, &e5}), std::invalid_argument);
52 REQUIRE_THROWS_AS(Configuration({&e2, &e3}), std::invalid_argument);
53 REQUIRE_THROWS_AS(Configuration({&e2, &e4}), std::invalid_argument);
54 REQUIRE_THROWS_AS(Configuration({&e2, &e5}), std::invalid_argument);
55 REQUIRE_THROWS_AS(Configuration({&e3, &e4}), std::invalid_argument);
56 REQUIRE_THROWS_AS(Configuration({&e3, &e5}), std::invalid_argument);
57 REQUIRE_THROWS_AS(Configuration({&e4, &e5}), std::invalid_argument);
59 // 5 choose 3 = 10 tests
60 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}));
61 REQUIRE_THROWS_AS(Configuration({&e1, &e2, &e4}), std::invalid_argument);
62 REQUIRE_THROWS_AS(Configuration({&e1, &e2, &e5}), std::invalid_argument);
63 REQUIRE_THROWS_AS(Configuration({&e1, &e3, &e4}), std::invalid_argument);
64 REQUIRE_THROWS_AS(Configuration({&e1, &e3, &e5}), std::invalid_argument);
65 REQUIRE_THROWS_AS(Configuration({&e1, &e4, &e5}), std::invalid_argument);
66 REQUIRE_THROWS_AS(Configuration({&e2, &e3, &e4}), std::invalid_argument);
67 REQUIRE_THROWS_AS(Configuration({&e2, &e3, &e5}), std::invalid_argument);
68 REQUIRE_THROWS_AS(Configuration({&e2, &e4, &e5}), std::invalid_argument);
69 REQUIRE_THROWS_AS(Configuration({&e3, &e4, &e5}), std::invalid_argument);
71 // 5 choose 4 = 5 tests
72 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}));
73 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e5}));
74 REQUIRE_THROWS_AS(Configuration({&e1, &e2, &e4, &e5}), std::invalid_argument);
75 REQUIRE_THROWS_AS(Configuration({&e1, &e3, &e4, &e5}), std::invalid_argument);
76 REQUIRE_THROWS_AS(Configuration({&e2, &e3, &e4, &e5}), std::invalid_argument);
78 // 5 choose 5 = 1 test
79 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4, &e5}));
83 TEST_CASE("simgrid::mc::udpor::Configuration: Adding Events")
85 // The following tests concern the given event structure:
93 UnfoldingEvent e2{&e1};
94 UnfoldingEvent e3{&e2};
95 UnfoldingEvent e4{&e2};
97 REQUIRE_THROWS_AS(Configuration().add_event(nullptr), std::invalid_argument);
98 REQUIRE_THROWS_AS(Configuration().add_event(&e2), std::invalid_argument);
99 REQUIRE_THROWS_AS(Configuration().add_event(&e3), std::invalid_argument);
100 REQUIRE_THROWS_AS(Configuration().add_event(&e4), std::invalid_argument);
101 REQUIRE_THROWS_AS(Configuration({&e1}).add_event(&e3), std::invalid_argument);
102 REQUIRE_THROWS_AS(Configuration({&e1}).add_event(&e4), std::invalid_argument);
104 REQUIRE_NOTHROW(Configuration().add_event(&e1));
105 REQUIRE_NOTHROW(Configuration({&e1}).add_event(&e1));
106 REQUIRE_NOTHROW(Configuration({&e1}).add_event(&e2));
107 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e1));
108 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e2));
109 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e3));
110 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e4));
111 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e1));
112 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e2));
113 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e3));
114 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e4));
115 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e1));
116 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e2));
117 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e3));
118 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e4));
119 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e1));
120 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e2));
121 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e3));
122 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e4));
125 TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order")
127 // The following tests concern the given event structure:
136 UnfoldingEvent e2{&e1};
137 UnfoldingEvent e3{&e2};
138 UnfoldingEvent e4{&e3};
140 SECTION("Topological ordering for entire set")
142 Configuration C{&e1, &e2, &e3, &e4};
143 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4});
144 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e4, &e3, &e2, &e1});
147 SECTION("Topological ordering for subsets")
149 SECTION("No elements")
152 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{});
153 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{});
158 Configuration C{&e1};
159 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1});
160 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e1});
163 SECTION("e1 and e2 only")
165 Configuration C{&e1, &e2};
166 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2});
167 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e2, &e1});
170 SECTION("e1, e2, and e3 only")
172 Configuration C{&e1, &e2, &e3};
173 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3});
174 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e3, &e2, &e1});
179 TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order More Complicated")
181 // The following tests concern the given event structure:
192 UnfoldingEvent e2{&e1};
193 UnfoldingEvent e3{&e2};
194 UnfoldingEvent e4{&e3}, e6{&e3};
195 UnfoldingEvent e5{&e4};
197 SECTION("Topological ordering for subsets")
199 SECTION("No elements")
202 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{});
203 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{});
208 Configuration C{&e1};
209 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1});
210 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e1});
213 SECTION("e1 and e2 only")
215 Configuration C{&e1, &e2};
216 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2});
217 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e2, &e1});
220 SECTION("e1, e2, and e3 only")
222 Configuration C{&e1, &e2, &e3};
223 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3});
224 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e3, &e2, &e1});
227 SECTION("e1, e2, e3, and e6 only")
229 Configuration C{&e1, &e2, &e3, &e6};
230 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e6});
231 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e6, &e3, &e2, &e1});
234 SECTION("e1, e2, e3, and e4 only")
236 Configuration C{&e1, &e2, &e3, &e4};
237 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4});
238 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e4, &e3, &e2, &e1});
241 SECTION("e1, e2, e3, e4, and e5 only")
243 Configuration C{&e1, &e2, &e3, &e4, &e5};
244 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e5});
245 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() ==
246 std::vector<UnfoldingEvent*>{&e5, &e4, &e3, &e2, &e1});
249 SECTION("e1, e2, e3, e4 and e6 only")
251 // In this case, e4 and e6 are interchangeable. Hence, we have to check
252 // if the sorting gives us *any* of the combinations
253 Configuration C{&e1, &e2, &e3, &e4, &e6};
254 REQUIRE((C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e6} ||
255 C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e6, &e4}));
256 REQUIRE((C.get_topologically_sorted_events_of_reverse_graph() ==
257 std::vector<UnfoldingEvent*>{&e6, &e4, &e3, &e2, &e1} ||
258 C.get_topologically_sorted_events_of_reverse_graph() ==
259 std::vector<UnfoldingEvent*>{&e4, &e6, &e3, &e2, &e1}));
262 SECTION("Topological ordering for entire set")
264 // In this case, e4/e5 are both interchangeable with e6. Hence, again we have to check
265 // if the sorting gives us *any* of the combinations
266 Configuration C{&e1, &e2, &e3, &e4, &e5, &e6};
267 REQUIRE((C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e5, &e6} ||
268 C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e6, &e5} ||
269 C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e6, &e4, &e5}));
270 REQUIRE((C.get_topologically_sorted_events_of_reverse_graph() ==
271 std::vector<UnfoldingEvent*>{&e6, &e5, &e4, &e3, &e2, &e1} ||
272 C.get_topologically_sorted_events_of_reverse_graph() ==
273 std::vector<UnfoldingEvent*>{&e5, &e6, &e4, &e3, &e2, &e1} ||
274 C.get_topologically_sorted_events_of_reverse_graph() ==
275 std::vector<UnfoldingEvent*>{&e5, &e4, &e6, &e3, &e2, &e1}));
280 TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order Very Complicated")
282 // The following tests concern the given event structure:
296 UnfoldingEvent e2{&e1}, e8{&e1};
297 UnfoldingEvent e3{&e2};
298 UnfoldingEvent e4{&e3};
299 UnfoldingEvent e5{&e4}, e6{&e4};
300 UnfoldingEvent e7{&e2, &e8}, e11{&e8};
301 UnfoldingEvent e10{&e7}, e9{&e6, &e7};
302 UnfoldingEvent e12{&e5, &e9, &e10};
304 SECTION("Test every combination of the maximal configuration (forward graph)")
306 // To test this, we ensure that for the `i`th event
307 // in `ordered_events`, each event in `ordered_events[0...<i]
308 // is contained in the history of `ordered_events[i]`.
309 Configuration C{&e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12};
310 const auto ordered_events = C.get_topologically_sorted_events();
312 EventSet events_seen;
313 for (size_t i = 0; i < ordered_events.size(); i++) {
314 UnfoldingEvent* e = ordered_events[i];
317 for (auto* e_hist : history) {
318 // In this demo, we want to make sure that
319 // we don't mark not yet seeing `e` as an error.
320 // The history of `e` traverses `e` itself. All
321 // other events in e's history are included in
325 // If this event has not been "seen" before,
326 // this implies that event `e` appears earlier
327 // in the list than one of its dependencies
328 REQUIRE(events_seen.contains(e_hist));
330 events_seen.insert(e);
334 SECTION("Test every combination of the maximal configuration (reverse graph)")
336 // To test this, we ensure that for the `i`th event
337 // in `ordered_events`, no event in `ordered_events[0...<i]
338 // is contained in the history of `ordered_events[i]`.
339 Configuration C{&e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12};
340 const auto ordered_events = C.get_topologically_sorted_events_of_reverse_graph();
342 EventSet events_seen;
343 for (size_t i = 0; i < ordered_events.size(); i++) {
344 UnfoldingEvent* e = ordered_events[i];
347 for (auto* e_hist : history) {
348 // Unlike the test above, we DO want to ensure
349 // that `e` itself ALSO isn't yet seen
351 // If this event has been "seen" before,
352 // this implies that event `e` appears later
353 // in the list than one of its ancestors
354 REQUIRE_FALSE(events_seen.contains(e_hist));
356 events_seen.insert(e);