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/odpor/WakeupTree.hpp"
9 #include "src/mc/explo/udpor/udpor_tests_private.hpp"
10 #include "src/xbt/utils/iter/LazyPowerset.hpp"
12 using namespace simgrid::mc;
13 using namespace simgrid::mc::odpor;
14 using namespace simgrid::mc::udpor;
16 static void test_tree_iterator(const WakeupTree& tree, const std::vector<PartialExecution>& expected)
18 auto tree_iter = tree.begin();
19 for (auto expected_iter = expected.begin(); expected_iter != expected.end(); ++expected_iter, ++tree_iter) {
20 REQUIRE(tree_iter != tree.end());
21 REQUIRE((*tree_iter)->get_sequence() == *expected_iter);
25 TEST_CASE("simgrid::mc::odpor::WakeupTree: Testing Insertion for Empty Executions")
27 SECTION("Following an execution")
29 // We imagine the following completed execution `E`
30 // consisting of executing actions a0-a3. Execution
31 // `E` cis the first such maximal execution explored
32 // by ODPOR, which implies that a) all sleep sets are
33 // empty and b) all wakeup trees (i.e. for each prefix) consist of the root
34 // node with a single leaf containing the action
35 // taken, save for the wakeup tree of the execution itself
38 // We first notice that there's a reversible race between
41 const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
42 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
43 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
44 const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
47 execution.push_transition(a0);
48 execution.push_transition(a1);
49 execution.push_transition(a2);
50 execution.push_transition(a3);
52 REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{0});
53 REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{0});
57 SECTION("Attempting to insert the empty sequence into an empty tree should have no effect")
59 tree.insert(Execution(), {});
60 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{}});
63 // First, we initialize the tree to how it looked prior
64 // to the insertion of the race
65 tree.insert(Execution(), {a0});
67 // Then, after insertion, we ensure that the node was
68 // indeed added to the tree
69 tree.insert(Execution(), {a1, a3});
70 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
71 PartialExecution{a1}, PartialExecution{}});
73 SECTION("Attempting to re-insert the same EXACT sequence should have no effect")
75 tree.insert(Execution(), {a1, a3});
76 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
77 PartialExecution{a1}, PartialExecution{}});
80 SECTION("Attempting to re-insert an equivalent sequence should have no effect")
82 // a3 and a1 are interchangeable since `a1` is independent with everything.
83 // Since we found an equivalent sequence that is a leaf, nothing should result
84 tree.insert(Execution(), {a3, a1});
85 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
86 PartialExecution{a1}, PartialExecution{}});
89 SECTION("Attempting to insert the empty sequence should have no effect")
91 tree.insert(Execution(), {});
92 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
93 PartialExecution{a1}, PartialExecution{}});
96 SECTION("Inserting an extension should create a branch point")
98 // `a1.a2` shares the same `a1` prefix as `a1.a3`. Thus, the tree
99 // should now look as follows:
107 // Recall that new nodes (in this case the one with
108 // action `a2`) are added such that they are "greater than" (under
109 // the tree's `<` relation) all those that exist under the given parent
110 tree.insert(Execution(), {a1, a2});
111 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
112 PartialExecution{a1, a2}, PartialExecution{a1},
113 PartialExecution{}});
117 SECTION("Performing Arbitrary Insertions")
119 const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
120 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
121 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
122 const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
123 const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
124 const auto a5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
127 execution.push_transition(a0);
128 execution.push_transition(a1);
129 execution.push_transition(a2);
130 execution.push_transition(a3);
131 execution.push_transition(a4);
132 execution.push_transition(a5);
136 SECTION("Attempting to insert the empty sequence into an empty tree should have no effect")
138 tree.insert(Execution(), {});
139 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{}});
142 SECTION("Attempting to re-insert the same sequence multiple times should have no extra effect")
144 tree.insert(Execution(), {a4});
145 tree.insert(Execution(), {a4});
146 tree.insert(Execution(), {a4});
147 REQUIRE(tree.get_num_nodes() == 2);
148 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a4}, PartialExecution{}});
151 SECTION("Attempting to insert an independent sequence same should have no extra effect")
153 // a4 and a1 are independent actions. Intuitively, then, we need only
154 // search one ordering of the two actions. The wakeup tree handles
155 // this by computing the `~` relation. The relation itself determines
156 // whether the `a1` is an initial of `a3`, which it is not. It then
157 // checks whether `a1` is independent with everything in the sequence
158 // (in this case, consisting only of `a1`) which IS true. Since `a4`
159 // is already a leaf node of the tree, it suffices to only have `a4`
160 // in this tree to guide ODPOR
161 tree.insert(Execution(), {a4});
162 tree.insert(Execution(), {a1});
163 REQUIRE(tree.get_num_nodes() == 2);
164 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a4}, PartialExecution{}});
168 "Attempting to insert a progression of executions should have no extra effect when the first process is a leaf")
170 // All progressions starting with `a0` are effectively already accounted
171 // for by inserting `a0` since we `a0` "can always be made to look like"
172 // (viz. the `~` relation) `a0.*` where `*` is some sequence of actions
173 tree.insert(Execution(), {a0});
174 tree.insert(Execution(), {a0, a3});
175 tree.insert(Execution(), {a0, a3, a2});
176 tree.insert(Execution(), {a0, a3, a2, a4});
177 tree.insert(Execution(), {a0, a3, a2, a4});
178 REQUIRE(tree.get_num_nodes() == 2);
179 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{}});
182 SECTION("Stress test with multiple branch points")
184 tree.insert(Execution(), {a0});
185 tree.insert(Execution(), {a2, a0});
186 tree.insert(Execution(), {a2, a3});
187 tree.insert(Execution(), {a2, a5});
188 REQUIRE(tree.get_num_nodes() == 6);
189 test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a2, a0},
190 PartialExecution{a2, a3}, PartialExecution{a2, a5},
191 PartialExecution{a2}, PartialExecution{}});
196 TEST_CASE("simgrid::mc::odpor::WakeupTree: Testing Subtree Rooting")
198 const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
199 const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
200 const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
201 const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
202 const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
203 const auto a5 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
206 execution.push_transition(a0);
207 execution.push_transition(a1);
208 execution.push_transition(a2);
209 execution.push_transition(a3);
211 REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{0});
212 REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{0});
214 // The wakeup tree should