diff --git a/packages/react-native/ReactCommon/callinvoker/ReactCommon/tests/TestCallInvoker.h b/packages/react-native/ReactCommon/callinvoker/ReactCommon/tests/TestCallInvoker.h new file mode 100644 index 00000000000000..1c48519631b23d --- /dev/null +++ b/packages/react-native/ReactCommon/callinvoker/ReactCommon/tests/TestCallInvoker.h @@ -0,0 +1,47 @@ +/* + * 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 + +namespace facebook::react { + +class TestCallInvoker : public CallInvoker { + public: + explicit TestCallInvoker(std::shared_ptr runtime) + : runtime_(runtime) {} + + void invokeAsync(CallFunc&& func) noexcept override { + queue_.push_back(std::move(func)); + } + + void invokeSync(CallFunc&& func) override { + if (auto runtime = runtime_.lock()) { + func(*runtime); + } + } + + void flushQueue() { + if (auto runtime = runtime_.lock()) { + while (!queue_.empty()) { + queue_.front()(*runtime); + queue_.pop_front(); + runtime->drainMicrotasks(); // Run microtasks every cycle. + } + } + } + + private: + std::list queue_{}; + std::weak_ptr runtime_{}; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.h b/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.h index 7fef3c9726a258..973fcad5b127ef 100644 --- a/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.h +++ b/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -15,34 +16,24 @@ namespace facebook::react { -class TestCallInvoker : public CallInvoker { +class BridgingTest : public ::testing::Test { public: - void invokeAsync(CallFunc&& fn) noexcept override { - queue_.push_back(std::move(fn)); - } - - void invokeSync(CallFunc&&) override { - FAIL() << "JSCallInvoker does not support invokeSync()"; - } - - private: - friend class BridgingTest; + BridgingTest(BridgingTest& other) = delete; + BridgingTest& operator=(BridgingTest& other) = delete; + BridgingTest(BridgingTest&& other) = delete; + BridgingTest& operator=(BridgingTest&& other) = delete; - std::list queue_; -}; - -class BridgingTest : public ::testing::Test { protected: BridgingTest() - : invoker(std::make_shared()), - runtime(hermes::makeHermesRuntime( + : runtime(hermes::makeHermesRuntime( ::hermes::vm::RuntimeConfig::Builder() // Make promises work with Hermes microtasks. .withMicrotaskQueue(true) .build())), - rt(*runtime) {} + rt(*runtime), + invoker(std::make_shared(runtime)) {} - ~BridgingTest() { + ~BridgingTest() override { LongLivedObjectCollection::get(rt).clear(); } @@ -62,16 +53,12 @@ class BridgingTest : public ::testing::Test { } void flushQueue() { - while (!invoker->queue_.empty()) { - invoker->queue_.front()(*runtime); - invoker->queue_.pop_front(); - rt.drainMicrotasks(); // Run microtasks every cycle. - } + invoker->flushQueue(); } - std::shared_ptr invoker; - std::unique_ptr runtime; + std::shared_ptr runtime; jsi::Runtime& rt; + std::shared_ptr invoker; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/tests/TurboModuleTestFixture.h b/packages/react-native/ReactCommon/react/nativemodule/core/tests/TurboModuleTestFixture.h new file mode 100644 index 00000000000000..c0039e3405906f --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/core/tests/TurboModuleTestFixture.h @@ -0,0 +1,31 @@ +/* + * 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 + +namespace facebook::react { + +template +class TurboModuleTestFixture : public ::testing::Test { + public: + explicit TurboModuleTestFixture(Args... args) + : runtime_(facebook::hermes::makeHermesRuntime()), + jsInvoker_(std::make_shared(runtime_)), + module_(std::make_shared(jsInvoker_, std::forward(args)...)) {} + + protected: + std::shared_ptr runtime_{}; + std::shared_ptr jsInvoker_{}; + std::shared_ptr module_; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCxxPlatform/react/io/tests/NetworkingModuleTests.cpp b/packages/react-native/ReactCxxPlatform/react/io/tests/NetworkingModuleTests.cpp index 62b113d0989333..1ff920cb37f607 100644 --- a/packages/react-native/ReactCxxPlatform/react/io/tests/NetworkingModuleTests.cpp +++ b/packages/react-native/ReactCxxPlatform/react/io/tests/NetworkingModuleTests.cpp @@ -10,7 +10,7 @@ #include #endif -#include +#include #include #include #include @@ -21,25 +21,11 @@ namespace facebook::react { -class TestCallInvoker : public CallInvoker { - public: - void invokeAsync(CallFunc&& fn) noexcept override { - queue_.push_back(std::move(fn)); - } - - void invokeSync(CallFunc&& /*func*/) override { - FAIL() << "JSCallInvoker does not support invokeSync()"; - } - - private: - std::list queue_; -}; - class NetworkingModuleTests : public testing::Test { protected: void SetUp() override { rt_ = facebook::hermes::makeHermesRuntime(); - jsInvoker_ = std::make_shared(); + jsInvoker_ = std::make_shared(rt_); } static void verifyFormData( @@ -55,7 +41,7 @@ class NetworkingModuleTests : public testing::Test { } } - std::unique_ptr rt_; + std::shared_ptr rt_; std::shared_ptr jsInvoker_; }; diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec index 73162939e40daf..f8957da75cb570 100644 --- a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.compiler_flags = '-Wno-nullability-completeness' s.author = "Meta Platforms, Inc. and its affiliates" s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "#{s.version}" } - s.source_files = "**/*.{h,cpp}" + s.source_files = "*.{h,cpp}" s.requires_arc = true s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", diff --git a/packages/rn-tester/NativeCxxModuleExample/tests/NativeCxxModuleExampleTests.cpp b/packages/rn-tester/NativeCxxModuleExample/tests/NativeCxxModuleExampleTests.cpp new file mode 100644 index 00000000000000..b182b529151c32 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/tests/NativeCxxModuleExampleTests.cpp @@ -0,0 +1,166 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::react { + +class NativeCxxModuleExampleTests + : public TurboModuleTestFixture {}; + +TEST_F(NativeCxxModuleExampleTests, GetArrayReturnsCorrectValues) { + std::vector> empty; + EXPECT_EQ(module_->getArray(*runtime_, empty), empty); + + std::vector> withNull = {std::nullopt}; + EXPECT_EQ(module_->getArray(*runtime_, withNull), withNull); + + std::vector> withObj = {ObjectStruct{1, "2"}}; + auto result = module_->getArray(*runtime_, withObj); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result[0]->a, 1); + EXPECT_EQ(result[0]->b, "2"); +} + +TEST_F(NativeCxxModuleExampleTests, GetBoolReturnsCorrectValues) { + EXPECT_FALSE(module_->getBool(*runtime_, false)); + EXPECT_TRUE(module_->getBool(*runtime_, true)); +} + +TEST_F(NativeCxxModuleExampleTests, GetConstantsReturnsCorrectValues) { + auto constants = module_->getConstants(*runtime_); + EXPECT_TRUE(constants.const1); + EXPECT_EQ(constants.const2, 69); + EXPECT_EQ(constants.const3, "react-native"); +} + +TEST_F(NativeCxxModuleExampleTests, GetCustomEnumReturnsCorrectValue) { + EXPECT_EQ( + module_->getCustomEnum(*runtime_, CustomEnumInt::A), CustomEnumInt::A); +} + +TEST_F(NativeCxxModuleExampleTests, GetAndConsumeCustomHostObject) { + auto hostObj = module_->getCustomHostObject(*runtime_); + ASSERT_NE(hostObj, nullptr); + EXPECT_EQ(module_->consumeCustomHostObject(*runtime_, hostObj), "answer42"); +} + +TEST_F(NativeCxxModuleExampleTests, GetBinaryTreeNodeReturnsCorrectValues) { + auto result = module_->getBinaryTreeNode( + *runtime_, + BinaryTreeNode{ + .left = std::make_unique( + BinaryTreeNode{nullptr, 2, nullptr}), + .value = 4, + .right = std::make_unique( + BinaryTreeNode{nullptr, 6, nullptr})}); + ASSERT_NE(result.left, nullptr); + EXPECT_EQ(result.left->value, 2); + EXPECT_EQ(result.value, 4); + ASSERT_NE(result.right, nullptr); + EXPECT_EQ(result.right->value, 6); +} + +TEST_F(NativeCxxModuleExampleTests, GetGraphNodeReturnsCorrectValues) { + GraphNode input{ + .label = "root", + .neighbors = std::vector{ + GraphNode{.label = "child1"}, GraphNode{.label = "child2"}}}; + auto result = module_->getGraphNode(*runtime_, input); + EXPECT_EQ(result.label, "root"); + ASSERT_EQ(result.neighbors.value().size(), 4); + EXPECT_EQ(result.neighbors.value()[0].label, "child1"); + EXPECT_EQ(result.neighbors.value()[1].label, "child2"); + EXPECT_EQ(result.neighbors.value()[2].label, "top"); + EXPECT_EQ(result.neighbors.value()[3].label, "down"); +} + +TEST_F(NativeCxxModuleExampleTests, GetNumEnumReturnsCorrectValues) { + EXPECT_EQ( + module_->getNumEnum(*runtime_, NativeCxxModuleExampleEnumInt::IA), + NativeCxxModuleExampleEnumInt::IA); + EXPECT_EQ( + module_->getNumEnum(*runtime_, NativeCxxModuleExampleEnumInt::IB), + NativeCxxModuleExampleEnumInt::IB); +} + +TEST_F(NativeCxxModuleExampleTests, GetStrEnumReturnsCorrectValues) { + EXPECT_EQ( + module_->getStrEnum(*runtime_, NativeCxxModuleExampleEnumNone::NA), + NativeCxxModuleExampleEnumStr::SB); + EXPECT_EQ( + module_->getStrEnum(*runtime_, NativeCxxModuleExampleEnumNone::NB), + NativeCxxModuleExampleEnumStr::SB); +} + +TEST_F(NativeCxxModuleExampleTests, GetMapReturnsCorrectValues) { + std::map> input = { + {"a", 0}, {"b", std::nullopt}, {"c", 3}}; + auto result = module_->getMap(*runtime_, input); + EXPECT_EQ(result["a"], 0); + EXPECT_EQ(result["b"], std::nullopt); + EXPECT_EQ(result["c"], 3); +} + +TEST_F(NativeCxxModuleExampleTests, GetNumberReturnsCorrectValues) { + EXPECT_EQ(module_->getNumber(*runtime_, 0), 0); + EXPECT_EQ(module_->getNumber(*runtime_, pow(2, 53)), pow(2, 53)); +} + +TEST_F(NativeCxxModuleExampleTests, GetObjectReturnsCorrectValues) { + ObjectStruct input1{2, "two"}; + auto result1 = module_->getObject(*runtime_, input1); + EXPECT_EQ(result1.a, 2); + EXPECT_EQ(result1.b, "two"); + ObjectStruct input2{4, "four", "seven"}; + auto result2 = module_->getObject(*runtime_, input2); + EXPECT_EQ(result2.a, 4); + EXPECT_EQ(result2.b, "four"); + EXPECT_EQ(result2.c, "seven"); +} + +TEST_F(NativeCxxModuleExampleTests, GetSetReturnsCorrectValues) { + std::set input = {1, 2, 3, 3, 3}; + auto result = module_->getSet(*runtime_, input); + EXPECT_EQ(result.size(), 3); + EXPECT_TRUE(result.count(1)); + EXPECT_TRUE(result.count(2)); + EXPECT_TRUE(result.count(3)); +} + +TEST_F(NativeCxxModuleExampleTests, GetStringReturnsCorrectValues) { + EXPECT_EQ(module_->getString(*runtime_, ""), ""); + EXPECT_EQ(module_->getString(*runtime_, "string"), "string"); +} + +TEST_F(NativeCxxModuleExampleTests, GetValueReturnsCorrectValues) { + ObjectStruct z{4, "four", "seven"}; + auto result = module_->getValue(*runtime_, 23, "forty-two", z); + EXPECT_EQ(result.x, 23); + EXPECT_EQ(result.y, "forty-two"); + EXPECT_EQ(result.z.a, 4); + EXPECT_EQ(result.z.b, "four"); + EXPECT_EQ(result.z.c, "seven"); +} + +TEST_F( + NativeCxxModuleExampleTests, + GetWithWithOptionalArgsReturnsCorrectValues) { + EXPECT_EQ( + module_->getWithWithOptionalArgs(*runtime_, std::nullopt), std::nullopt); + EXPECT_EQ(module_->getWithWithOptionalArgs(*runtime_, true), true); + EXPECT_EQ(module_->getWithWithOptionalArgs(*runtime_, false), false); +} + +} // namespace facebook::react