mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
39704b8937
Summary: setBreakpoint differs from the existing setBreakpointByUrl in that you can specify a scriptId directly. This was not really possible before now, since scriptIds were mapped via filenames rather than to RuntimeModules. When (re)loading files with the same name, this call can be used to set breakpoints in a specific one, rather than just the latest version. Changelog: [Internal] Reviewed By: bestander Differential Revision: D18362054 fbshipit-source-id: 6ff1e736c79a4e75dc424316675d426ce9902f5f
463 lines
13 KiB
C++
463 lines
13 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <hermes/inspector/chrome/MessageTypes.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <folly/dynamic.h>
|
|
#include <folly/json.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace facebook {
|
|
namespace hermes {
|
|
namespace inspector {
|
|
namespace chrome {
|
|
namespace message {
|
|
|
|
using folly::dynamic;
|
|
|
|
TEST(MessageTests, testSerializeSomeFieldsInRequest) {
|
|
debugger::SetBreakpointByUrlRequest req;
|
|
// req.id should default to 0
|
|
req.lineNumber = 2;
|
|
req.url = "http://example.com/example.js";
|
|
|
|
dynamic result = req.toDynamic();
|
|
dynamic expected = folly::parseJson(R"({
|
|
"id": 0,
|
|
"method": "Debugger.setBreakpointByUrl",
|
|
"params": {
|
|
"lineNumber": 2,
|
|
"url": "http://example.com/example.js"
|
|
}
|
|
})");
|
|
EXPECT_EQ(result, expected);
|
|
}
|
|
|
|
TEST(MessageTests, testDeserializeSomeFieldsInRequest) {
|
|
dynamic message = folly::parseJson(R"(
|
|
{
|
|
"id": 10,
|
|
"method": "Debugger.setBreakpointByUrl",
|
|
"params": {
|
|
"lineNumber": 42,
|
|
"url": "http://example.com"
|
|
}
|
|
}
|
|
)");
|
|
debugger::SetBreakpointByUrlRequest req(message);
|
|
|
|
EXPECT_EQ(req.id, 10);
|
|
EXPECT_EQ(req.method, "Debugger.setBreakpointByUrl");
|
|
EXPECT_EQ(req.lineNumber, 42);
|
|
EXPECT_FALSE(req.columnNumber.hasValue());
|
|
EXPECT_FALSE(req.condition.hasValue());
|
|
EXPECT_EQ(req.url, "http://example.com");
|
|
EXPECT_FALSE(req.urlRegex.hasValue());
|
|
}
|
|
|
|
TEST(MessageTests, testSerializeAllFieldsInRequest) {
|
|
debugger::SetBreakpointByUrlRequest req;
|
|
req.id = 1;
|
|
req.lineNumber = 2;
|
|
req.columnNumber = 3;
|
|
req.condition = "foo == 42";
|
|
req.url = "http://example.com/example.js";
|
|
req.urlRegex = "http://example.com/.*";
|
|
|
|
dynamic result = req.toDynamic();
|
|
dynamic expected = folly::parseJson(R"({
|
|
"id": 1,
|
|
"method": "Debugger.setBreakpointByUrl",
|
|
"params": {
|
|
"lineNumber": 2,
|
|
"columnNumber": 3,
|
|
"condition": "foo == 42",
|
|
"url": "http://example.com/example.js",
|
|
"urlRegex": "http://example.com/.*"
|
|
}
|
|
})");
|
|
EXPECT_EQ(result, expected);
|
|
}
|
|
|
|
TEST(MessageTests, testDeserializeAllFieldsInRequest) {
|
|
dynamic message = folly::parseJson(R"({
|
|
"id": 1,
|
|
"method": "Debugger.setBreakpointByUrl",
|
|
"params": {
|
|
"lineNumber": 2,
|
|
"columnNumber": 3,
|
|
"condition": "foo == 42",
|
|
"url": "http://example.com/example.js",
|
|
"urlRegex": "http://example.com/.*"
|
|
}
|
|
})");
|
|
debugger::SetBreakpointByUrlRequest req(message);
|
|
|
|
EXPECT_EQ(req.id, 1);
|
|
EXPECT_EQ(req.method, "Debugger.setBreakpointByUrl");
|
|
EXPECT_EQ(req.lineNumber, 2);
|
|
EXPECT_EQ(req.columnNumber, 3);
|
|
EXPECT_EQ(req.condition, "foo == 42");
|
|
EXPECT_EQ(req.url, "http://example.com/example.js");
|
|
EXPECT_EQ(req.urlRegex, "http://example.com/.*");
|
|
}
|
|
|
|
TEST(MessageTests, testSerializeResponse) {
|
|
debugger::Location location;
|
|
location.scriptId = "myScriptId";
|
|
location.lineNumber = 2;
|
|
location.columnNumber = 3;
|
|
|
|
debugger::SetBreakpointByUrlResponse resp;
|
|
resp.id = 1;
|
|
resp.breakpointId = "myBreakpointId";
|
|
resp.locations = {location};
|
|
|
|
dynamic result = resp.toDynamic();
|
|
dynamic expected = folly::parseJson(R"({
|
|
"id": 1,
|
|
"result": {
|
|
"breakpointId": "myBreakpointId",
|
|
"locations": [
|
|
{
|
|
"lineNumber": 2,
|
|
"columnNumber": 3,
|
|
"scriptId": "myScriptId"
|
|
}
|
|
]
|
|
}
|
|
})");
|
|
EXPECT_EQ(result, expected);
|
|
}
|
|
|
|
TEST(MessageTests, testDeserializeResponse) {
|
|
dynamic message = folly::parseJson(R"({
|
|
"id": 1,
|
|
"result": {
|
|
"breakpointId": "myBreakpointId",
|
|
"locations": [
|
|
{
|
|
"lineNumber": 2,
|
|
"columnNumber": 3,
|
|
"scriptId": "myScriptId"
|
|
}
|
|
]
|
|
}
|
|
})");
|
|
debugger::SetBreakpointByUrlResponse resp(message);
|
|
|
|
EXPECT_EQ(resp.id, 1);
|
|
EXPECT_EQ(resp.breakpointId, "myBreakpointId");
|
|
EXPECT_EQ(resp.locations.size(), 1);
|
|
EXPECT_EQ(resp.locations[0].lineNumber, 2);
|
|
EXPECT_EQ(resp.locations[0].columnNumber, 3);
|
|
EXPECT_EQ(resp.locations[0].scriptId, "myScriptId");
|
|
}
|
|
|
|
TEST(MessageTests, testSerializeNotification) {
|
|
debugger::Location startLocation;
|
|
startLocation.lineNumber = 1;
|
|
startLocation.scriptId = "script1";
|
|
|
|
debugger::Location endLocation;
|
|
endLocation.lineNumber = 2;
|
|
endLocation.scriptId = "script2";
|
|
|
|
debugger::Scope scope;
|
|
scope.type = "closure";
|
|
scope.object.type = "object";
|
|
scope.object.subtype = "regexp";
|
|
scope.object.className = "RegExp";
|
|
scope.object.value = dynamic::object("foo", "bar");
|
|
scope.object.unserializableValue = "nope";
|
|
scope.object.description = "myDesc";
|
|
scope.object.objectId = "id1";
|
|
scope.name = "myScope";
|
|
scope.startLocation = startLocation;
|
|
scope.endLocation = endLocation;
|
|
|
|
debugger::CallFrame frame;
|
|
frame.callFrameId = "callFrame1";
|
|
frame.functionName = "foo1";
|
|
frame.location.scriptId = "script1";
|
|
frame.location.lineNumber = 3;
|
|
frame.location.columnNumber = 4;
|
|
frame.url = "foo.js";
|
|
frame.scopeChain = std::vector<debugger::Scope>{scope};
|
|
frame.thisObj.type = "function";
|
|
|
|
debugger::PausedNotification note;
|
|
note.callFrames = std::vector<debugger::CallFrame>{frame};
|
|
note.reason = "debugCommand";
|
|
note.data = dynamic::object("foo", "bar");
|
|
note.hitBreakpoints = std::vector<std::string>{"a", "b"};
|
|
|
|
dynamic result = note.toDynamic();
|
|
dynamic expected = folly::parseJson(R"({
|
|
"method": "Debugger.paused",
|
|
"params": {
|
|
"callFrames": [
|
|
{
|
|
"callFrameId": "callFrame1",
|
|
"functionName": "foo1",
|
|
"location": {
|
|
"scriptId": "script1",
|
|
"lineNumber": 3,
|
|
"columnNumber": 4
|
|
},
|
|
"url": "foo.js",
|
|
"scopeChain": [
|
|
{
|
|
"type": "closure",
|
|
"object": {
|
|
"type": "object",
|
|
"subtype": "regexp",
|
|
"className": "RegExp",
|
|
"value": { "foo": "bar" },
|
|
"unserializableValue": "nope",
|
|
"description": "myDesc",
|
|
"objectId": "id1"
|
|
},
|
|
"name": "myScope",
|
|
"startLocation": {
|
|
"lineNumber": 1,
|
|
"scriptId": "script1"
|
|
},
|
|
"endLocation": {
|
|
"lineNumber": 2,
|
|
"scriptId": "script2"
|
|
}
|
|
}
|
|
],
|
|
"this": { "type": "function" }
|
|
}
|
|
],
|
|
"reason": "debugCommand",
|
|
"data": {
|
|
"foo": "bar"
|
|
},
|
|
"hitBreakpoints": [ "a", "b" ]
|
|
}
|
|
})");
|
|
EXPECT_EQ(result, expected);
|
|
}
|
|
|
|
TEST(MessageTests, testDeserializeNotification) {
|
|
dynamic message = folly::parseJson(R"({
|
|
"method": "Debugger.paused",
|
|
"params": {
|
|
"callFrames": [
|
|
{
|
|
"callFrameId": "callFrame1",
|
|
"functionName": "foo1",
|
|
"location": {
|
|
"scriptId": "script1",
|
|
"lineNumber": 3,
|
|
"columnNumber": 4
|
|
},
|
|
"url": "foo.js",
|
|
"scopeChain": [
|
|
{
|
|
"type": "closure",
|
|
"object": {
|
|
"type": "object",
|
|
"subtype": "regexp",
|
|
"className": "RegExp",
|
|
"value": { "foo": "bar" },
|
|
"unserializableValue": "nope",
|
|
"description": "myDesc",
|
|
"objectId": "id1"
|
|
},
|
|
"name": "myScope",
|
|
"startLocation": {
|
|
"lineNumber": 1,
|
|
"scriptId": "script1"
|
|
},
|
|
"endLocation": {
|
|
"lineNumber": 2,
|
|
"scriptId": "script2"
|
|
}
|
|
}
|
|
],
|
|
"this": { "type": "function" }
|
|
}
|
|
],
|
|
"reason": "debugCommand",
|
|
"data": {
|
|
"foo": "bar"
|
|
},
|
|
"hitBreakpoints": [ "a", "b" ]
|
|
}
|
|
})");
|
|
debugger::PausedNotification note(message);
|
|
|
|
EXPECT_EQ(note.method, "Debugger.paused");
|
|
EXPECT_EQ(note.callFrames.size(), 1);
|
|
EXPECT_EQ(note.reason, "debugCommand");
|
|
EXPECT_EQ(note.data, static_cast<dynamic>(dynamic::object("foo", "bar")));
|
|
auto expectedHitBreakpoints = std::vector<std::string>{"a", "b"};
|
|
EXPECT_EQ(note.hitBreakpoints, expectedHitBreakpoints);
|
|
|
|
debugger::CallFrame &callFrame = note.callFrames[0];
|
|
EXPECT_EQ(callFrame.callFrameId, "callFrame1");
|
|
EXPECT_EQ(callFrame.functionName, "foo1");
|
|
EXPECT_EQ(callFrame.location.scriptId, "script1");
|
|
EXPECT_EQ(callFrame.location.lineNumber, 3);
|
|
EXPECT_EQ(callFrame.location.columnNumber, 4);
|
|
EXPECT_EQ(callFrame.url, "foo.js");
|
|
EXPECT_EQ(callFrame.scopeChain.size(), 1);
|
|
EXPECT_EQ(callFrame.thisObj.type, "function");
|
|
|
|
debugger::Scope &scope = callFrame.scopeChain[0];
|
|
EXPECT_EQ(scope.type, "closure");
|
|
EXPECT_EQ(scope.object.type, "object");
|
|
EXPECT_EQ(scope.object.subtype, "regexp");
|
|
EXPECT_EQ(scope.object.className, "RegExp");
|
|
EXPECT_EQ(
|
|
scope.object.value, static_cast<dynamic>(dynamic::object("foo", "bar")));
|
|
EXPECT_EQ(scope.object.unserializableValue, "nope");
|
|
EXPECT_EQ(scope.object.description, "myDesc");
|
|
EXPECT_EQ(scope.object.objectId, "id1");
|
|
EXPECT_EQ(scope.name, "myScope");
|
|
|
|
debugger::Location &startLocation = scope.startLocation.value();
|
|
EXPECT_EQ(startLocation.lineNumber, 1);
|
|
EXPECT_FALSE(startLocation.columnNumber.hasValue());
|
|
EXPECT_EQ(startLocation.scriptId, "script1");
|
|
|
|
debugger::Location &endLocation = scope.endLocation.value();
|
|
EXPECT_EQ(endLocation.lineNumber, 2);
|
|
EXPECT_FALSE(endLocation.columnNumber.hasValue());
|
|
EXPECT_EQ(endLocation.scriptId, "script2");
|
|
}
|
|
|
|
TEST(MessageTests, TestSerializeAsyncStackTrace) {
|
|
runtime::StackTrace stack;
|
|
stack.description = "childStack";
|
|
stack.parent = std::make_unique<runtime::StackTrace>();
|
|
stack.parent->description = "parentStack";
|
|
|
|
dynamic result = stack.toDynamic();
|
|
dynamic expected = folly::parseJson(R"({
|
|
"description": "childStack",
|
|
"callFrames": [],
|
|
"parent": {
|
|
"description": "parentStack",
|
|
"callFrames": []
|
|
}
|
|
})");
|
|
EXPECT_EQ(result, expected);
|
|
}
|
|
|
|
TEST(MessageTests, TestDeserializeAsyncStackTrace) {
|
|
dynamic message = folly::parseJson(R"({
|
|
"description": "childStack",
|
|
"callFrames": [],
|
|
"parent": {
|
|
"description": "parentStack",
|
|
"callFrames": []
|
|
}
|
|
})");
|
|
runtime::StackTrace stack(message);
|
|
|
|
EXPECT_EQ(stack.description, "childStack");
|
|
EXPECT_EQ(stack.callFrames.size(), 0);
|
|
EXPECT_EQ(stack.parent->description, "parentStack");
|
|
EXPECT_EQ(stack.parent->callFrames.size(), 0);
|
|
}
|
|
|
|
TEST(MessageTests, TestRequestFromJson) {
|
|
std::unique_ptr<Request> baseReq1 = Request::fromJsonThrowOnError(R"({
|
|
"id": 1,
|
|
"method": "Debugger.enable"
|
|
})");
|
|
auto req1 = static_cast<debugger::EnableRequest *>(baseReq1.get());
|
|
EXPECT_EQ(req1->id, 1);
|
|
EXPECT_EQ(req1->method, "Debugger.enable");
|
|
|
|
std::unique_ptr<Request> baseReq2 = Request::fromJsonThrowOnError(R"({
|
|
"id": 2,
|
|
"method": "Debugger.removeBreakpoint",
|
|
"params": {
|
|
"breakpointId": "foobar"
|
|
}
|
|
})");
|
|
auto req2 = static_cast<debugger::RemoveBreakpointRequest *>(baseReq2.get());
|
|
EXPECT_EQ(req2->id, 2);
|
|
EXPECT_EQ(req2->method, "Debugger.removeBreakpoint");
|
|
EXPECT_EQ(req2->breakpointId, "foobar");
|
|
|
|
folly::Try<std::unique_ptr<Request>> invalidReq =
|
|
Request::fromJson("invalid");
|
|
EXPECT_TRUE(invalidReq.hasException());
|
|
}
|
|
|
|
TEST(MessageTests, TestBreakpointRequestFromJSON) {
|
|
std::unique_ptr<Request> baseReq = Request::fromJsonThrowOnError(R"({
|
|
"id": 1,
|
|
"method": "Debugger.setBreakpoint",
|
|
"params": {
|
|
"location": {
|
|
"scriptId": "23",
|
|
"lineNumber": 45,
|
|
"columnNumber": 67
|
|
}
|
|
}
|
|
})");
|
|
auto req = static_cast<debugger::SetBreakpointRequest *>(baseReq.get());
|
|
EXPECT_EQ(req->location.scriptId, "23");
|
|
EXPECT_EQ(req->location.lineNumber, 45);
|
|
EXPECT_EQ(req->location.columnNumber.value(), 67);
|
|
}
|
|
|
|
struct MyHandler : public NoopRequestHandler {
|
|
void handle(const debugger::EnableRequest &req) override {
|
|
enableReq = req;
|
|
}
|
|
|
|
void handle(const debugger::RemoveBreakpointRequest &req) override {
|
|
removeReq = req;
|
|
}
|
|
|
|
debugger::EnableRequest enableReq;
|
|
debugger::RemoveBreakpointRequest removeReq;
|
|
};
|
|
|
|
TEST(MessageTests, TestRequestHandler) {
|
|
MyHandler handler;
|
|
|
|
std::unique_ptr<Request> enableReq = Request::fromJsonThrowOnError(R"({
|
|
"id": 1,
|
|
"method": "Debugger.enable"
|
|
})");
|
|
enableReq->accept(handler);
|
|
|
|
EXPECT_EQ(handler.enableReq.id, 1);
|
|
EXPECT_EQ(handler.enableReq.method, "Debugger.enable");
|
|
|
|
std::unique_ptr<Request> removeReq = Request::fromJsonThrowOnError(R"({
|
|
"id": 2,
|
|
"method": "Debugger.removeBreakpoint",
|
|
"params": {
|
|
"breakpointId": "foobar"
|
|
}
|
|
})");
|
|
removeReq->accept(handler);
|
|
|
|
EXPECT_EQ(handler.removeReq.id, 2);
|
|
EXPECT_EQ(handler.removeReq.method, "Debugger.removeBreakpoint");
|
|
EXPECT_EQ(handler.removeReq.breakpointId, "foobar");
|
|
}
|
|
|
|
} // namespace message
|
|
} // namespace chrome
|
|
} // namespace inspector
|
|
} // namespace hermes
|
|
} // namespace facebook
|