Skip to content

Commit 8cbf46a

Browse files
robhoganfacebook-github-bot
authored andcommitted
Move integration tests to OSS (#43094)
Summary: Pull Request resolved: #43094 Merge the internal `cxxcdp-tester` project into `jsinspector-modern/tests`. Note: These tests still use RN default feature flags and therefore test against the legacy CDP registry - that's addressed in the next diff. Changelog: [Internal] Reviewed By: motiz88 Differential Revision: D53766994 fbshipit-source-id: eec144124b20a4500e28398e98763febaed52748
1 parent 765e542 commit 8cbf46a

5 files changed

Lines changed: 998 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "ReactInstanceIntegrationTest.h"
9+
#include "FollyDynamicMatchers.h"
10+
#include "UniquePtrFactory.h"
11+
#include "prelude.js.h"
12+
13+
#include <folly/json.h>
14+
#include <glog/logging.h>
15+
#include <react/runtime/hermes/HermesInstance.h>
16+
17+
namespace facebook::react::jsinspector_modern {
18+
19+
using namespace ::testing;
20+
21+
ReactInstanceIntegrationTest::ReactInstanceIntegrationTest()
22+
: runtime(nullptr),
23+
instance(nullptr),
24+
messageQueueThread(std::make_shared<MockMessageQueueThread>()),
25+
errorHandler(std::make_shared<ErrorUtils>()) {}
26+
27+
void ReactInstanceIntegrationTest::SetUp() {
28+
auto mockRegistry = std::make_unique<MockTimerRegistry>();
29+
auto timerManager =
30+
std::make_shared<react::TimerManager>(std::move(mockRegistry));
31+
32+
auto jsErrorHandlingFunc = [](react::MapBuffer error) noexcept {
33+
LOG(INFO) << "Error: \nFile: " << error.getString(react::kFrameFileName)
34+
<< "\nLine: " << error.getInt(react::kFrameLineNumber)
35+
<< "\nColumn: " << error.getInt(react::kFrameColumnNumber)
36+
<< "\nMethod: " << error.getString(react::kFrameMethodName);
37+
};
38+
39+
auto jsRuntimeFactory = std::make_unique<react::HermesInstance>();
40+
std::unique_ptr<react::JSRuntime> runtime_ =
41+
jsRuntimeFactory->createJSRuntime(nullptr, nullptr, messageQueueThread);
42+
jsi::Runtime* jsiRuntime = &runtime_->getRuntime();
43+
44+
// Error handler:
45+
jsiRuntime->global().setProperty(
46+
*jsiRuntime,
47+
"ErrorUtils",
48+
jsi::Object::createFromHostObject(*jsiRuntime, errorHandler));
49+
50+
instance = std::make_unique<react::ReactInstance>(
51+
std::move(runtime_),
52+
messageQueueThread,
53+
timerManager,
54+
std::move(jsErrorHandlingFunc));
55+
timerManager->setRuntimeExecutor(instance->getBufferedRuntimeExecutor());
56+
57+
// JS Environment:
58+
initializeRuntime(preludeJsCode);
59+
60+
// Inspector:
61+
auto& inspector = getInspectorInstance();
62+
auto pages = inspector.getPages();
63+
64+
// We should now have at least a single page once the above runtime has been
65+
// initialized.
66+
assert(pages.size() > 0);
67+
size_t pageId = pages.back().id;
68+
69+
clientToVM_ = inspector.connect(pageId, mockRemoteConnections_.make_unique());
70+
}
71+
72+
void ReactInstanceIntegrationTest::TearDown() {
73+
clientToVM_->disconnect();
74+
}
75+
76+
void ReactInstanceIntegrationTest::initializeRuntime(std::string_view script) {
77+
react::ReactInstance::JSRuntimeFlags flags{
78+
.isProfiling = false,
79+
};
80+
instance->initializeRuntime(flags, [](jsi::Runtime&) {});
81+
82+
messageQueueThread->tick();
83+
84+
std::string init(script);
85+
// JS calls no longer buffered after calling loadScript
86+
instance->loadScript(std::make_unique<react::JSBigStdString>(init), "");
87+
88+
messageQueueThread->flush();
89+
}
90+
91+
void ReactInstanceIntegrationTest::send(
92+
const std::string& method,
93+
const folly::dynamic& params) {
94+
folly::dynamic request = folly::dynamic::object();
95+
96+
request["method"] = method;
97+
request["id"] = id_++;
98+
request["params"] = params;
99+
100+
sendJSONString(folly::toJson(request));
101+
}
102+
103+
void ReactInstanceIntegrationTest::sendJSONString(const std::string& message) {
104+
// The runtime must be initialized and connected to before messaging
105+
clientToVM_->sendMessage(message);
106+
}
107+
108+
jsi::Value ReactInstanceIntegrationTest::run(const std::string& script) {
109+
auto runtimeExecutor = instance->getUnbufferedRuntimeExecutor();
110+
auto ret = jsi::Value::undefined();
111+
112+
runtimeExecutor([script, &ret](jsi::Runtime& rt) {
113+
ret = rt.evaluateJavaScript(
114+
std::make_unique<jsi::StringBuffer>(script), "<test>");
115+
});
116+
117+
messageQueueThread->flush();
118+
119+
while (verbose_ && errorHandler->size() > 0) {
120+
LOG(INFO) << "Error: " << errorHandler->getLastError().getMessage();
121+
}
122+
123+
return ret;
124+
}
125+
126+
bool ReactInstanceIntegrationTest::verbose(bool isVerbose) {
127+
const bool previous = verbose_;
128+
verbose_ = isVerbose;
129+
return previous;
130+
}
131+
132+
TEST_F(ReactInstanceIntegrationTest, RuntimeEvalTest) {
133+
auto val = run("1 + 2");
134+
EXPECT_EQ(val.asNumber(), 3);
135+
}
136+
137+
TEST_F(ReactInstanceIntegrationTest, ConsoleLogTest) {
138+
InSequence s;
139+
140+
EXPECT_CALL(getRemoteConnection(), onMessage(_))
141+
.Times(2)
142+
.RetiresOnSaturation();
143+
144+
EXPECT_CALL(
145+
getRemoteConnection(),
146+
onMessage(JsonParsed(AllOf(
147+
AtJsonPtr("/params/args/0/value", Eq("Hello, World!")),
148+
AtJsonPtr("/method", Eq("Runtime.consoleAPICalled"))))));
149+
150+
EXPECT_CALL(getRemoteConnection(), onDisconnect());
151+
152+
send("Runtime.enable");
153+
run("console.log('Hello, World!');");
154+
}
155+
156+
} // namespace facebook::react::jsinspector_modern
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <folly/json.h>
11+
#include <gtest/gtest.h>
12+
#include <jsinspector-modern/InspectorInterfaces.h>
13+
#include <memory>
14+
15+
#include "InspectorMocks.h"
16+
#include "ReactNativeMocks.h"
17+
#include "UniquePtrFactory.h"
18+
19+
namespace facebook::react::jsinspector_modern {
20+
21+
class ReactInstanceIntegrationTest : public ::testing::Test {
22+
protected:
23+
ReactInstanceIntegrationTest();
24+
void SetUp() override;
25+
void TearDown() override;
26+
27+
jsi::Value run(const std::string& script);
28+
bool verbose(bool isVerbose);
29+
30+
void send(
31+
const std::string& method,
32+
const folly::dynamic& params = folly::dynamic::object());
33+
void sendJSONString(const std::string& message);
34+
35+
jsi::Runtime* runtime;
36+
std::unique_ptr<react::ReactInstance> instance;
37+
std::shared_ptr<MockMessageQueueThread> messageQueueThread;
38+
std::shared_ptr<ErrorUtils> errorHandler;
39+
40+
MockRemoteConnection& getRemoteConnection() {
41+
return *mockRemoteConnections_[0];
42+
}
43+
44+
private:
45+
void initializeRuntime(std::string_view script);
46+
47+
size_t id_ = 1;
48+
bool verbose_ = false;
49+
UniquePtrFactory<MockRemoteConnection> mockRemoteConnections_;
50+
std::unique_ptr<ILocalConnection> clientToVM_;
51+
};
52+
53+
} // namespace facebook::react::jsinspector_modern
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "ReactNativeMocks.h"
9+
10+
namespace facebook::react::jsinspector_modern {
11+
12+
//
13+
// MockMessageQueueThread
14+
//
15+
16+
void MockMessageQueueThread::runOnQueue(std::function<void()>&& func) {
17+
callbackQueue_.push(func);
18+
}
19+
20+
void MockMessageQueueThread::tick() {
21+
if (!callbackQueue_.empty()) {
22+
auto callback = callbackQueue_.front();
23+
callback();
24+
callbackQueue_.pop();
25+
}
26+
}
27+
28+
void MockMessageQueueThread::guardedTick() {
29+
try {
30+
tick();
31+
} catch (const std::exception& e) {
32+
// For easier debugging
33+
FAIL() << e.what();
34+
}
35+
}
36+
37+
void MockMessageQueueThread::flush() {
38+
while (!callbackQueue_.empty()) {
39+
tick();
40+
}
41+
}
42+
43+
size_t MockMessageQueueThread::size() {
44+
return callbackQueue_.size();
45+
}
46+
47+
void MockMessageQueueThread::quitSynchronous() {
48+
assert(false && "Not implemented");
49+
}
50+
void MockMessageQueueThread::runOnQueueSync(std::function<void()>&& callback) {
51+
callback();
52+
}
53+
54+
//
55+
// ErrorUtils
56+
//
57+
jsi::Value ErrorUtils::get(jsi::Runtime& rt, const jsi::PropNameID& name) {
58+
auto methodName = name.utf8(rt);
59+
60+
if (methodName == "reportFatalError") {
61+
return jsi::Function::createFromHostFunction(
62+
rt,
63+
name,
64+
1,
65+
[this](
66+
jsi::Runtime& runtime,
67+
/* thisValue */ const jsi::Value&,
68+
const jsi::Value* arguments,
69+
size_t count) {
70+
if (count >= 1) {
71+
auto value = jsi::Value(runtime, arguments[0]);
72+
auto error = jsi::JSError(runtime, std::move(value));
73+
LOG(INFO) << "JSI Fatal: " << error.getMessage();
74+
reportFatalError(std::move(error));
75+
}
76+
return jsi::Value::undefined();
77+
});
78+
} else {
79+
throw std::runtime_error("Unknown method: " + methodName);
80+
}
81+
}
82+
83+
size_t ErrorUtils::size() {
84+
return errors_.size();
85+
}
86+
87+
jsi::JSError ErrorUtils::getLastError() {
88+
auto error = errors_.back();
89+
errors_.pop_back();
90+
return error;
91+
}
92+
93+
} // namespace facebook::react::jsinspector_modern
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <gmock/gmock.h>
11+
#include <gtest/gtest.h>
12+
#include <queue>
13+
14+
#include <ReactCommon/RuntimeExecutor.h>
15+
#include <jsi/jsi.h>
16+
#include <react/runtime/ReactInstance.h>
17+
18+
namespace facebook::react::jsinspector_modern {
19+
20+
class MockTimerRegistry : public react::PlatformTimerRegistry {
21+
public:
22+
MOCK_METHOD2(createTimer, void(uint32_t, double));
23+
MOCK_METHOD2(createRecurringTimer, void(uint32_t, double));
24+
MOCK_METHOD1(deleteTimer, void(uint32_t));
25+
};
26+
27+
class MockMessageQueueThread : public react::MessageQueueThread {
28+
public:
29+
void runOnQueue(std::function<void()>&& func) override;
30+
31+
// Unused
32+
void runOnQueueSync(std::function<void()>&&) override;
33+
34+
// Unused
35+
void quitSynchronous() override;
36+
37+
void tick();
38+
39+
void flush();
40+
41+
void guardedTick();
42+
43+
size_t size();
44+
45+
private:
46+
std::queue<std::function<void()>> callbackQueue_;
47+
};
48+
49+
class ErrorUtils : public jsi::HostObject {
50+
public:
51+
jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override;
52+
53+
void reportFatalError(jsi::JSError&& error) {
54+
errors_.push_back(std::move(error));
55+
}
56+
57+
size_t size();
58+
59+
jsi::JSError getLastError();
60+
61+
private:
62+
std::vector<jsi::JSError> errors_;
63+
};
64+
65+
} // namespace facebook::react::jsinspector_modern

0 commit comments

Comments
 (0)