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/odpor/Execution.hpp"
8 #include "src/mc/explo/udpor/udpor_tests_private.hpp"
9 #include "src/mc/transition/TransitionComm.hpp"
11 using namespace simgrid::mc;
12 using namespace simgrid::mc::odpor;
13 using namespace simgrid::mc::udpor;
15 TEST_CASE("simgrid::mc::odpor::Execution: Constructing Executions")
18 REQUIRE(execution.empty());
19 REQUIRE(execution.size() == 0);
20 REQUIRE_FALSE(execution.get_latest_event_handle().has_value());
23 TEST_CASE("simgrid::mc::odpor::Execution: Testing Happens-Before")
27 // We check each permutation for happens before
28 // among the given actions added to the execution
29 const auto a1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
30 const auto a2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
31 const auto a3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
32 const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 4);
35 execution.push_transition(a1);
36 execution.push_transition(a2);
37 execution.push_transition(a3);
38 execution.push_transition(a4);
40 SECTION("Happens-before is irreflexive")
42 REQUIRE_FALSE(execution.happens_before(0, 0));
43 REQUIRE_FALSE(execution.happens_before(1, 1));
44 REQUIRE_FALSE(execution.happens_before(2, 2));
45 REQUIRE_FALSE(execution.happens_before(3, 3));
48 SECTION("Happens-before values for each pair of events")
50 REQUIRE_FALSE(execution.happens_before(0, 1));
51 REQUIRE_FALSE(execution.happens_before(0, 2));
52 REQUIRE(execution.happens_before(0, 3));
53 REQUIRE_FALSE(execution.happens_before(1, 2));
54 REQUIRE_FALSE(execution.happens_before(1, 3));
55 REQUIRE_FALSE(execution.happens_before(2, 3));
58 SECTION("Happens-before is a subset of 'occurs-before' ")
60 REQUIRE_FALSE(execution.happens_before(1, 0));
61 REQUIRE_FALSE(execution.happens_before(2, 0));
62 REQUIRE_FALSE(execution.happens_before(3, 0));
63 REQUIRE_FALSE(execution.happens_before(2, 1));
64 REQUIRE_FALSE(execution.happens_before(3, 1));
65 REQUIRE_FALSE(execution.happens_before(3, 2));
71 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
72 const auto a2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
73 const auto a3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
74 const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
76 // Notice that `a5` and `a6` are executed by the same actor; thus, although
77 // the actor is executing independent actions, each still "happen-before"
81 execution.push_transition(a1);
82 execution.push_transition(a2);
83 execution.push_transition(a3);
84 execution.push_transition(a4);
86 SECTION("Happens-before is irreflexive")
88 REQUIRE_FALSE(execution.happens_before(0, 0));
89 REQUIRE_FALSE(execution.happens_before(1, 1));
90 REQUIRE_FALSE(execution.happens_before(2, 2));
91 REQUIRE_FALSE(execution.happens_before(3, 3));
94 SECTION("Happens-before values for each pair of events")
96 REQUIRE_FALSE(execution.happens_before(0, 1));
97 REQUIRE_FALSE(execution.happens_before(0, 2));
98 REQUIRE_FALSE(execution.happens_before(0, 3));
99 REQUIRE(execution.happens_before(1, 2));
100 REQUIRE(execution.happens_before(1, 3));
101 REQUIRE(execution.happens_before(2, 3));
104 SECTION("Happens-before is a subset of 'occurs-before'")
106 REQUIRE_FALSE(execution.happens_before(1, 0));
107 REQUIRE_FALSE(execution.happens_before(2, 0));
108 REQUIRE_FALSE(execution.happens_before(3, 0));
109 REQUIRE_FALSE(execution.happens_before(2, 1));
110 REQUIRE_FALSE(execution.happens_before(3, 1));
111 REQUIRE_FALSE(execution.happens_before(3, 2));
115 SECTION("Example 3: Happens-before is transitively-closed")
117 // Given a reversible race between events `e1` and `e3` in a simulation,
118 // we assert that `e5` would be eliminated from being contained in
119 // the sequence `notdep(e1, E)`
120 const auto e0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
121 const auto e1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
122 const auto e2 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
123 const auto e3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
124 const auto e4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
127 execution.push_transition(e0);
128 execution.push_transition(e1);
129 execution.push_transition(e2);
130 execution.push_transition(e3);
131 execution.push_transition(e4);
133 REQUIRE(execution.happens_before(0, 2));
134 REQUIRE(execution.happens_before(2, 4));
135 REQUIRE(execution.happens_before(0, 4));
139 TEST_CASE("simgrid::mc::odpor::Execution: Testing Racing Events and Initials")
143 const auto a1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
144 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
145 const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
146 const auto a4 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
147 const auto a5 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
150 execution.push_transition(a1);
151 execution.push_transition(a2);
152 execution.push_transition(a3);
153 execution.push_transition(a4);
154 execution.push_transition(a5);
156 // Nothing comes before event 0
157 REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
159 // Events 0 and 1 are independent
160 REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
162 // 2 and 1 are executed by different actors and happen right after each other
163 REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{1});
165 // Although a3 and a4 are dependent, they are executed by the same actor
166 REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{});
168 // Event 4 is in a race with neither event 0 nor event 2 since those events
169 // "happen-before" event 3 with which event 4 races
171 // Furthermore, event 1 is run by the same actor and thus also is not in a race.
172 // Hence, only event 3 races with event 4
173 REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{3});
176 SECTION("Example 2: Events with multiple races")
178 const auto a1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
179 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
180 const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
181 const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
184 execution.push_transition(a1);
185 execution.push_transition(a2);
186 execution.push_transition(a3);
187 execution.push_transition(a4);
189 // Nothing comes before event 0
190 REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
192 // Events 0 and 1 are independent
193 REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
195 // Event 2 is independent with event 1 and run by the same actor as event 0
196 REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{});
198 // All events are dependent with event 3, but event 0 "happens-before" event 2
199 REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{1, 2});
201 SECTION("Check initials with respect to event 1")
203 // First, note that v := notdep(1, execution).p == {e2}.{e3} == {e2, e3}
204 // Since e2 -->_E e3, actor 3 is not an initial for E' := pre(1, execution)
205 // with respect to `v`. e2, however, has nothing happening before it in dom_E(v),
206 // so it is an initial of E' wrt. `v`
207 const auto initial_wrt_event1 = execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{});
208 REQUIRE(initial_wrt_event1 == std::unordered_set<aid_t>{1});
211 SECTION("Check initials with respect to event 2")
213 // First, note that v := notdep(1, execution).p == {}.{e3} == {e3}
214 // e3 has nothing happening before it in dom_E(v), so it is an initial
216 const auto initial_wrt_event2 = execution.get_missing_source_set_actors_from(2, std::unordered_set<aid_t>{});
217 REQUIRE(initial_wrt_event2 == std::unordered_set<aid_t>{3});
221 SECTION("Example 3: Testing 'Lock' Example")
223 // In this example, `e0` and `e1` are lock actions that
224 // are in a race. We assert that
225 const auto e0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
226 const auto e1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
227 const auto e2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
228 const auto e3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
229 const auto e4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
232 execution.push_transition(e0);
233 execution.push_transition(e1);
234 execution.push_transition(e2);
235 execution.push_transition(e3);
236 execution.push_transition(e4);
237 REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{0});
240 SECTION("Example 4: Indirect Races")
242 const auto a0 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
243 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
244 const auto a2 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
245 const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
246 const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 6);
247 const auto a5 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
248 const auto a6 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
249 const auto a7 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
250 const auto a8 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
251 const auto a9 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
254 execution.push_transition(a0);
255 execution.push_transition(a1);
256 execution.push_transition(a2);
257 execution.push_transition(a3);
258 execution.push_transition(a4);
259 execution.push_transition(a5);
260 execution.push_transition(a6);
261 execution.push_transition(a7);
262 execution.push_transition(a8);
263 execution.push_transition(a9);
265 // Nothing comes before event 0
266 REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
268 // Events 0 and 1 are independent
269 REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
271 // Events 1 and 2 are independent
272 REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{});
274 // Events 1 and 3 are independent; the rest are executed by the same actor
275 REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{});
277 // 1. Events 3 and 4 race
278 // 2. Events 2 and 4 do NOT race since 2 --> 3 --> 4
279 // 3. Events 1 and 4 do NOT race since 1 is independent of 4
280 // 4. Events 0 and 4 do NOT race since 0 --> 2 --> 4
281 REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{3});
283 // Events 4 and 5 race; and because everyone before 4 (including 3) either
284 // a) happens-before, b) races, or c) does not race with 4, 4 is the race
285 REQUIRE(execution.get_racing_events_of(5) == std::unordered_set<Execution::EventHandle>{4});
287 // The same logic that applied to event 5 applies to event 6
288 REQUIRE(execution.get_racing_events_of(6) == std::unordered_set<Execution::EventHandle>{5});
290 // The same logic applies, except that this time since events 6 and 7 are run
291 // by the same actor, they don'tt actually race with one another
292 REQUIRE(execution.get_racing_events_of(7) == std::unordered_set<Execution::EventHandle>{});
294 // Event 8 is independent with everything
295 REQUIRE(execution.get_racing_events_of(8) == std::unordered_set<Execution::EventHandle>{});
297 // Event 9 is independent with events 7 and 8; event 6, however, is in race with 9.
298 // The same logic above eliminates events before 6
299 REQUIRE(execution.get_racing_events_of(9) == std::unordered_set<Execution::EventHandle>{6});
303 TEST_CASE("simgrid::mc::odpor::Execution: notdep(e, E) Simulation")
305 // Given a reversible race between events `e1` and `e3` in a simulation,
306 // we assert that `e5` would be eliminated from being contained in
307 // the sequence `notdep(e1, E)`
308 const auto e0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
309 const auto e1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
310 const auto e2 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
311 const auto e3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
312 const auto e4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
315 execution.push_transition(e0);
316 execution.push_transition(e1);
317 execution.push_transition(e2);
318 execution.push_transition(e3);
319 execution.push_transition(e4);
321 REQUIRE(execution.happens_before(0, 4));
324 TEST_CASE("simgrid::mc::odpor::Execution: Independence Tests")
326 SECTION("Complete independence")
328 // Every transition is independent with every other and run by different actors. Hopefully
329 // we say that the events are independent with each other...
330 const auto a0 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
331 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
332 const auto a2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
333 const auto a3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
334 const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 5);
335 const auto a5 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 6);
336 const auto a6 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 7);
337 const auto a7 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 7);
339 execution.push_transition(a0);
340 execution.push_transition(a1);
341 execution.push_transition(a2);
342 execution.push_transition(a3);
344 REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a4, a5}, a6));
345 REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a6, a5}, a1));
346 REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a6, a1}, a0));
347 REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a7, a7, a1}, a3));
348 REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a4, a0, a1}, a3));
349 REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a0, a7, a1}, a2));
351 // In this case, we notice that `a6` and `a7` are executed by the same actor.
352 // Hence, a6 cannot be independent with extending the execution with a4.a5.a7.
353 // Notice that we are treating *a6* as the next step of actor 7 (that is what we
354 // supplied as an argument)
355 REQUIRE_FALSE(execution.is_independent_with_execution_of(PartialExecution{a4, a5, a7}, a6));
358 SECTION("Independence is trivial with an empty extension")
360 REQUIRE(Execution().is_independent_with_execution_of(
361 PartialExecution{}, std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1)));
364 SECTION("Dependencies stopping independence from being possible")
366 const auto a0 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
367 const auto a1 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
368 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
369 const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
370 const auto a4 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
371 const auto a5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
372 const auto a6 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
373 const auto a7 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
374 const auto a8 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
375 const auto indep = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
377 execution.push_transition(a0);
378 execution.push_transition(a1);
379 execution.push_transition(a2);
380 execution.push_transition(a3);
382 // We see that although `a4` comes after `a1` with which it is dependent, it
383 // would come before "a6" in the partial execution `w`, so it would not be independent
384 REQUIRE_FALSE(execution.is_independent_with_execution_of(PartialExecution{a5, a6, a7}, a4));
386 // Removing `a6` from the picture, though, gives us the independence we're looking for.
387 REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a5, a7}, a4));
389 // BUT, we we ask about a transition which is run by the same actor, even if they would be
390 // independent otherwise, we again lose independence
391 REQUIRE_FALSE(execution.is_independent_with_execution_of(PartialExecution{a5, a7, a8}, a4));
393 // This is a more interesting case:
394 // `indep` clearly is independent with the rest of the series, even though
395 // there are dependencies among the other events in the partial execution
396 REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a1, a6, a7}, indep));
398 // This ensures that independence is trivial with an empty partial execution
399 REQUIRE(execution.is_independent_with_execution_of(PartialExecution{}, a1));
403 TEST_CASE("simgrid::mc::odpor::Execution: Initials Test")
405 const auto a0 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
406 const auto a1 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
407 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
408 const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
409 const auto a4 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
410 const auto a5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
411 const auto a6 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
412 const auto a7 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
413 const auto a8 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
414 const auto indep = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
416 execution.push_transition(a0);
417 execution.push_transition(a1);
418 execution.push_transition(a2);
419 execution.push_transition(a3);
421 SECTION("Initials trivial with empty executions")
423 // There are no initials for an empty extension since
424 // a requirement is that the actor be contained in the
426 REQUIRE_FALSE(execution.is_initial_after_execution_of(PartialExecution{}, 0));
427 REQUIRE_FALSE(execution.is_initial_after_execution_of(PartialExecution{}, 1));
428 REQUIRE_FALSE(execution.is_initial_after_execution_of(PartialExecution{}, 2));
429 REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{}, 0));
430 REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{}, 1));
431 REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{}, 2));
434 SECTION("The first actor is always an initial")
436 // Even in the case that the action is dependent with every
437 // other, if it is the first action to occur as part of the
438 // extension of the execution sequence, by definition it is
439 // an initial (recall that initials intuitively tell you which
440 // actions can be run starting from an execution `E`; if we claim
441 // to witness `E.w`, then clearly at least the first step of `w` is an initial)
442 REQUIRE(execution.is_initial_after_execution_of(PartialExecution{a3}, a3->aid_));
443 REQUIRE(execution.is_initial_after_execution_of(PartialExecution{a2, a3, a6}, a2->aid_));
444 REQUIRE(execution.is_initial_after_execution_of(PartialExecution{a6, a1, a0}, a6->aid_));
445 REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a0, a1, a2, a3}, a0->aid_));
446 REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a5, a2, a8, a7, a6, a0}, a5->aid_));
447 REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a8, a7, a1}, a8->aid_));
450 SECTION("Example: Disqualified and re-qualified after switching ordering")
452 // Even though actor `2` executes an independent
453 // transition later on, it is blocked since its first transition
454 // is dependent with actor 1's transition
455 REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{a1, a5, indep}, 2));
457 // However, if actor 2 executes its independent action first, it DOES become an initial
458 REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a1, indep, a5}, 2));
461 SECTION("Example: Large partial executions")
463 // The record trace is `1 3 4 4 3 1 4 2`
465 // Actor 1 starts the execution
466 REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 1));
468 // Actor 2 all the way at the end is independent with everybody: despite
469 // the tangle that comes before it, we can more it to the front with no issue
470 REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 2));
472 // Actor 3 is eliminated since `a1` is dependent with `a2`
473 REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 3));
475 // Likewise with actor 4
476 REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 4));
480 TEST_CASE("simgrid::mc::odpor::Execution: ODPOR Smallest Sequence Tests")
482 const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
483 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
484 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
485 const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
486 const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
487 const auto a5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
490 execution.push_transition(a0);
491 execution.push_transition(a1);
492 execution.push_transition(a2);
493 execution.push_transition(a3);
494 execution.push_transition(a4);
495 execution.push_transition(a5);
497 SECTION("Equivalent insertions")
499 SECTION("Example: Eliminated through independence")
501 // TODO: Is this even a sensible question to ask if the two sequences
502 // don't agree upon what each actor executed after `E`?
503 const auto insertion =
504 Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a2}, PartialExecution{a2, a5});
505 REQUIRE(insertion.has_value());
506 REQUIRE(insertion.value() == PartialExecution{});
509 SECTION("Exact match yields empty set")
511 const auto insertion =
512 Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a2}, PartialExecution{a1, a2});
513 REQUIRE(insertion.has_value());
514 REQUIRE(insertion.value() == PartialExecution{});
518 SECTION("Match against empty executions")
520 SECTION("Empty `v` trivially yields `w`")
523 Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{}, PartialExecution{a1, a5, a2});
524 REQUIRE(insertion.has_value());
525 REQUIRE(insertion.value() == PartialExecution{a1, a5, a2});
527 insertion = execution.get_shortest_odpor_sq_subset_insertion(PartialExecution{}, PartialExecution{a1, a5, a2});
528 REQUIRE(insertion.has_value());
529 REQUIRE(insertion.value() == PartialExecution{a1, a5, a2});
532 SECTION("Empty `w` yields empty execution")
535 Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a4, a5}, PartialExecution{});
536 REQUIRE(insertion.has_value());
537 REQUIRE(insertion.value() == PartialExecution{});
539 insertion = execution.get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a5, a2}, PartialExecution{});
540 REQUIRE(insertion.has_value());
541 REQUIRE(insertion.value() == PartialExecution{});