diff --git a/change/react-native-windows-2019-12-11-12-44-46-JSValue_Read_Write.json b/change/react-native-windows-2019-12-11-12-44-46-JSValue_Read_Write.json new file mode 100644 index 00000000000..6da3069cc6f --- /dev/null +++ b/change/react-native-windows-2019-12-11-12-44-46-JSValue_Read_Write.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Strongly typed value serialization and deserialization using IJSValueReader, JSValue, and IJSValueWriter", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "commit": "d7599a1fbbb88300ccd2b3e280b74fda9f41f061", + "date": "2019-12-11T20:44:46.143Z" +} \ No newline at end of file diff --git a/packages/microsoft-reactnative-sampleapps/index.windows.js b/packages/microsoft-reactnative-sampleapps/index.windows.js index 95cf4f33a0a..cafab488a58 100644 --- a/packages/microsoft-reactnative-sampleapps/index.windows.js +++ b/packages/microsoft-reactnative-sampleapps/index.windows.js @@ -38,6 +38,12 @@ var getCallback = function(prefix) { }; }; +var getErrorCallback = function(prefix) { + return function(error) { + log(prefix + (error || {}).message); + }; +}; + class SampleApp extends Component { componentDidMount() { this._TimedEventCSSub = SampleModuleCSEmitter.addListener('TimedEventCS', getCallback('SampleModuleCS.TimedEventCS() => ')); @@ -77,10 +83,10 @@ class SampleApp extends Component { NativeModules.SampleModuleCS.ExplicitCallbackMethodWithArgs(numberArg, getCallback('SampleModuleCS.ExplicitCallbackMethodWithArgs => ')); var promise1 = NativeModules.SampleModuleCS.ExplicitPromiseMethod(); - promise1.then(getCallback('SampleModuleCS.ExplicitPromiseMethod then => ')).catch(getCallback('SampleModuleCS.ExplicitPromiseMethod catch => ')); + promise1.then(getCallback('SampleModuleCS.ExplicitPromiseMethod then => ')).catch(getErrorCallback('SampleModuleCS.ExplicitPromiseMethod catch => ')); var promise2 = NativeModules.SampleModuleCS.ExplicitPromiseMethodWithArgs(numberArg); - promise2.then(getCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs then => ')).catch(getCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs catch => ')); + promise2.then(getCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs then => ')).catch(getErrorCallback('SampleModuleCS.ExplicitPromiseMethodWithArgs catch => ')); log('SampleModuleCS.SyncReturnMethod => ' + NativeModules.SampleModuleCS.SyncReturnMethod()); @@ -115,10 +121,10 @@ class SampleApp extends Component { NativeModules.SampleModuleCPP.ExplicitCallbackMethodWithArgs(numberArg, getCallback('SampleModuleCPP.ExplicitCallbackMethodWithArgs => ')); var promise1 = NativeModules.SampleModuleCPP.ExplicitPromiseMethod(); - promise1.then(getCallback('SampleModuleCPP.ExplicitPromiseMethod then => ')).catch(getCallback('SampleModuleCPP.ExplicitPromiseMethod catch => ')); + promise1.then(getCallback('SampleModuleCPP.ExplicitPromiseMethod then => ')).catch(getErrorCallback('SampleModuleCPP.ExplicitPromiseMethod catch => ')); var promise2 = NativeModules.SampleModuleCPP.ExplicitPromiseMethodWithArgs(numberArg); - promise2.then(getCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs then => ')).catch(getCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs catch => ')); + promise2.then(getCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs then => ')).catch(getErrorCallback('SampleModuleCPP.ExplicitPromiseMethodWithArgs catch => ')); log('SampleModuleCPP.SyncReturnMethod => ' + NativeModules.SampleModuleCPP.SyncReturnMethod()); @@ -191,7 +197,6 @@ class SampleApp extends Component { CircleCPP! - Hello from Microsoft! diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp index 96a1bd527cf..e6dd0f47b3c 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/ReactPackageProvider.cpp @@ -6,7 +6,6 @@ #include "DebugConsole.h" -using namespace Microsoft::ReactNative; using namespace winrt::Microsoft::ReactNative::Bridge; namespace winrt::SampleApp::implementation { diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters index 87afcd43391..872971f9b86 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleAppCPP/SampleApp.vcxproj.filters @@ -44,9 +44,6 @@ - - - {e48dc53e-40b1-40cb-970a-f89935452892} @@ -54,6 +51,7 @@ + diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln b/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln index 36c9235cc8f..bdef09722ff 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleApps.sln @@ -46,14 +46,20 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\..\..\vnext\Co EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx.UnitTests", "..\..\..\vnext\Microsoft.ReactNative.Cxx.UnitTests\Microsoft.ReactNative.Cxx.UnitTests.vcxproj", "{6C60E295-C8CA-4DC5-B8BE-09888F58B249}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.ReactNative.Managed.UnitTests", "..\..\..\vnext\Microsoft.ReactNative.Managed.UnitTests\Microsoft.ReactNative.Managed.UnitTests.csproj", "{46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{09f4e6c1-2d12-4059-aa96-0b190861fd6a}*SharedItemsImports = 4 ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9 ..\..\..\vnext\Chakra\Chakra.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 ..\..\..\vnext\Shared\Shared.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 + ..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{46d76f7a-8fd9-4a7d-8102-2857e5da6b84}*SharedItemsImports = 4 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{47eec7f3-40d3-49ba-82c1-eaf103b54215}*SharedItemsImports = 4 ..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13 + ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{6c60e295-c8ca-4dc5-b8be-09888f58b249}*SharedItemsImports = 4 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{93f7572c-64b9-4096-9ef9-6ba0ede2b50d}*SharedItemsImports = 4 ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4 ..\..\..\vnext\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{c0a6bd9c-3ee5-4b12-8ce4-cee95178539c}*SharedItemsImports = 4 @@ -62,9 +68,11 @@ Global EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection @@ -72,6 +80,9 @@ Global {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM.ActiveCfg = Debug|ARM {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM.Build.0 = Debug|ARM {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM.Deploy.0 = Debug|ARM + {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM64.Build.0 = Debug|ARM64 + {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|ARM64.Deploy.0 = Debug|ARM64 {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|x64.ActiveCfg = Debug|x64 {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|x64.Build.0 = Debug|x64 {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Debug|x64.Deploy.0 = Debug|x64 @@ -81,6 +92,9 @@ Global {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM.ActiveCfg = Release|ARM {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM.Build.0 = Release|ARM {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM.Deploy.0 = Release|ARM + {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM64.ActiveCfg = Release|ARM64 + {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM64.Build.0 = Release|ARM64 + {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|ARM64.Deploy.0 = Release|ARM64 {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|x64.ActiveCfg = Release|x64 {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|x64.Build.0 = Release|x64 {09F4E6C1-2D12-4059-AA96-0B190861FD6A}.Release|x64.Deploy.0 = Release|x64 @@ -90,6 +104,7 @@ Global {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM.ActiveCfg = Debug|ARM {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM.Build.0 = Debug|ARM {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM.Deploy.0 = Debug|ARM + {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|ARM64.ActiveCfg = Debug|Win32 {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|x64.ActiveCfg = Debug|x64 {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|x64.Build.0 = Debug|x64 {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Debug|x64.Deploy.0 = Debug|x64 @@ -99,6 +114,7 @@ Global {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM.ActiveCfg = Release|ARM {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM.Build.0 = Release|ARM {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM.Deploy.0 = Release|ARM + {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|ARM64.ActiveCfg = Release|Win32 {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x64.ActiveCfg = Release|x64 {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x64.Build.0 = Release|x64 {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x64.Deploy.0 = Release|x64 @@ -107,112 +123,166 @@ Global {93F7572C-64B9-4096-9EF9-6BA0EDE2B50D}.Release|x86.Deploy.0 = Release|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|ARM.ActiveCfg = Debug|ARM {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|ARM.Build.0 = Debug|ARM + {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|ARM64.ActiveCfg = Debug|Win32 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x64.ActiveCfg = Debug|x64 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x64.Build.0 = Debug|x64 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x86.ActiveCfg = Debug|Win32 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Debug|x86.Build.0 = Debug|Win32 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|ARM.ActiveCfg = Release|ARM {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|ARM.Build.0 = Release|ARM + {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|ARM64.ActiveCfg = Release|Win32 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x64.ActiveCfg = Release|x64 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x64.Build.0 = Release|x64 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x86.ActiveCfg = Release|Win32 {2D5D43D9-CFFC-4C40-B4CD-02EFB4E2742B}.Release|x86.Build.0 = Release|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32 {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM + {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|Win32 {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64 {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64 {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32 {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32 {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM + {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|Win32 {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64 {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64 {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32 {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|ARM.ActiveCfg = Debug|ARM {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|ARM.Build.0 = Debug|ARM + {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|ARM64.ActiveCfg = Debug|x86 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x64.ActiveCfg = Debug|x64 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x64.Build.0 = Debug|x64 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x86.ActiveCfg = Debug|x86 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Debug|x86.Build.0 = Debug|x86 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|ARM.ActiveCfg = Release|ARM {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|ARM.Build.0 = Release|ARM + {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|ARM64.ActiveCfg = Release|x86 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x64.ActiveCfg = Release|x64 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x64.Build.0 = Release|x64 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x86.ActiveCfg = Release|x86 {C0A6BD9C-3EE5-4B12-8CE4-CEE95178539C}.Release|x86.Build.0 = Release|x86 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|ARM.ActiveCfg = Debug|ARM {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|ARM.Build.0 = Debug|ARM + {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|ARM64.ActiveCfg = Debug|Win32 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x64.ActiveCfg = Debug|x64 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x64.Build.0 = Debug|x64 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x86.ActiveCfg = Debug|Win32 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Debug|x86.Build.0 = Debug|Win32 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|ARM.ActiveCfg = Release|ARM {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|ARM.Build.0 = Release|ARM + {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|ARM64.ActiveCfg = Release|Win32 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x64.ActiveCfg = Release|x64 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x64.Build.0 = Release|x64 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x86.ActiveCfg = Release|Win32 {47EEC7F3-40D3-49BA-82C1-EAF103B54215}.Release|x86.Build.0 = Release|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM + {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM + {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|ARM.ActiveCfg = Debug|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|ARM64.ActiveCfg = Debug|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x64.ActiveCfg = Debug|x64 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x64.Build.0 = Debug|x64 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x86.ActiveCfg = Debug|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Debug|x86.Build.0 = Debug|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|ARM.ActiveCfg = Release|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|ARM64.ActiveCfg = Release|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x64.ActiveCfg = Release|x64 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x64.Build.0 = Release|x64 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x86.ActiveCfg = Release|Win32 + {6C60E295-C8CA-4DC5-B8BE-09888F58B249}.Release|x86.Build.0 = Release|Win32 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM.ActiveCfg = Debug|ARM + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM.Build.0 = Debug|ARM + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM.Deploy.0 = Debug|ARM + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM64.Build.0 = Debug|ARM64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x64.ActiveCfg = Debug|x64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x64.Build.0 = Debug|x64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x64.Deploy.0 = Debug|x64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x86.ActiveCfg = Debug|x86 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x86.Build.0 = Debug|x86 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Debug|x86.Deploy.0 = Debug|x86 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM.ActiveCfg = Release|ARM + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM.Build.0 = Release|ARM + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM.Deploy.0 = Release|ARM + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM64.ActiveCfg = Release|ARM64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM64.Build.0 = Release|ARM64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|ARM64.Deploy.0 = Release|ARM64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x64.ActiveCfg = Release|x64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x64.Build.0 = Release|x64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x64.Deploy.0 = Release|x64 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x86.ActiveCfg = Release|x86 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x86.Build.0 = Release|x86 + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84}.Release|x86.Deploy.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -229,6 +299,8 @@ Global {DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {67A1076F-7790-4203-86EA-4402CCB5E782} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {6C60E295-C8CA-4DC5-B8BE-09888F58B249} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {46D76F7A-8FD9-4A7D-8102-2857E5DA6B84} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AE882847-CAE9-450F-8962-E76672DC23CE} diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp index 3c624107e1c..fec7cfa9523 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.cpp @@ -38,7 +38,7 @@ FrameworkElement CircleViewManagerCPP::CreateView() noexcept { // IViewManagerWithChildren -void CircleViewManagerCPP::AddView(FrameworkElement const &parent, UIElement const &child, int64_t index) noexcept { +void CircleViewManagerCPP::AddView(FrameworkElement const &parent, UIElement const &child, int64_t /*index*/) noexcept { if (auto const &border = parent.try_as()) { border.Child(child); } @@ -50,7 +50,7 @@ void CircleViewManagerCPP::RemoveAllChildren(FrameworkElement const &parent) noe } } -void CircleViewManagerCPP::RemoveChildAt(FrameworkElement const &parent, int64_t index) noexcept { +void CircleViewManagerCPP::RemoveChildAt(FrameworkElement const &parent, int64_t /*index*/) noexcept { if (auto const &border = parent.try_as()) { border.Child(nullptr); } @@ -58,7 +58,7 @@ void CircleViewManagerCPP::RemoveChildAt(FrameworkElement const &parent, int64_t void CircleViewManagerCPP::ReplaceChild( FrameworkElement const &parent, - UIElement const &oldChild, + UIElement const & /*oldChild*/, UIElement const &newChild) noexcept { if (auto const &border = parent.try_as()) { border.Child(newChild); diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h index 303ef925f05..12f2e12fbf6 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/CircleViewManagerCPP.h @@ -40,9 +40,9 @@ struct HeightToCornerRadiusConverter winrt::Windows::Foundation::IInspectable Convert( winrt::Windows::Foundation::IInspectable const &value, - winrt::Windows::UI::Xaml::Interop::TypeName const &targetType, - winrt::Windows::Foundation::IInspectable const ¶meter, - winrt::hstring const &language) noexcept { + winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/, + winrt::Windows::Foundation::IInspectable const & /*parameter*/, + winrt::hstring const & /*language*/) noexcept { double d = winrt::unbox_value(value); if (isnan(d)) { @@ -54,9 +54,9 @@ struct HeightToCornerRadiusConverter winrt::Windows::Foundation::IInspectable ConvertBack( winrt::Windows::Foundation::IInspectable const &value, - winrt::Windows::UI::Xaml::Interop::TypeName const &targetType, - winrt::Windows::Foundation::IInspectable const ¶meter, - winrt::hstring const &language) noexcept { + winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/, + winrt::Windows::Foundation::IInspectable const & /*parameter*/, + winrt::hstring const & /*language*/) noexcept { return value; } diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp index 2ee07288c1e..58a46bec3c9 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/ReactPackageProvider.cpp @@ -12,7 +12,6 @@ #include "SampleModuleCPP.h" using namespace winrt::Microsoft::ReactNative::Bridge; -using namespace Microsoft::ReactNative; namespace winrt::SampleLibraryCPP::implementation { @@ -21,8 +20,9 @@ void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuil packageBuilder.AddViewManager(L"CustomUserControlViewManagerCPP", [](IReactContext const &reactContext) { return winrt::make(reactContext); }); - packageBuilder.AddViewManager( - L"CircleViewManagerCPP", [](IReactContext const &reactContext) { return winrt::make(); }); + packageBuilder.AddViewManager(L"CircleViewManagerCPP", [](IReactContext const & /*reactContext*/) { + return winrt::make(); + }); } } // namespace winrt::SampleLibraryCPP::implementation diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj index 38901561b68..fa6314550c2 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleLibraryCPP.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -158,13 +158,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h index 141c6fa115a..c59bc041f49 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h @@ -29,9 +29,9 @@ struct SampleModuleCPP { const std::string StringConstant = "Hello World"; REACT_CONSTANT_PROVIDER(ConstantsViaConstantsProvider) - void ConstantsViaConstantsProvider(const winrt::Microsoft::ReactNative::Bridge::IJSValueWriter &writer) noexcept { - ::Microsoft::ReactNative::WriteProperty(writer, "NumberConstantViaProvider", M_PI); - ::Microsoft::ReactNative::WriteProperty(writer, "StringConstantViaProvider", "Hello World"); + void ConstantsViaConstantsProvider(ReactConstantProvider &constants) noexcept { + constants.Add(L"NumberConstantViaProvider", M_PI); + constants.Add(L"StringConstantViaProvider", "Hello World"); } #pragma endregion @@ -77,27 +77,24 @@ struct SampleModuleCPP { } REACT_METHOD(ExplicitPromiseMethod); - void ExplicitPromiseMethod( - std::function &&resolve, - std::function &&reject) noexcept { + void ExplicitPromiseMethod(winrt::Microsoft::ReactNative::Bridge::ReactPromise &&result) noexcept { DebugWriteLine(Name, "ExplicitPromiseMethod"); try { - resolve(M_PI); + result.Resolve(M_PI); } catch (const std::exception &ex) { - reject(ex.what()); + result.Reject(ex.what()); } } REACT_METHOD(ExplicitPromiseMethodWithArgs); void ExplicitPromiseMethodWithArgs( double arg, - std::function &&resolve, - std::function &&reject) noexcept { + winrt::Microsoft::ReactNative::Bridge::ReactPromise &&result) noexcept { DebugWriteLine(Name, "ExplicitPromiseMethodWithArgs", arg); try { - resolve(M_PI); + result.Resolve(M_PI); } catch (const std::exception &ex) { - reject(ex.what()); + result.Reject(ex.what()); } } @@ -121,7 +118,7 @@ struct SampleModuleCPP { #pragma region Events - REACT_EVENT(TimedEvent, "TimedEventCPP"); + REACT_EVENT(TimedEvent, L"TimedEventCPP"); std::function TimedEvent; #pragma endregion diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs index a12360c4238..b96d8fc7406 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs @@ -1,15 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Microsoft.ReactNative.Managed; using System; -using System.Collections.Generic; using System.Diagnostics; - using Windows.System.Threading; -using Microsoft.ReactNative.Bridge; -using Microsoft.ReactNative.Managed; - namespace SampleLibraryCS { // Sample ReactModule @@ -83,32 +79,32 @@ public void ExplicitCallbackMethodWithArgs(double arg, ReactCallback cal } [ReactMethod] - public void ExplicitPromiseMethod(ReactCallback resolve, ReactCallback reject) + public void ExplicitPromiseMethod(IReactPromise result) { Debug.WriteLine($"{Name}.{nameof(ExplicitPromiseMethod)}()"); try { - resolve(Math.PI); + result.Resolve(Math.PI); } catch (Exception ex) { - reject(ex.Message); + result.Reject(new ReactError { Message = ex.Message }); } } [ReactMethod] - public void ExplicitPromiseMethodWithArgs(double arg, ReactCallback resolve, ReactCallback reject) + public void ExplicitPromiseMethodWithArgs(double arg, IReactPromise result) { Debug.WriteLine($"{Name}.{nameof(ExplicitPromiseMethodWithArgs)}({arg})"); try { - resolve(Math.PI); + result.Resolve(Math.PI); } catch (Exception ex) { - reject(ex.Message); + result.Reject(new ReactError { Message = ex.Message }); } } diff --git a/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj b/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj index 6aeb7404fc8..4a7bc4f3790 100644 --- a/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj +++ b/vnext/Desktop.Test.DLL/React.Windows.Desktop.Test.DLL.vcxproj @@ -77,15 +77,15 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj b/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj index a13b12a0645..60897835fe1 100644 --- a/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj +++ b/vnext/JSI.Desktop.UnitTests/JSI.Desktop.UnitTests.vcxproj @@ -120,8 +120,8 @@ - - + + @@ -130,7 +130,7 @@ - - + + \ No newline at end of file diff --git a/vnext/JSI/Desktop/JSI.Desktop.vcxproj b/vnext/JSI/Desktop/JSI.Desktop.vcxproj index 3cd2dac30fc..914c809bc3d 100644 --- a/vnext/JSI/Desktop/JSI.Desktop.vcxproj +++ b/vnext/JSI/Desktop/JSI.Desktop.vcxproj @@ -80,14 +80,14 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueReaderTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueReaderTest.cpp new file mode 100644 index 00000000000..be9941c7d02 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueReaderTest.cpp @@ -0,0 +1,466 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "JSValueReader.h" +#include +#include "JSValueWriter.h" +#include "JsonJSValueReader.h" +#include "catch.hpp" + +namespace winrt::Microsoft::ReactNative::Bridge { + +enum struct RobotModel { + T2, + R2D2, + C3PO, +}; + +// We are generating ReadValue for it. +enum struct RobotShape { + Humanoid, + Trashcan, + Beercan, + Quadrocopter, +}; + +struct RobotTool { + std::string Name; + int Weight; + bool IsEnabled; +}; + +// This is how we can provide "reflection" information about type outside of struct. +FieldMap GetStructInfo(RobotTool *) { + return {{L"Name", &RobotTool::Name}, {L"Weight", &RobotTool::Weight}, {L"IsEnabled", &RobotTool::IsEnabled}}; +} + +struct RobotPoint { + int X; + int Y; +}; + +FieldMap GetStructInfo(RobotPoint *) { + return {{L"X", &RobotPoint::X}, {L"Y", &RobotPoint::Y}}; +} + +// We can provide "reflection" information about struct using REACT_STRUCT and REACT_FIELD macros. +REACT_STRUCT(T2Extra) +struct T2Extra { + REACT_FIELD(ActorName) + std::string ActorName; + + REACT_FIELD(MovieYear) + int MovieYear; +}; + +REACT_STRUCT(R2D2Extra) +struct R2D2Extra { + REACT_FIELD(MovieSeries) + std::string MovieSeries; +}; + +struct RobotInfo { + RobotModel Model; + std::string Name; + int Age; + RobotShape Shape; + std::optional Shape2; + std::optional Shape3; + std::vector Steps; + std::map Dimensions; + std::tuple Badges; + std::vector Tools; + std::vector Path; + std::variant Extra; +}; + +// Reading RobotModel enum value. We could use template-based version instead. +void ReadValue(IJSValueReader const &reader, RobotModel &value) noexcept { + value = static_cast(ReadValue(reader)); +} + +// Writing RobotModel enum value. We could use template-based version instead. +void WriteValue(IJSValueWriter const &writer, RobotModel value) noexcept { + WriteValue(writer, static_cast(value)); +} + +// Reading discriminating union requires using JSValue. +void ReadValue(const JSValue &jsValue, std::variant &value) noexcept { + switch (ReadValue(jsValue["Kind"])) { + case RobotModel::T2: + value = ReadValue(jsValue); + break; + case RobotModel::R2D2: + value = ReadValue(jsValue); + break; + } +} + +// Writing discriminating union. +void WriteValue(IJSValueWriter const &writer, std::variant const &value) noexcept { + writer.WriteObjectBegin(); + if (const T2Extra *t2 = std::get_if(&value)) { + WriteProperty(writer, L"Kind", RobotModel::T2); + WriteProperties(writer, *t2); + } else if (const R2D2Extra *r2d2 = std::get_if(&value)) { + WriteProperty(writer, L"Kind", RobotModel::R2D2); + WriteProperties(writer, *r2d2); + } + writer.WriteObjectEnd(); +} + +// Reading RobotInfo value. It could be generated instead. +void ReadValue(IJSValueReader const &reader, RobotInfo &value) noexcept { + value = RobotInfo(); + if (reader.ValueType() == JSValueType::Object) { + hstring propertyName; + while (reader.GetNextObjectProperty(/*out*/ propertyName)) { + if (propertyName == L"Model") + ReadValue(reader, value.Model); + else if (propertyName == L"Name") + ReadValue(reader, value.Name); + else if (propertyName == L"Age") + ReadValue(reader, value.Age); + else if (propertyName == L"Shape") + ReadValue(reader, value.Shape); + else if (propertyName == L"Shape2") + ReadValue(reader, value.Shape2); + else if (propertyName == L"Shape3") + ReadValue(reader, value.Shape3); + else if (propertyName == L"Steps") + ReadValue(reader, value.Steps); + else if (propertyName == L"Dimensions") + ReadValue(reader, value.Dimensions); + else if (propertyName == L"Badges") + ReadValue(reader, value.Badges); + else if (propertyName == L"Tools") + ReadValue(reader, value.Tools); + else if (propertyName == L"Path") + ReadValue(reader, value.Path); + else if (propertyName == L"Extra") + ReadValue(reader, value.Extra); + else + ReadValue(reader); + } + } +} + +// Writing RobotInfo value. It could be generated instead. +void WriteValue(IJSValueWriter const &writer, RobotInfo const &value) noexcept { + writer.WriteObjectBegin(); + WriteProperty(writer, L"Model", value.Model); + WriteProperty(writer, L"Name", value.Name); + WriteProperty(writer, L"Age", value.Age); + WriteProperty(writer, L"Shape", value.Shape); + WriteProperty(writer, L"Shape2", value.Shape2); + WriteProperty(writer, L"Shape3", value.Shape3); + WriteProperty(writer, L"Steps", value.Steps); + WriteProperty(writer, L"Dimensions", value.Dimensions); + WriteProperty(writer, L"Badges", value.Badges); + WriteProperty(writer, L"Tools", value.Tools); + WriteProperty(writer, L"Path", value.Path); + WriteProperty(writer, L"Extra", value.Extra); + writer.WriteObjectEnd(); +} + +TEST_CASE("TestReadCustomType", "JSValueReaderTest") { + const wchar_t *json = + LR"JSON({ + "Model": 1, + "Name": "Bob", + "Age": 42, + "Shape": 1, + "Shape2": 2, + "Shape3": null, + "Steps": [1, 2, 3], + "Dimensions": {"Width": 24, "Height": 78}, + "Badges": [2, "Maverick", true], + "Tools": [ + {"Name": "Screwdriver", "Weight": 2, "IsEnabled": true}, + {"Name": "Electro-shocker", "Weight": 3, "IsEnabled": false}], + "Path": [{"X": 5, "Y": 6}, {"X": 45, "Y": 90}, {"X": 15, "Y": 16}], + "Extra": {"Kind": 1, "MovieSeries" : "Episode 2"} + })JSON"; + + IJSValueReader reader = make(json); + + RobotInfo robot = ReadValue(reader); + REQUIRE(robot.Model == RobotModel::R2D2); + REQUIRE(robot.Name == "Bob"); + REQUIRE(robot.Age == 42); + REQUIRE(robot.Shape == RobotShape::Trashcan); + REQUIRE(*robot.Shape2 == RobotShape::Beercan); + REQUIRE(!robot.Shape3.has_value()); + REQUIRE(robot.Steps.size() == 3); + REQUIRE(robot.Steps[0] == 1); + REQUIRE(robot.Steps[1] == 2); + REQUIRE(robot.Steps[2] == 3); + REQUIRE(robot.Dimensions.size() == 2); + REQUIRE(robot.Dimensions["Width"] == 24); + REQUIRE(robot.Dimensions["Height"] == 78); + REQUIRE(std::get<0>(robot.Badges) == 2); + REQUIRE(std::get<1>(robot.Badges) == "Maverick"); + REQUIRE(std::get<2>(robot.Badges) == true); + REQUIRE(robot.Tools.size() == 2); + REQUIRE(robot.Tools[0].Name == "Screwdriver"); + REQUIRE(robot.Tools[0].Weight == 2); + REQUIRE(robot.Tools[0].IsEnabled == true); + REQUIRE(robot.Tools[1].Name == "Electro-shocker"); + REQUIRE(robot.Tools[1].Weight == 3); + REQUIRE(robot.Tools[1].IsEnabled == false); + REQUIRE(robot.Path.size() == 3); + REQUIRE(robot.Path[0].X == 5); + REQUIRE(robot.Path[0].Y == 6); + REQUIRE(robot.Path[1].X == 45); + REQUIRE(robot.Path[1].Y == 90); + REQUIRE(robot.Path[2].X == 15); + REQUIRE(robot.Path[2].Y == 16); + const R2D2Extra *r2d2Extra = std::get_if(&robot.Extra); + REQUIRE(r2d2Extra != nullptr); + REQUIRE(r2d2Extra->MovieSeries == "Episode 2"); +} + +TEST_CASE("TestWriteCustomType", "JSValueReaderTest") { + RobotInfo robot{}; + robot.Model = RobotModel::R2D2; + robot.Name = "Bob"; + robot.Age = 42; + robot.Shape = RobotShape::Trashcan; + robot.Shape2 = RobotShape::Beercan; + robot.Shape3 = std::nullopt; + robot.Steps = std::vector{1, 2, 3}; + robot.Dimensions = std::map{{"Width", 24}, {"Height", 78}}; + robot.Badges = std::tuple{2, "Maverick", true}; + robot.Tools = std::vector{RobotTool{/*Name =*/"Screwdriver", /*Weight =*/2, /*IsEnabled =*/true}, + RobotTool{/*Name =*/"Electro-shocker", /*Weight =*/3, /*IsEnabled =*/false}}; + robot.Path = std::vector{ + RobotPoint{/*X =*/5, /*Y =*/6}, RobotPoint{/*X =*/45, /*Y =*/90}, RobotPoint{/*X =*/15, /*Y =*/16}}; + robot.Extra = R2D2Extra{/*MovieSeries =*/"Episode 2"}; + + JSValue jsValue; + auto writer = MakeJSValueTreeWriter(jsValue); + WriteValue(writer, robot); + + REQUIRE(jsValue["Model"] == (int)RobotModel::R2D2); + REQUIRE(jsValue["Name"] == "Bob"); + REQUIRE(jsValue["Age"] == 42); + REQUIRE(jsValue["Shape"] == (int)RobotShape::Trashcan); + REQUIRE(jsValue["Shape2"] == (int)RobotShape::Beercan); + REQUIRE(jsValue["Shape3"] == nullptr); + REQUIRE(jsValue["Steps"].ItemCount() == 3); + REQUIRE(jsValue["Steps"][0] == 1); + REQUIRE(jsValue["Steps"][1] == 2); + REQUIRE(jsValue["Steps"][2] == 3); + REQUIRE(jsValue["Dimensions"].PropertyCount() == 2); + REQUIRE(jsValue["Dimensions"]["Width"] == 24); + REQUIRE(jsValue["Dimensions"]["Height"] == 78); + REQUIRE(jsValue["Badges"][0] == 2); + REQUIRE(jsValue["Badges"][1] == "Maverick"); + REQUIRE(jsValue["Badges"][2] == true); + REQUIRE(jsValue["Tools"].ItemCount() == 2); + REQUIRE(jsValue["Tools"][0]["Name"] == "Screwdriver"); + REQUIRE(jsValue["Tools"][0]["Weight"] == 2); + REQUIRE(jsValue["Tools"][0]["IsEnabled"] == true); + REQUIRE(jsValue["Tools"][1]["Name"] == "Electro-shocker"); + REQUIRE(jsValue["Tools"][1]["Weight"] == 3); + REQUIRE(jsValue["Tools"][1]["IsEnabled"] == false); + REQUIRE(jsValue["Path"].ItemCount() == 3); + REQUIRE(jsValue["Path"][0]["X"] == 5); + REQUIRE(jsValue["Path"][0]["Y"] == 6); + REQUIRE(jsValue["Path"][1]["X"] == 45); + REQUIRE(jsValue["Path"][1]["Y"] == 90); + REQUIRE(jsValue["Path"][2]["X"] == 15); + REQUIRE(jsValue["Path"][2]["Y"] == 16); + REQUIRE(jsValue["Extra"]["MovieSeries"] == "Episode 2"); +} + +TEST_CASE("TestReadValueDefaultExtensions", "JSValueReaderTest") { + const wchar_t *json = + LR"JSON({ + "StringValue1": "", + "StringValue2": "5", + "StringValue3": "Hello", + "BoolValue1": false, + "BoolValue2": true, + "IntValue1": 0, + "IntValue2": 42, + "FloatValue": 3.14, + "NullValue": null + })JSON"; + + IJSValueReader reader = make(json); + + REQUIRE(reader.ValueType() == JSValueType::Object); + int properyCount = 0; + hstring propertyName; + while (reader.GetNextObjectProperty(/*out*/ propertyName)) { + if (propertyName == L"StringValue1") { + REQUIRE(ReadValue(reader) == ""); + REQUIRE(ReadValue(reader) == L""); + REQUIRE(ReadValue(reader) == false); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + ++properyCount; + } else if (propertyName == L"StringValue2") { + REQUIRE(ReadValue(reader) == "5"); + REQUIRE(ReadValue(reader) == L"5"); + REQUIRE(ReadValue(reader) == true); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + REQUIRE(ReadValue(reader) == 5); + ++properyCount; + } else if (propertyName == L"StringValue3") { + REQUIRE(ReadValue(reader) == "Hello"); + REQUIRE(ReadValue(reader) == L"Hello"); + REQUIRE(ReadValue(reader) == true); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + ++properyCount; + } else if (propertyName == L"BoolValue1") { + REQUIRE(ReadValue(reader) == "false"); + REQUIRE(ReadValue(reader) == L"false"); + REQUIRE(ReadValue(reader) == false); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + ++properyCount; + } else if (propertyName == L"BoolValue2") { + REQUIRE(ReadValue(reader) == "true"); + REQUIRE(ReadValue(reader) == L"true"); + REQUIRE(ReadValue(reader) == true); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + REQUIRE(ReadValue(reader) == 1); + ++properyCount; + } else if (propertyName == L"IntValue1") { + REQUIRE(ReadValue(reader) == "0"); + REQUIRE(ReadValue(reader) == L"0"); + REQUIRE(ReadValue(reader) == false); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + ++properyCount; + } else if (propertyName == L"IntValue2") { + REQUIRE(ReadValue(reader) == "42"); + REQUIRE(ReadValue(reader) == L"42"); + REQUIRE(ReadValue(reader) == true); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + REQUIRE(ReadValue(reader) == 42); + ++properyCount; + } else if (propertyName == L"FloatValue") { + REQUIRE(ReadValue(reader) == "3.14"); + REQUIRE(ReadValue(reader) == L"3.14"); + REQUIRE(ReadValue(reader) == true); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3); + REQUIRE(ReadValue(reader) == 3.14f); + REQUIRE(ReadValue(reader) == 3.14); + ++properyCount; + } else if (propertyName == L"NullValue") { + REQUIRE(ReadValue(reader) == ""); + REQUIRE(ReadValue(reader) == L""); + REQUIRE(ReadValue(reader) == false); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + REQUIRE(ReadValue(reader) == 0); + + ++properyCount; + } + } + REQUIRE(properyCount == 9); +} + +TEST_CASE("TestWriteValueDefaultExtensions", "JSValueReaderTest") { + JSValue jsValue; + auto writer = MakeJSValueTreeWriter(jsValue); + writer.WriteObjectBegin(); + WriteProperty(writer, L"StringValue1", ""); + WriteProperty(writer, L"StringValue2", "5"); + WriteProperty(writer, L"StringValue3", "Hello"); + WriteProperty(writer, L"BoolValue1", false); + WriteProperty(writer, L"BoolValue2", true); + WriteProperty(writer, L"IntValue1", 0); + WriteProperty(writer, L"IntValue2", 42); + WriteProperty(writer, L"FloatValue", 3.14); + WriteProperty(writer, L"NullValue", nullptr); + writer.WriteObjectEnd(); + + REQUIRE(jsValue["StringValue1"] == ""); + REQUIRE(jsValue["StringValue2"] == "5"); + REQUIRE(jsValue["StringValue3"] == "Hello"); + REQUIRE(jsValue["BoolValue1"] == false); + REQUIRE(jsValue["BoolValue2"] == true); + REQUIRE(jsValue["IntValue1"] == 0); + REQUIRE(jsValue["IntValue2"] == 42); + REQUIRE(jsValue["FloatValue"] == 3.14); + REQUIRE(jsValue["NullValue"] == nullptr); + REQUIRE(jsValue["NullValue"] == JSValue::Null); +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp new file mode 100644 index 00000000000..4096b502235 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JSValueTest.cpp @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "JSValue.h" +#include "JsonJSValueReader.h" +#include "catch.hpp" + +namespace winrt::Microsoft::ReactNative::Bridge { + +TEST_CASE("TestReadObject", "JSValueTest") { + const wchar_t *json = + LR"JSON({ + "NullValue": null, + "ObjValue": {}, + "ArrayValue": [], + "StringValue": "Hello", + "BoolValue": true, + "IntValue": 42, + "DoubleValue": 4.5 + })JSON"; + + IJSValueReader reader = make(json); + + JSValue jsValue = JSValue::ReadFrom(reader); + REQUIRE(jsValue.Type() == JSValueType::Object); + REQUIRE(jsValue.Object().at("NullValue").IsNull()); + REQUIRE(jsValue.Object().at("ObjValue").Object().empty()); + REQUIRE(jsValue.Object().at("ArrayValue").Array().empty()); + REQUIRE(jsValue.Object().at("StringValue").String() == "Hello"); + REQUIRE(jsValue.Object().at("BoolValue").Boolean() == true); + REQUIRE(jsValue.Object().at("IntValue").Int64() == 42); + REQUIRE(jsValue.Object().at("DoubleValue").Double() == 4.5); +} + +TEST_CASE("TestReadNestedObject", "JSValueTest") { + const wchar_t *json = + LR"JSON({ + "NestedObj": { + "NullValue": null, + "ObjValue": {}, + "ArrayValue": [], + "StringValue": "Hello", + "BoolValue": true, + "IntValue": 42, + "DoubleValue": 4.5 + } + })JSON"; + + IJSValueReader reader = make(json); + + JSValue jsValue = JSValue::ReadFrom(reader); + REQUIRE(jsValue.Type() == JSValueType::Object); + const auto &nestedObj = jsValue.Object().at("NestedObj").Object(); + REQUIRE(nestedObj.at("NullValue").IsNull()); + REQUIRE(nestedObj.at("ObjValue").Object().empty()); + REQUIRE(nestedObj.at("ArrayValue").Array().empty()); + REQUIRE(nestedObj.at("StringValue").String() == "Hello"); + REQUIRE(nestedObj.at("BoolValue").Boolean() == true); + REQUIRE(nestedObj.at("IntValue").Int64() == 42); + REQUIRE(nestedObj.at("DoubleValue").Double() == 4.5); +} + +TEST_CASE("TestReadArray", "JSValueTest") { + const wchar_t *json = LR"JSON([null, {}, [], "Hello", true, 42, 4.5])JSON"; + IJSValueReader reader = make(json); + + JSValue jsValue = JSValue::ReadFrom(reader); + REQUIRE(jsValue.Type() == JSValueType::Array); + REQUIRE(jsValue.Array()[0].IsNull()); + REQUIRE(jsValue.Array()[1].Object().empty()); + REQUIRE(jsValue.Array()[2].Array().empty()); + REQUIRE(jsValue.Array()[3].String() == "Hello"); + REQUIRE(jsValue.Array()[4].Boolean() == true); + REQUIRE(jsValue.Array()[5].Int64() == 42); + REQUIRE(jsValue.Array()[6].Double() == 4.5); +} + +TEST_CASE("TestReadNestedArray", "JSValueTest") { + const wchar_t *json = LR"JSON([[null, {}, [], "Hello", true, 42, 4.5]])JSON"; + IJSValueReader reader = make(json); + + JSValue jsValue = JSValue::ReadFrom(reader); + REQUIRE(jsValue.Type() == JSValueType::Array); + const auto &nestedArr = jsValue.Array()[0].Array(); + REQUIRE(nestedArr[0].IsNull()); + REQUIRE(nestedArr[1].Object().empty()); + REQUIRE(nestedArr[2].Array().empty()); + REQUIRE(nestedArr[3].String() == "Hello"); + REQUIRE(nestedArr[4].Boolean() == true); + REQUIRE(nestedArr[5].Int64() == 42); + REQUIRE(nestedArr[6].Double() == 4.5); +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.cpp new file mode 100644 index 00000000000..9af889d2a38 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.cpp @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "JsonJSValueReader.h" + +namespace winrt::Microsoft::ReactNative::Bridge { + +//=========================================================================== +// JsonJSValueReader implementation +//=========================================================================== + +JsonJSValueReader::JsonJSValueReader(std::wstring &&jsonText) noexcept + : m_jsonText{std::move(jsonText)}, m_jsonReader{JsonSource{m_jsonText}} { + SetCurrentValue(m_jsonReader.ReadNext()); +} + +JSValueType JsonJSValueReader::ValueType() noexcept { + return m_valueType; +} + +bool JsonJSValueReader::GetNextObjectProperty(hstring &propertyName) noexcept { + if (!m_isIterating) { + if (m_valueType == JSValueType::Object) { + if (m_jsonReader.ReadNext() == JsonParseState::Name) { + m_stack.push_back(JSValueType::Object); + propertyName = GetHString(); + SetCurrentValue(m_jsonReader.ReadNext()); + return true; + } else { + m_isIterating = !m_stack.empty(); + } + } + } else if (!m_stack.empty()) { + JSValueType valueType = m_stack.back(); + if (valueType == JSValueType::Object) { + if (m_jsonReader.ReadNext() == JsonParseState::Name) { + propertyName = GetHString(); + SetCurrentValue(m_jsonReader.ReadNext()); + return true; + } else { + m_valueType = valueType; + m_stack.pop_back(); + m_isIterating = !m_stack.empty(); + } + } + } + + propertyName = to_hstring(L""); + return false; +} + +bool JsonJSValueReader::GetNextArrayItem() noexcept { + if (!m_isIterating) { + if (m_valueType == JSValueType::Array) { + JsonParseState parseState = m_jsonReader.ReadNext(); + if (IsContainerOrValue(parseState)) { + m_stack.push_back(JSValueType::Array); + SetCurrentValue(parseState); + return true; + } else { + m_isIterating = !m_stack.empty(); + } + } + } else if (!m_stack.empty()) { + JSValueType valueType = m_stack.back(); + if (valueType == JSValueType::Array) { + JsonParseState parseState = m_jsonReader.ReadNext(); + if (IsContainerOrValue(parseState)) { + SetCurrentValue(parseState); + return true; + } else { + m_valueType = valueType; + m_stack.pop_back(); + m_isIterating = !m_stack.empty(); + } + } + } + + return false; +} + +void JsonJSValueReader::SetCurrentValue(JsonParseState parseState) noexcept { + switch (parseState) { + case JsonParseState::StartObject: + m_valueType = JSValueType::Object; + m_isIterating = false; + break; + + case JsonParseState::StartArray: + m_valueType = JSValueType::Array; + m_isIterating = false; + break; + + case JsonParseState::Value: { + m_isIterating = true; + + if (m_jsonReader.IsString()) { + m_valueType = JSValueType::String; + m_stringValue = GetHString(); + break; + } + + if (m_jsonReader.GetInt64(&m_int64Value)) { + m_valueType = JSValueType::Int64; + break; + } + + if (m_jsonReader.GetDouble(&m_doubleValue)) { + m_valueType = JSValueType::Double; + break; + } + + if (m_jsonReader.GetBool(&m_boolValue)) { + m_valueType = JSValueType::Boolean; + break; + } + + m_valueType = JSValueType::Null; + break; + } + + default: + m_valueType = JSValueType::Null; + break; + } +} + +hstring JsonJSValueReader::GetString() noexcept { + return (m_valueType == JSValueType::String) ? m_stringValue : hstring(L""); +} + +bool JsonJSValueReader::GetBoolean() noexcept { + return (m_valueType == JSValueType::Boolean) ? m_boolValue : false; +} + +int64_t JsonJSValueReader::GetInt64() noexcept { + return (m_valueType == JSValueType::Int64) ? m_int64Value : 0; +} + +double JsonJSValueReader::GetDouble() noexcept { + return (m_valueType == JSValueType::Double) ? m_doubleValue : 0; +} + +hstring JsonJSValueReader::GetHString() noexcept { + const wchar_t *data; + size_t length; + if (m_jsonReader.GetString(&data, &length)) { + return hstring(data, static_cast(length)); + } else { + return hstring(L""); + } +} + +bool JsonJSValueReader::IsContainerOrValue(JsonParseState parseState) noexcept { + return parseState == JsonParseState::StartArray || parseState == JsonParseState::StartObject || + parseState == JsonParseState::Value; +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.h b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.h new file mode 100644 index 00000000000..08361f20012 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonJSValueReader.h @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "JsonReader.h" +#include "winrt/Microsoft.ReactNative.Bridge.h" + +namespace winrt::Microsoft::ReactNative::Bridge { + +struct JsonJSValueReader : implements { + JsonJSValueReader(std::wstring &&jsonText) noexcept; + + public: // IJSValueReader + JSValueType ValueType() noexcept; + bool GetNextObjectProperty(hstring &propertyName) noexcept; + bool GetNextArrayItem() noexcept; + hstring GetString() noexcept; + bool GetBoolean() noexcept; + int64_t GetInt64() noexcept; + double GetDouble() noexcept; + + private: + void SetCurrentValue(JsonParseState parseState) noexcept; + hstring GetHString() noexcept; + static bool IsContainerOrValue(JsonParseState parseState) noexcept; + + private: + const std::wstring m_jsonText; + JsonReader m_jsonReader; + JSValueType m_valueType{JSValueType::Null}; + std::vector m_stack; + bool m_isIterating{false}; + hstring m_stringValue; + union { + bool m_boolValue; + int64_t m_int64Value; + double m_doubleValue; + }; +}; + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.cpp new file mode 100644 index 00000000000..f67579fa23c --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.cpp @@ -0,0 +1,379 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Terms used in here: +// Container: represents a [] or {} +// isStringContent: is the current value a string? Either a name or quoted value + +#include "pch.h" +#include "JsonReader.h" +#include "Crash.h" + +namespace winrt::Microsoft::ReactNative::Bridge { + +static bool IsSimpleWhitespace(wchar_t c) noexcept { + return (c == ' ' || c == '\t' || c == '\r' || c == '\n'); +} + +// Main processing loop: reads tokens until end of stream, error, or +// a valid state transition is reached. +JsonParseState JsonReader::ReadNext() noexcept { + for (;; m_source.Inc()) { + if (m_source.IsEndOfData()) + return JsonParseState::ErrorEndOfData; + + wchar_t ch = m_source.PeekChar(); + + if (m_isAllowed.Option.StringChar) { + if (m_isAllowed.Option.EscapeChar) { + if (HandleEscapeChar(ch)) + continue; + return HandleInvalidData(); + } + + if (m_isAllowed.Option.EscapeCharHex) { + if (HandleEscapeCharHex(ch)) + continue; + return HandleInvalidData(); + } + + if (ch == '\\') { + m_isAllowed.Option.EscapeChar = 1; + continue; + } + + if (ch == '\"') { + if (HandleEndName()) { + m_source.Inc(); + return JsonParseState::Name; + } + + if (HandleEndContainerOrValue(JsonParseState::Value)) { + m_source.Inc(); + return JsonParseState::Value; + } + + VerifyElseCrashSz(false, "This should be impossible to hit"); + } + + // Regular string character + m_textBuffer += ch; + continue; + } + + // All whitespace outside of strings is ignored + if (IsSimpleWhitespace(ch)) + continue; + + switch (ch) { + case '[': + return HandleStartContainer(JsonParseState::StartArray); + case ']': + return HandleEndContainer(JsonParseState::StartArray, JsonParseState::EndArray); + case '{': + return HandleStartContainer(JsonParseState::StartObject); + case '}': + return HandleEndContainer(JsonParseState::StartObject, JsonParseState::EndObject); + + case ',': { + if (m_isAllowed.Option.EndComma) { + if (HandleEndContainerOrValue(JsonParseState::Value)) + return JsonParseState::Value; + + ResetContainerState(); + continue; + } + break; + } + + case '"': { + if (HandleBeginString()) + continue; + break; + } + + case ':': { + if (m_isAllowed.Option.NameDelim) { + m_isAllowed.All = 0; + OnValueExpected(); + continue; + } + break; + } + + default: { + // Regular non-string character + if (HandleNonStringChar(ch)) + continue; + + break; + } + } + + return HandleInvalidData(); + } +} + +// Called when a new Container is started +// Called after each item in a Container +void JsonReader::ResetContainerState() noexcept { + m_isAllowed.All = 0; + + if (m_states.top() == JsonParseState::StartArray) { + OnValueExpected(); + } else { + m_isAllowed.Option.BeginName = 1; + m_isStringContent = false; + m_textBuffer.clear(); + } +} + +// Called after '[', ':', ',' +void JsonReader::OnValueExpected() noexcept { + m_isAllowed.Option.StartContainer = 1; + m_isAllowed.Option.BeginValue = 1; + m_isStringContent = false; + m_textBuffer.clear(); +} + +// Handle the start of a new Container: [ { +JsonParseState JsonReader::HandleStartContainer(JsonParseState state) noexcept { + if (!m_isAllowed.Option.StartContainer) + return HandleInvalidData(); + + m_states.push(state); + ResetContainerState(); + m_isAllowed.Option.EndContainer = 1; + + m_source.Inc(); + return state; +} + +// Handle the end of a Container: } ] +JsonParseState JsonReader::HandleEndContainer(JsonParseState oldState, JsonParseState newState) noexcept { + if (!m_isAllowed.Option.EndContainer) + return HandleInvalidData(); + + // Check for previously ended value first + if (HandleEndContainerOrValue(JsonParseState::Value)) + return JsonParseState::Value; + + if (HandleEndContainerOrValue(oldState)) { + m_isStringContent = false; + m_textBuffer.clear(); + + m_source.Inc(); + return newState; + } + + return HandleInvalidData(); +} + +// Handle starting a new string: " +bool JsonReader::HandleBeginString() noexcept { + if (m_isAllowed.Option.BeginName || m_isAllowed.Option.BeginValue) { + m_states.push(m_isAllowed.Option.BeginName ? JsonParseState::Name : JsonParseState::Value); + + m_isAllowed.All = 0; + m_isAllowed.Option.StringChar = 1; + m_isStringContent = true; + return true; + } + + return false; +} + +// Handle value characters: numbers, true, false, null +bool JsonReader::HandleNonStringChar(wchar_t ch) noexcept { + // FUTURE: could add more validation here + + if (m_isAllowed.Option.NonStringChar) { + m_textBuffer += ch; + return true; + } + + if (m_isAllowed.Option.BeginValue) { + m_isAllowed.All = 0; + m_isAllowed.Option.NonStringChar = 1; + m_isAllowed.Option.EndComma = 1; + m_isAllowed.Option.EndContainer = 1; + m_states.push(JsonParseState::Value); + m_textBuffer.clear(); + + m_textBuffer += ch; + return true; + } + + return false; +} + +// Handle an escaped string character +bool JsonReader::HandleEscapeChar(wchar_t ch) noexcept { + switch (ch) { + case '\"': + case '\\': + case '/': + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'v': + ch = '\v'; + break; + + case 'u': + // Switch to hex-mode + m_isAllowed.Option.EscapeChar = 0; + m_isAllowed.Option.EscapeCharHex = 1; + m_hexStartIndex = m_textBuffer.length(); + return true; + + default: + return false; + } + + m_textBuffer += ch; + + m_isAllowed.Option.EscapeChar = 0; + return true; +} + +// Appends the current char into the buffer +// If all hex characters have been collected, try converting to wchar +bool JsonReader::HandleEscapeCharHex(wchar_t ch) noexcept { + constexpr size_t HexCharCount = 4; + + m_textBuffer += ch; + if (m_textBuffer.length() < m_hexStartIndex + HexCharCount) + return true; + + const wchar_t *hexStart = m_textBuffer.c_str() + m_hexStartIndex; + wchar_t *hexEnd = nullptr; + ch = static_cast(wcstoul(hexStart, &hexEnd, 16 /*base*/)); + + if (hexStart + HexCharCount != hexEnd) + return false; + + m_textBuffer.resize(m_hexStartIndex); + m_textBuffer += ch; + + m_isAllowed.Option.EscapeCharHex = 0; + return true; +} + +// Helper for handling the end of a Container or value: , ] } +bool JsonReader::HandleEndContainerOrValue(JsonParseState state) noexcept { + if (m_states.top() == state) { + m_states.pop(); + + m_isAllowed.All = 0; + if (m_states.size() > 0) { + m_isAllowed.Option.EndComma = 1; + m_isAllowed.Option.EndContainer = 1; + } + + return true; + } + + return false; +} + +// Handle the end of a name: " +bool JsonReader::HandleEndName() noexcept { + if (m_states.top() == JsonParseState::Name) { + m_states.pop(); + m_isAllowed.All = 0; + m_isAllowed.Option.NameDelim = 1; + return true; + } + + return false; +} + +// Return the current string literal +bool JsonReader::GetString(const wchar_t **value, size_t *valueLength) noexcept { + if (m_isStringContent) { + *value = m_textBuffer.c_str(); + *valueLength = m_textBuffer.length(); + return true; + } + + *value = nullptr; + *valueLength = 0; + return false; +} + +// Return the current value as a bool +bool JsonReader::GetBool(bool *value) noexcept { + if (!m_isStringContent) { + if (m_textBuffer.compare(L"true") == 0) { + *value = true; + return true; + } + + if (m_textBuffer.compare(L"false") == 0) { + *value = false; + return true; + } + } + + *value = false; + return false; +} + +// Return the current value as an Int64 +bool JsonReader::GetInt64(int64_t *value) noexcept { + if (!m_isStringContent && m_textBuffer.length()) { + wchar_t *end = nullptr; + auto iValue = _wcstoi64(m_textBuffer.c_str(), &end, 10 /*base*/); + if (end == m_textBuffer.c_str() + m_textBuffer.size()) { + *value = iValue; + return true; + } + + // Didn't read enough characters, fall through and fail + } + + *value = 0; + return false; +} + +// Return the current value as a Double +bool JsonReader::GetDouble(double *value) noexcept { + if (!m_isStringContent && m_textBuffer.length()) { + wchar_t *end = nullptr; + auto dvalue = wcstod(m_textBuffer.c_str(), &end); + if (end == m_textBuffer.c_str() + m_textBuffer.size()) { + *value = dvalue; + return true; + } + + // Didn't read enough characters, fall through and fail + } + + *value = 0; + return false; +} + +// Return true if the current value is the null value +bool JsonReader::IsNull() noexcept { + if (!m_isStringContent) { + return (m_textBuffer.compare(L"null") == 0); + } + + return false; +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.h b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.h new file mode 100644 index 00000000000..34b4f332f07 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/JsonReader.h @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Simple JsonReader + +#include +#include +#include + +namespace winrt::Microsoft::ReactNative::Bridge { + +struct JsonSource { + static_assert(sizeof(wchar_t) == 2, "This code expects 2-byte wchars"); + + JsonSource(std::wstring_view buffer) noexcept : m_current{buffer.data()}, m_end{buffer.data() + buffer.size()} {} + + // Caller is responsible for checking IsEndOfData() before calling PeekChar() + wchar_t PeekChar() const noexcept { + return *m_current; + } + + // Caller is responsible for checking IsEndOfData() before calling Inc() + void Inc() noexcept { + ++m_current; + } + + bool IsEndOfData() const noexcept { + return m_current == m_end; + } + + private: + const wchar_t *m_current = nullptr; + const wchar_t *m_end = nullptr; +}; + +enum class JsonParseState { + StartArray, + StartObject, + Name, + Value, + EndObject, + EndArray, + + // Returned for invalid data + ErrorInvalidData, + + // End of input reached before end of Json. + ErrorEndOfData, +}; + +struct JsonReader { + public: + JsonReader(JsonSource source) : m_source{source} { + m_isAllowed.All = 0; + m_isAllowed.Option.StartContainer = 1; + } + + JsonReader(const JsonReader &) = delete; + JsonReader &operator=(const JsonReader &) = delete; + + JsonParseState ReadNext() noexcept; + bool GetString(_Deref_post_count_(*valueLength) const wchar_t **value, _Out_ size_t *valueLength) noexcept; + bool GetBool(_Out_ bool *value) noexcept; + bool GetInt64(_Out_ std::int64_t *value) noexcept; + bool GetDouble(_Out_ double *value) noexcept; + bool IsNull() noexcept; + bool IsString() noexcept { + return m_isStringContent; + } + + private: + void ResetContainerState() noexcept; + void OnValueExpected() noexcept; + + JsonParseState HandleStartContainer(JsonParseState state) noexcept; + JsonParseState HandleEndContainer(JsonParseState oldState, JsonParseState newState) noexcept; + + bool HandleBeginString() noexcept; + bool HandleNonStringChar(wchar_t wch) noexcept; + bool HandleEscapeChar(wchar_t wch) noexcept; + bool HandleEscapeCharHex(wchar_t wch) noexcept; + bool HandleEndContainerOrValue(JsonParseState state) noexcept; + bool HandleEndName() noexcept; + + JsonParseState HandleInvalidData() noexcept { + m_isAllowed.All = 0; + return JsonParseState::ErrorInvalidData; + } + + private: + JsonSource m_source; + + // Tracks the next allowed state(s) + union { + struct { + // [ or { + uint32_t StartContainer : 1; + + // " + uint32_t BeginName : 1; + + // : + uint32_t NameDelim : 1; + + // " or value char + uint32_t BeginValue : 1; + + // valid name or value char + uint32_t NonStringChar : 1; + + // valid string character + uint32_t StringChar : 1; + + // valid escape char + uint32_t EscapeChar : 1; + + // 4 hex digits + uint32_t EscapeCharHex : 1; + + // ] or } + uint32_t EndContainer : 1; + + // , + uint32_t EndComma : 1; + } Option; + + uint32_t All; + + } m_isAllowed; + + // Stores the current name / value text + std::wstring m_textBuffer; + + // Stack of parse states (Array, Object, Name, Value) + std::stack m_states; + + // Used when decoding \uHHHH values + size_t m_hexStartIndex = 0; + + // True when m_textBuffer contains a name or value string + bool m_isStringContent = false; +}; + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj new file mode 100644 index 00000000000..090ceaf2d69 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj @@ -0,0 +1,153 @@ + + + + + true + true + true + 15.0 + {6c60e295-c8ca-4dc5-b8be-09888f58b249} + Win32Proj + Microsoft_ReactNative_Cxx_UnitTests + 10.0.18362.0 + 10.0.17134.0 + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + Application + v140 + v141 + v142 + Unicode + + + true + true + + + false + true + false + + + + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + _CONSOLE;%(PreprocessorDefinitions) + Level4 + %(AdditionalOptions) /permissive- /bigobj + + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + + + Console + false + + + C:\git\vmoroz\react-native-windows\vnext\Microsoft.ReactNative;%(AdditionalIncludeDirectories) + + + + + WIN32;%(PreprocessorDefinitions) + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + + + Console + true + true + false + + + + + + + + + + + + + + + + + + Create + + + + + + + + + false + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/NativeModuleTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/NativeModuleTest.cpp new file mode 100644 index 00000000000..554e1510084 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/NativeModuleTest.cpp @@ -0,0 +1,775 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include +#include "NativeModules.h" +#include "ReactModuleBuilderMock.h" +#include "catch.hpp" + +namespace winrt::Microsoft::ReactNative::Bridge { + +REACT_STRUCT(Point) +struct Point { + REACT_FIELD(X) + int X; + + REACT_FIELD(Y) + int Y; +}; + +REACT_MODULE(SimpleNativeModule) +struct SimpleNativeModule { + REACT_METHOD(Add) + int Add(int x, int y) noexcept { + return x + y; + } + + REACT_METHOD(Negate) + int Negate(int x) noexcept { + return -x; + } + + REACT_METHOD(SayHello) + std::string SayHello() noexcept { + return "Hello"; + } + + REACT_METHOD(StaticAdd) + static int StaticAdd(int x, int y) noexcept { + return x + y; + } + + REACT_METHOD(StaticNegate) + static int StaticNegate(int x) noexcept { + return -x; + } + + REACT_METHOD(StaticSayHello) + static std::string StaticSayHello() noexcept { + return "Hello"; + } + + REACT_METHOD(SayHello0) + void SayHello0() noexcept { + Message = "Hello_0"; + } + + REACT_METHOD(PrintPoint) + void PrintPoint(Point pt) noexcept { + std::stringstream ss; + ss << "Point: (" << pt.X << ", " << pt.Y << ")"; + Message = ss.str(); + } + + REACT_METHOD(PrintLine) + void PrintLine(Point start, Point end) noexcept { + std::stringstream ss; + ss << "Line: (" << start.X << ", " << start.Y << ")-(" << end.X << ", " << end.Y << ")"; + Message = ss.str(); + } + + REACT_METHOD(StaticSayHello1) + static void StaticSayHello1() noexcept { + StaticMessage = "Hello_1"; + } + + REACT_METHOD(StaticPrintPoint) + static void StaticPrintPoint(Point pt) noexcept { + std::stringstream ss; + ss << "Static Point: (" << pt.X << ", " << pt.Y << ")"; + StaticMessage = ss.str(); + } + + REACT_METHOD(StaticPrintLine) + static void StaticPrintLine(Point start, Point end) noexcept { + std::stringstream ss; + ss << "Static Line: (" << start.X << ", " << start.Y << ")-(" << end.X << ", " << end.Y << ")"; + StaticMessage = ss.str(); + } + + REACT_METHOD(AddCallback) + void AddCallback(int x, int y, std::function const &resolve) noexcept { + resolve(x + y); + } + + REACT_METHOD(NegateCallback) + void NegateCallback(int x, std::function const &resolve) noexcept { + resolve(-x); + } + + REACT_METHOD(SayHelloCallback) + void SayHelloCallback(std::function const &resolve) noexcept { + resolve("Hello_2"); + } + + REACT_METHOD(StaticAddCallback) + static void StaticAddCallback(int x, int y, std::function const &resolve) noexcept { + resolve(x + y); + } + + REACT_METHOD(StaticNegateCallback) + static void StaticNegateCallback(int x, std::function const &resolve) noexcept { + resolve(-x); + } + + REACT_METHOD(StaticSayHelloCallback) + static void StaticSayHelloCallback(std::function const &resolve) noexcept { + resolve("Static Hello_2"); + } + + REACT_METHOD(DivideCallbacks) + void DivideCallbacks( + int x, + int y, + std::function const &resolve, + std::function const &reject) noexcept { + if (y != 0) { + resolve(x / y); + } else { + reject("Division by 0"); + } + } + + REACT_METHOD(NegateCallbacks) + void NegateCallbacks( + int x, + std::function const &resolve, + std::function const &reject) noexcept { + if (x >= 0) { + resolve(-x); + } else { + reject("Already negative"); + } + } + + REACT_METHOD(ResolveSayHelloCallbacks) + void ResolveSayHelloCallbacks( + std::function const &resolve, + std::function const & /*reject*/) noexcept { + resolve("Hello_3"); + } + + REACT_METHOD(RejectSayHelloCallbacks) + void RejectSayHelloCallbacks( + std::function const & /*resolve*/, + std::function const &reject) noexcept { + reject("Goodbye"); + } + + REACT_METHOD(StaticDivideCallbacks) + static void StaticDivideCallbacks( + int x, + int y, + std::function const &resolve, + std::function const &reject) noexcept { + if (y != 0) { + resolve(x / y); + } else { + reject("Division by 0"); + } + } + + REACT_METHOD(StaticNegateCallbacks) + static void StaticNegateCallbacks( + int x, + std::function const &resolve, + std::function const &reject) noexcept { + if (x >= 0) { + resolve(-x); + } else { + reject("Already negative"); + } + } + + REACT_METHOD(StaticResolveSayHelloCallbacks) + static void StaticResolveSayHelloCallbacks( + std::function const &resolve, + std::function const & /*reject*/) noexcept { + resolve("Hello_3"); + } + + REACT_METHOD(StaticRejectSayHelloCallbacks) + static void StaticRejectSayHelloCallbacks( + std::function const & /*resolve*/, + std::function const &reject) noexcept { + reject("Goodbye"); + } + + REACT_METHOD(DividePromise) + void DividePromise(int x, int y, ReactPromise &&result) noexcept { + if (y != 0) { + result.Resolve(x / y); + } else { + ReactError error{}; + error.Message = "Division by 0"; + result.Reject(std::move(error)); + } + } + + REACT_METHOD(NegatePromise) + void NegatePromise(int x, ReactPromise &&result) noexcept { + if (x >= 0) { + result.Resolve(-x); + } else { + ReactError error{}; + error.Message = "Already negative"; + result.Reject(std::move(error)); + } + } + + REACT_METHOD(ResolveSayHelloPromise) + void ResolveSayHelloPromise(ReactPromise &&result) noexcept { + result.Resolve("Hello_4"); + } + + REACT_METHOD(RejectSayHelloPromise) + void RejectSayHelloPromise(ReactPromise &&result) noexcept { + ReactError error{}; + error.Message = "Promise rejected"; + result.Reject(std::move(error)); + } + + REACT_METHOD(StaticDividePromise) + static void StaticDividePromise(int x, int y, ReactPromise &&result) noexcept { + if (y != 0) { + result.Resolve(x / y); + } else { + ReactError error{}; + error.Message = "Division by 0"; + result.Reject(std::move(error)); + } + } + + REACT_METHOD(StaticNegatePromise) + static void StaticNegatePromise(int x, ReactPromise &&result) noexcept { + if (x >= 0) { + result.Resolve(-x); + } else { + ReactError error{}; + error.Message = "Already negative"; + result.Reject(std::move(error)); + } + } + + REACT_METHOD(StaticResolveSayHelloPromise) + static void StaticResolveSayHelloPromise(ReactPromise &&result) noexcept { + result.Resolve("Hello_4"); + } + + REACT_METHOD(StaticRejectSayHelloPromise) + static void StaticRejectSayHelloPromise(ReactPromise &&result) noexcept { + ReactError error{}; + error.Message = "Promise rejected"; + result.Reject(std::move(error)); + } + + REACT_SYNC_METHOD(AddSync) + int AddSync(int x, int y) noexcept { + return x + y; + } + + REACT_SYNC_METHOD(NegateSync) + int NegateSync(int x) noexcept { + return -x; + } + + REACT_SYNC_METHOD(SayHelloSync) + std::string SayHelloSync() noexcept { + return "Hello"; + } + + REACT_SYNC_METHOD(StaticAddSync) + static int StaticAddSync(int x, int y) noexcept { + return x + y; + } + + REACT_SYNC_METHOD(StaticNegateSync) + static int StaticNegateSync(int x) noexcept { + return -x; + } + + REACT_SYNC_METHOD(StaticSayHelloSync) + static std::string StaticSayHelloSync() noexcept { + return "Hello"; + } + + REACT_CONSTANT(Constant1) + const std::string Constant1{"MyConstant1"}; + + REACT_CONSTANT(Constant2, L"const2") + const std::string Constant2{"MyConstant2"}; + + REACT_CONSTANT(Constant3, L"const3") + static constexpr Point Constant3{/*X =*/2, /*Y =*/3}; + + REACT_CONSTANT(Constant4) + static constexpr Point Constant4{/*X =*/3, /*Y =*/4}; + + REACT_CONSTANT_PROVIDER(Constant5) + void Constant5(ReactConstantProvider &provider) noexcept { + provider.Add(L"const51", Point{/*X =*/12, /*Y =*/14}); + provider.Add(L"const52", "MyConstant52"); + } + + REACT_CONSTANT_PROVIDER(Constant6) + static void Constant6(ReactConstantProvider &provider) noexcept { + provider.Add(L"const61", Point{/*X =*/15, /*Y =*/17}); + provider.Add(L"const62", "MyConstant62"); + } + + REACT_EVENT(OnIntResult1) + std::function OnIntResult1; + + REACT_EVENT(OnPointResult2, L"onPointResult2") + std::function OnPointResult2; + + std::string Message; + static std::string StaticMessage; +}; + +/*static*/ std::string SimpleNativeModule::StaticMessage; + +// Common base class used by all unit tests below. +struct NativeModuleTestFixture { + NativeModuleTestFixture() { + m_moduleBuilder = make(m_builderMock); + auto provider = MakeModuleProvider(); + m_moduleObject = provider(m_moduleBuilder); + auto reactModule = m_moduleObject.as(); + m_module = &BoxedValue::GetImpl(reactModule); + } + + protected: + ReactModuleBuilderMock m_builderMock{}; + IReactModuleBuilder m_moduleBuilder; + Windows::Foundation::IInspectable m_moduleObject{nullptr}; + SimpleNativeModule *m_module; +}; + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_Add", "NativeModuleTest") { + m_builderMock.Call1(L"Add", std::function([](int result) noexcept { REQUIRE(result == 8); }), 3, 5); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_Negate", "NativeModuleTest") { + m_builderMock.Call1(L"Negate", std::function([](int result) noexcept { REQUIRE(result == -3); }), 3); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_SayHello", "NativeModuleTest") { + m_builderMock.Call1(L"SayHello", std::function([](const std::string &result) noexcept { + REQUIRE(result == "Hello"); + })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticAdd", "NativeModuleTest") { + m_builderMock.Call1( + L"StaticAdd", std::function([](int result) noexcept { REQUIRE(result == 25); }), 20, 5); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegate", "NativeModuleTest") { + m_builderMock.Call1(L"StaticNegate", std::function([](int result) noexcept { REQUIRE(result == -7); }), 7); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticSayHello", "NativeModuleTest") { + m_builderMock.Call1( + L"StaticSayHello", + std::function([](const std::string &result) noexcept { REQUIRE(result == "Hello"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_SayHello0", "NativeModuleTest") { + m_builderMock.Call0(L"SayHello0"); + REQUIRE(m_module->Message == "Hello_0"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_PrintPoint", "NativeModuleTest") { + m_builderMock.Call0(L"PrintPoint", Point{/*X =*/3, /*Y =*/5}); + REQUIRE(m_module->Message == "Point: (3, 5)"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_PrintLine", "NativeModuleTest") { + m_builderMock.Call0(L"PrintLine", Point{/*X =*/3, /*Y =*/5}, Point{/*X =*/6, /*Y =*/8}); + REQUIRE(m_module->Message == "Line: (3, 5)-(6, 8)"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticSayHello1", "NativeModuleTest") { + m_builderMock.Call0(L"StaticSayHello1"); + REQUIRE(SimpleNativeModule::StaticMessage == "Hello_1"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticPrintPoint", "NativeModuleTest") { + m_builderMock.Call0(L"StaticPrintPoint", Point{/*X =*/13, /*Y =*/15}); + REQUIRE(SimpleNativeModule::StaticMessage == "Static Point: (13, 15)"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticPrintLine", "NativeModuleTest") { + m_builderMock.Call0(L"StaticPrintLine", Point{/*X =*/13, /*Y =*/15}, Point{/*X =*/16, /*Y =*/18}); + REQUIRE(SimpleNativeModule::StaticMessage == "Static Line: (13, 15)-(16, 18)"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_AddCallback", "NativeModuleTest") { + m_builderMock.Call1( + L"AddCallback", std::function([](int result) noexcept { REQUIRE(result == -1); }), 7, -8); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegateCallback", "NativeModuleTest") { + m_builderMock.Call1( + L"NegateCallback", std::function([](int result) noexcept { REQUIRE(result == -4); }), 4); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_SayHelloCallback", "NativeModuleTest") { + m_builderMock.Call1(L"SayHelloCallback", std::function([ + ](const std::string &result) noexcept { REQUIRE(result == "Hello_2"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticAddCallback", "NativeModuleTest") { + m_builderMock.Call1( + L"StaticAddCallback", std::function([](int result) noexcept { REQUIRE(result == 60); }), 4, 56); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegateCallback", "NativeModuleTest") { + m_builderMock.Call1( + L"StaticNegateCallback", std::function([](int result) noexcept { REQUIRE(result == -33); }), 33); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticSayHelloCallback", "NativeModuleTest") { + m_builderMock.Call1(L"StaticSayHelloCallback", std::function([ + ](const std::string &result) noexcept { REQUIRE(result == "Static Hello_2"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DivideCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"DivideCallbacks", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 2); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DivideCallbacksError", "NativeModuleTest") { + m_builderMock.Call2( + L"DivideCallbacks", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 0); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegateCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"NegateCallbacks", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + 5); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegateCallbacksError", "NativeModuleTest") { + m_builderMock.Call2( + L"NegateCallbacks", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + -5); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_ResolveSayHelloCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"ResolveSayHelloCallbacks", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_RejectSayHelloCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"RejectSayHelloCallbacks", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); })); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticDivideCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticDivideCallbacks", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 2); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticDivideCallbacksError", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticDivideCallbacks", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 0); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegateCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticNegateCallbacks", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + 5); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegateCallbacksError", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticNegateCallbacks", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + -5); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticResolveSayHelloCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticResolveSayHelloCallbacks", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticRejectSayHelloCallbacks", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticRejectSayHelloCallbacks", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_3"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Goodbye"); })); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DividePromise", "NativeModuleTest") { + m_builderMock.Call2( + L"DividePromise", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 2); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_DividePromiseError", "NativeModuleTest") { + m_builderMock.Call2( + L"DividePromise", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 0); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegatePromise", "NativeModuleTest") { + m_builderMock.Call2( + L"NegatePromise", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + 5); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_NegatePromiseError", "NativeModuleTest") { + m_builderMock.Call2( + L"NegatePromise", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + -5); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_ResolveSayHelloPromise", "NativeModuleTest") { + m_builderMock.Call2( + L"ResolveSayHelloPromise", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_4"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Promise rejected"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_RejectSayHelloPromise", "NativeModuleTest") { + m_builderMock.Call2( + L"RejectSayHelloPromise", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_4"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Promise rejected"); })); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticDividePromise", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticDividePromise", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 2); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticDividePromiseError", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticDividePromise", + std::function([](int result) noexcept { REQUIRE(result == 3); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Division by 0"); }), + 6, + 0); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegatePromise", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticNegatePromise", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + 5); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticNegatePromiseError", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticNegatePromise", + std::function([](int result) noexcept { REQUIRE(result == -5); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Already negative"); }), + -5); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticResolveSayHelloPromise", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticResolveSayHelloPromise", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_4"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Promise rejected"); })); + REQUIRE(m_builderMock.IsResolveCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodCall_StaticRejectSayHelloPromise", "NativeModuleTest") { + m_builderMock.Call2( + L"StaticRejectSayHelloPromise", + std::function( + [](const std::string &result) noexcept { REQUIRE(result == "Hello_4"); }), + std::function( + [](JSValue const &error) noexcept { REQUIRE(error["message"] == "Promise rejected"); })); + REQUIRE(m_builderMock.IsRejectCallbackCalled()); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodSyncCall_AddSync", "NativeModuleTest") { + int result; + m_builderMock.CallSync(L"AddSync", /*out*/ result, 3, 5); + REQUIRE(result == 8); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodSyncCall_NegateSync", "NativeModuleTest") { + int result; + m_builderMock.CallSync(L"NegateSync", /*out*/ result, 7); + REQUIRE(result == -7); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodSyncCall_SayHelloSync", "NativeModuleTest") { + std::string result; + m_builderMock.CallSync(L"SayHelloSync", /*out*/ result); + REQUIRE(result == "Hello"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodSyncCall_StaticAddSync", "NativeModuleTest") { + int result; + m_builderMock.CallSync(L"StaticAddSync", /*out*/ result, 3, 5); + REQUIRE(result == 8); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodSyncCall_StaticNegateSync", "NativeModuleTest") { + int result; + m_builderMock.CallSync(L"StaticNegateSync", /*out*/ result, 7); + REQUIRE(result == -7); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestMethodSyncCall_StaticSayHelloSync", "NativeModuleTest") { + std::string result; + m_builderMock.CallSync(L"StaticSayHelloSync", /*out*/ result); + REQUIRE(result == "Hello"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestConstants", "NativeModuleTest") { + auto constants = m_builderMock.GetConstants(); + REQUIRE(constants["Constant1"] == "MyConstant1"); + REQUIRE(constants["const2"] == "MyConstant2"); + REQUIRE(constants["const3"]["X"] == 2); + REQUIRE(constants["const3"]["Y"] == 3); + REQUIRE(constants["Constant4"]["X"] == 3); + REQUIRE(constants["Constant4"]["Y"] == 4); + REQUIRE(constants["const51"]["X"] == 12); + REQUIRE(constants["const51"]["Y"] == 14); + REQUIRE(constants["const52"] == "MyConstant52"); + REQUIRE(constants["const61"]["X"] == 15); + REQUIRE(constants["const61"]["Y"] == 17); + REQUIRE(constants["const62"] == "MyConstant62"); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestEvent_EventField1", "NativeModuleTest") { + bool eventRaised = false; + m_builderMock.SetEventHandler(L"OnIntResult1", std::function([&eventRaised](int eventArg) noexcept { + REQUIRE(eventArg == 42); + eventRaised = true; + })); + + m_module->OnIntResult1(42); + REQUIRE(eventRaised == true); +} + +TEST_CASE_METHOD(NativeModuleTestFixture, "TestEvent_EventField2", "NativeModuleTest") { + bool eventRaised = false; + m_builderMock.SetEventHandler( + L"onPointResult2", std::function([&eventRaised](const Point &eventArg) noexcept { + REQUIRE(eventArg.X == 4); + REQUIRE(eventArg.Y == 2); + eventRaised = true; + })); + + m_module->OnPointResult2(Point{/*X =*/4, /*Y =*/2}); + REQUIRE(eventRaised == true); +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/PropertySheet.props b/vnext/Microsoft.ReactNative.Cxx.UnitTests/PropertySheet.props new file mode 100644 index 00000000000..c6b0691ddda --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp new file mode 100644 index 00000000000..d798a9ee6e3 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "ReactModuleBuilderMock.h" +#include "JSValue.h" + +namespace winrt::Microsoft::ReactNative::Bridge { + +//=========================================================================== +// ReactModuleBuilderMock implementation +//=========================================================================== + +void ReactModuleBuilderMock::SetEventEmitterName(hstring const &name) noexcept { + m_eventEmitterName = name; +} + +void ReactModuleBuilderMock::AddMethod( + hstring const &name, + MethodReturnType returnType, + MethodDelegate const &method) noexcept { + m_methods.emplace(name, std::tuple{returnType, method}); +} + +void ReactModuleBuilderMock::AddSyncMethod(hstring const &name, SyncMethodDelegate const &method) noexcept { + m_syncMethods.emplace(name, method); +} + +void ReactModuleBuilderMock::AddConstantProvider(ConstantProvider const &constantProvider) noexcept { + m_constProviders.push_back(constantProvider); +} + +void ReactModuleBuilderMock::AddEventHandlerSetter( + hstring const &name, + ReactEventHandlerSetter const &eventHandlerSetter) noexcept { + eventHandlerSetter([ this, name = std::wstring{name} ](ReactArgWriter const &argWriter) noexcept { + auto it = m_eventHandlers.find(name); + if (it != m_eventHandlers.end()) { + JSValue jsValue; + auto writer = MakeJSValueTreeWriter(jsValue); + writer.WriteArrayBegin(); + argWriter(writer); + writer.WriteArrayEnd(); + auto reader = MakeJSValueTreeReader(jsValue); + it->second(reader); + } + }); +} + +JSValueObject ReactModuleBuilderMock::GetConstants() noexcept { + JSValue jsValue; + auto constantWriter = MakeJSValueTreeWriter(jsValue); + constantWriter.WriteObjectBegin(); + for (const auto &constantProvider : m_constProviders) { + constantProvider(constantWriter); + } + + constantWriter.WriteObjectEnd(); + return jsValue.TakeObject(); +} + +MethodDelegate ReactModuleBuilderMock::GetMethod0(std::wstring const &methodName) const noexcept { + auto it = m_methods.find(methodName); + return (it != m_methods.end() && std::get<0>(it->second) == MethodReturnType::Void) ? std::get<1>(it->second) + : nullptr; +} + +MethodDelegate ReactModuleBuilderMock::GetMethod1(std::wstring const &methodName) const noexcept { + auto it = m_methods.find(methodName); + return (it != m_methods.end() && std::get<0>(it->second) == MethodReturnType::Callback) ? std::get<1>(it->second) + : nullptr; +} + +MethodDelegate ReactModuleBuilderMock::GetMethod2(std::wstring const &methodName) const noexcept { + auto it = m_methods.find(methodName); + return (it != m_methods.end() && + (std::get<0>(it->second) == MethodReturnType::TwoCallbacks || + std::get<0>(it->second) == MethodReturnType::Promise)) + ? std::get<1>(it->second) + : nullptr; +} + +SyncMethodDelegate ReactModuleBuilderMock::GetSyncMethod(std::wstring const &methodName) const noexcept { + auto it = m_syncMethods.find(methodName); + return (it != m_syncMethods.end()) ? it->second : nullptr; +} + +/*static*/ IJSValueWriter ReactModuleBuilderMock::ArgWriter(JSValue &jsValue) noexcept { + return MakeJSValueTreeWriter(jsValue); +} + +/*static*/ IJSValueReader ReactModuleBuilderMock::CreateArgReader( + std::function const &argWriter) noexcept { + JSValue jsValue; + auto writer = MakeJSValueTreeWriter(jsValue); + argWriter(writer); + return MakeJSValueTreeReader(std::move(jsValue)); +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.h b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.h new file mode 100644 index 00000000000..35fa6bb2233 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.h @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include "JSValue.h" + +namespace winrt::Microsoft::ReactNative::Bridge { + +struct ReactModuleBuilderMock { + template + void Call0(std::wstring const &methodName, TArgs &&... args) noexcept; + + template + void Call1(std::wstring const &methodName, std::function const &resolve, TArgs &&... args) noexcept; + + template + void Call2( + std::wstring const &methodName, + std::function const &resolve, + std::function const &reject, + TArgs &&... args) noexcept; + + template + void CallSync(std::wstring const &methodName, TResult &result, TArgs &&... args) noexcept; + + JSValueObject GetConstants() noexcept; + + template + void SetEventHandler(std::wstring const &eventName, std::function const &eventHandler) noexcept; + + bool IsResolveCallbackCalled() const noexcept; + void IsResolveCallbackCalled(bool value) noexcept; + bool IsRejectCallbackCalled() const noexcept; + void IsRejectCallbackCalled(bool value) noexcept; + + public: // IReactModuleBuilder + void SetEventEmitterName(hstring const &name) noexcept; + void AddMethod(hstring const &name, MethodReturnType returnType, MethodDelegate const &method) noexcept; + void AddSyncMethod(hstring const &name, SyncMethodDelegate const &method) noexcept; + void AddConstantProvider(ConstantProvider const &constantProvider) noexcept; + void AddEventHandlerSetter(hstring const &name, ReactEventHandlerSetter const &eventHandlerSetter) noexcept; + + private: + MethodDelegate GetMethod0(std::wstring const &methodName) const noexcept; + MethodDelegate GetMethod1(std::wstring const &methodName) const noexcept; + MethodDelegate GetMethod2(std::wstring const &methodName) const noexcept; + SyncMethodDelegate GetSyncMethod(std::wstring const &methodName) const noexcept; + + static IJSValueWriter ArgWriter(JSValue &jsValue) noexcept; + template + static IJSValueReader ArgReader(TArgs &&... args) noexcept; + static IJSValueReader CreateArgReader(std::function const &argWriter) noexcept; + + template + MethodResultCallback ResolveCallback(JSValue const &jsValue, std::function const &resolve) noexcept; + template + MethodResultCallback RejectCallback(JSValue const &jsValue, std::function const &reject) noexcept; + + private: + std::wstring m_eventEmitterName; + std::map> m_methods; + std::map m_syncMethods; + std::vector m_constProviders; + std::map> m_eventHandlers; + bool m_isResolveCallbackCalled; + bool m_isRejectCallbackCalled; +}; + +struct ReactModuleBuilderImpl : implements { + ReactModuleBuilderImpl(ReactModuleBuilderMock &mock) noexcept; + + public: // IReactModuleBuilder + void SetEventEmitterName(hstring const &name) noexcept; + void AddMethod(hstring const &name, MethodReturnType returnType, MethodDelegate const &method) noexcept; + void AddSyncMethod(hstring const &name, SyncMethodDelegate const &method) noexcept; + void AddConstantProvider(ConstantProvider const &constantProvider) noexcept; + void AddEventHandlerSetter(hstring const &name, ReactEventHandlerSetter const &eventHandlerSetter) noexcept; + + private: + ReactModuleBuilderMock &m_mock; +}; + +//=========================================================================== +// ReactModuleBuilderMock inline implementation +//=========================================================================== + +template +inline void ReactModuleBuilderMock::Call0(std::wstring const &methodName, TArgs &&... args) noexcept { + if (auto method = GetMethod0(methodName)) { + JSValue jsValue; + method(ArgReader(std::forward(args)...), ArgWriter(jsValue), nullptr, nullptr); + } +} + +template +inline void ReactModuleBuilderMock::Call1( + std::wstring const &methodName, + std::function const &resolve, + TArgs &&... args) noexcept { + if (auto method = GetMethod1(methodName)) { + JSValue jsValue; + method(ArgReader(std::forward(args)...), ArgWriter(jsValue), ResolveCallback(jsValue, resolve), nullptr); + } +} + +template +inline void ReactModuleBuilderMock::Call2( + std::wstring const &methodName, + std::function const &resolve, + std::function const &reject, + TArgs &&... args) noexcept { + if (auto method = GetMethod2(methodName)) { + JSValue jsValue; + method( + ArgReader(std::forward(args)...), + ArgWriter(jsValue), + ResolveCallback(jsValue, resolve), + RejectCallback(jsValue, reject)); + } +} + +template +inline void +ReactModuleBuilderMock::CallSync(std::wstring const &methodName, TResult &result, TArgs &&... args) noexcept { + if (auto method = GetSyncMethod(methodName)) { + JSValue jsValue; + method(ArgReader(std::forward(args)...), ArgWriter(jsValue)); + ReadArgs(MakeJSValueTreeReader(jsValue), result); + } +} + +template +inline void ReactModuleBuilderMock::SetEventHandler( + std::wstring const &eventName, + std::function const &eventHandler) noexcept { + m_eventHandlers.emplace(eventName, [eventHandler](IJSValueReader const &reader) noexcept { + std::remove_const_t> arg; + ReadArgs(reader, /*out*/ arg); + eventHandler(arg); + }); +} + +template +inline /*static*/ IJSValueReader ReactModuleBuilderMock::ArgReader(TArgs &&... args) noexcept { + return CreateArgReader([&args...](IJSValueWriter const &writer) mutable noexcept { + WriteArgs(writer, std::forward(args)...); + }); +} + +template +inline MethodResultCallback ReactModuleBuilderMock::ResolveCallback( + JSValue const &jsValue, + std::function const &resolve) noexcept { + return [ this, &jsValue, &resolve ](IJSValueWriter const & /*writer*/) noexcept { + std::remove_const_t> arg; + ReadArgs(MakeJSValueTreeReader(jsValue), arg); + resolve(arg); + m_isResolveCallbackCalled = true; + }; +} + +template +inline MethodResultCallback ReactModuleBuilderMock::RejectCallback( + JSValue const &jsValue, + std::function const &reject) noexcept { + return [ this, &jsValue, &reject ](IJSValueWriter const & /*writer*/) noexcept { + std::remove_const_t> arg; + ReadArgs(MakeJSValueTreeReader(jsValue), arg); + reject(arg); + m_isRejectCallbackCalled = true; + }; +} + +inline bool ReactModuleBuilderMock::IsResolveCallbackCalled() const noexcept { + return m_isResolveCallbackCalled; +} + +inline void ReactModuleBuilderMock::IsResolveCallbackCalled(bool value) noexcept { + m_isResolveCallbackCalled = value; +} + +inline bool ReactModuleBuilderMock::IsRejectCallbackCalled() const noexcept { + return m_isRejectCallbackCalled; +} + +inline void ReactModuleBuilderMock::IsRejectCallbackCalled(bool value) noexcept { + m_isRejectCallbackCalled = value; +} + +//=========================================================================== +// ReactModuleBuilderImpl inline implementation +//=========================================================================== + +inline ReactModuleBuilderImpl::ReactModuleBuilderImpl(ReactModuleBuilderMock &mock) noexcept : m_mock{mock} {} + +inline void ReactModuleBuilderImpl::SetEventEmitterName(hstring const &name) noexcept { + m_mock.SetEventEmitterName(name); +} + +inline void ReactModuleBuilderImpl::AddMethod( + hstring const &name, + MethodReturnType returnType, + MethodDelegate const &method) noexcept { + m_mock.AddMethod(name, returnType, method); +} + +inline void ReactModuleBuilderImpl::AddSyncMethod(hstring const &name, SyncMethodDelegate const &method) noexcept { + m_mock.AddSyncMethod(name, method); +} + +inline void ReactModuleBuilderImpl::AddConstantProvider(ConstantProvider const &constantProvider) noexcept { + m_mock.AddConstantProvider(constantProvider); +} + +inline void ReactModuleBuilderImpl::AddEventHandlerSetter( + hstring const &name, + ReactEventHandlerSetter const &eventHandlerSetter) noexcept { + m_mock.AddEventHandlerSetter(name, eventHandlerSetter); +} + +} // namespace winrt::Microsoft::ReactNative::Bridge diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/catch.hpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/catch.hpp new file mode 100644 index 00000000000..b4eccfc1483 --- /dev/null +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/catch.hpp @@ -0,0 +1,17597 @@ +/* + * Catch v2.11.0 + * Generated: 2019-11-15 15:01:56.628356 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 11 +#define CATCH_VERSION_PATCH 0 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if defined(__UCLIBC__) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template