yeet
This commit is contained in:
412
node_modules/react-native/ReactCommon/hermes/inspector/tests/InspectorTests.cpp
generated
vendored
Normal file
412
node_modules/react-native/ReactCommon/hermes/inspector/tests/InspectorTests.cpp
generated
vendored
Normal file
@ -0,0 +1,412 @@
|
||||
/*
|
||||
* 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/Inspector.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <folly/futures/Future.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hermes/inspector/RuntimeAdapter.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace hermes {
|
||||
namespace inspector {
|
||||
|
||||
namespace debugger = facebook::hermes::debugger;
|
||||
using namespace std::chrono_literals;
|
||||
using Unit = folly::Unit;
|
||||
|
||||
static auto constexpr kDefaultTimeout = 5000ms;
|
||||
|
||||
namespace {
|
||||
|
||||
int getCurrentLine(const debugger::ProgramState &state) {
|
||||
return state.getStackTrace().callFrameForIndex(0).location.line;
|
||||
}
|
||||
|
||||
debugger::SourceLocation locationForLine(int line) {
|
||||
debugger::SourceLocation loc;
|
||||
loc.line = line;
|
||||
return loc;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
* LambdaInspectorObserver is useful for sequencing calls to the debugger based
|
||||
* on the number of onPause() callbacks.
|
||||
*/
|
||||
|
||||
using OnPauseFunction =
|
||||
std::function<void(Inspector &, const debugger::ProgramState &, int)>;
|
||||
|
||||
class LambdaInspectorObserver : public InspectorObserver {
|
||||
public:
|
||||
LambdaInspectorObserver(OnPauseFunction func)
|
||||
: onPauseFunc_(func), pauseCount_(0) {}
|
||||
~LambdaInspectorObserver() = default;
|
||||
|
||||
void onBreakpointResolved(
|
||||
Inspector &inspector,
|
||||
const debugger::BreakpointInfo &info) override {}
|
||||
|
||||
void onContextCreated(Inspector &inspector) override {}
|
||||
|
||||
void onPause(Inspector &inspector, const debugger::ProgramState &state)
|
||||
override {
|
||||
pauseCount_++;
|
||||
onPauseFunc_(inspector, state, pauseCount_);
|
||||
}
|
||||
|
||||
void onResume(Inspector &inspector) override {}
|
||||
|
||||
void onScriptParsed(Inspector &inspector, const ScriptInfo &info) override {}
|
||||
|
||||
void onMessageAdded(Inspector &inspector, const ConsoleMessageInfo &info)
|
||||
override{};
|
||||
|
||||
int getPauseCount() {
|
||||
return pauseCount_;
|
||||
}
|
||||
|
||||
private:
|
||||
OnPauseFunction onPauseFunc_;
|
||||
int pauseCount_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers for running JS in a separate thread.
|
||||
*/
|
||||
|
||||
struct HermesDebugContext {
|
||||
HermesDebugContext(
|
||||
InspectorObserver &observer,
|
||||
folly::Future<Unit> &&finished)
|
||||
: runtime(makeHermesRuntime()),
|
||||
inspector(
|
||||
std::make_shared<SharedRuntimeAdapter>(
|
||||
runtime,
|
||||
runtime->getDebugger()),
|
||||
observer,
|
||||
false),
|
||||
stopFlag(false),
|
||||
finished(std::move(finished)) {
|
||||
runtime->global().setProperty(
|
||||
*runtime,
|
||||
"shouldStop",
|
||||
jsi::Function::createFromHostFunction(
|
||||
*runtime,
|
||||
jsi::PropNameID::forAscii(*runtime, "shouldStop"),
|
||||
0,
|
||||
[this](
|
||||
jsi::Runtime &,
|
||||
const jsi::Value &,
|
||||
const jsi::Value *args,
|
||||
size_t count) {
|
||||
return stopFlag.load() ? jsi::Value(true) : jsi::Value(false);
|
||||
}));
|
||||
}
|
||||
~HermesDebugContext() = default;
|
||||
|
||||
void setStopFlag() {
|
||||
stopFlag.store(true);
|
||||
}
|
||||
|
||||
void wait(std::chrono::milliseconds timeout = kDefaultTimeout) {
|
||||
std::move(finished).get(timeout);
|
||||
}
|
||||
|
||||
std::shared_ptr<HermesRuntime> runtime;
|
||||
Inspector inspector;
|
||||
std::atomic<bool> stopFlag{};
|
||||
folly::Future<Unit> finished;
|
||||
};
|
||||
|
||||
static std::shared_ptr<HermesDebugContext> runScriptAsync(
|
||||
InspectorObserver &observer,
|
||||
const std::string &script) {
|
||||
auto promise = std::make_shared<folly::Promise<Unit>>();
|
||||
auto future = promise->getFuture();
|
||||
auto context =
|
||||
std::make_shared<HermesDebugContext>(observer, std::move(future));
|
||||
|
||||
std::thread t([=]() {
|
||||
HermesRuntime::DebugFlags flags{};
|
||||
context->runtime->debugJavaScript(script, "url", flags);
|
||||
promise->setValue();
|
||||
});
|
||||
t.detach();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests
|
||||
*/
|
||||
|
||||
TEST(InspectorTests, testStepOver) {
|
||||
std::string script = R"(
|
||||
var a = 1 + 2;
|
||||
debugger;
|
||||
var b = a / 2;
|
||||
var c = a + b;
|
||||
var d = b - c;
|
||||
var e = c * d;
|
||||
var f = 10;
|
||||
)";
|
||||
|
||||
// TODO: move this vector into lambdaInspectorObserver
|
||||
std::vector<folly::Future<Unit>> futures;
|
||||
|
||||
OnPauseFunction onPauseFunc = [&futures](
|
||||
Inspector &inspector,
|
||||
const debugger::ProgramState &state,
|
||||
int pauseCount) {
|
||||
switch (pauseCount) {
|
||||
case 1: {
|
||||
EXPECT_EQ(
|
||||
state.getPauseReason(), debugger::PauseReason::DebuggerStatement);
|
||||
EXPECT_EQ(getCurrentLine(state), 3);
|
||||
|
||||
futures.emplace_back(inspector.stepOver());
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish);
|
||||
EXPECT_EQ(getCurrentLine(state), 4);
|
||||
|
||||
futures.emplace_back(inspector.stepOver());
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish);
|
||||
EXPECT_EQ(getCurrentLine(state), 5);
|
||||
|
||||
futures.emplace_back(inspector.resume());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LambdaInspectorObserver observer(onPauseFunc);
|
||||
std::shared_ptr<HermesDebugContext> context =
|
||||
runScriptAsync(observer, script);
|
||||
|
||||
// TODO: temporarily do this to ensure i hit failure case
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
|
||||
futures.emplace_back(context->inspector.enable());
|
||||
|
||||
context->wait();
|
||||
folly::collectAll(futures).get(kDefaultTimeout);
|
||||
|
||||
EXPECT_EQ(observer.getPauseCount(), 3);
|
||||
}
|
||||
|
||||
TEST(InspectorTests, testSetBreakpoint) {
|
||||
std::string script = R"(
|
||||
var a = 1 + 2;
|
||||
debugger;
|
||||
var b = a / 2;
|
||||
var c = a + b;
|
||||
var d = b - c;
|
||||
var e = c * d;
|
||||
var f = 10;
|
||||
)";
|
||||
|
||||
std::vector<folly::Future<Unit>> futures;
|
||||
|
||||
OnPauseFunction onPauseFunc = [&futures](
|
||||
Inspector &inspector,
|
||||
const debugger::ProgramState &state,
|
||||
int pauseCount) {
|
||||
switch (pauseCount) {
|
||||
case 1: {
|
||||
EXPECT_EQ(
|
||||
state.getPauseReason(), debugger::PauseReason::DebuggerStatement);
|
||||
EXPECT_EQ(getCurrentLine(state), 3);
|
||||
|
||||
auto stepFuture = inspector.stepOver();
|
||||
futures.emplace_back(std::move(stepFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish);
|
||||
EXPECT_EQ(getCurrentLine(state), 4);
|
||||
|
||||
auto breakpointFuture = inspector.setBreakpoint(locationForLine(6));
|
||||
futures.emplace_back(std::move(breakpointFuture)
|
||||
.thenValue([](debugger::BreakpointInfo info) {
|
||||
EXPECT_EQ(info.resolvedLocation.line, 6);
|
||||
}));
|
||||
|
||||
auto resumeFuture = inspector.resume();
|
||||
futures.emplace_back(std::move(resumeFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::Breakpoint);
|
||||
EXPECT_EQ(getCurrentLine(state), 6);
|
||||
|
||||
auto resumeFuture = inspector.resume();
|
||||
futures.emplace_back(std::move(resumeFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LambdaInspectorObserver observer(onPauseFunc);
|
||||
std::shared_ptr<HermesDebugContext> context =
|
||||
runScriptAsync(observer, script);
|
||||
|
||||
auto enablePromise = context->inspector.enable();
|
||||
futures.emplace_back(std::move(enablePromise));
|
||||
|
||||
context->wait();
|
||||
folly::collectAll(futures).get(kDefaultTimeout);
|
||||
|
||||
EXPECT_EQ(observer.getPauseCount(), 3);
|
||||
}
|
||||
|
||||
TEST(InspectorTests, testAsyncSetBreakpoint) {
|
||||
std::string script = R"(
|
||||
while (!shouldStop()) {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
var c = a + b;
|
||||
var d = 10;
|
||||
}
|
||||
)";
|
||||
|
||||
std::vector<folly::Future<Unit>> futures;
|
||||
folly::Func stopFunc;
|
||||
|
||||
OnPauseFunction onPauseFunc = [&futures, &stopFunc](
|
||||
Inspector &inspector,
|
||||
const debugger::ProgramState &state,
|
||||
int pauseCount) {
|
||||
switch (pauseCount) {
|
||||
case 1: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::Breakpoint);
|
||||
EXPECT_EQ(getCurrentLine(state), 4);
|
||||
|
||||
auto stepFuture = inspector.stepOver();
|
||||
futures.emplace_back(std::move(stepFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish);
|
||||
EXPECT_EQ(getCurrentLine(state), 5);
|
||||
|
||||
stopFunc();
|
||||
|
||||
auto resumeFuture = inspector.resume();
|
||||
futures.emplace_back(std::move(resumeFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LambdaInspectorObserver observer(onPauseFunc);
|
||||
std::shared_ptr<HermesDebugContext> context =
|
||||
runScriptAsync(observer, script);
|
||||
|
||||
stopFunc = [context]() { context->setStopFlag(); };
|
||||
|
||||
context->inspector.enable();
|
||||
|
||||
auto breakpointPromise = context->inspector.setBreakpoint(locationForLine(4))
|
||||
.thenValue([](debugger::BreakpointInfo info) {
|
||||
EXPECT_EQ(info.resolvedLocation.line, 4);
|
||||
});
|
||||
|
||||
context->wait();
|
||||
|
||||
futures.emplace_back(std::move(breakpointPromise));
|
||||
folly::collectAll(futures).get(kDefaultTimeout);
|
||||
|
||||
EXPECT_EQ(observer.getPauseCount(), 2);
|
||||
}
|
||||
|
||||
TEST(InspectorTests, testDisable) {
|
||||
std::string script = R"(
|
||||
var a = 1 + 2;
|
||||
debugger;
|
||||
var b = a / 2;
|
||||
var c = a + b;
|
||||
var d = b - c;
|
||||
var e = c * d;
|
||||
var f = 10;
|
||||
)";
|
||||
|
||||
std::vector<folly::Future<Unit>> futures;
|
||||
|
||||
OnPauseFunction onPauseFunc = [&futures](
|
||||
Inspector &inspector,
|
||||
const debugger::ProgramState &state,
|
||||
int pauseCount) {
|
||||
switch (pauseCount) {
|
||||
case 1: {
|
||||
EXPECT_EQ(
|
||||
state.getPauseReason(), debugger::PauseReason::DebuggerStatement);
|
||||
EXPECT_EQ(getCurrentLine(state), 3);
|
||||
|
||||
auto stepFuture = inspector.stepOver();
|
||||
futures.emplace_back(std::move(stepFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish);
|
||||
EXPECT_EQ(getCurrentLine(state), 4);
|
||||
|
||||
futures.emplace_back(inspector.setBreakpoint(locationForLine(6))
|
||||
.thenValue([](debugger::BreakpointInfo info) {
|
||||
EXPECT_EQ(info.resolvedLocation.line, 6);
|
||||
}));
|
||||
futures.emplace_back(inspector.setBreakpoint(locationForLine(7))
|
||||
.thenValue([](debugger::BreakpointInfo info) {
|
||||
EXPECT_EQ(info.resolvedLocation.line, 7);
|
||||
}));
|
||||
|
||||
auto detachFuture = inspector.disable();
|
||||
futures.emplace_back(std::move(detachFuture));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LambdaInspectorObserver observer(onPauseFunc);
|
||||
std::shared_ptr<HermesDebugContext> context =
|
||||
runScriptAsync(observer, script);
|
||||
|
||||
auto enablePromise = context->inspector.enable();
|
||||
futures.emplace_back(std::move(enablePromise));
|
||||
|
||||
context->wait();
|
||||
folly::collectAll(futures).get(kDefaultTimeout);
|
||||
|
||||
EXPECT_EQ(observer.getPauseCount(), 2);
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace hermes
|
||||
} // namespace facebook
|
Reference in New Issue
Block a user