• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "combined_event_loop.h"
17 #include "expect_pauses.h"
18 #include "inspector_test_base.h"
19 #include "instruction_pointer.h"
20 #include "json_object_matcher.h"
21 #include "test_frame.h"
22 #include "test_method.h"
23 
24 #include "utils/json_builder.h"
25 #include "utils/json_parser.h"
26 #include "utils/utf.h"
27 
28 #include "gtest/gtest.h"
29 #include "gmock/gmock.h"
30 
31 #include <cstddef>
32 #include <optional>
33 #include <string>
34 #include <utility>
35 
36 using testing::_;
37 using testing::Matcher;
38 
39 namespace panda::tooling::inspector::test {
40 class BreakpointTest : public InspectorTestBase {
41 protected:
SetUpSourceFiles()42     void SetUpSourceFiles() override
43     {
44         auto klass = LoadSourceFile(R"(
45             .function void func() {
46                 nop
47 
48                 return
49             }
50 
51             .function void main() {
52                 call func
53                 call func
54                 return
55             }
56         )");
57 
58         mainFrame_.SetMethod(klass->GetDirectMethod(utf::CStringAsMutf8("main")));
59         func_.Set(klass->GetDirectMethod(utf::CStringAsMutf8("func")));
60     }
61 
SetUp()62     void SetUp() override
63     {
64         InspectorTestBase::SetUp();
65 
66         client_.OnCall("Debugger.scriptParsed", [this](const JsonObject &script) {
67             auto scriptId = script.GetValue<JsonObject::StringT>("scriptId");
68             ASSERT_NE(scriptId, nullptr) << "No 'scriptId' property";
69             scriptId_ = *scriptId;
70 
71             auto url = script.GetValue<JsonObject::StringT>("url");
72             ASSERT_NE(url, nullptr) << "No 'url' property";
73             url_ = *url;
74         });
75 
76         main_.Resume();
77         (server_ + client_).Poll();
78         EXPECT_FALSE(scriptId_.empty());
79     }
80 
TearDown()81     void TearDown() override
82     {
83         main_.Finish();
84         InspectorTestBase::TearDown();
85     }
86 
87     template <size_t... Breakpoint>
88     void CheckPossibleBreakpoints(size_t startLine, std::optional<size_t> endLine,
89                                   std::optional<bool> restrictToFunction,
90                                   std::index_sequence<Breakpoint...> /* breakpoints */);
91 
92     std::string scriptId_;
93     std::string url_;
94     TestFrame mainFrame_ {debugger_};
95     InstructionPointer main_ {mainFrame_, client_, debugger_};
96     TestMethod func_ {debugger_, client_};
97 };
98 
99 template <size_t... Breakpoint>
CheckPossibleBreakpoints(size_t startLine,std::optional<size_t> endLine,std::optional<bool> restrictToFunction,std::index_sequence<Breakpoint...>)100 void BreakpointTest::CheckPossibleBreakpoints(size_t startLine, std::optional<size_t> endLine,
101                                               std::optional<bool> restrictToFunction,
102                                               std::index_sequence<Breakpoint...> /* breakpoints */)
103 {
104     CallSync(
105         "Debugger.getPossibleBreakpoints",
106         [this, startLine, endLine, restrictToFunction](JsonObjectBuilder &params) {
107             params.AddProperty("start", [this, startLine](JsonObjectBuilder &start) {
108                 start.AddProperty("scriptId", scriptId_);
109                 start.AddProperty("lineNumber", startLine);
110             });
111             if (endLine) {
112                 params.AddProperty("end", [this, endLine](JsonObjectBuilder &end) {
113                     end.AddProperty("scriptId", scriptId_);
114                     end.AddProperty("lineNumber", *endLine);
115                 });
116             }
117             if (restrictToFunction) {
118                 params.AddProperty("restrictToFunction", *restrictToFunction);
119             }
120         },
121         [this](const JsonObject &result) {
122             (void)this;
123             auto resultMatcher = JsonProperties(JsonProperty<JsonObject::ArrayT> {
124                 "array", JsonElements(Matcher<JsonObject::JsonObjPointer> {
125                              Pointee(JsonProperties(JsonProperty<JsonObject::StringT> {"scriptId", scriptId_},
126                                                     JsonProperty<JsonObject::NumT> {"lineNumber", Breakpoint}))}...)});
127             EXPECT_THAT(result, resultMatcher);
128         });  // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) due to bug in clang-tidy #3553 (gtest repo)
129 }
130 
TEST_F(BreakpointTest,GetsPossibleBreakpoints)131 TEST_F(BreakpointTest, GetsPossibleBreakpoints)
132 {
133     CheckPossibleBreakpoints(4, 10, std::nullopt, std::index_sequence<4, 8, 9> {});
134     CheckPossibleBreakpoints(9, std::nullopt, std::nullopt, std::index_sequence<9, 10> {});
135     CheckPossibleBreakpoints(4, 9, true, std::index_sequence<4> {});
136     CheckPossibleBreakpoints(3, std::nullopt, true, std::index_sequence<4> {});
137     CheckPossibleBreakpoints(6, 10, true, std::index_sequence<> {});
138     CheckPossibleBreakpoints(0, std::nullopt, true, std::index_sequence<> {});
139 }
140 
141 // When stopped at a breakpoint while doing a step and resumed,
142 // the unfinished step command should be canceled.
TEST_F(BreakpointTest,InterruptsStep)143 TEST_F(BreakpointTest, InterruptsStep)
144 {
145     CallSync("Debugger.setBreakpoint", [&](auto &params) {
146         params.AddProperty("location", [&](JsonObjectBuilder &location) {
147             location.AddProperty("scriptId", scriptId_);
148             location.AddProperty("lineNumber", 2);
149         });
150     });
151 
152     CallSync("Debugger.setBreakpoint", [&](auto &params) {
153         params.AddProperty("location", [&](JsonObjectBuilder &location) {
154             location.AddProperty("scriptId", scriptId_);
155             location.AddProperty("lineNumber", 9);
156         });
157     });
158 
159     ExpectPauses(client_, {
160                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 8)),
161                               Breakpoint(CallFrame(main_.GetMethod(), 9)),
162                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 9)),
163                           });
164 
165     func_.Call([](auto &func) { func.Resume(); });
166     main_.StepOver();
167     func_.Call([](auto &func) { func.Resume(); });
168     main_.Finish();
169 }
170 
TEST_F(BreakpointTest,RemovesBreakpoint)171 TEST_F(BreakpointTest, RemovesBreakpoint)
172 {
173     auto breakpointId = CallSync(
174         "Debugger.setBreakpoint",
175         [&this](auto &params) {
176             params.AddProperty("location", [&this](JsonObjectBuilder &location) {
177                 location.AddProperty("scriptId", scriptId_);
178                 location.AddProperty("lineNumber", 2);
179             });
180         },
181         [](const JsonObject &result) {
182             auto id = result.GetValue<JsonObject::StringT>("breakpointId");
183             return id != nullptr ? std::make_optional(*id) : std::nullopt;
184         });
185     ASSERT_TRUE(breakpointId.has_value());
186 
187     ExpectPauses(client_, {
188                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 8)),
189                           });
190 
191     func_.Call([](auto &func) { func.Resume(); });
192     main_.Step();
193 
194     CallSync(
195         "Debugger.removeBreakpoint", [&](auto &params) { params.AddProperty("breakpointId", *breakpointId); },
196         // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) due to bug in clang-tidy #3553 (gtest repo)
197         [](auto &result) { EXPECT_THAT(result, JsonProperties()); });
198 
199     func_.Call([](auto &func) { func.Resume(); });
200     main_.Finish();
201 }
202 
TEST_F(BreakpointTest,SetsBreakpoint)203 TEST_F(BreakpointTest, SetsBreakpoint)
204 {
205     CallSync(
206         "Debugger.setBreakpoint",
207         [&](auto &params) {
208             params.AddProperty("location", [&](JsonObjectBuilder &location) {
209                 location.AddProperty("scriptId", scriptId_);
210                 location.AddProperty("lineNumber", 2);
211             });
212         },
213         [&](auto &result) {
214             EXPECT_THAT(
215                 result,
216                 JsonProperties(JsonProperty<JsonObject::StringT> {"breakpointId", _},
217                                JsonProperty<JsonObject::JsonObjPointer> {
218                                    "actualLocation",
219                                    Pointee(JsonProperties(JsonProperty<JsonObject::StringT> {"scriptId", scriptId_},
220                                                           JsonProperty<JsonObject::NumT> {"lineNumber", 2}))}));
221         });
222 
223     ExpectPauses(client_, {
224                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 8)),
225                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 9)),
226                           });
227 
228     func_.Call([](auto &func) { func.Resume(); });
229     main_.Step();
230     func_.Call([](auto &func) { func.Resume(); });
231     main_.Finish();
232 }
233 
TEST_F(BreakpointTest,SetsBreakpointByUrl)234 TEST_F(BreakpointTest, SetsBreakpointByUrl)
235 {
236     CallSync(
237         "Debugger.setBreakpointByUrl",
238         [&](auto &params) {
239             params.AddProperty("lineNumber", 2);
240             params.AddProperty("url", url_);
241         },
242         [&](auto &result) {
243             EXPECT_THAT(result,
244                         JsonProperties(JsonProperty<JsonObject::StringT> {"breakpointId", _},
245                                        JsonProperty<JsonObject::ArrayT> {
246                                            "locations",
247                                            JsonElements(Matcher<JsonObject::JsonObjPointer> {Pointee(
248                                                JsonProperties(JsonProperty<JsonObject::StringT> {"scriptId", scriptId_},
249                                                               JsonProperty<JsonObject::NumT> {"lineNumber", 2}))})}));
250         });
251 
252     ExpectPauses(client_, {
253                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 8)),
254                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 9)),
255                           });
256 
257     func_.Call([](auto &func) { func.Resume(); });
258     main_.Step();
259     func_.Call([](auto &func) { func.Resume(); });
260     main_.Finish();
261 }
262 
TEST_F(BreakpointTest,SetsBreakpointByUrlRegex)263 TEST_F(BreakpointTest, SetsBreakpointByUrlRegex)
264 {
265     CallSync(
266         "Debugger.setBreakpointByUrl",
267         [](auto &params) {
268             params.AddProperty("lineNumber", 2);
269             params.AddProperty("urlRegex", ".*");
270         },
271         [&](auto &result) {
272             EXPECT_THAT(result,
273                         JsonProperties(JsonProperty<JsonObject::StringT> {"breakpointId", _},
274                                        JsonProperty<JsonObject::ArrayT> {
275                                            "locations",
276                                            JsonElements(Matcher<JsonObject::JsonObjPointer> {Pointee(
277                                                JsonProperties(JsonProperty<JsonObject::StringT> {"scriptId", scriptId_},
278                                                               JsonProperty<JsonObject::NumT> {"lineNumber", 2}))})}));
279         });
280 
281     ExpectPauses(client_, {
282                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 8)),
283                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 9)),
284                           });
285 
286     func_.Call([](auto &func) { func.Resume(); });
287     main_.Step();
288     func_.Call([](auto &func) { func.Resume(); });
289     main_.Finish();
290 }
291 
TEST_F(BreakpointTest,SetsBreakpointsActive)292 TEST_F(BreakpointTest, SetsBreakpointsActive)
293 {
294     CallSync("Debugger.setBreakpoint", [&](auto &params) {
295         params.AddProperty("location", [&](JsonObjectBuilder &location) {
296             location.AddProperty("scriptId", scriptId_);
297             location.AddProperty("lineNumber", 2);
298         });
299     });
300 
301     CallSync(
302         "Debugger.setBreakpointsActive", [](auto &params) { params.AddProperty("active", false); },
303         // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) due to bug in clang-tidy #3553 (gtest repo)
304         [](auto &result) { EXPECT_THAT(result, JsonProperties()); });
305 
306     ExpectPauses(client_, {
307                               Breakpoint(CallFrame(func_, 2), CallFrame(main_.GetMethod(), 9)),
308                           });
309 
310     func_.Call([](auto &func) { func.Resume(); });
311     main_.Step();
312 
313     CallSync(
314         "Debugger.setBreakpointsActive", [](auto &params) { params.AddProperty("active", true); },
315         // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) due to bug in clang-tidy #3553 (gtest repo)
316         [](auto &result) { EXPECT_THAT(result, JsonProperties()); });
317 
318     func_.Call([](auto &func) { func.Resume(); });
319     main_.Finish();
320 }
321 }  // namespace panda::tooling::inspector::test
322