xref: /llvm-project/mlir/unittests/Debug/ExecutionContextTest.cpp (revision ad09cd3f0dad5d34b803c85c94c048a2f6a89d0d)
1fa51c175SMehdi Amini //===- ExecutionContextTest.cpp - Debug Execution Context first impl ------===//
2fa51c175SMehdi Amini //
3fa51c175SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fa51c175SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
5fa51c175SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fa51c175SMehdi Amini //
7fa51c175SMehdi Amini //===----------------------------------------------------------------------===//
8fa51c175SMehdi Amini 
9fa51c175SMehdi Amini #include "mlir/Debug/ExecutionContext.h"
10fa51c175SMehdi Amini #include "mlir/Debug/BreakpointManagers/TagBreakpointManager.h"
11fa51c175SMehdi Amini #include "llvm/ADT/MapVector.h"
12fa51c175SMehdi Amini #include "gmock/gmock.h"
13fa51c175SMehdi Amini 
14fa51c175SMehdi Amini using namespace mlir;
15fa51c175SMehdi Amini using namespace mlir::tracing;
16fa51c175SMehdi Amini 
17fa51c175SMehdi Amini namespace {
18fa51c175SMehdi Amini struct DebuggerAction : public ActionImpl<DebuggerAction> {
19fa51c175SMehdi Amini   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(DebuggerAction)
20fa51c175SMehdi Amini   static constexpr StringLiteral tag = "debugger-action";
21fa51c175SMehdi Amini };
22fa51c175SMehdi Amini struct OtherAction : public ActionImpl<OtherAction> {
23fa51c175SMehdi Amini   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OtherAction)
24fa51c175SMehdi Amini   static constexpr StringLiteral tag = "other-action";
25fa51c175SMehdi Amini };
26fa51c175SMehdi Amini struct ThirdAction : public ActionImpl<ThirdAction> {
27fa51c175SMehdi Amini   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ThirdAction)
28fa51c175SMehdi Amini   static constexpr StringLiteral tag = "third-action";
29fa51c175SMehdi Amini };
30fa51c175SMehdi Amini 
31fa51c175SMehdi Amini // Simple action that does nothing.
noOp()32*ad09cd3fSAdrian Kuegel void noOp() {}
33fa51c175SMehdi Amini 
34fa51c175SMehdi Amini /// This test executes a stack of nested action and check that the backtrace is
35fa51c175SMehdi Amini /// as expect.
TEST(ExecutionContext,ActionActiveStackTest)36fa51c175SMehdi Amini TEST(ExecutionContext, ActionActiveStackTest) {
37fa51c175SMehdi Amini 
38fa51c175SMehdi Amini   // We'll break three time, once on each action, the backtraces should match
39fa51c175SMehdi Amini   // each of the entries here.
40fa51c175SMehdi Amini   std::vector<std::vector<StringRef>> expectedStacks = {
41fa51c175SMehdi Amini       {DebuggerAction::tag},
42fa51c175SMehdi Amini       {OtherAction::tag, DebuggerAction::tag},
43fa51c175SMehdi Amini       {ThirdAction::tag, OtherAction::tag, DebuggerAction::tag}};
44fa51c175SMehdi Amini 
45fa51c175SMehdi Amini   auto checkStacks = [&](const ActionActiveStack *backtrace,
46fa51c175SMehdi Amini                          const std::vector<StringRef> &currentStack) {
47fa51c175SMehdi Amini     ASSERT_EQ((int)currentStack.size(), backtrace->getDepth() + 1);
48fa51c175SMehdi Amini     for (StringRef stackEntry : currentStack) {
49fa51c175SMehdi Amini       ASSERT_NE(backtrace, nullptr);
50fa51c175SMehdi Amini       ASSERT_EQ(stackEntry, backtrace->getAction().getTag());
51fa51c175SMehdi Amini       backtrace = backtrace->getParent();
52fa51c175SMehdi Amini     }
53fa51c175SMehdi Amini   };
54fa51c175SMehdi Amini 
55fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
56fa51c175SMehdi Amini       ExecutionContext::Step, ExecutionContext::Step, ExecutionContext::Apply};
57fa51c175SMehdi Amini   int idx = 0;
58fa51c175SMehdi Amini   StringRef current;
59fa51c175SMehdi Amini   int currentDepth = -1;
60fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
61fa51c175SMehdi Amini     current = backtrace->getAction().getTag();
62fa51c175SMehdi Amini     currentDepth = backtrace->getDepth();
63fa51c175SMehdi Amini     checkStacks(backtrace, expectedStacks[idx]);
64fa51c175SMehdi Amini     return controlSequence[idx++];
65fa51c175SMehdi Amini   };
66fa51c175SMehdi Amini 
67fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
68fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
69fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
70fa51c175SMehdi Amini   std::vector<TagBreakpoint *> breakpoints;
71fa51c175SMehdi Amini   breakpoints.push_back(simpleManager.addBreakpoint(DebuggerAction::tag));
72fa51c175SMehdi Amini   breakpoints.push_back(simpleManager.addBreakpoint(OtherAction::tag));
73fa51c175SMehdi Amini   breakpoints.push_back(simpleManager.addBreakpoint(ThirdAction::tag));
74fa51c175SMehdi Amini 
75fa51c175SMehdi Amini   auto third = [&]() {
76fa51c175SMehdi Amini     EXPECT_EQ(current, ThirdAction::tag);
77fa51c175SMehdi Amini     EXPECT_EQ(currentDepth, 2);
78fa51c175SMehdi Amini   };
79fa51c175SMehdi Amini   auto nested = [&]() {
80fa51c175SMehdi Amini     EXPECT_EQ(current, OtherAction::tag);
81fa51c175SMehdi Amini     EXPECT_EQ(currentDepth, 1);
82fa51c175SMehdi Amini     executionCtx(third, ThirdAction{});
83fa51c175SMehdi Amini   };
84fa51c175SMehdi Amini   auto original = [&]() {
85fa51c175SMehdi Amini     EXPECT_EQ(current, DebuggerAction::tag);
86fa51c175SMehdi Amini     EXPECT_EQ(currentDepth, 0);
87fa51c175SMehdi Amini     executionCtx(nested, OtherAction{});
88fa51c175SMehdi Amini     return;
89fa51c175SMehdi Amini   };
90fa51c175SMehdi Amini 
91fa51c175SMehdi Amini   executionCtx(original, DebuggerAction{});
92fa51c175SMehdi Amini }
93fa51c175SMehdi Amini 
TEST(ExecutionContext,DebuggerTest)94fa51c175SMehdi Amini TEST(ExecutionContext, DebuggerTest) {
95fa51c175SMehdi Amini   // Check matching and non matching breakpoints, with various enable/disable
96fa51c175SMehdi Amini   // schemes.
97fa51c175SMehdi Amini   int match = 0;
98fa51c175SMehdi Amini   auto onBreakpoint = [&match](const ActionActiveStack *backtrace) {
99fa51c175SMehdi Amini     match++;
100fa51c175SMehdi Amini     return ExecutionContext::Skip;
101fa51c175SMehdi Amini   };
102fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
103fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
104fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
105fa51c175SMehdi Amini 
106fa51c175SMehdi Amini   executionCtx(noOp, DebuggerAction{});
107fa51c175SMehdi Amini   EXPECT_EQ(match, 0);
108fa51c175SMehdi Amini 
109fa51c175SMehdi Amini   Breakpoint *dbgBreakpoint = simpleManager.addBreakpoint(DebuggerAction::tag);
110fa51c175SMehdi Amini   executionCtx(noOp, DebuggerAction{});
111fa51c175SMehdi Amini   EXPECT_EQ(match, 1);
112fa51c175SMehdi Amini 
113fa51c175SMehdi Amini   dbgBreakpoint->disable();
114fa51c175SMehdi Amini   executionCtx(noOp, DebuggerAction{});
115fa51c175SMehdi Amini   EXPECT_EQ(match, 1);
116fa51c175SMehdi Amini 
117fa51c175SMehdi Amini   dbgBreakpoint->enable();
118fa51c175SMehdi Amini   executionCtx(noOp, DebuggerAction{});
119fa51c175SMehdi Amini   EXPECT_EQ(match, 2);
120fa51c175SMehdi Amini 
121fa51c175SMehdi Amini   executionCtx(noOp, OtherAction{});
122fa51c175SMehdi Amini   EXPECT_EQ(match, 2);
123fa51c175SMehdi Amini }
124fa51c175SMehdi Amini 
TEST(ExecutionContext,ApplyTest)125fa51c175SMehdi Amini TEST(ExecutionContext, ApplyTest) {
126fa51c175SMehdi Amini   // Test the "apply" control.
127fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag};
128fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
129fa51c175SMehdi Amini       ExecutionContext::Apply};
130fa51c175SMehdi Amini   int idx = 0, counter = 0;
131fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
132fa51c175SMehdi Amini     ++counter;
133fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
134fa51c175SMehdi Amini     return controlSequence[idx++];
135fa51c175SMehdi Amini   };
136fa51c175SMehdi Amini   auto callback = [&]() { EXPECT_EQ(counter, 1); };
137fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
138fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
139fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
140fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
141fa51c175SMehdi Amini 
142fa51c175SMehdi Amini   executionCtx(callback, DebuggerAction{});
143fa51c175SMehdi Amini   EXPECT_EQ(counter, 1);
144fa51c175SMehdi Amini }
145fa51c175SMehdi Amini 
TEST(ExecutionContext,SkipTest)146fa51c175SMehdi Amini TEST(ExecutionContext, SkipTest) {
147fa51c175SMehdi Amini   // Test the "skip" control.
148fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag,
149fa51c175SMehdi Amini                                         DebuggerAction::tag};
150fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
151fa51c175SMehdi Amini       ExecutionContext::Apply, ExecutionContext::Skip};
152fa51c175SMehdi Amini   int idx = 0, counter = 0, executionCounter = 0;
153fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
154fa51c175SMehdi Amini     ++counter;
155fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
156fa51c175SMehdi Amini     return controlSequence[idx++];
157fa51c175SMehdi Amini   };
158fa51c175SMehdi Amini   auto callback = [&]() { ++executionCounter; };
159fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
160fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
161fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
162fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
163fa51c175SMehdi Amini 
164fa51c175SMehdi Amini   executionCtx(callback, DebuggerAction{});
165fa51c175SMehdi Amini   executionCtx(callback, DebuggerAction{});
166fa51c175SMehdi Amini   EXPECT_EQ(counter, 2);
167fa51c175SMehdi Amini   EXPECT_EQ(executionCounter, 1);
168fa51c175SMehdi Amini }
169fa51c175SMehdi Amini 
TEST(ExecutionContext,StepApplyTest)170fa51c175SMehdi Amini TEST(ExecutionContext, StepApplyTest) {
171fa51c175SMehdi Amini   // Test the "step" control with a nested action.
172fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag, OtherAction::tag};
173fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
174fa51c175SMehdi Amini       ExecutionContext::Step, ExecutionContext::Apply};
175fa51c175SMehdi Amini   int idx = 0, counter = 0;
176fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
177fa51c175SMehdi Amini     ++counter;
178fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
179fa51c175SMehdi Amini     return controlSequence[idx++];
180fa51c175SMehdi Amini   };
181fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
182fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
183fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
184fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
185fa51c175SMehdi Amini   auto nested = [&]() { EXPECT_EQ(counter, 2); };
186fa51c175SMehdi Amini   auto original = [&]() {
187fa51c175SMehdi Amini     EXPECT_EQ(counter, 1);
188fa51c175SMehdi Amini     executionCtx(nested, OtherAction{});
189fa51c175SMehdi Amini   };
190fa51c175SMehdi Amini 
191fa51c175SMehdi Amini   executionCtx(original, DebuggerAction{});
192fa51c175SMehdi Amini   EXPECT_EQ(counter, 2);
193fa51c175SMehdi Amini }
194fa51c175SMehdi Amini 
TEST(ExecutionContext,StepNothingInsideTest)195fa51c175SMehdi Amini TEST(ExecutionContext, StepNothingInsideTest) {
196fa51c175SMehdi Amini   // Test the "step" control without a nested action.
197fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag,
198fa51c175SMehdi Amini                                         DebuggerAction::tag};
199fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
200fa51c175SMehdi Amini       ExecutionContext::Step, ExecutionContext::Step};
201fa51c175SMehdi Amini   int idx = 0, counter = 0;
202fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
203fa51c175SMehdi Amini     ++counter;
204fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
205fa51c175SMehdi Amini     return controlSequence[idx++];
206fa51c175SMehdi Amini   };
207fa51c175SMehdi Amini   auto callback = [&]() { EXPECT_EQ(counter, 1); };
208fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
209fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
210fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
211fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
212fa51c175SMehdi Amini 
213fa51c175SMehdi Amini   executionCtx(callback, DebuggerAction{});
214fa51c175SMehdi Amini   EXPECT_EQ(counter, 2);
215fa51c175SMehdi Amini }
216fa51c175SMehdi Amini 
TEST(ExecutionContext,NextTest)217fa51c175SMehdi Amini TEST(ExecutionContext, NextTest) {
218fa51c175SMehdi Amini   // Test the "next" control.
219fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag,
220fa51c175SMehdi Amini                                         DebuggerAction::tag};
221fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
222fa51c175SMehdi Amini       ExecutionContext::Next, ExecutionContext::Next};
223fa51c175SMehdi Amini   int idx = 0, counter = 0;
224fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
225fa51c175SMehdi Amini     ++counter;
226fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
227fa51c175SMehdi Amini     return controlSequence[idx++];
228fa51c175SMehdi Amini   };
229fa51c175SMehdi Amini   auto callback = [&]() { EXPECT_EQ(counter, 1); };
230fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
231fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
232fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
233fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
234fa51c175SMehdi Amini 
235fa51c175SMehdi Amini   executionCtx(callback, DebuggerAction{});
236fa51c175SMehdi Amini   EXPECT_EQ(counter, 2);
237fa51c175SMehdi Amini }
238fa51c175SMehdi Amini 
TEST(ExecutionContext,FinishTest)239fa51c175SMehdi Amini TEST(ExecutionContext, FinishTest) {
240fa51c175SMehdi Amini   // Test the "finish" control.
241fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag, OtherAction::tag,
242fa51c175SMehdi Amini                                         DebuggerAction::tag};
243fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
244fa51c175SMehdi Amini       ExecutionContext::Step, ExecutionContext::Finish,
245fa51c175SMehdi Amini       ExecutionContext::Apply};
246fa51c175SMehdi Amini   int idx = 0, counter = 0;
247fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
248fa51c175SMehdi Amini     ++counter;
249fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
250fa51c175SMehdi Amini     return controlSequence[idx++];
251fa51c175SMehdi Amini   };
252fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
253fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
254fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
255fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
256fa51c175SMehdi Amini   auto nested = [&]() { EXPECT_EQ(counter, 2); };
257fa51c175SMehdi Amini   auto original = [&]() {
258fa51c175SMehdi Amini     EXPECT_EQ(counter, 1);
259fa51c175SMehdi Amini     executionCtx(nested, OtherAction{});
260fa51c175SMehdi Amini     EXPECT_EQ(counter, 2);
261fa51c175SMehdi Amini   };
262fa51c175SMehdi Amini 
263fa51c175SMehdi Amini   executionCtx(original, DebuggerAction{});
264fa51c175SMehdi Amini   EXPECT_EQ(counter, 3);
265fa51c175SMehdi Amini }
266fa51c175SMehdi Amini 
TEST(ExecutionContext,FinishBreakpointInNestedTest)267fa51c175SMehdi Amini TEST(ExecutionContext, FinishBreakpointInNestedTest) {
268fa51c175SMehdi Amini   // Test the "finish" control with a breakpoint in the nested action.
269fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {OtherAction::tag, DebuggerAction::tag};
270fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
271fa51c175SMehdi Amini       ExecutionContext::Finish, ExecutionContext::Apply};
272fa51c175SMehdi Amini   int idx = 0, counter = 0;
273fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
274fa51c175SMehdi Amini     ++counter;
275fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
276fa51c175SMehdi Amini     return controlSequence[idx++];
277fa51c175SMehdi Amini   };
278fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
279fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
280fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
281fa51c175SMehdi Amini   simpleManager.addBreakpoint(OtherAction::tag);
282fa51c175SMehdi Amini 
283fa51c175SMehdi Amini   auto nested = [&]() { EXPECT_EQ(counter, 1); };
284fa51c175SMehdi Amini   auto original = [&]() {
285fa51c175SMehdi Amini     EXPECT_EQ(counter, 0);
286fa51c175SMehdi Amini     executionCtx(nested, OtherAction{});
287fa51c175SMehdi Amini     EXPECT_EQ(counter, 1);
288fa51c175SMehdi Amini   };
289fa51c175SMehdi Amini 
290fa51c175SMehdi Amini   executionCtx(original, DebuggerAction{});
291fa51c175SMehdi Amini   EXPECT_EQ(counter, 2);
292fa51c175SMehdi Amini }
293fa51c175SMehdi Amini 
TEST(ExecutionContext,FinishNothingBackTest)294fa51c175SMehdi Amini TEST(ExecutionContext, FinishNothingBackTest) {
295fa51c175SMehdi Amini   // Test the "finish" control without a nested action.
296fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag};
297fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
298fa51c175SMehdi Amini       ExecutionContext::Finish};
299fa51c175SMehdi Amini   int idx = 0, counter = 0;
300fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
301fa51c175SMehdi Amini     ++counter;
302fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
303fa51c175SMehdi Amini     return controlSequence[idx++];
304fa51c175SMehdi Amini   };
305fa51c175SMehdi Amini   auto callback = [&]() { EXPECT_EQ(counter, 1); };
306fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
307fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
308fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
309fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
310fa51c175SMehdi Amini 
311fa51c175SMehdi Amini   executionCtx(callback, DebuggerAction{});
312fa51c175SMehdi Amini   EXPECT_EQ(counter, 1);
313fa51c175SMehdi Amini }
314fa51c175SMehdi Amini 
TEST(ExecutionContext,EnableDisableBreakpointOnCallback)315fa51c175SMehdi Amini TEST(ExecutionContext, EnableDisableBreakpointOnCallback) {
316fa51c175SMehdi Amini   // Test enabling and disabling breakpoints while executing the action.
317fa51c175SMehdi Amini   std::vector<StringRef> tagSequence = {DebuggerAction::tag, ThirdAction::tag,
318fa51c175SMehdi Amini                                         OtherAction::tag, DebuggerAction::tag};
319fa51c175SMehdi Amini   std::vector<ExecutionContext::Control> controlSequence = {
320fa51c175SMehdi Amini       ExecutionContext::Apply, ExecutionContext::Finish,
321fa51c175SMehdi Amini       ExecutionContext::Finish, ExecutionContext::Apply};
322fa51c175SMehdi Amini   int idx = 0, counter = 0;
323fa51c175SMehdi Amini   auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
324fa51c175SMehdi Amini     ++counter;
325fa51c175SMehdi Amini     EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
326fa51c175SMehdi Amini     return controlSequence[idx++];
327fa51c175SMehdi Amini   };
328fa51c175SMehdi Amini 
329fa51c175SMehdi Amini   TagBreakpointManager simpleManager;
330fa51c175SMehdi Amini   ExecutionContext executionCtx(onBreakpoint);
331fa51c175SMehdi Amini   executionCtx.addBreakpointManager(&simpleManager);
332fa51c175SMehdi Amini   simpleManager.addBreakpoint(DebuggerAction::tag);
333fa51c175SMehdi Amini   Breakpoint *toBeDisabled = simpleManager.addBreakpoint(OtherAction::tag);
334fa51c175SMehdi Amini 
335fa51c175SMehdi Amini   auto third = [&]() { EXPECT_EQ(counter, 2); };
336fa51c175SMehdi Amini   auto nested = [&]() {
337fa51c175SMehdi Amini     EXPECT_EQ(counter, 1);
338fa51c175SMehdi Amini     executionCtx(third, ThirdAction{});
339fa51c175SMehdi Amini     EXPECT_EQ(counter, 2);
340fa51c175SMehdi Amini   };
341fa51c175SMehdi Amini   auto original = [&]() {
342fa51c175SMehdi Amini     EXPECT_EQ(counter, 1);
343fa51c175SMehdi Amini     toBeDisabled->disable();
344fa51c175SMehdi Amini     simpleManager.addBreakpoint(ThirdAction::tag);
345fa51c175SMehdi Amini     executionCtx(nested, OtherAction{});
346fa51c175SMehdi Amini     EXPECT_EQ(counter, 3);
347fa51c175SMehdi Amini   };
348fa51c175SMehdi Amini 
349fa51c175SMehdi Amini   executionCtx(original, DebuggerAction{});
350fa51c175SMehdi Amini   EXPECT_EQ(counter, 4);
351fa51c175SMehdi Amini }
352fa51c175SMehdi Amini } // namespace
353