diff --git a/change/react-native-windows-2020-05-11-11-06-42-FixReactContext.json b/change/react-native-windows-2020-05-11-11-06-42-FixReactContext.json new file mode 100644 index 00000000000..d3bc77290fd --- /dev/null +++ b/change/react-native-windows-2020-05-11-11-06-42-FixReactContext.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Fixed ReactContext copy/move semantic", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-11T18:06:42.425Z" +} diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj index 1849911ed19..79d66fa1e3a 100644 --- a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj @@ -126,6 +126,7 @@ Create + diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactContextTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactContextTest.cpp new file mode 100644 index 00000000000..c2d28914c93 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactContextTest.cpp @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include +#include "ReactModuleBuilderMock.h" + +using namespace winrt::Microsoft::ReactNative; + +struct ReactContextStub : implements { + IReactPropertyBag Properties() noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } + + void DispatchEvent( + xaml::FrameworkElement const & /*view*/, + hstring const & /*eventName*/, + JSValueArgWriter const & /*eventDataArgWriter*/) noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } + + void CallJSFunction( + hstring const & /*moduleName*/, + hstring const & /*functionName*/, + JSValueArgWriter const & /*paramsArgWriter*/) noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } + + void EmitJSEvent( + hstring const & /*eventEmitterName*/, + hstring const & /*eventName*/, + JSValueArgWriter const & /*paramsArgWriter*/) noexcept { + VerifyElseCrashSz(false, "Not implemented"); + } +}; + +namespace ReactNativeTests { + +TEST_CLASS (ReactContextTest) { + TEST_METHOD(Test_ctor_Default) { + ReactContext context; + TestCheck(!context); + } + + TEST_METHOD(Test_ctor_IReactContext) { + auto reactContextMock = winrt::make(); + ReactContext context{reactContextMock}; + TestCheck(context); + } + + TEST_METHOD(Test_ctor_copy) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2{context1}; + TestCheck(context1); + TestCheck(context2); + TestCheckEqual(context1, context2); + } + + TEST_METHOD(Test_ctor_move) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2{std::move(context1)}; + TestCheck(!context1); + TestCheck(context2); + } + + TEST_METHOD(Test_assign_nullptr) { + ReactContext context; + context = nullptr; + TestCheck(!context); + } + + TEST_METHOD(Test_assign_copy) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2; + context2 = context1; + TestCheck(context1); + TestCheck(context2); + TestCheckEqual(context1, context2); + } + + TEST_METHOD(Test_assign_move) { + auto reactContextMock = winrt::make(); + ReactContext context1{reactContextMock}; + ReactContext context2; + context2 = std::move(context1); + TestCheck(!context1); + TestCheck(context2); + } + + TEST_METHOD(Test_compare) { + auto reactContextMock1 = winrt::make(); + auto reactContextMock2 = winrt::make(); + ReactContext context11{reactContextMock1}; + ReactContext context12{context11}; + ReactContext context2{reactContextMock2}; + ReactContext context3; + TestCheck(context11 == context12); + TestCheck(context12 == context11); + TestCheck(context11 != context2); + TestCheck(context11 != context3); + TestCheck(context2 != context11); + TestCheck(context3 != context11); + TestCheck(context3 == nullptr); + TestCheck(nullptr == context3); + TestCheck(context11 != nullptr); + TestCheck(nullptr != context11); + } +}; + +} // namespace ReactNativeTests diff --git a/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h b/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h index 547ac7ead65..80b6cb68748 100644 --- a/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h +++ b/vnext/Microsoft.ReactNative.Cxx/JSValueWriter.h @@ -5,8 +5,9 @@ #ifndef MICROSOFT_REACTNATIVE_JSVALUEWRITER #define MICROSOFT_REACTNATIVE_JSVALUEWRITER +#include +#include "JSValue.h" #include "StructInfo.h" -#include "winrt/Microsoft.ReactNative.h" namespace winrt::Microsoft::ReactNative { diff --git a/vnext/Microsoft.ReactNative.Cxx/ReactContext.h b/vnext/Microsoft.ReactNative.Cxx/ReactContext.h index a40c869f3c6..3dc09ee14ad 100644 --- a/vnext/Microsoft.ReactNative.Cxx/ReactContext.h +++ b/vnext/Microsoft.ReactNative.Cxx/ReactContext.h @@ -16,6 +16,8 @@ namespace winrt::Microsoft::ReactNative { // It wraps up the IReactContext and adds convenience methods for // working with C++ types. struct ReactContext { + ReactContext(std::nullptr_t = nullptr) noexcept {} + ReactContext(IReactContext const &handle) noexcept : m_handle{handle} {} IReactContext const &Handle() const noexcept { @@ -66,8 +68,32 @@ struct ReactContext { m_handle.DispatchEvent(view, eventName, paramsArgWriter); } + friend bool operator==(ReactContext const &left, ReactContext const &right) noexcept { + return left.m_handle == right.m_handle; + } + + friend bool operator!=(ReactContext const &left, ReactContext const &right) noexcept { + return left.m_handle != right.m_handle; + } + + friend bool operator==(ReactContext const &left, std::nullptr_t) noexcept { + return !static_cast(left.m_handle); + } + + friend bool operator!=(ReactContext const &left, std::nullptr_t) noexcept { + return static_cast(left.m_handle); + } + + friend bool operator==(std::nullptr_t, ReactContext const &right) noexcept { + return !static_cast(right.m_handle); + } + + friend bool operator!=(std::nullptr_t, ReactContext const &right) noexcept { + return static_cast(right.m_handle); + } + private: - const IReactContext m_handle; + IReactContext m_handle; }; } // namespace winrt::Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp b/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp index 11d16009c32..2f11c1d9291 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp +++ b/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp @@ -1,5 +1,7 @@ #include "pch.h" +#include + using namespace React; namespace ReactNativeIntegrationTests { diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp b/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp index 7553d4be66a..27168257d81 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp +++ b/vnext/Microsoft.ReactNative.IntegrationTests/ReactPropertyBagTests.cpp @@ -491,7 +491,6 @@ TEST_CLASS (ReactPropertyBagTests) { } TEST_METHOD(PropertyBag_Property_string) { - // We only support enums defined in IDL. ReactPropertyId fooName{L"Foo"}; ReactPropertyBag pb{ReactPropertyBagHelper::CreatePropertyBag()}; @@ -504,6 +503,25 @@ TEST_CLASS (ReactPropertyBagTests) { pb.Remove(fooName); TestCheck(!pb.Get(fooName)); } + + TEST_METHOD(PropertyBag_Property_delegate) { + ReactPropertyId fooName{L"Foo"}; + ReactPropertyBag pb{ReactPropertyBagHelper::CreatePropertyBag()}; + + TestCheck(!pb.Get(fooName)); + ReactCreatePropertyValue createValue1 = []() { return winrt::box_value(5); }; + TestCheckEqual(createValue1, *pb.GetOrCreate(fooName, [&createValue1]() { return createValue1; })); + TestCheckEqual(createValue1, *pb.Get(fooName)); + TestCheckEqual(5, winrt::unbox_value((*pb.Get(fooName))())); + + ReactCreatePropertyValue createValue2 = []() { return winrt::box_value(10); }; + pb.Set(fooName, createValue2); + TestCheckEqual(createValue2, *pb.Get(fooName)); + TestCheckEqual(10, winrt::unbox_value((*pb.Get(fooName))())); + TestCheck(createValue1 != *pb.Get(fooName)); + pb.Remove(fooName); + TestCheck(!pb.Get(fooName)); + } }; } // namespace ReactNativeIntegrationTests diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/pch.h b/vnext/Microsoft.ReactNative.IntegrationTests/pch.h index cde08a53662..19f70f55b7b 100644 --- a/vnext/Microsoft.ReactNative.IntegrationTests/pch.h +++ b/vnext/Microsoft.ReactNative.IntegrationTests/pch.h @@ -3,7 +3,6 @@ #define NOMINMAX #include -#include "NativeModules.h" #include "functional/functor.h" #include "motifCpp/testCheck.h"