From 121b26184acbb77ff4f2360647cb322ff560b145 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Fri, 15 Mar 2024 04:23:57 -0700 Subject: [PATCH] Make JsiIntegrationPortableTest reusable in other test suites Summary: Changelog: [Internal] TSIA - useful for the `console` test suite I'm currently writing (D54846940) which would be awkward to fit into `JsiIntegrationTest.cpp`. bypass-github-export-checks Reviewed By: huntie Differential Revision: D54846938 fbshipit-source-id: 9e20bfae7c518b3822da468adf484e51cdc4d0b9 --- .../tests/JsiIntegrationTest.cpp | 146 +--------------- .../tests/JsiIntegrationTest.h | 158 ++++++++++++++++++ 2 files changed, 159 insertions(+), 145 deletions(-) create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.h diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp index 748a67c9645..447570de986 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp @@ -6,20 +6,8 @@ */ #include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "FollyDynamicMatchers.h" -#include "InspectorMocks.h" -#include "UniquePtrFactory.h" +#include "JsiIntegrationTest.h" #include "engines/JsiIntegrationTestGenericEngineAdapter.h" #include "engines/JsiIntegrationTestHermesEngineAdapter.h" #include "engines/JsiIntegrationTestHermesWithCDPAgentEngineAdapter.h" @@ -29,138 +17,6 @@ using folly::sformat; namespace facebook::react::jsinspector_modern { -namespace { - -/** - * A text fixture class for the integration between the modern RN CDP backend - * and a JSI engine, mocking out the rest of RN. For simplicity, everything is - * single-threaded and "async" work is actually done through a queued immediate - * executor ( = run immediately and finish all queued sub-tasks before - * returning). - * - * The main limitation of the simpler threading model is that we can't cover - * breakpoints etc - since pausing during JS execution would prevent the test - * from making progress. Such functionality is better suited for a full RN+CDP - * integration test (using RN's own thread management) as well as for each - * engine's unit tests. - * - * \tparam EngineAdapter An adapter class that implements RuntimeTargetDelegate - * for a particular engine, plus exposes access to a RuntimeExecutor (based on - * the provided folly::Executor) and the corresponding jsi::Runtime. - */ -template -class JsiIntegrationPortableTest : public Test, private HostTargetDelegate { - folly::QueuedImmediateExecutor immediateExecutor_; - - protected: - JsiIntegrationPortableTest() - : inspectorFlagsGuard_{EngineAdapter::getInspectorFlagOverrides()}, - engineAdapter_{immediateExecutor_} { - instance_ = &page_->registerInstance(instanceTargetDelegate_); - runtimeTarget_ = &instance_->registerRuntime( - engineAdapter_->getRuntimeTargetDelegate(), - engineAdapter_->getRuntimeExecutor()); - } - - ~JsiIntegrationPortableTest() override { - toPage_.reset(); - if (runtimeTarget_) { - EXPECT_TRUE(instance_); - instance_->unregisterRuntime(*runtimeTarget_); - runtimeTarget_ = nullptr; - } - if (instance_) { - page_->unregisterInstance(*instance_); - instance_ = nullptr; - } - } - - void connect() { - ASSERT_FALSE(toPage_) << "Can only connect once in a JSI integration test."; - toPage_ = page_->connect( - remoteConnections_.make_unique(), - {.integrationName = "JsiIntegrationTest"}); - - // We'll always get an onDisconnect call when we tear - // down the test. Expect it in order to satisfy the strict mock. - EXPECT_CALL(*remoteConnections_[0], onDisconnect()); - } - - void reload() { - if (runtimeTarget_) { - ASSERT_TRUE(instance_); - instance_->unregisterRuntime(*runtimeTarget_); - runtimeTarget_ = nullptr; - } - if (instance_) { - page_->unregisterInstance(*instance_); - instance_ = nullptr; - } - // Recreate the engine (e.g. to wipe any state in the inner jsi::Runtime) - engineAdapter_.emplace(immediateExecutor_); - instance_ = &page_->registerInstance(instanceTargetDelegate_); - runtimeTarget_ = &instance_->registerRuntime( - engineAdapter_->getRuntimeTargetDelegate(), - engineAdapter_->getRuntimeExecutor()); - } - - MockRemoteConnection& fromPage() { - assert(toPage_); - return *remoteConnections_[0]; - } - - VoidExecutor inspectorExecutor_ = [this](auto callback) { - immediateExecutor_.add(callback); - }; - - jsi::Value eval(std::string_view code) { - return engineAdapter_->getRuntime().evaluateJavaScript( - std::make_shared(std::string(code)), ""); - } - - /** - * Expect a message matching the provided gmock \c matcher and return a holder - * that will eventually contain the parsed JSON payload. - */ - template - std::shared_ptr> expectMessageFromPage( - Matcher&& matcher) { - std::shared_ptr result = - std::make_shared>(std::nullopt); - EXPECT_CALL(fromPage(), onMessage(matcher)) - .WillOnce( - ([result](auto message) { *result = folly::parseJson(message); })) - .RetiresOnSaturation(); - return result; - } - - std::shared_ptr page_ = - HostTarget::create(*this, inspectorExecutor_); - InstanceTarget* instance_{}; - RuntimeTarget* runtimeTarget_{}; - - InspectorFlagOverridesGuard inspectorFlagsGuard_; - MockInstanceTargetDelegate instanceTargetDelegate_; - std::optional engineAdapter_; - - private: - UniquePtrFactory> remoteConnections_; - - protected: - // NOTE: Needs to be destroyed before page_. - std::unique_ptr toPage_; - - private: - // HostTargetDelegate methods - - void onReload(const PageReloadRequest& request) override { - (void)request; - reload(); - } -}; - -} // namespace - //////////////////////////////////////////////////////////////////////////////// // Some tests are specific to Hermes's CDP capabilities and some are not. diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.h b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.h new file mode 100644 index 00000000000..5f0d9e572a9 --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "FollyDynamicMatchers.h" +#include "InspectorMocks.h" +#include "UniquePtrFactory.h" +#include "utils/InspectorFlagOverridesGuard.h" + +namespace facebook::react::jsinspector_modern { + +/** + * A text fixture class for the integration between the modern RN CDP backend + * and a JSI engine, mocking out the rest of RN. For simplicity, everything is + * single-threaded and "async" work is actually done through a queued immediate + * executor ( = run immediately and finish all queued sub-tasks before + * returning). + * + * The main limitation of the simpler threading model is that we can't cover + * breakpoints etc - since pausing during JS execution would prevent the test + * from making progress. Such functionality is better suited for a full RN+CDP + * integration test (using RN's own thread management) as well as for each + * engine's unit tests. + * + * \tparam EngineAdapter An adapter class that implements RuntimeTargetDelegate + * for a particular engine, plus exposes access to a RuntimeExecutor (based on + * the provided folly::Executor) and the corresponding jsi::Runtime. + */ +template +class JsiIntegrationPortableTest : public ::testing::Test, + private HostTargetDelegate { + folly::QueuedImmediateExecutor immediateExecutor_; + + protected: + JsiIntegrationPortableTest() + : inspectorFlagsGuard_{EngineAdapter::getInspectorFlagOverrides()}, + engineAdapter_{immediateExecutor_} { + instance_ = &page_->registerInstance(instanceTargetDelegate_); + runtimeTarget_ = &instance_->registerRuntime( + engineAdapter_->getRuntimeTargetDelegate(), + engineAdapter_->getRuntimeExecutor()); + } + + ~JsiIntegrationPortableTest() override { + toPage_.reset(); + if (runtimeTarget_) { + EXPECT_TRUE(instance_); + instance_->unregisterRuntime(*runtimeTarget_); + runtimeTarget_ = nullptr; + } + if (instance_) { + page_->unregisterInstance(*instance_); + instance_ = nullptr; + } + } + + void connect() { + ASSERT_FALSE(toPage_) << "Can only connect once in a JSI integration test."; + toPage_ = page_->connect( + remoteConnections_.make_unique(), + {.integrationName = "JsiIntegrationTest"}); + + // We'll always get an onDisconnect call when we tear + // down the test. Expect it in order to satisfy the strict mock. + EXPECT_CALL(*remoteConnections_[0], onDisconnect()); + } + + void reload() { + if (runtimeTarget_) { + ASSERT_TRUE(instance_); + instance_->unregisterRuntime(*runtimeTarget_); + runtimeTarget_ = nullptr; + } + if (instance_) { + page_->unregisterInstance(*instance_); + instance_ = nullptr; + } + // Recreate the engine (e.g. to wipe any state in the inner jsi::Runtime) + engineAdapter_.emplace(immediateExecutor_); + instance_ = &page_->registerInstance(instanceTargetDelegate_); + runtimeTarget_ = &instance_->registerRuntime( + engineAdapter_->getRuntimeTargetDelegate(), + engineAdapter_->getRuntimeExecutor()); + } + + MockRemoteConnection& fromPage() { + assert(toPage_); + return *remoteConnections_[0]; + } + + VoidExecutor inspectorExecutor_ = [this](auto callback) { + immediateExecutor_.add(callback); + }; + + jsi::Value eval(std::string_view code) { + return engineAdapter_->getRuntime().evaluateJavaScript( + std::make_shared(std::string(code)), ""); + } + + /** + * Expect a message matching the provided gmock \c matcher and return a holder + * that will eventually contain the parsed JSON payload. + */ + template + std::shared_ptr> expectMessageFromPage( + Matcher&& matcher) { + std::shared_ptr result = + std::make_shared>(std::nullopt); + EXPECT_CALL(fromPage(), onMessage(matcher)) + .WillOnce( + ([result](auto message) { *result = folly::parseJson(message); })) + .RetiresOnSaturation(); + return result; + } + + std::shared_ptr page_ = + HostTarget::create(*this, inspectorExecutor_); + InstanceTarget* instance_{}; + RuntimeTarget* runtimeTarget_{}; + + InspectorFlagOverridesGuard inspectorFlagsGuard_; + MockInstanceTargetDelegate instanceTargetDelegate_; + std::optional engineAdapter_; + + private: + UniquePtrFactory<::testing::StrictMock> + remoteConnections_; + + protected: + // NOTE: Needs to be destroyed before page_. + std::unique_ptr toPage_; + + private: + // HostTargetDelegate methods + + void onReload(const PageReloadRequest& request) override { + (void)request; + reload(); + } +}; + +} // namespace facebook::react::jsinspector_modern