12
12
13
13
#include < folly/json.h>
14
14
#include < glog/logging.h>
15
+ #include < jsinspector-modern/InspectorFlags.h>
16
+ #include < react/featureflags/ReactNativeFeatureFlags.h>
17
+ #include < react/featureflags/ReactNativeFeatureFlagsDefaults.h>
15
18
#include < react/runtime/hermes/HermesInstance.h>
16
19
17
20
namespace facebook ::react::jsinspector_modern {
18
21
19
22
using namespace ::testing;
20
23
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
+
21
42
ReactInstanceIntegrationTest::ReactInstanceIntegrationTest ()
22
43
: runtime(nullptr ),
23
44
instance (nullptr ),
24
45
messageQueueThread(std::make_shared<MockMessageQueueThread>()),
25
- errorHandler(std::make_shared<ErrorUtils>()) {}
46
+ errorHandler(std::make_shared<ErrorUtils>()),
47
+ featureFlags(std::make_unique<FeatureFlags>()) {}
26
48
27
49
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
+
28
62
auto mockRegistry = std::make_unique<MockTimerRegistry>();
29
63
auto timerManager =
30
64
std::make_shared<react::TimerManager>(std::move (mockRegistry));
@@ -47,30 +81,78 @@ void ReactInstanceIntegrationTest::SetUp() {
47
81
" ErrorUtils" ,
48
82
jsi::Object::createFromHostObject (*jsiRuntime, errorHandler));
49
83
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
+
50
95
instance = std::make_unique<react::ReactInstance>(
51
96
std::move (runtime_),
52
97
messageQueueThread,
53
98
timerManager,
54
- std::move (jsErrorHandlingFunc));
99
+ std::move (jsErrorHandlingFunc),
100
+ hostTargetIfModernCDP == nullptr ? nullptr : hostTargetIfModernCDP.get ());
101
+
55
102
timerManager->setRuntimeExecutor (instance->getBufferedRuntimeExecutor ());
56
103
57
104
// JS Environment:
58
105
initializeRuntime (preludeJsCode);
59
106
60
107
// Inspector:
61
108
auto & inspector = getInspectorInstance ();
62
- auto pages = inspector.getPages ();
63
109
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
+ }
68
134
69
- clientToVM_ = inspector.connect (pageId, mockRemoteConnections_.make_unique ());
135
+ clientToVM_ =
136
+ inspector.connect (pageId_.value (), mockRemoteConnections_.make_unique ());
137
+
138
+ ASSERT_NE (clientToVM_, nullptr );
70
139
}
71
140
72
141
void ReactInstanceIntegrationTest::TearDown () {
73
142
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 ();
74
156
}
75
157
76
158
void ReactInstanceIntegrationTest::initializeRuntime (std::string_view script) {
@@ -84,8 +166,6 @@ void ReactInstanceIntegrationTest::initializeRuntime(std::string_view script) {
84
166
std::string init (script);
85
167
// JS calls no longer buffered after calling loadScript
86
168
instance->loadScript (std::make_unique<react::JSBigStdString>(init), " " );
87
-
88
- messageQueueThread->flush ();
89
169
}
90
170
91
171
void ReactInstanceIntegrationTest::send (
@@ -134,23 +214,47 @@ TEST_F(ReactInstanceIntegrationTest, RuntimeEvalTest) {
134
214
EXPECT_EQ (val.asNumber (), 3 );
135
215
}
136
216
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) {
144
218
EXPECT_CALL (
145
219
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
+ }
149
239
150
240
EXPECT_CALL (getRemoteConnection (), onDisconnect ());
151
241
152
242
send (" Runtime.enable" );
153
243
run (" console.log('Hello, World!');" );
154
244
}
155
245
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
+
156
260
} // namespace facebook::react::jsinspector_modern
0 commit comments