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};
28 UnfoldingEvent e5{&e3};
30 SECTION("Creating a configuration without events")
33 REQUIRE(C.get_events().empty());
34 REQUIRE(C.get_latest_event() == nullptr);
37 SECTION("Creating a configuration with events")
39 // 5 choose 0 = 1 test
40 REQUIRE_NOTHROW(Configuration({&e1}));
42 // 5 choose 1 = 5 tests
43 REQUIRE_THROWS_AS(Configuration({&e2}), std::invalid_argument);
44 REQUIRE_THROWS_AS(Configuration({&e3}), std::invalid_argument);
45 REQUIRE_THROWS_AS(Configuration({&e4}), std::invalid_argument);
46 REQUIRE_THROWS_AS(Configuration({&e5}), std::invalid_argument);
48 // 5 choose 2 = 10 tests
49 REQUIRE_NOTHROW(Configuration({&e1, &e2}));
50 REQUIRE_THROWS_AS(Configuration({&e1, &e3}), std::invalid_argument);
51 REQUIRE_THROWS_AS(Configuration({&e1, &e4}), std::invalid_argument);
52 REQUIRE_THROWS_AS(Configuration({&e1, &e5}), std::invalid_argument);
53 REQUIRE_THROWS_AS(Configuration({&e2, &e3}), std::invalid_argument);
54 REQUIRE_THROWS_AS(Configuration({&e2, &e4}), std::invalid_argument);
55 REQUIRE_THROWS_AS(Configuration({&e2, &e5}), std::invalid_argument);
56 REQUIRE_THROWS_AS(Configuration({&e3, &e4}), std::invalid_argument);
57 REQUIRE_THROWS_AS(Configuration({&e3, &e5}), std::invalid_argument);
58 REQUIRE_THROWS_AS(Configuration({&e4, &e5}), std::invalid_argument);
60 // 5 choose 3 = 10 tests
61 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}));
62 REQUIRE_THROWS_AS(Configuration({&e1, &e2, &e4}), std::invalid_argument);
63 REQUIRE_THROWS_AS(Configuration({&e1, &e2, &e5}), std::invalid_argument);
64 REQUIRE_THROWS_AS(Configuration({&e1, &e3, &e4}), std::invalid_argument);
65 REQUIRE_THROWS_AS(Configuration({&e1, &e3, &e5}), std::invalid_argument);
66 REQUIRE_THROWS_AS(Configuration({&e1, &e4, &e5}), std::invalid_argument);
67 REQUIRE_THROWS_AS(Configuration({&e2, &e3, &e4}), std::invalid_argument);
68 REQUIRE_THROWS_AS(Configuration({&e2, &e3, &e5}), std::invalid_argument);
69 REQUIRE_THROWS_AS(Configuration({&e2, &e4, &e5}), std::invalid_argument);
70 REQUIRE_THROWS_AS(Configuration({&e3, &e4, &e5}), std::invalid_argument);
72 // 5 choose 4 = 5 tests
73 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}));
74 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e5}));
75 REQUIRE_THROWS_AS(Configuration({&e1, &e2, &e4, &e5}), std::invalid_argument);
76 REQUIRE_THROWS_AS(Configuration({&e1, &e3, &e4, &e5}), std::invalid_argument);
77 REQUIRE_THROWS_AS(Configuration({&e2, &e3, &e4, &e5}), std::invalid_argument);
79 // 5 choose 5 = 1 test
80 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4, &e5}));
84 TEST_CASE("simgrid::mc::udpor::Configuration: Adding Events")
86 // The following tests concern the given event structure:
94 UnfoldingEvent e2{&e1};
95 UnfoldingEvent e3{&e2};
96 UnfoldingEvent e4{&e2};
98 REQUIRE_THROWS_AS(Configuration().add_event(nullptr), std::invalid_argument);
99 REQUIRE_THROWS_AS(Configuration().add_event(&e2), std::invalid_argument);
100 REQUIRE_THROWS_AS(Configuration().add_event(&e3), std::invalid_argument);
101 REQUIRE_THROWS_AS(Configuration().add_event(&e4), std::invalid_argument);
102 REQUIRE_THROWS_AS(Configuration({&e1}).add_event(&e3), std::invalid_argument);
103 REQUIRE_THROWS_AS(Configuration({&e1}).add_event(&e4), std::invalid_argument);
105 REQUIRE_NOTHROW(Configuration().add_event(&e1));
106 REQUIRE_NOTHROW(Configuration({&e1}).add_event(&e1));
107 REQUIRE_NOTHROW(Configuration({&e1}).add_event(&e2));
108 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e1));
109 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e2));
110 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e3));
111 REQUIRE_NOTHROW(Configuration({&e1, &e2}).add_event(&e4));
112 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e1));
113 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e2));
114 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e3));
115 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3}).add_event(&e4));
116 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e1));
117 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e2));
118 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e3));
119 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e4}).add_event(&e4));
120 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e1));
121 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e2));
122 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e3));
123 REQUIRE_NOTHROW(Configuration({&e1, &e2, &e3, &e4}).add_event(&e4));
126 TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order")
128 // The following tests concern the given event structure:
137 UnfoldingEvent e2{&e1};
138 UnfoldingEvent e3{&e2};
139 UnfoldingEvent e4{&e3};
141 SECTION("Topological ordering for entire set")
143 Configuration C{&e1, &e2, &e3, &e4};
144 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4});
145 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e4, &e3, &e2, &e1});
148 SECTION("Topological ordering for subsets")
150 SECTION("No elements")
153 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{});
154 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{});
159 Configuration C{&e1};
160 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1});
161 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e1});
164 SECTION("e1 and e2 only")
166 Configuration C{&e1, &e2};
167 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2});
168 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e2, &e1});
171 SECTION("e1, e2, and e3 only")
173 Configuration C{&e1, &e2, &e3};
174 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3});
175 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e3, &e2, &e1});
180 TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order More Complicated")
182 // The following tests concern the given event structure:
193 UnfoldingEvent e2{&e1};
194 UnfoldingEvent e3{&e2};
195 UnfoldingEvent e4{&e3};
196 UnfoldingEvent e5{&e4};
197 UnfoldingEvent e6{&e3};
199 SECTION("Topological ordering for subsets")
201 SECTION("No elements")
204 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{});
205 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{});
210 Configuration C{&e1};
211 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1});
212 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e1});
215 SECTION("e1 and e2 only")
217 Configuration C{&e1, &e2};
218 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2});
219 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e2, &e1});
222 SECTION("e1, e2, and e3 only")
224 Configuration C{&e1, &e2, &e3};
225 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3});
226 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e3, &e2, &e1});
229 SECTION("e1, e2, e3, and e6 only")
231 Configuration C{&e1, &e2, &e3, &e6};
232 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e6});
233 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e6, &e3, &e2, &e1});
236 SECTION("e1, e2, e3, and e4 only")
238 Configuration C{&e1, &e2, &e3, &e4};
239 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4});
240 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() == std::vector<UnfoldingEvent*>{&e4, &e3, &e2, &e1});
243 SECTION("e1, e2, e3, e4, and e5 only")
245 Configuration C{&e1, &e2, &e3, &e4, &e5};
246 REQUIRE(C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e5});
247 REQUIRE(C.get_topologically_sorted_events_of_reverse_graph() ==
248 std::vector<UnfoldingEvent*>{&e5, &e4, &e3, &e2, &e1});
251 SECTION("e1, e2, e3, e4 and e6 only")
253 // In this case, e4 and e6 are interchangeable. Hence, we have to check
254 // if the sorting gives us *any* of the combinations
255 Configuration C{&e1, &e2, &e3, &e4, &e6};
256 REQUIRE((C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e6} ||
257 C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e6, &e4}));
258 REQUIRE((C.get_topologically_sorted_events_of_reverse_graph() ==
259 std::vector<UnfoldingEvent*>{&e6, &e4, &e3, &e2, &e1} ||
260 C.get_topologically_sorted_events_of_reverse_graph() ==
261 std::vector<UnfoldingEvent*>{&e4, &e6, &e3, &e2, &e1}));
264 SECTION("Topological ordering for entire set")
266 // In this case, e4/e5 are both interchangeable with e6. Hence, again we have to check
267 // if the sorting gives us *any* of the combinations
268 Configuration C{&e1, &e2, &e3, &e4, &e5, &e6};
269 REQUIRE((C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e5, &e6} ||
270 C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e4, &e6, &e5} ||
271 C.get_topologically_sorted_events() == std::vector<UnfoldingEvent*>{&e1, &e2, &e3, &e6, &e4, &e5}));
272 REQUIRE((C.get_topologically_sorted_events_of_reverse_graph() ==
273 std::vector<UnfoldingEvent*>{&e6, &e5, &e4, &e3, &e2, &e1} ||
274 C.get_topologically_sorted_events_of_reverse_graph() ==
275 std::vector<UnfoldingEvent*>{&e5, &e6, &e4, &e3, &e2, &e1} ||
276 C.get_topologically_sorted_events_of_reverse_graph() ==
277 std::vector<UnfoldingEvent*>{&e5, &e4, &e6, &e3, &e2, &e1}));
282 TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order Very Complicated")
284 // The following tests concern the given event structure:
298 UnfoldingEvent e2{&e1};
299 UnfoldingEvent e8{&e1};
300 UnfoldingEvent e3{&e2};
301 UnfoldingEvent e4{&e3};
302 UnfoldingEvent e5{&e4};
303 UnfoldingEvent e6{&e4};
304 UnfoldingEvent e7{&e2, &e8};
305 UnfoldingEvent e9{&e6, &e7};
306 UnfoldingEvent e10{&e7};
307 UnfoldingEvent e11{&e8};
308 UnfoldingEvent e12{&e5, &e9, &e10};
309 Configuration C{&e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12};
311 SECTION("Test every combination of the maximal configuration (forward graph)")
313 // To test this, we ensure that for the `i`th event
314 // in `ordered_events`, each event in `ordered_events[0...<i]
315 // is contained in the history of `ordered_events[i]`.
316 EventSet events_seen;
317 const auto ordered_events = C.get_topologically_sorted_events();
319 std::for_each(ordered_events.begin(), ordered_events.end(), [&events_seen](UnfoldingEvent* e) {
321 for (auto* e_hist : history) {
322 // In this demo, we want to make sure that
323 // we don't mark not yet seeing `e` as an error.
324 // The history of `e` traverses `e` itself. All
325 // other events in e's history are included in
329 // If this event has not been "seen" before,
330 // this implies that event `e` appears earlier
331 // in the list than one of its dependencies
332 REQUIRE(events_seen.contains(e_hist));
334 events_seen.insert(e);
338 SECTION("Test every combination of the maximal configuration (reverse graph)")
340 // To test this, we ensure that for the `i`th event
341 // in `ordered_events`, no event in `ordered_events[0...<i]
342 // is contained in the history of `ordered_events[i]`.
343 EventSet events_seen;
344 const auto ordered_events = C.get_topologically_sorted_events_of_reverse_graph();
346 std::for_each(ordered_events.begin(), ordered_events.end(), [&events_seen](UnfoldingEvent* e) {
349 for (auto* e_hist : history) {
350 // Unlike the test above, we DO want to ensure
351 // that `e` itself ALSO isn't yet seen
353 // If this event has been "seen" before,
354 // this implies that event `e` appears later
355 // in the list than one of its ancestors
356 REQUIRE_FALSE(events_seen.contains(e_hist));
358 events_seen.insert(e);