Skip to content

Commit f0363f5

Browse files
robhoganfacebook-github-bot
authored andcommitted
Enable varying feature flags in ReactInstanceIntegrationTest, use modern CDP registry by default (#43101)
Summary: Pull Request resolved: #43101 Extends `ReactInstanceIntegrationTest` to allow varying feature flags in tests, using gtest's parameterised tests. Exercise this in `ConsoleLogTest` to test against the modern CDP registry, for which we also needed to modify some initialisation logic to account for the fact that under the modern registry, the page is added by the host, rather than by Hermes `DecoratedRuntime`. Changelog: [Internal] Reviewed By: motiz88 Differential Revision: D53919148 fbshipit-source-id: 4eb87abf548f30b5483b819a2dadd444d1d5c80d
1 parent 451fffb commit f0363f5

File tree

4 files changed

+161
-25
lines changed

4 files changed

+161
-25
lines changed

packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ bool InspectorFlags::getEnableCxxInspectorPackagerConnection() const {
3737
enableModernCDPRegistry_;
3838
}
3939

40+
void InspectorFlags::dangerouslyResetFlags() {
41+
*this = InspectorFlags{};
42+
}
43+
4044
void InspectorFlags::assertFlagsMatchUpstream() const {
4145
if (inconsistentFlagsStateLogged_) {
4246
return;

packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,20 @@ class InspectorFlags {
3030
*/
3131
bool getEnableCxxInspectorPackagerConnection() const;
3232

33+
/**
34+
* Reset flags to their upstream values. The caller must ensure any resources
35+
* that have read previous flag values have been cleaned up.
36+
*/
37+
void dangerouslyResetFlags();
38+
3339
private:
3440
InspectorFlags();
3541
InspectorFlags(const InspectorFlags&) = delete;
36-
InspectorFlags& operator=(const InspectorFlags&) = delete;
42+
InspectorFlags& operator=(const InspectorFlags&) = default;
3743
~InspectorFlags() = default;
3844

39-
const bool enableModernCDPRegistry_;
40-
const bool enableCxxInspectorPackagerConnection_;
45+
bool enableModernCDPRegistry_;
46+
bool enableCxxInspectorPackagerConnection_;
4147

4248
mutable bool inconsistentFlagsStateLogged_;
4349
void assertFlagsMatchUpstream() const;

packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp

Lines changed: 124 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,53 @@
1212

1313
#include <folly/json.h>
1414
#include <glog/logging.h>
15+
#include <jsinspector-modern/InspectorFlags.h>
16+
#include <react/featureflags/ReactNativeFeatureFlags.h>
17+
#include <react/featureflags/ReactNativeFeatureFlagsDefaults.h>
1518
#include <react/runtime/hermes/HermesInstance.h>
1619

1720
namespace facebook::react::jsinspector_modern {
1821

1922
using namespace ::testing;
2023

24+
class ReactInstanceIntegrationTestFeatureFlagsProvider
25+
: public ReactNativeFeatureFlagsDefaults {
26+
private:
27+
FeatureFlags flags_;
28+
29+
public:
30+
explicit ReactInstanceIntegrationTestFeatureFlagsProvider(
31+
FeatureFlags featureFlags)
32+
: flags_(featureFlags) {}
33+
34+
bool inspectorEnableModernCDPRegistry() override {
35+
return flags_.enableModernCDPRegistry;
36+
}
37+
bool inspectorEnableCxxInspectorPackagerConnection() override {
38+
return flags_.enableCxxInspectorPackagerConnection;
39+
}
40+
};
41+
2142
ReactInstanceIntegrationTest::ReactInstanceIntegrationTest()
2243
: runtime(nullptr),
2344
instance(nullptr),
2445
messageQueueThread(std::make_shared<MockMessageQueueThread>()),
25-
errorHandler(std::make_shared<ErrorUtils>()) {}
46+
errorHandler(std::make_shared<ErrorUtils>()),
47+
featureFlags(std::make_unique<FeatureFlags>()) {}
2648

2749
void ReactInstanceIntegrationTest::SetUp() {
50+
ReactNativeFeatureFlags::override(
51+
std::make_unique<ReactInstanceIntegrationTestFeatureFlagsProvider>(
52+
*featureFlags));
53+
// Reset InspectorFlags to overridden upstream values before creating objects
54+
// that may access them.
55+
InspectorFlags::getInstance().dangerouslyResetFlags();
56+
57+
// Double check that a flag matches our overrides.
58+
ASSERT_EQ(
59+
InspectorFlags::getInstance().getEnableModernCDPRegistry(),
60+
featureFlags->enableModernCDPRegistry);
61+
2862
auto mockRegistry = std::make_unique<MockTimerRegistry>();
2963
auto timerManager =
3064
std::make_shared<react::TimerManager>(std::move(mockRegistry));
@@ -47,30 +81,78 @@ void ReactInstanceIntegrationTest::SetUp() {
4781
"ErrorUtils",
4882
jsi::Object::createFromHostObject(*jsiRuntime, errorHandler));
4983

84+
std::shared_ptr<HostTarget> hostTargetIfModernCDP = nullptr;
85+
86+
if (featureFlags->enableModernCDPRegistry) {
87+
VoidExecutor inspectorExecutor = [this](auto callback) {
88+
immediateExecutor_.add(callback);
89+
};
90+
MockHostTargetDelegate hostTargetDelegate;
91+
hostTargetIfModernCDP =
92+
HostTarget::create(hostTargetDelegate, inspectorExecutor);
93+
}
94+
5095
instance = std::make_unique<react::ReactInstance>(
5196
std::move(runtime_),
5297
messageQueueThread,
5398
timerManager,
54-
std::move(jsErrorHandlingFunc));
99+
std::move(jsErrorHandlingFunc),
100+
hostTargetIfModernCDP == nullptr ? nullptr : hostTargetIfModernCDP.get());
101+
55102
timerManager->setRuntimeExecutor(instance->getBufferedRuntimeExecutor());
56103

57104
// JS Environment:
58105
initializeRuntime(preludeJsCode);
59106

60107
// Inspector:
61108
auto& inspector = getInspectorInstance();
62-
auto pages = inspector.getPages();
63109

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;
110+
if (hostTargetIfModernCDP != nullptr) {
111+
// Under modern CDP, the React host is responsible for adding itself as
112+
// the root target on startup.
113+
pageId_ = inspector.addPage(
114+
"mock-title",
115+
"mock-vm",
116+
[hostTargetIfModernCDP](std::unique_ptr<IRemoteConnection> remote)
117+
-> std::unique_ptr<ILocalConnection> {
118+
auto localConnection = hostTargetIfModernCDP->connect(
119+
std::move(remote),
120+
{
121+
.integrationName = "ReactInstanceIntegrationTest",
122+
});
123+
return localConnection;
124+
},
125+
// TODO: Allow customisation of InspectorTargetCapabilities
126+
{});
127+
} else {
128+
// Under legacy CDP, Hermes' DecoratedRuntime adds its page automatically
129+
// within ConnectionDemux.enableDebugging.
130+
auto pages = inspector.getPages();
131+
ASSERT_GT(pages.size(), 0);
132+
pageId_ = pages.back().id;
133+
}
68134

69-
clientToVM_ = inspector.connect(pageId, mockRemoteConnections_.make_unique());
135+
clientToVM_ =
136+
inspector.connect(pageId_.value(), mockRemoteConnections_.make_unique());
137+
138+
ASSERT_NE(clientToVM_, nullptr);
70139
}
71140

72141
void ReactInstanceIntegrationTest::TearDown() {
73142
clientToVM_->disconnect();
143+
// Destroy the local connection.
144+
clientToVM_.reset();
145+
146+
if (pageId_.has_value() && featureFlags->enableModernCDPRegistry) {
147+
// Under modern CDP, clean up the page we added in SetUp and destroy
148+
// resources owned by HostTarget.
149+
getInspectorInstance().removePage(pageId_.value());
150+
}
151+
pageId_.reset();
152+
153+
// Expect the remote connection to have been destroyed.
154+
EXPECT_EQ(mockRemoteConnections_[0], nullptr);
155+
ReactNativeFeatureFlags::dangerouslyReset();
74156
}
75157

76158
void ReactInstanceIntegrationTest::initializeRuntime(std::string_view script) {
@@ -84,8 +166,6 @@ void ReactInstanceIntegrationTest::initializeRuntime(std::string_view script) {
84166
std::string init(script);
85167
// JS calls no longer buffered after calling loadScript
86168
instance->loadScript(std::make_unique<react::JSBigStdString>(init), "");
87-
88-
messageQueueThread->flush();
89169
}
90170

91171
void ReactInstanceIntegrationTest::send(
@@ -134,23 +214,47 @@ TEST_F(ReactInstanceIntegrationTest, RuntimeEvalTest) {
134214
EXPECT_EQ(val.asNumber(), 3);
135215
}
136216

137-
TEST_F(ReactInstanceIntegrationTest, ConsoleLogTest) {
138-
InSequence s;
139-
140-
EXPECT_CALL(getRemoteConnection(), onMessage(_))
141-
.Times(2)
142-
.RetiresOnSaturation();
143-
217+
TEST_P(ReactInstanceIntegrationTestWithFlags, ConsoleLog) {
144218
EXPECT_CALL(
145219
getRemoteConnection(),
146-
onMessage(JsonParsed(AllOf(
147-
AtJsonPtr("/params/args/0/value", Eq("Hello, World!")),
148-
AtJsonPtr("/method", Eq("Runtime.consoleAPICalled"))))));
220+
onMessage(JsonParsed(
221+
AtJsonPtr("/method", Eq("Runtime.executionContextCreated")))));
222+
223+
EXPECT_CALL(
224+
getRemoteConnection(), onMessage(JsonParsed(AtJsonPtr("/id", Eq(1)))));
225+
226+
InSequence s;
227+
228+
// Hermes console.* interception is currently explicitly disabled under the
229+
// modern registry, and the runtime does not yet fire these events. When the
230+
// implementation is more complete we should be able to remove this
231+
// condition.
232+
if (!featureFlags->enableModernCDPRegistry) {
233+
EXPECT_CALL(
234+
getRemoteConnection(),
235+
onMessage(JsonParsed(AllOf(
236+
AtJsonPtr("/params/args/0/value", Eq("Hello, World!")),
237+
AtJsonPtr("/method", Eq("Runtime.consoleAPICalled"))))));
238+
}
149239

150240
EXPECT_CALL(getRemoteConnection(), onDisconnect());
151241

152242
send("Runtime.enable");
153243
run("console.log('Hello, World!');");
154244
}
155245

246+
INSTANTIATE_TEST_SUITE_P(
247+
ReactInstanceVaryingInspectorFlags,
248+
ReactInstanceIntegrationTestWithFlags,
249+
::testing::Values(
250+
FeatureFlags{
251+
.enableCxxInspectorPackagerConnection = true,
252+
.enableModernCDPRegistry = true},
253+
FeatureFlags{
254+
.enableCxxInspectorPackagerConnection = false,
255+
.enableModernCDPRegistry = false},
256+
FeatureFlags{
257+
.enableCxxInspectorPackagerConnection = true,
258+
.enableModernCDPRegistry = false}));
259+
156260
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#pragma once
99

10+
#include <folly/executors/QueuedImmediateExecutor.h>
1011
#include <folly/json.h>
1112
#include <gtest/gtest.h>
1213
#include <jsinspector-modern/InspectorInterfaces.h>
@@ -18,7 +19,14 @@
1819

1920
namespace facebook::react::jsinspector_modern {
2021

21-
class ReactInstanceIntegrationTest : public ::testing::Test {
22+
using namespace ::testing;
23+
24+
struct FeatureFlags {
25+
const bool enableCxxInspectorPackagerConnection = true;
26+
const bool enableModernCDPRegistry = true;
27+
};
28+
29+
class ReactInstanceIntegrationTest : public Test {
2230
protected:
2331
ReactInstanceIntegrationTest();
2432
void SetUp() override;
@@ -36,18 +44,32 @@ class ReactInstanceIntegrationTest : public ::testing::Test {
3644
std::unique_ptr<react::ReactInstance> instance;
3745
std::shared_ptr<MockMessageQueueThread> messageQueueThread;
3846
std::shared_ptr<ErrorUtils> errorHandler;
47+
std::unique_ptr<FeatureFlags> featureFlags;
3948

4049
MockRemoteConnection& getRemoteConnection() {
41-
return *mockRemoteConnections_[0];
50+
EXPECT_EQ(mockRemoteConnections_.objectsVended(), 1);
51+
auto rawPtr = mockRemoteConnections_[0];
52+
ASSERT(rawPtr != nullptr);
53+
return *rawPtr;
4254
}
4355

4456
private:
4557
void initializeRuntime(std::string_view script);
4658

4759
size_t id_ = 1;
4860
bool verbose_ = false;
61+
std::optional<int> pageId_;
4962
UniquePtrFactory<MockRemoteConnection> mockRemoteConnections_;
5063
std::unique_ptr<ILocalConnection> clientToVM_;
64+
folly::QueuedImmediateExecutor immediateExecutor_;
5165
};
5266

67+
class ReactInstanceIntegrationTestWithFlags
68+
: public ReactInstanceIntegrationTest,
69+
public ::testing::WithParamInterface<FeatureFlags> {
70+
void SetUp() override {
71+
featureFlags = std::make_unique<FeatureFlags>(GetParam());
72+
ReactInstanceIntegrationTest::SetUp();
73+
}
74+
};
5375
} // namespace facebook::react::jsinspector_modern

0 commit comments

Comments
 (0)