From 1dbaee678cdc683db68e12ea3f357e19fa98fdf3 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 14 Jan 2020 16:23:33 -0800 Subject: [PATCH 1/3] testing --- cppwinrt.sln | 24 ++ cppwinrt/cppwinrt.vcxproj | 4 + prebuild/prebuild.vcxproj | 8 + scratch/scratch.vcxproj | 8 + strings/base_activation.h | 9 +- strings/base_extern.h | 2 - test/test/test.vcxproj | 78 +--- test/test_component/test_component.vcxproj | 8 + .../test_component_base.vcxproj | 8 + .../test_component_derived.vcxproj | 8 + .../test_component_fast.vcxproj | 8 + .../test_component_folders.vcxproj | 8 + .../test_component_no_pch.vcxproj | 8 + test/test_fast/test_fast.vcxproj | 8 + test/test_fast_fwd/test_fast_fwd.vcxproj | 22 + .../test_module_lock_custom.vcxproj | 8 + .../test_module_lock_none.vcxproj | 8 + test/test_slow/test_slow.vcxproj | 8 + test/test_win7/GetMany.cpp | 378 +++++++++++++++++ test/test_win7/abi_guard.cpp | 293 ++++++++++++++ test/test_win7/agile_ref.cpp | 54 +++ test/test_win7/agility.cpp | 140 +++++++ test/test_win7/async_auto_cancel.cpp | 73 ++++ test/test_win7/async_cancel_callback.cpp | 93 +++++ test/test_win7/async_check_cancel.cpp | 107 +++++ test/test_win7/async_local.cpp | 74 ++++ test/test_win7/async_no_suspend.cpp | 85 ++++ test/test_win7/async_progress.cpp | 82 ++++ test/test_win7/async_result.cpp | 78 ++++ test/test_win7/async_return.cpp | 52 +++ test/test_win7/async_suspend.cpp | 90 +++++ test/test_win7/async_throw.cpp | 86 ++++ test/test_win7/async_wait_for.cpp | 136 +++++++ test/test_win7/capture.cpp | 71 ++++ test/test_win7/cmd_reader.cpp | 154 +++++++ test/test_win7/coro_foundation.cpp | 21 + test/test_win7/coro_threadpool.cpp | 20 + test/test_win7/custom_error.cpp | 52 +++ test/test_win7/delegate.cpp | 72 ++++ test/test_win7/delegates.cpp | 98 +++++ test/test_win7/disconnected.cpp | 124 ++++++ test/test_win7/enum.cpp | 24 ++ test/test_win7/fast_iterator.cpp | 23 ++ test/test_win7/final_release.cpp | 66 +++ test/test_win7/generic_type_names.cpp | 150 +++++++ test/test_win7/generic_types.cpp | 16 + test/test_win7/generic_types.h | 165 ++++++++ test/test_win7/iid_ppv_args.cpp | 39 ++ test/test_win7/in_params.cpp | 52 +++ test/test_win7/inspectable_interop.cpp | 64 +++ test/test_win7/interop.cpp | 80 ++++ test/test_win7/invalid_events.cpp | 63 +++ test/test_win7/main.cpp | 17 + test/test_win7/module_lock_dll.cpp | 50 +++ test/test_win7/names.cpp | 19 + test/test_win7/no_make_detection.cpp | 48 +++ test/test_win7/noexcept.cpp | 17 + test/test_win7/notify_awaiter.cpp | 217 ++++++++++ test/test_win7/numerics.cpp | 13 + test/test_win7/out_params.cpp | 268 ++++++++++++ test/test_win7/parent_includes.cpp | 17 + test/test_win7/pch.cpp | 1 + test/test_win7/pch.h | 9 + test/test_win7/return_params.cpp | 80 ++++ test/test_win7/structs.cpp | 18 + test/test_win7/test_win7.vcxproj | 380 ++++++++++++++++++ test/test_win7/thread_pool.cpp | 61 +++ test/test_win7/uniform_in_params.cpp | 29 ++ test/test_win7/velocity.cpp | 44 ++ test/test_win7/when.cpp | 74 ++++ 70 files changed, 4699 insertions(+), 73 deletions(-) create mode 100644 test/test_win7/GetMany.cpp create mode 100644 test/test_win7/abi_guard.cpp create mode 100644 test/test_win7/agile_ref.cpp create mode 100644 test/test_win7/agility.cpp create mode 100644 test/test_win7/async_auto_cancel.cpp create mode 100644 test/test_win7/async_cancel_callback.cpp create mode 100644 test/test_win7/async_check_cancel.cpp create mode 100644 test/test_win7/async_local.cpp create mode 100644 test/test_win7/async_no_suspend.cpp create mode 100644 test/test_win7/async_progress.cpp create mode 100644 test/test_win7/async_result.cpp create mode 100644 test/test_win7/async_return.cpp create mode 100644 test/test_win7/async_suspend.cpp create mode 100644 test/test_win7/async_throw.cpp create mode 100644 test/test_win7/async_wait_for.cpp create mode 100644 test/test_win7/capture.cpp create mode 100644 test/test_win7/cmd_reader.cpp create mode 100644 test/test_win7/coro_foundation.cpp create mode 100644 test/test_win7/coro_threadpool.cpp create mode 100644 test/test_win7/custom_error.cpp create mode 100644 test/test_win7/delegate.cpp create mode 100644 test/test_win7/delegates.cpp create mode 100644 test/test_win7/disconnected.cpp create mode 100644 test/test_win7/enum.cpp create mode 100644 test/test_win7/fast_iterator.cpp create mode 100644 test/test_win7/final_release.cpp create mode 100644 test/test_win7/generic_type_names.cpp create mode 100644 test/test_win7/generic_types.cpp create mode 100644 test/test_win7/generic_types.h create mode 100644 test/test_win7/iid_ppv_args.cpp create mode 100644 test/test_win7/in_params.cpp create mode 100644 test/test_win7/inspectable_interop.cpp create mode 100644 test/test_win7/interop.cpp create mode 100644 test/test_win7/invalid_events.cpp create mode 100644 test/test_win7/main.cpp create mode 100644 test/test_win7/module_lock_dll.cpp create mode 100644 test/test_win7/names.cpp create mode 100644 test/test_win7/no_make_detection.cpp create mode 100644 test/test_win7/noexcept.cpp create mode 100644 test/test_win7/notify_awaiter.cpp create mode 100644 test/test_win7/numerics.cpp create mode 100644 test/test_win7/out_params.cpp create mode 100644 test/test_win7/parent_includes.cpp create mode 100644 test/test_win7/pch.cpp create mode 100644 test/test_win7/pch.h create mode 100644 test/test_win7/return_params.cpp create mode 100644 test/test_win7/structs.cpp create mode 100644 test/test_win7/test_win7.vcxproj create mode 100644 test/test_win7/thread_pool.cpp create mode 100644 test/test_win7/uniform_in_params.cpp create mode 100644 test/test_win7/velocity.cpp create mode 100644 test/test_win7/when.cpp diff --git a/cppwinrt.sln b/cppwinrt.sln index 2e81621fe..3189d1555 100644 --- a/cppwinrt.sln +++ b/cppwinrt.sln @@ -96,6 +96,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_module_lock_custom", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3C7EA5F8-6E8C-4376-B499-2CAF596384B0}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_win7", "test\test_win7\test_win7.vcxproj", "{2EF696B9-7F4A-410F-AE5C-5301565C0F08}" + ProjectSection(ProjectDependencies) = postProject + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + {F1C915B3-2C64-4992-AFB7-7F035B1A7607} = {F1C915B3-2C64-4992-AFB7-7F035B1A7607} + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -412,6 +419,22 @@ Global {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x64.Build.0 = Release|x64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x86.ActiveCfg = Release|Win32 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x86.Build.0 = Release|Win32 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM.ActiveCfg = Debug|ARM + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM.Build.0 = Debug|ARM + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM64.Build.0 = Debug|ARM64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x64.ActiveCfg = Debug|x64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x64.Build.0 = Debug|x64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x86.ActiveCfg = Debug|Win32 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x86.Build.0 = Debug|Win32 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM.ActiveCfg = Release|ARM + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM.Build.0 = Release|ARM + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM64.ActiveCfg = Release|ARM64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM64.Build.0 = Release|ARM64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x64.ActiveCfg = Release|x64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x64.Build.0 = Release|x64 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x86.ActiveCfg = Release|Win32 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -433,6 +456,7 @@ Global {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} {08C40663-B6A3-481E-8755-AE32BAD99501} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} + {2EF696B9-7F4A-410F-AE5C-5301565C0F08} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2783B8FD-EA3B-4D6B-9F81-662D289E02AA} diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index 342c3b3d3..4b0372508 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -201,6 +201,7 @@ Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); + MultiThreadedDebug Console @@ -217,6 +218,7 @@ Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); + MultiThreadedDebug Console @@ -233,6 +235,7 @@ Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); + MultiThreadedDebug Console @@ -249,6 +252,7 @@ Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); + MultiThreadedDebug Console diff --git a/prebuild/prebuild.vcxproj b/prebuild/prebuild.vcxproj index a6015bd53..92df71b65 100644 --- a/prebuild/prebuild.vcxproj +++ b/prebuild/prebuild.vcxproj @@ -125,6 +125,7 @@ Disabled ..\cppwinrt + MultiThreadedDebug Console @@ -134,6 +135,7 @@ Disabled ..\cppwinrt + MultiThreadedDebug Console @@ -143,6 +145,7 @@ Disabled ..\cppwinrt + MultiThreadedDebug Console @@ -152,6 +155,7 @@ Disabled ..\cppwinrt + MultiThreadedDebug Console @@ -163,6 +167,7 @@ true true ..\cppwinrt + MultiThreaded Console @@ -176,6 +181,7 @@ true true ..\cppwinrt + MultiThreaded Console @@ -189,6 +195,7 @@ true true ..\cppwinrt + MultiThreaded Console @@ -202,6 +209,7 @@ true true ..\cppwinrt + MultiThreaded Console diff --git a/scratch/scratch.vcxproj b/scratch/scratch.vcxproj index d65af2e48..ae9bca8fb 100644 --- a/scratch/scratch.vcxproj +++ b/scratch/scratch.vcxproj @@ -130,6 +130,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreaded Console @@ -151,6 +152,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreadedDebug Console @@ -170,6 +172,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreadedDebug Console @@ -189,6 +192,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreadedDebug Console @@ -208,6 +212,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreadedDebug Console @@ -229,6 +234,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreaded Console @@ -252,6 +258,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreaded Console @@ -275,6 +282,7 @@ $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) /await %(AdditionalOptions) + MultiThreaded Console diff --git a/strings/base_activation.h b/strings/base_activation.h index fe7b94b84..1d3817977 100644 --- a/strings/base_activation.h +++ b/strings/base_activation.h @@ -34,8 +34,15 @@ namespace winrt::impl if (hr == impl::error_not_initialized) { + auto usage = static_cast(WINRT_GetProcAddress(WINRT_LoadLibraryW(L"combase.dll"), "CoIncrementMTAUsage")); + + if (!usage) + { + return hr; + } + void* cookie; - WINRT_CoIncrementMTAUsage(&cookie); + usage(&cookie); hr = handler(*(void**)(&name), guid_of(), result); } diff --git a/strings/base_extern.h b/strings/base_extern.h index 194e32fa1..f6c0bc24e 100644 --- a/strings/base_extern.h +++ b/strings/base_extern.h @@ -13,7 +13,6 @@ extern "C" int32_t __stdcall WINRT_GetErrorInfo(uint32_t reserved, void** info) noexcept; int32_t __stdcall WINRT_CoInitializeEx(void*, uint32_t type) noexcept; void __stdcall WINRT_CoUninitialize() noexcept; - int32_t __stdcall WINRT_CoIncrementMTAUsage(void** cookie) noexcept; int32_t __stdcall WINRT_CoCreateFreeThreadedMarshaler(void* outer, void** marshaler) noexcept; int32_t __stdcall WINRT_CoCreateInstance(winrt::guid const& clsid, void* outer, uint32_t context, winrt::guid const& iid, void** object) noexcept; @@ -96,7 +95,6 @@ WINRT_IMPL_LINK(SetErrorInfo, 8) WINRT_IMPL_LINK(GetErrorInfo, 8) WINRT_IMPL_LINK(CoInitializeEx, 8) WINRT_IMPL_LINK(CoUninitialize, 0) -WINRT_IMPL_LINK(CoIncrementMTAUsage, 4) WINRT_IMPL_LINK(CoCreateFreeThreadedMarshaler, 8) WINRT_IMPL_LINK(CoCreateInstance, 20) diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index 89323600c..147f5371d 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -129,6 +129,7 @@ true $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -148,6 +149,7 @@ Disabled $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -165,6 +167,7 @@ Disabled $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -182,6 +185,7 @@ Disabled $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -199,6 +203,7 @@ Disabled $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -218,6 +223,7 @@ true $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -239,6 +245,7 @@ true $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -260,6 +267,7 @@ true $(OutputPath);Generated Files;..;..\..\cppwinrt NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -275,14 +283,9 @@ - - - - - @@ -298,92 +301,27 @@ - - - NotUsing - NotUsing - - NotUsing - NotUsing - - - - - - - - - - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - - - - - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - - - - NotUsing - - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - - - - - - NotUsing - - - - Create - - - - - - diff --git a/test/test_component/test_component.vcxproj b/test/test_component/test_component.vcxproj index a404f884d..ef66896e9 100644 --- a/test/test_component/test_component.vcxproj +++ b/test/test_component/test_component.vcxproj @@ -157,6 +157,7 @@ Disabled .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreadedDebug exports.def @@ -204,6 +205,7 @@ Disabled .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreadedDebug exports.def @@ -265,6 +267,7 @@ Disabled .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreadedDebug exports.def @@ -326,6 +329,7 @@ Disabled .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreadedDebug exports.def @@ -375,6 +379,7 @@ true .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreaded true @@ -426,6 +431,7 @@ true .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreaded true @@ -491,6 +497,7 @@ true .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreaded true @@ -556,6 +563,7 @@ true .;$(OutputPath);Generated Files /Zc:threadSafeInit- /we4640 %(AdditionalOptions) + MultiThreaded true diff --git a/test/test_component_base/test_component_base.vcxproj b/test/test_component_base/test_component_base.vcxproj index 0ac0b01aa..43f67d347 100644 --- a/test/test_component_base/test_component_base.vcxproj +++ b/test/test_component_base/test_component_base.vcxproj @@ -157,6 +157,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -204,6 +205,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -265,6 +267,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -326,6 +329,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -375,6 +379,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true @@ -426,6 +431,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true @@ -491,6 +497,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true @@ -556,6 +563,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true diff --git a/test/test_component_derived/test_component_derived.vcxproj b/test/test_component_derived/test_component_derived.vcxproj index 2e9bf9cbc..fcb82ee3b 100644 --- a/test/test_component_derived/test_component_derived.vcxproj +++ b/test/test_component_derived/test_component_derived.vcxproj @@ -157,6 +157,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreadedDebug exports.def @@ -205,6 +206,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreadedDebug exports.def @@ -267,6 +269,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreadedDebug exports.def @@ -329,6 +332,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreadedDebug exports.def @@ -379,6 +383,7 @@ true $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreaded true @@ -431,6 +436,7 @@ true $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreaded true @@ -497,6 +503,7 @@ true $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreaded true @@ -563,6 +570,7 @@ true $(ProjectDir);$(OutputPath);Generated Files;..\test_component_base\Generated Files 4100 + MultiThreaded true diff --git a/test/test_component_fast/test_component_fast.vcxproj b/test/test_component_fast/test_component_fast.vcxproj index 030c61341..55d1ed371 100644 --- a/test/test_component_fast/test_component_fast.vcxproj +++ b/test/test_component_fast/test_component_fast.vcxproj @@ -159,6 +159,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreadedDebug exports.def @@ -207,6 +208,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreadedDebug exports.def @@ -269,6 +271,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreadedDebug exports.def @@ -331,6 +334,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreadedDebug exports.def @@ -381,6 +385,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreaded true @@ -433,6 +438,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreaded true @@ -499,6 +505,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreaded true @@ -565,6 +572,7 @@ $(ProjectDir);$(OutputPath);Generated Files /DWINRT_FAST_ABI_SIZE=50 %(AdditionalOptions) 4100 + MultiThreaded true diff --git a/test/test_component_folders/test_component_folders.vcxproj b/test/test_component_folders/test_component_folders.vcxproj index 4ea524f1d..d470ed048 100644 --- a/test/test_component_folders/test_component_folders.vcxproj +++ b/test/test_component_folders/test_component_folders.vcxproj @@ -157,6 +157,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -204,6 +205,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -265,6 +267,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -326,6 +329,7 @@ Disabled $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreadedDebug exports.def @@ -375,6 +379,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true @@ -426,6 +431,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true @@ -491,6 +497,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true @@ -556,6 +563,7 @@ true $(ProjectDir);$(OutputPath);Generated Files 4100 + MultiThreaded true diff --git a/test/test_component_no_pch/test_component_no_pch.vcxproj b/test/test_component_no_pch/test_component_no_pch.vcxproj index 932aaac27..e6ca64dc1 100644 --- a/test/test_component_no_pch/test_component_no_pch.vcxproj +++ b/test/test_component_no_pch/test_component_no_pch.vcxproj @@ -158,6 +158,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreadedDebug exports.def @@ -206,6 +207,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreadedDebug exports.def @@ -268,6 +270,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreadedDebug exports.def @@ -330,6 +333,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreadedDebug exports.def @@ -380,6 +384,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreaded true @@ -432,6 +437,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreaded true @@ -498,6 +504,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreaded true @@ -564,6 +571,7 @@ $(ProjectDir);$(OutputPath);Generated Files 4100 NotUsing + MultiThreaded true diff --git a/test/test_fast/test_fast.vcxproj b/test/test_fast/test_fast.vcxproj index 974468a85..567c8f8f2 100644 --- a/test/test_fast/test_fast.vcxproj +++ b/test/test_fast/test_fast.vcxproj @@ -129,6 +129,7 @@ true $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -149,6 +150,7 @@ Disabled $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -167,6 +169,7 @@ Disabled $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -185,6 +188,7 @@ Disabled $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -203,6 +207,7 @@ Disabled $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -223,6 +228,7 @@ true $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -245,6 +251,7 @@ true $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -267,6 +274,7 @@ true $(OutputPath);Generated Files;..\; WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console diff --git a/test/test_fast_fwd/test_fast_fwd.vcxproj b/test/test_fast_fwd/test_fast_fwd.vcxproj index 4afb13aee..14b559bcd 100644 --- a/test/test_fast_fwd/test_fast_fwd.vcxproj +++ b/test/test_fast_fwd/test_fast_fwd.vcxproj @@ -325,6 +325,28 @@ false + MultiThreaded + + + MultiThreaded + + + MultiThreaded + + + MultiThreaded + + + MultiThreadedDebug + + + MultiThreadedDebug + + + MultiThreadedDebug + + + MultiThreadedDebug diff --git a/test/test_module_lock_custom/test_module_lock_custom.vcxproj b/test/test_module_lock_custom/test_module_lock_custom.vcxproj index 2aea222b8..fab6a61f6 100644 --- a/test/test_module_lock_custom/test_module_lock_custom.vcxproj +++ b/test/test_module_lock_custom/test_module_lock_custom.vcxproj @@ -129,6 +129,7 @@ true $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -149,6 +150,7 @@ Disabled $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -167,6 +169,7 @@ Disabled $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -185,6 +188,7 @@ Disabled $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -203,6 +207,7 @@ Disabled $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -223,6 +228,7 @@ true $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -245,6 +251,7 @@ true $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -267,6 +274,7 @@ true $(OutputPath);Generated Files;..\; NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console diff --git a/test/test_module_lock_none/test_module_lock_none.vcxproj b/test/test_module_lock_none/test_module_lock_none.vcxproj index 450ce9f91..fb64f7d94 100644 --- a/test/test_module_lock_none/test_module_lock_none.vcxproj +++ b/test/test_module_lock_none/test_module_lock_none.vcxproj @@ -129,6 +129,7 @@ true $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -149,6 +150,7 @@ Disabled $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -167,6 +169,7 @@ Disabled $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -185,6 +188,7 @@ Disabled $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -203,6 +207,7 @@ Disabled $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -223,6 +228,7 @@ true $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -245,6 +251,7 @@ true $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -267,6 +274,7 @@ true $(OutputPath);Generated Files;..\ NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console diff --git a/test/test_slow/test_slow.vcxproj b/test/test_slow/test_slow.vcxproj index cda245c3c..492f05848 100644 --- a/test/test_slow/test_slow.vcxproj +++ b/test/test_slow/test_slow.vcxproj @@ -129,6 +129,7 @@ true $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -148,6 +149,7 @@ Disabled $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -165,6 +167,7 @@ Disabled $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -182,6 +185,7 @@ Disabled $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -199,6 +203,7 @@ Disabled $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug Console @@ -218,6 +223,7 @@ true $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -239,6 +245,7 @@ true $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console @@ -260,6 +267,7 @@ true $(OutputPath);Generated Files;..\ WINRT_DIAGNOSTICS;NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded Console diff --git a/test/test_win7/GetMany.cpp b/test/test_win7/GetMany.cpp new file mode 100644 index 000000000..23ea63e1e --- /dev/null +++ b/test/test_win7/GetMany.cpp @@ -0,0 +1,378 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation::Collections; + +// +// Now that all of the generics are generated (rather than hand-written), it's far less likely +// that something like GetMany is incorrect. And the "FillArray" pattern used by GetMany is +// tested elsewhere in the out_params and return_params tests. However since C++/WinRT provides +// an implementation of GetMany over and above the projection, these tests validate the this +// implementation is correct. Other tests exist for collections under 'old_tests' but new +// optimizations are coming for GetMany and I want to make sure that GetMany is completely +// covered. +// + +namespace +{ + template + IIterator single_threaded_generator(std::vector&& values = {}) + { + // This iterator may only be advanced once, ensuring the GetMany complexity optimization + // is actually enforced with this test. + + struct generator_container + { + explicit generator_container(IIterator const& first) : m_current(first) + { + if (!m_current.HasCurrent()) + { + m_current = nullptr; + } + } + + IIterator begin() const { return m_current; } + IIterator end() const { return nullptr; } + + private: + IIterator m_current; + }; + + struct generator : implements>, iterable_base + { + explicit generator(IIterator const& first) : m_container(first) + { + } + + auto& get_container() noexcept + { + return m_container; + } + + auto& get_container() const noexcept + { + return m_container; + } + + private: + generator_container m_container; + }; + + auto v = single_threaded_vector(std::move(values)); + return make(v.First()).First(); + } +} + +TEST_CASE("GetMany") +{ + // All + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.GetMany(0, buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + } + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.First().GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + } + + // None + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC }; + REQUIRE(0 == v.GetMany(3, buffer)); + REQUIRE(buffer[0] == 0xCC); + REQUIRE(buffer[1] == 0xCC); + REQUIRE(buffer[2] == 0xCC); + } + { + auto v = single_threaded_vector({ 1,2,3,4 }); + auto pos = v.First(); + std::array buffer{ 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == pos.GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + buffer = { 0xCC, 0xCC, 0xCC }; + REQUIRE(1 == pos.GetMany(buffer)); + REQUIRE(buffer[0] == 4); + REQUIRE(buffer[1] == 0xCC); + REQUIRE(buffer[2] == 0xCC); + buffer = { 0xCC, 0xCC, 0xCC }; + REQUIRE(0 == pos.GetMany(buffer)); + REQUIRE(buffer[0] == 0xCC); + REQUIRE(buffer[1] == 0xCC); + REQUIRE(buffer[2] == 0xCC); + } + + // Less + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC }; + REQUIRE(2 == v.GetMany(0, buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + } + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC }; + REQUIRE(2 == v.First().GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + } + + // More + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.GetMany(0, buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + REQUIRE(buffer[3] == 0xCC); + } + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.First().GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + REQUIRE(buffer[3] == 0xCC); + } + + // Offset + { + auto v = single_threaded_vector({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC, 0xCC }; + REQUIRE(2 == v.GetMany(1, buffer)); + REQUIRE(buffer[0] == 2); + REQUIRE(buffer[1] == 3); + REQUIRE(buffer[2] == 0xCC); + REQUIRE(buffer[3] == 0xCC); + } + + // The same tests but with a non-trivially destructible type... + + // All + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old" }; + REQUIRE(3 == v.GetMany(0, buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + } + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old" }; + REQUIRE(3 == v.First().GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + } + + // None + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old" }; + REQUIRE(0 == v.GetMany(3, buffer)); + REQUIRE(buffer[0] == L""); + REQUIRE(buffer[1] == L""); + REQUIRE(buffer[2] == L""); + } + { + auto v = single_threaded_vector({ L"1",L"2",L"3",L"4" }); + auto pos = v.First(); + std::array buffer{ L"old", L"old", L"old" }; + REQUIRE(3 == pos.GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + buffer = { L"old", L"old", L"old" }; + REQUIRE(1 == pos.GetMany(buffer)); + REQUIRE(buffer[0] == L"4"); + REQUIRE(buffer[1] == L""); + REQUIRE(buffer[2] == L""); + buffer = { L"old", L"old", L"old" }; + REQUIRE(0 == pos.GetMany(buffer)); + REQUIRE(buffer[0] == L""); + REQUIRE(buffer[1] == L""); + REQUIRE(buffer[2] == L""); + } + + // Less + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old" }; + REQUIRE(2 == v.GetMany(0, buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + } + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old" }; + REQUIRE(2 == v.First().GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + } + + // More + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old", L"old" }; + REQUIRE(3 == v.GetMany(0, buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + REQUIRE(buffer[3] == L""); + } + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old", L"old" }; + REQUIRE(3 == v.First().GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + REQUIRE(buffer[3] == L""); + } + + // Offset + { + auto v = single_threaded_vector({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old", L"old" }; + REQUIRE(2 == v.GetMany(1, buffer)); + REQUIRE(buffer[0] == L"2"); + REQUIRE(buffer[1] == L"3"); + REQUIRE(buffer[2] == L""); + REQUIRE(buffer[3] == L""); + } + + // Similar tests but with a list to ensure optimal code gen for containers that don't offer random access. + + // All + { + IIterator v = single_threaded_generator({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + } + + // None + { + IIterator v = single_threaded_generator({ 1,2,3,4,5 }); + std::array buffer{ 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + buffer = { 0xCC, 0xCC, 0xCC }; + REQUIRE(2 == v.GetMany(buffer)); + REQUIRE(buffer[0] == 4); + REQUIRE(buffer[1] == 5); + REQUIRE(buffer[2] == 0xCC); + buffer = { 0xCC, 0xCC, 0xCC }; + REQUIRE(0 == v.GetMany(buffer)); + REQUIRE(buffer[0] == 0xCC); + REQUIRE(buffer[1] == 0xCC); + REQUIRE(buffer[2] == 0xCC); + } + + // Less + { + IIterator v = single_threaded_generator({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC }; + REQUIRE(2 == v.GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + } + + // More + { + IIterator v = single_threaded_generator({ 1,2,3 }); + std::array buffer{ 0xCC, 0xCC, 0xCC, 0xCC }; + REQUIRE(3 == v.GetMany(buffer)); + REQUIRE(buffer[0] == 1); + REQUIRE(buffer[1] == 2); + REQUIRE(buffer[2] == 3); + REQUIRE(buffer[3] == 0xCC); + } + + // The same tests but with a non-trivially destructible type... + + // All + { + IIterator v = single_threaded_generator({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old" }; + REQUIRE(3 == v.GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + } + + // None + { + IIterator v = single_threaded_generator({ L"1",L"2",L"3",L"4" }); + std::array buffer{ L"old", L"old", L"old" }; + REQUIRE(3 == v.GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + buffer = { L"old", L"old", L"old" }; + REQUIRE(1 == v.GetMany(buffer)); + REQUIRE(buffer[0] == L"4"); + REQUIRE(buffer[1] == L""); + REQUIRE(buffer[2] == L""); + buffer = { L"old", L"old", L"old" }; + REQUIRE(0 == v.GetMany(buffer)); + REQUIRE(buffer[0] == L""); + REQUIRE(buffer[1] == L""); + REQUIRE(buffer[2] == L""); + } + + // Less + { + IIterator v = single_threaded_generator({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old" }; + REQUIRE(2 == v.GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + } + + // More + { + IIterator v = single_threaded_generator({ L"1",L"2",L"3" }); + std::array buffer{ L"old", L"old", L"old", L"old" }; + REQUIRE(3 == v.GetMany(buffer)); + REQUIRE(buffer[0] == L"1"); + REQUIRE(buffer[1] == L"2"); + REQUIRE(buffer[2] == L"3"); + REQUIRE(buffer[3] == L""); + } + + // Pair + { + auto m = single_threaded_map(); + m.Insert(1, L"1"); + m.Insert(2, L"2"); + m.Insert(3, L"3"); + m.Insert(4, L"4"); + std::array, 3> buffer; + REQUIRE(3 == m.First().GetMany(buffer)); + REQUIRE(buffer[0].Key() == 1); + REQUIRE(buffer[1].Key() == 2); + REQUIRE(buffer[2].Key() == 3); + REQUIRE(buffer[0].Value() == L"1"); + REQUIRE(buffer[1].Value() == L"2"); + REQUIRE(buffer[2].Value() == L"3"); + } +} diff --git a/test/test_win7/abi_guard.cpp b/test/test_win7/abi_guard.cpp new file mode 100644 index 000000000..c0244a475 --- /dev/null +++ b/test/test_win7/abi_guard.cpp @@ -0,0 +1,293 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // This implemenetation uses the simplest abi_enter and abi_exit methods + // + struct Simple : implements + { + void Close() + { + } + + hstring ToString() + { + return L""; + } + + void abi_enter() + { + ++m_enter; + } + + void abi_exit() + { + ++m_exit; + } + + int m_enter{}; + int m_exit{}; + }; + + // + // This implemenetation uses the abi_enter but omits the abi_exit method + // + struct OnlyEnter : implements + { + void Close() + { + } + + hstring ToString() + { + return L""; + } + + void abi_enter() + { + ++m_enter; + } + + int m_enter{}; + }; + + // + // This implemenetation throws from the abi_enter method + // + struct Throwing : implements + { + void Close() + { + } + + hstring ToString() + { + return L""; + } + + void abi_enter() + { + throw hresult_wrong_thread(); + } + + void abi_exit() + { + ++m_exit; + } + + int m_exit{}; + }; + + // + // This implemenetation provides a nested abi_guard + // + struct NestedGuard : implements + { + void Close() + { + } + + hstring ToString() + { + return L""; + } + + int m_enter{}; + int m_exit{}; + + struct abi_guard + { + abi_guard(NestedGuard& that) : + m_that(that) + { + ++m_that.m_enter; + } + + ~abi_guard() + { + ++m_that.m_exit; + } + + private: + + NestedGuard& m_that; + }; + }; + + template + struct CountGuard + { + CountGuard(T& that) : + m_that(that) + { + ++m_that.m_enter; + } + + ~CountGuard() + { + ++m_that.m_exit; + } + + private: + + T& m_that; + }; + + // + // This implemenetation use an abi_guard type alias + // + struct GuardAlias : implements + { + void Close() + { + } + + hstring ToString() + { + return L""; + } + + int m_enter{}; + int m_exit{}; + + using abi_guard = CountGuard; + }; + + template + struct ThrowGuard + { + ThrowGuard(T&) + { + throw hresult_wrong_thread(); + } + }; + + // + // This implemenetation use an abi_guard type alias that thows + // + struct ThrowAlias : implements + { + void Close() + { + } + + hstring ToString() + { + return L""; + } + + using abi_guard = ThrowGuard; + }; +} + +TEST_CASE("abi_guard") +{ + { + com_ptr impl = make_self(); + + impl->Close(); + impl->ToString(); + REQUIRE(impl->m_enter == 0); + REQUIRE(impl->m_exit == 0); + + IClosable closable = impl.as(); + closable.Close(); + REQUIRE(impl->m_enter == 1); + REQUIRE(impl->m_exit == 1); + + IStringable stringable = impl.as(); + stringable.ToString(); + REQUIRE(impl->m_enter == 2); + REQUIRE(impl->m_exit == 2); + } + { + com_ptr impl = make_self(); + + impl->Close(); + impl->ToString(); + + REQUIRE(impl->m_enter == 0); + + IClosable closable = impl.as(); + closable.Close(); + + REQUIRE(impl->m_enter == 1); + + IStringable stringable = impl.as(); + stringable.ToString(); + + REQUIRE(impl->m_enter == 2); + } + { + com_ptr impl = make_self(); + + impl->Close(); + impl->ToString(); + + IClosable closable = impl.as(); + REQUIRE_THROWS_AS(closable.Close(), hresult_wrong_thread); + + IStringable stringable = impl.as(); + REQUIRE_THROWS_AS(stringable.ToString(), hresult_wrong_thread); + + REQUIRE(impl->m_exit == 0); + } + { + com_ptr impl = make_self(); + + impl->Close(); + impl->ToString(); + + REQUIRE(impl->m_enter == 0); + REQUIRE(impl->m_exit == 0); + + IClosable closable = impl.as(); + closable.Close(); + + REQUIRE(impl->m_enter == 1); + REQUIRE(impl->m_exit == 1); + + IStringable stringable = impl.as(); + stringable.ToString(); + + REQUIRE(impl->m_enter == 2); + REQUIRE(impl->m_exit == 2); + } + { + com_ptr impl = make_self(); + + impl->Close(); + impl->ToString(); + + REQUIRE(impl->m_enter == 0); + REQUIRE(impl->m_exit == 0); + + IClosable closable = impl.as(); + closable.Close(); + + REQUIRE(impl->m_enter == 1); + REQUIRE(impl->m_exit == 1); + + IStringable stringable = impl.as(); + stringable.ToString(); + + REQUIRE(impl->m_enter == 2); + REQUIRE(impl->m_exit == 2); + } + { + com_ptr impl = make_self(); + + impl->Close(); + impl->ToString(); + + IClosable closable = impl.as(); + REQUIRE_THROWS_AS(closable.Close(), hresult_wrong_thread); + + IStringable stringable = impl.as(); + REQUIRE_THROWS_AS(stringable.ToString(), hresult_wrong_thread); + } +} diff --git a/test/test_win7/agile_ref.cpp b/test/test_win7/agile_ref.cpp new file mode 100644 index 000000000..e0ab43a34 --- /dev/null +++ b/test/test_win7/agile_ref.cpp @@ -0,0 +1,54 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + struct Object : implements + { + hstring ToString() + { + return L"Object"; + } + }; +} + +TEST_CASE("agile_ref") +{ + agile_ref ref = make(); + + // + // Here we're creating an agile_ref explicitly and using a traditional lambda variable capture + // to pass it to the delegate. + // + + delegate<> a = [ref] + { + IStringable object = ref.get(); + REQUIRE(object.ToString() == L"Object"); + }; + + a(); + + // + // Here's we're using the make_agile helper with generalized lambda capture to produce a + // variable local to the lambda. + // + + delegate<> b = [ref = make_agile(make())] + { + IStringable object = ref.get(); + REQUIRE(object.ToString() == L"Object"); + }; + + b(); + + // + // And it's ok to resolve a nullptr agile_ref. + // + + agile_ref empty; + IStringable object = empty.get(); + REQUIRE(object == nullptr); +} diff --git a/test/test_win7/agility.cpp b/test/test_win7/agility.cpp new file mode 100644 index 000000000..78fed2fda --- /dev/null +++ b/test/test_win7/agility.cpp @@ -0,0 +1,140 @@ +#include "pch.h" +#include "winrt/test_component.Delegates.h" + +// +// These tests confirm the COM identity and other behaviours for agile implementations. +// + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + struct TestAgile : implements + { + bool& m_destroyed; + TestAgile(bool& destroyed) : m_destroyed(destroyed) { m_destroyed = false; } + ~TestAgile() { m_destroyed = true; } + + void Close() {} + }; + + struct TestNonAgile : implements + { + void Close() {} + }; +} + +TEST_CASE("agility") +{ + using Windows::Foundation::IUnknown; + using Windows::Foundation::IInspectable; + + // Test agility + { + bool destroyed = false; + + { + IInspectable object = make(destroyed); + + // Confirm agility + object.as(); + + // Confirm legacy agility + com_ptr marshal = object.as(); + + // Confirm tear-off identity + IUnknown object_identity = object.as(); + IUnknown marshal_identity = marshal.as(); + + REQUIRE(object_identity == marshal_identity); + REQUIRE(!destroyed); + } + + // Confirm tear-off does not leak reference + REQUIRE(destroyed); + } + + // Test non-agility + { + IInspectable object = make(); + + // Does not implement IAgileObject + REQUIRE_THROWS_AS(object.as(), hresult_no_interface); + + // Does not implement IMarshal + REQUIRE_THROWS_AS(object.as(), hresult_no_interface); + } + + // Test IMarshal tearoff lifetime + { + bool destroyed = false; + + IInspectable object = make(destroyed); + com_ptr marshal = object.as(); + object = nullptr; + REQUIRE(!destroyed); + + // Confirm agility (back to object) + marshal.as(); + + // QI on tearoff itself + com_ptr marshal2 = marshal.as(); + REQUIRE(marshal == marshal2); + marshal2 = nullptr; + + // Confirm tear-off does not leak reference + REQUIRE(!destroyed); + marshal = nullptr; + REQUIRE(destroyed); + } + + // Test agile delegate + { + IUnknown object = test_component::Delegates::AgileDelegate([] {}); + com_ptr marshal = object.as(); + object = nullptr; + + // Confirm agility (back to object) + marshal.as(); + + // QI on tearoff itself + com_ptr marshal2 = marshal.as(); + REQUIRE(marshal == marshal2); + } + + // Test agile weak reference + { + bool destroyed = false; + IClosable object = make(destroyed); + com_ptr source = object.as(); + + // Clear object but source keeps object alive + object = nullptr; + source.as(); + + com_ptr ref; + check_hresult(source->GetWeakReference(ref.put())); + + // Drop the source object + REQUIRE(S_OK == ref->Resolve(guid_of(), put_abi(object))); + REQUIRE(object != nullptr); + source = nullptr; + REQUIRE(!destroyed); + object = nullptr; + REQUIRE(destroyed); + REQUIRE(S_OK == ref->Resolve(guid_of(), put_abi(object))); + REQUIRE(object == nullptr); + + // Marshaling support on weak ref + com_ptr marshal = ref.as(); + ref = nullptr; + + // Confirm agility (back to object): + marshal.as(); + + // QI on tearoff itself + com_ptr marshal2 = marshal.as(); + REQUIRE(marshal == marshal2); + } +} diff --git a/test/test_win7/async_auto_cancel.cpp b/test/test_win7/async_auto_cancel.cpp new file mode 100644 index 000000000..45ff30fcf --- /dev/null +++ b/test/test_win7/async_auto_cancel.cpp @@ -0,0 +1,73 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that the coroutine is automatically canceled when reaching a suspension point. + // + + IAsyncAction Action(HANDLE event) + { + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + + IAsyncActionWithProgress ActionWithProgress(HANDLE event) + { + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + + IAsyncOperation Operation(HANDLE event) + { + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress(HANDLE event) + { + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + co_return 1; + } + + template + void Check(F make) + { + handle start{ CreateEvent(nullptr, true, false, nullptr) }; + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + auto async = make(start.get()); + REQUIRE(async.Status() == AsyncStatus::Started); + + async.Completed([&](auto&& sender, AsyncStatus status) + { + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Canceled); + SetEvent(completed.get()); + }); + + async.Cancel(); + SetEvent(start.get()); + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + + REQUIRE(async.Status() == AsyncStatus::Canceled); + REQUIRE(async.ErrorCode() == HRESULT_FROM_WIN32(ERROR_CANCELLED)); + REQUIRE_THROWS_AS(async.GetResults(), hresult_canceled); + } +} + +TEST_CASE("async_auto_cancel") +{ + Check(Action); + Check(ActionWithProgress); + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_cancel_callback.cpp b/test/test_win7/async_cancel_callback.cpp new file mode 100644 index 000000000..0ab5b6bb1 --- /dev/null +++ b/test/test_win7/async_cancel_callback.cpp @@ -0,0 +1,93 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that the cancellation callback is invoked. + // + + IAsyncAction Action(HANDLE event, bool& canceled) + { + auto cancel = co_await get_cancellation_token(); + + cancel.callback([&] + { + REQUIRE(!canceled); + canceled = true; + }); + + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + + IAsyncActionWithProgress ActionWithProgress(HANDLE event, bool& canceled) + { + auto cancel = co_await get_cancellation_token(); + + cancel.callback([&] + { + REQUIRE(!canceled); + canceled = true; + }); + + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + + IAsyncOperation Operation(HANDLE event, bool& canceled) + { + auto cancel = co_await get_cancellation_token(); + + cancel.callback([&] + { + REQUIRE(!canceled); + canceled = true; + }); + + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress(HANDLE event, bool& canceled) + { + auto cancel = co_await get_cancellation_token(); + + cancel.callback([&] + { + REQUIRE(!canceled); + canceled = true; + }); + + co_await resume_on_signal(event); + co_await std::experimental::suspend_never(); + REQUIRE(false); + co_return 1; + } + + template + void Check(F make) + { + handle event{ CreateEvent(nullptr, true, false, nullptr) }; + bool canceled = false; + auto async = make(event.get(), canceled); + async.Cancel(); + REQUIRE(canceled); + SetEvent(event.get()); + REQUIRE_THROWS_AS(async.GetResults(), hresult_canceled); + } +} + +TEST_CASE("async_cancel_callback") +{ + Check(Action); + Check(ActionWithProgress); + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_check_cancel.cpp b/test/test_win7/async_check_cancel.cpp new file mode 100644 index 000000000..38d21f48b --- /dev/null +++ b/test/test_win7/async_check_cancel.cpp @@ -0,0 +1,107 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that manual cancellation checks work. + // + + IAsyncAction Action(HANDLE event, bool& canceled) + { + co_await resume_on_signal(event); + auto cancel = co_await get_cancellation_token(); + + if (cancel()) + { + REQUIRE(!canceled); + canceled = true; + } + + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + + IAsyncActionWithProgress ActionWithProgress(HANDLE event, bool& canceled) + { + co_await resume_on_signal(event); + auto cancel = co_await get_cancellation_token(); + + if (cancel()) + { + REQUIRE(!canceled); + canceled = true; + } + + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + + IAsyncOperation Operation(HANDLE event, bool& canceled) + { + co_await resume_on_signal(event); + auto cancel = co_await get_cancellation_token(); + + if (cancel()) + { + REQUIRE(!canceled); + canceled = true; + } + + co_await std::experimental::suspend_never(); + REQUIRE(false); + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress(HANDLE event, bool& canceled) + { + co_await resume_on_signal(event); + auto cancel = co_await get_cancellation_token(); + + if (cancel()) + { + REQUIRE(!canceled); + canceled = true; + } + + co_await std::experimental::suspend_never(); + REQUIRE(false); + co_return 1; + } + + template + void Check(F make) + { + handle start{ CreateEvent(nullptr, true, false, nullptr) }; + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + bool canceled = false; + auto async = make(start.get(), canceled); + REQUIRE(async.Status() == AsyncStatus::Started); + + async.Completed([&](auto&& sender, AsyncStatus status) + { + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Canceled); + REQUIRE(canceled); + SetEvent(completed.get()); + }); + + async.Cancel(); + SetEvent(start.get()); + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + + REQUIRE(async.Status() == AsyncStatus::Canceled); + REQUIRE(async.ErrorCode() == HRESULT_FROM_WIN32(ERROR_CANCELLED)); + REQUIRE_THROWS_AS(async.GetResults(), hresult_canceled); + } +} + +TEST_CASE("async_check_cancel") +{ + Check(Action); + Check(ActionWithProgress); + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_local.cpp b/test/test_win7/async_local.cpp new file mode 100644 index 000000000..fa96dabb5 --- /dev/null +++ b/test/test_win7/async_local.cpp @@ -0,0 +1,74 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that coroutine locals are destroyed prior to notifying waiters. + // + + struct Local + { + bool& destroyed; + + ~Local() + { + REQUIRE(!destroyed); + destroyed = true; + } + }; + + IAsyncAction Action(HANDLE event, bool& destroyed) + { + co_await resume_on_signal(event); + Local local{ destroyed }; + } + + IAsyncActionWithProgress ActionWithProgress(HANDLE event, bool& destroyed) + { + co_await resume_on_signal(event); + Local local{ destroyed }; + } + + IAsyncOperation Operation(HANDLE event, bool& destroyed) + { + co_await resume_on_signal(event); + Local local{ destroyed }; + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress(HANDLE event, bool& destroyed) + { + co_await resume_on_signal(event); + Local local{ destroyed }; + co_return 1; + } + + template + void Check(F make) + { + handle start{ CreateEvent(nullptr, true, false, nullptr) }; + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + bool destroyed = false; + auto async = make(start.get(), destroyed); + + async.Completed([&](auto&&...) + { + REQUIRE(destroyed); + SetEvent(completed.get()); + }); + + SetEvent(start.get()); + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + } +} + +TEST_CASE("async_local") +{ + Check(Action); + Check(ActionWithProgress); + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_no_suspend.cpp b/test/test_win7/async_no_suspend.cpp new file mode 100644 index 000000000..6888d0b98 --- /dev/null +++ b/test/test_win7/async_no_suspend.cpp @@ -0,0 +1,85 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that coroutines lacking suspension points work. + // + + IAsyncAction Action() + { + co_return; + } + + IAsyncActionWithProgress ActionWithProgress() + { + co_return; + } + + IAsyncOperation Operation() + { + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress() + { + co_return 1; + } + + IAsyncAction Await() + { + co_await Action(); + co_await ActionWithProgress(); + co_await Operation(); + co_await OperationWithProgress(); + } + + template + void Check(T const& async) + { + REQUIRE(async.Status() == AsyncStatus::Completed); + REQUIRE(async.ErrorCode() == 0); + REQUIRE(async.Id() == 1); + + // Should not throw in the Completed state. + async.GetResults(); + + bool completed = false; + + async.Completed([&](auto&& sender, AsyncStatus status) + { + completed = true; + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Completed); + }); + + REQUIRE(completed); + + // May only assign Completed handler once. + REQUIRE_THROWS_AS(async.Completed([&](auto && ...) {}), hresult_illegal_delegate_assignment); + + // Close does nothing. + async.Close(); + + // Harmless but too late to cancel. + async.Cancel(); + REQUIRE(async.Status() == AsyncStatus::Completed); + } +} + +TEST_CASE("async_no_suspend") +{ + Action().get(); + ActionWithProgress().get(); + Operation().get(); + OperationWithProgress().get(); + Await().get(); + + Check(Action()); + Check(ActionWithProgress()); + Check(Operation()); + Check(OperationWithProgress()); +} diff --git a/test/test_win7/async_progress.cpp b/test/test_win7/async_progress.cpp new file mode 100644 index 000000000..0a2790f50 --- /dev/null +++ b/test/test_win7/async_progress.cpp @@ -0,0 +1,82 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that progress reporting works. + // + + IAsyncActionWithProgress Action(HANDLE event) + { + co_await resume_on_signal(event); + auto progress = co_await get_progress_token(); + progress(123); + } + + IAsyncOperationWithProgress Operation(HANDLE event) + { + co_await resume_on_signal(event); + auto progress = co_await get_progress_token(); + progress(123); + co_return 1; + } + + template + IAsyncAction Check(F make) + { + // Event not set to allow Progress handler to be wired up. + handle start{ CreateEvent(nullptr, true, false, nullptr) }; + + auto async = make(start.get()); + bool progress = false; + + async.Progress([&](auto&& sender, int value) + { + progress = true; + REQUIRE(async == sender); + REQUIRE(value == 123); + }); + + SetEvent(start.get()); + co_await async; + + REQUIRE(progress); + REQUIRE(async.Status() == AsyncStatus::Completed); + REQUIRE(async.ErrorCode() == S_OK); + } + + template + IAsyncAction TooLate(F make) + { + // Event initially set so that coroutine does not suspend. + handle start{ CreateEvent(nullptr, true, true, nullptr) }; + + auto async = make(start.get()); + REQUIRE(async.Status() == AsyncStatus::Completed); + + bool progress = false; + + async.Progress([&](auto&&...) + { + REQUIRE(false); + }); + + co_await async; + + REQUIRE(!progress); + REQUIRE(async.Status() == AsyncStatus::Completed); + REQUIRE(async.ErrorCode() == S_OK); + } +} + +TEST_CASE("async_progress") +{ + Check(Action); + Check(Operation); + + TooLate(Action); + TooLate(Operation); +} diff --git a/test/test_win7/async_result.cpp b/test/test_win7/async_result.cpp new file mode 100644 index 000000000..f02c10ce4 --- /dev/null +++ b/test/test_win7/async_result.cpp @@ -0,0 +1,78 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that result values are propagated properly. + // + + IAsyncOperation Operation(HANDLE event) + { + co_await resume_on_signal(event); + co_return 123; + } + + IAsyncOperationWithProgress OperationWithProgress(HANDLE event) + { + co_await resume_on_signal(event); + co_return 123; + } + + IAsyncAction Await() + { + // Manual reset so that all waiters will resume and initially set so they won't block. + handle event{ CreateEvent(nullptr, true, true, nullptr) }; + + int a = co_await Operation(event.get()); + int b = co_await OperationWithProgress(event.get()); + + REQUIRE(a == 123); + REQUIRE(b == 123); + } + + template + void Check(F make) + { + handle start{ CreateEvent(nullptr, true, false, nullptr) }; + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + auto async = make(start.get()); + REQUIRE(async.Status() == AsyncStatus::Started); + REQUIRE_THROWS_AS(async.GetResults(), hresult_illegal_method_call); + + async.Completed([&](auto&& sender, AsyncStatus status) + { + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Completed); + SetEvent(completed.get()); + }); + + // Still in Started state waiting for signal. + Sleep(100); + REQUIRE(WaitForSingleObject(completed.get(), 0) == WAIT_TIMEOUT); + REQUIRE(async.Status() == AsyncStatus::Started); + + // Signal async to run. + SetEvent(start.get()); + + // Wait for async to complete. + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + + REQUIRE(async.Status() == AsyncStatus::Completed); + REQUIRE(async.ErrorCode() == S_OK); + REQUIRE(async.GetResults() == 123); + } +} + +TEST_CASE("async_result") +{ + handle start{ CreateEvent(nullptr, true, true, nullptr) }; + REQUIRE(123 == Operation(start.get()).get()); + REQUIRE(123 == OperationWithProgress(start.get()).get()); + Await().get(); + + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_return.cpp b/test/test_win7/async_return.cpp new file mode 100644 index 000000000..23b1c1eec --- /dev/null +++ b/test/test_win7/async_return.cpp @@ -0,0 +1,52 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that return values support both rvalue and lvalue. + // + + IAsyncOperation Operation(bool rvalue) + { + if (rvalue) + { + hstring value = L"rvalue"; + co_return std::move(value); + } + else + { + hstring value = L"lvalue"; + co_return value; + } + } + + IAsyncOperationWithProgress OperationWithProgress(bool rvalue) + { + if (rvalue) + { + hstring value = L"rvalue"; + co_return std::move(value); + } + else + { + hstring value = L"lvalue"; + co_return value; + } + } + + template + void Check(F make) + { + REQUIRE(make(true).get() == L"rvalue"); + REQUIRE(make(false).get() == L"lvalue"); + } +} + +TEST_CASE("async_return") +{ + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_suspend.cpp b/test/test_win7/async_suspend.cpp new file mode 100644 index 000000000..3c0fd791e --- /dev/null +++ b/test/test_win7/async_suspend.cpp @@ -0,0 +1,90 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks basic suspension behavior. + // + + IAsyncAction Action(HANDLE event) + { + co_await resume_on_signal(event); + } + + IAsyncActionWithProgress ActionWithProgress(HANDLE event) + { + co_await resume_on_signal(event); + } + + IAsyncOperation Operation(HANDLE event) + { + co_await resume_on_signal(event); + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress(HANDLE event) + { + co_await resume_on_signal(event); + co_return 1; + } + + IAsyncAction Await() + { + // Manual reset so that all waiters will resume and initially set so they won't block. + handle event{ CreateEvent(nullptr, true, true, nullptr) }; + + co_await Action(event.get()); + co_await ActionWithProgress(event.get()); + co_await Operation(event.get()); + co_await OperationWithProgress(event.get()); + } + + template + void Check(F make) + { + handle start{ CreateEvent(nullptr, true, false, nullptr) }; + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + auto async = make(start.get()); + REQUIRE(async.Status() == AsyncStatus::Started); + REQUIRE_THROWS_AS(async.GetResults(), hresult_illegal_method_call); + + async.Completed([&](auto&& sender, AsyncStatus status) + { + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Completed); + SetEvent(completed.get()); + }); + + // Still in Started state waiting for signal. + Sleep(100); + REQUIRE(WaitForSingleObject(completed.get(), 0) == WAIT_TIMEOUT); + REQUIRE(async.Status() == AsyncStatus::Started); + + // Signal async to run. + SetEvent(start.get()); + + // Wait for async to complete. + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + + REQUIRE(async.Status() == AsyncStatus::Completed); + REQUIRE(async.ErrorCode() == S_OK); + } +} + +TEST_CASE("async_suspend") +{ + handle start{ CreateEvent(nullptr, true, true, nullptr) }; + Action(start.get()).get(); + ActionWithProgress(start.get()).get(); + Operation(start.get()).get(); + OperationWithProgress(start.get()).get(); + Await().get(); + + Check(Action); + Check(ActionWithProgress); + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_throw.cpp b/test/test_win7/async_throw.cpp new file mode 100644 index 000000000..2d50acfd9 --- /dev/null +++ b/test/test_win7/async_throw.cpp @@ -0,0 +1,86 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace std::chrono_literals; + +namespace +{ + // + // Checks that exceptions are correctly captured and propagated. + // + + IAsyncAction Action() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + } + + IAsyncActionWithProgress ActionWithProgress() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + } + + IAsyncOperation Operation() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress() + { + co_await 10ms; + throw hresult_invalid_argument(L"Async"); + co_return 1; + } + + template + void Check(F make) + { + try + { + make().get(); + REQUIRE(false); + } + catch (hresult_invalid_argument const& e) + { + REQUIRE(e.message() == L"Async"); + } + + handle completed{ CreateEvent(nullptr, true, false, nullptr) }; + auto async = make(); + + async.Completed([&](auto&& sender, AsyncStatus status) + { + REQUIRE(async == sender); + REQUIRE(status == AsyncStatus::Error); + SetEvent(completed.get()); + }); + + REQUIRE(WaitForSingleObject(completed.get(), 1000) == WAIT_OBJECT_0); + REQUIRE(async.Status() == AsyncStatus::Error); + + hresult_error e(async.ErrorCode(), take_ownership_from_abi); + REQUIRE(e.message() == L"Async"); + + try + { + async.GetResults(); + REQUIRE(false); + } + catch (hresult_invalid_argument const& e) + { + REQUIRE(e.message() == L"Async"); + } + } +} + +TEST_CASE("async_throw") +{ + Check(Action); + Check(ActionWithProgress); + Check(Operation); + Check(OperationWithProgress); +} diff --git a/test/test_win7/async_wait_for.cpp b/test/test_win7/async_wait_for.cpp new file mode 100644 index 000000000..2173a5e86 --- /dev/null +++ b/test/test_win7/async_wait_for.cpp @@ -0,0 +1,136 @@ +#include "pch.h" + +using namespace std::literals; +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + IAsyncAction Action(TimeSpan delay, AsyncStatus result) + { + co_await resume_after(delay); + + if (result == AsyncStatus::Error) + { + throw hresult_invalid_argument(); + } + + if (result == AsyncStatus::Canceled) + { + throw hresult_canceled(); + } + } + + IAsyncActionWithProgress ActionWithProgress(TimeSpan delay, AsyncStatus result) + { + co_await resume_after(delay); + + if (result == AsyncStatus::Error) + { + throw hresult_invalid_argument(); + } + + if (result == AsyncStatus::Canceled) + { + throw hresult_canceled(); + } + } + + IAsyncOperation Operation(TimeSpan delay, AsyncStatus result) + { + co_await resume_after(delay); + + if (result == AsyncStatus::Error) + { + throw hresult_invalid_argument(); + } + + if (result == AsyncStatus::Canceled) + { + throw hresult_canceled(); + } + + co_return 1; + } + + IAsyncOperationWithProgress OperationWithProgress(TimeSpan delay, AsyncStatus result) + { + co_await resume_after(delay); + + if (result == AsyncStatus::Error) + { + throw hresult_invalid_argument(); + } + + if (result == AsyncStatus::Canceled) + { + throw hresult_canceled(); + } + + co_return 1; + } + + template + void check(T const& no_suspend_ok, T const& no_suspend_fail, T const& delay_ok, T const& delay_fail, T const& no_suspend_cancel, T const& delay_cancel, T const& long_delay) + { + REQUIRE(no_suspend_ok.wait_for(0s) == AsyncStatus::Completed); + no_suspend_ok.get(); + REQUIRE_THROWS_AS(no_suspend_ok.wait_for(0s), hresult_illegal_delegate_assignment); + + REQUIRE(no_suspend_fail.wait_for(0s) == AsyncStatus::Error); + REQUIRE_THROWS_AS(no_suspend_fail.get(), hresult_invalid_argument); + + REQUIRE(delay_ok.wait_for(1s) == AsyncStatus::Completed); + delay_ok.get(); + + REQUIRE(delay_fail.wait_for(1s) == AsyncStatus::Error); + REQUIRE_THROWS_AS(delay_fail.get(), hresult_invalid_argument); + + REQUIRE(no_suspend_cancel.wait_for(0s) == AsyncStatus::Canceled); + REQUIRE_THROWS_AS(no_suspend_cancel.get(), hresult_canceled); + + REQUIRE(delay_cancel.wait_for(1s) == AsyncStatus::Canceled); + REQUIRE_THROWS_AS(delay_cancel.get(), hresult_canceled); + + REQUIRE(long_delay.wait_for(100ms) == AsyncStatus::Started); + } +} + +TEST_CASE("async_wait_for") +{ + check( + Action(0s, AsyncStatus::Completed), + Action(0s, AsyncStatus::Error), + Action(100ms, AsyncStatus::Completed), + Action(100ms, AsyncStatus::Error), + Action(0s, AsyncStatus::Canceled), + Action(100ms, AsyncStatus::Canceled), + Action(1h, AsyncStatus::Completed)); + + check( + ActionWithProgress(0s, AsyncStatus::Completed), + ActionWithProgress(0s, AsyncStatus::Error), + ActionWithProgress(100ms, AsyncStatus::Completed), + ActionWithProgress(100ms, AsyncStatus::Error), + ActionWithProgress(0s, AsyncStatus::Canceled), + ActionWithProgress(100ms, AsyncStatus::Canceled), + ActionWithProgress(1h, AsyncStatus::Completed)); + + check( + Operation(0s, AsyncStatus::Completed), + Operation(0s, AsyncStatus::Error), + Operation(100ms, AsyncStatus::Completed), + Operation(100ms, AsyncStatus::Error), + Operation(0s, AsyncStatus::Canceled), + Operation(100ms, AsyncStatus::Canceled), + Operation(1h, AsyncStatus::Completed)); + + check( + OperationWithProgress(0s, AsyncStatus::Completed), + OperationWithProgress(0s, AsyncStatus::Error), + OperationWithProgress(100ms, AsyncStatus::Completed), + OperationWithProgress(100ms, AsyncStatus::Error), + OperationWithProgress(0s, AsyncStatus::Canceled), + OperationWithProgress(100ms, AsyncStatus::Canceled), + OperationWithProgress(1h, AsyncStatus::Completed)); +} diff --git a/test/test_win7/capture.cpp b/test/test_win7/capture.cpp new file mode 100644 index 000000000..8c75681b4 --- /dev/null +++ b/test/test_win7/capture.cpp @@ -0,0 +1,71 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +struct __declspec(uuid("5fb96f8d-409c-42a9-99a7-8a95c1459dbd")) ICapture : ::IUnknown +{ + virtual int32_t __stdcall GetValue() noexcept = 0; + virtual int32_t __stdcall CreateMemberCapture(int32_t value, GUID const& iid, void** object) noexcept = 0; +}; + +struct Capture : implements +{ + int32_t const m_value{}; + + Capture(int32_t value) : + m_value{ value } + { + } + + hstring ToString() + { + return hstring{ std::to_wstring(m_value) }; + } + + int32_t __stdcall GetValue() noexcept override + { + return m_value; + } + + int32_t __stdcall CreateMemberCapture(int32_t value, GUID const& iid, void** object) noexcept override + { + auto capture = make(value); + return capture->QueryInterface(iid, object); + } +}; + +HRESULT __stdcall CreateNonMemberCapture(int value, GUID const& iid, void** object) noexcept +{ + auto capture = make(value); + return capture->QueryInterface(iid, object); +} + +TEST_CASE("capture") +{ + com_ptr a = capture(CreateNonMemberCapture, 10); + REQUIRE(a->GetValue() == 10); + a = nullptr; + a.capture(CreateNonMemberCapture, 20); + REQUIRE(a->GetValue() == 20); + + com_ptr b = capture(a, &ICapture::CreateMemberCapture, 30); + REQUIRE(b->GetValue() == 30); + b = nullptr; + b.capture(a, &ICapture::CreateMemberCapture, 40); + REQUIRE(b->GetValue() == 40); + + IStringable c = capture(CreateNonMemberCapture, 50); + REQUIRE(c.ToString() == L"50"); + c = capture(a, &ICapture::CreateMemberCapture, 60); + REQUIRE(c.ToString() == L"60"); + + com_ptr d; + + REQUIRE_THROWS_AS(capture(CreateNonMemberCapture, 0), hresult_no_interface); + REQUIRE_THROWS_AS(capture(CreateNonMemberCapture, 0), hresult_no_interface); + REQUIRE_THROWS_AS(d.capture(CreateNonMemberCapture, 0), hresult_no_interface); + REQUIRE_THROWS_AS(capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); + REQUIRE_THROWS_AS(capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); + REQUIRE_THROWS_AS(d.capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); +} diff --git a/test/test_win7/cmd_reader.cpp b/test/test_win7/cmd_reader.cpp new file mode 100644 index 000000000..731922fd8 --- /dev/null +++ b/test/test_win7/cmd_reader.cpp @@ -0,0 +1,154 @@ +#include "pch.h" +#include "cmd_reader.h" +#include + +using namespace cppwinrt; + +class response_file +{ + const char* resp_file_name = "respfile.txt"; + void write_response_file(const char* input) + { + std::ofstream resp_file(resp_file_name); + if (!resp_file.is_open()) + FAIL("Response file could not be created"); + resp_file << input; + resp_file.close(); + } + + void remove_response_file() + { + std::remove(resp_file_name); + } + +public: + response_file(const char* input) + { + write_response_file(input); + } + + template + reader create_reader(size_t const argc, const char* argv[], const option(&options)[numOptions]) + { + return reader{ argc, argv, options }; + } + + ~response_file() + { + remove_response_file(); + } +}; + +TEST_CASE("cmd_reader") +{ + static constexpr option options[] + { + { "input", 1 }, + { "reference", 0 }, + { "output", 0, 1 }, + { "component", 0, 1 }, + { "filter", 0 }, + { "name", 0, 1 }, + { "verbose", 0, 0 }, + }; + + // input and output + { + const char* argv[] = { "progname", "-in", "example_file.in", "-out", "example_file.out" }; + const size_t argc = 5; + reader args{ argc, argv, options }; + + REQUIRE(args.exists("input")); + REQUIRE(args.value("input") == "example_file.in"); + REQUIRE_FALSE(args.exists("reference")); + REQUIRE(args.exists("output")); + REQUIRE(args.value("output") == "example_file.out"); + REQUIRE_FALSE(args.exists("filter")); + REQUIRE_FALSE(args.exists("name")); + REQUIRE_FALSE(args.exists("verbose")); + } + + // response file #1: filename no quotes + { + const char* argv[] = { "progname", "@respfile.txt" }; + const size_t argc = _countof(argv); + + response_file rf{ R"(-in example_file.in -out example_file.out)" }; + reader args = rf.create_reader(argc, argv, options); + + REQUIRE(args.exists("input")); + REQUIRE(args.value("input") == "example_file.in"); + REQUIRE_FALSE(args.exists("reference")); + REQUIRE(args.exists("output")); + REQUIRE(args.value("output") == "example_file.out"); + REQUIRE_FALSE(args.exists("filter")); + REQUIRE_FALSE(args.exists("name")); + REQUIRE_FALSE(args.exists("verbose")); + } + + // response file #2: filename with quotes + { + const char* argv[] = { "progname", "@respfile.txt" }; + const size_t argc = _countof(argv); + + response_file rf{ R"(-in "example file.in" -out "example file.out")" }; + reader args = rf.create_reader(argc, argv, options); + + REQUIRE(args.exists("input")); + REQUIRE(args.value("input") == "example file.in"); + REQUIRE_FALSE(args.exists("reference")); + REQUIRE(args.exists("output")); + REQUIRE(args.value("output") == "example file.out"); + REQUIRE_FALSE(args.exists("filter")); + REQUIRE_FALSE(args.exists("name")); + REQUIRE_FALSE(args.exists("verbose")); + } + + // response file #3: filename with quote within name + { + const char* argv[] = { "progname", "@respfile.txt" }; + const size_t argc = _countof(argv); + + response_file rf{ R"(-in example\"file.in -out example\"file.out)" }; + reader args = rf.create_reader(argc, argv, options); + + REQUIRE(args.exists("input")); + REQUIRE(args.value("input") == R"(example"file.in)"); + REQUIRE_FALSE(args.exists("reference")); + REQUIRE(args.exists("output")); + REQUIRE(args.value("output") == R"(example"file.out)"); + REQUIRE_FALSE(args.exists("filter")); + REQUIRE_FALSE(args.exists("name")); + REQUIRE_FALSE(args.exists("verbose")); + } + + // response file #4: really really long path + { + const char* argv[] = { "progname", "@respfile.txt" }; + const size_t argc = _countof(argv); + std::string file_name_in(R"(C:\)"); + std::string file_name_out(R"(C:\)"); + std::string input_str("-in "); + + for (int i = 0; i < 500; i++) { + file_name_in.append(R"(dirname\)"); + file_name_out.append(R"(dirname\)"); + } + + file_name_in.append("example_file.in"); + file_name_out.append("example_file.out"); + input_str.append(file_name_in).append(" -out ").append(file_name_out); + + response_file rf{ input_str.data() }; + reader args = rf.create_reader(argc, argv, options); + + REQUIRE(args.exists("input")); + REQUIRE(args.value("input") == file_name_in); + REQUIRE_FALSE(args.exists("reference")); + REQUIRE(args.exists("output")); + REQUIRE(args.value("output") == file_name_out); + REQUIRE_FALSE(args.exists("filter")); + REQUIRE_FALSE(args.exists("name")); + REQUIRE_FALSE(args.exists("verbose")); + } +} diff --git a/test/test_win7/coro_foundation.cpp b/test/test_win7/coro_foundation.cpp new file mode 100644 index 000000000..c2705982a --- /dev/null +++ b/test/test_win7/coro_foundation.cpp @@ -0,0 +1,21 @@ +// Intentionally not using pch... +#include "catch.hpp" + +// Only need winrt/Windows.Foundation.h for IAsyncXxx coroutine support +#include "winrt/Windows.Foundation.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + IAsyncOperation Async() + { + co_return L"hello"; + } +} + +TEST_CASE("coro_foundation") +{ + REQUIRE(Async().get() == L"hello"); +} diff --git a/test/test_win7/coro_threadpool.cpp b/test/test_win7/coro_threadpool.cpp new file mode 100644 index 000000000..4c31f8a03 --- /dev/null +++ b/test/test_win7/coro_threadpool.cpp @@ -0,0 +1,20 @@ +// Intentionally not using pch... +#include "catch.hpp" + +// Only need winrt/base.h for coroutine thread pool support. +#include "winrt/base.h" + +using namespace winrt; + +namespace +{ + fire_and_forget Async() + { + co_await resume_background(); + } +} + +TEST_CASE("coro_base") +{ + Async(); +} diff --git a/test/test_win7/custom_error.cpp b/test/test_win7/custom_error.cpp new file mode 100644 index 000000000..b9850845a --- /dev/null +++ b/test/test_win7/custom_error.cpp @@ -0,0 +1,52 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // Some custom exception type unknown to C++/WinRT + struct CustomError + { + }; + + struct Sample : implements + { + hstring ToString() + { + // Throw custom exception inside C++/WinRT projection + throw CustomError(); + } + }; + + // Global handler to translate custom exception + int32_t __stdcall handler(void* address) noexcept + { + REQUIRE(address); + + try + { + throw; + } + catch (CustomError) + { + return 0x80000018; // E_ILLEGAL_DELEGATE_ASSIGNMENT + } + + REQUIRE(false); + return 0; + } +} + +TEST_CASE("custom_error") +{ + // Set up global handler + REQUIRE(!winrt_to_hresult_handler); + winrt_to_hresult_handler = handler; + + // Validate that handler translated exception + REQUIRE_THROWS_AS(make().ToString(), hresult_illegal_delegate_assignment); + + // Remove global handler + winrt_to_hresult_handler = nullptr; +} diff --git a/test/test_win7/delegate.cpp b/test/test_win7/delegate.cpp new file mode 100644 index 000000000..bade55060 --- /dev/null +++ b/test/test_win7/delegate.cpp @@ -0,0 +1,72 @@ +#include "pch.h" + +using namespace winrt; + +TEST_CASE("delegate") +{ + // <> + { + bool invoked = false; + delegate<> d = [&] {invoked = true; }; + d(); + REQUIRE(invoked); + } + + // + { + int result = 0; + delegate d = [&](int a) {result = a; }; + d(123); + REQUIRE(result == 123); + } + + // + { + int result = 0; + delegate d = [&](int a, int b) {result = a + b; }; + d(4,5); + REQUIRE(result == 9); + } + + // void() + { + bool invoked = false; + delegate d = [&] {invoked = true; }; + d(); + REQUIRE(invoked); + } + + // void(int) + { + int result = 0; + delegate d = [&](int a) {result = a; }; + d(123); + REQUIRE(result == 123); + } + + // void(int,int) + { + int result = 0; + delegate d = [&](int a, int b) {result = a + b; }; + d(4, 5); + REQUIRE(result == 9); + } + + // int() + { + delegate d = [] { return 123; }; + REQUIRE(d() == 123); + } + + // int(int) + { + delegate d = [](int a) {return a; }; + REQUIRE(d(123) == 123); + } + + // int(int,int) + { + delegate d = [](int a, int b) {return a + b; }; + REQUIRE(d(4, 5) == 9); + } +} diff --git a/test/test_win7/delegates.cpp b/test/test_win7/delegates.cpp new file mode 100644 index 000000000..af2eb6daf --- /dev/null +++ b/test/test_win7/delegates.cpp @@ -0,0 +1,98 @@ +#include "pch.h" +#include "winrt/test_component.Delegates.h" + +using namespace winrt; +using namespace test_component::Delegates; + +TEST_CASE("delegates") +{ + { + bool run{}; + AgileDelegate d = [&] {run = true; }; + REQUIRE(!run); + d(); + REQUIRE(run); + } + { + hstring value; + InDelegate d = [&](hstring const& in) + { + value = in; + }; + REQUIRE(value.empty()); + d(L"Test"); + REQUIRE(value == L"Test"); + } + { + ReturnStringDelegate d = [] {return L"Test"; }; + REQUIRE(d() == L"Test"); + } + { + ReturnInt32Delegate d = [] {return 123; }; + REQUIRE(d() == 123); + } + { + OutStringDelegate d = [](hstring& value) + { + value = L"Test"; + }; + hstring value; + d(value); + REQUIRE(value == L"Test"); + } + { + OutStringDelegate d = [](hstring&) + { + }; + hstring value = L"old"; + d(value); + REQUIRE(value == L""); + } + { + OutInt32Delegate d = [](int32_t & value) + { + value = 123; + }; + int32_t value{ 0xCC }; + d(value); + REQUIRE(value == 123); + } + { + OutInt32Delegate d = [](int32_t&) + { + }; + int32_t value{ 123 }; + d(value); + REQUIRE(value == 123); + } + { + ReturnStringArrayDelegate d = [] { return com_array{ L"One", L"Two", L"Three" }; }; + com_array value = d(); + REQUIRE(value.size() == 3); + REQUIRE(value[0] == L"One"); + REQUIRE(value[1] == L"Two"); + REQUIRE(value[2] == L"Three"); + } + { + OutStringArrayDelegate d = [](com_array& value) { value = { L"One", L"Two", L"Three" }; }; + + com_array value; + d(value); + + REQUIRE(value.size() == 3); + REQUIRE(value[0] == L"One"); + REQUIRE(value[1] == L"Two"); + REQUIRE(value[2] == L"Three"); + } + { + RefStringArrayDelegate d = [](array_view value) { value[0] = L"One"; value[1] = L"Two"; value[2] = L"Three"; }; + + std::array value{ L"r1", L"r2", L"r3", L"r4" }; + d(value); + + REQUIRE(value[0] == L"One"); + REQUIRE(value[1] == L"Two"); + REQUIRE(value[2] == L"Three"); + REQUIRE(value[3] == L""); + } +} diff --git a/test/test_win7/disconnected.cpp b/test/test_win7/disconnected.cpp new file mode 100644 index 000000000..583b7df36 --- /dev/null +++ b/test/test_win7/disconnected.cpp @@ -0,0 +1,124 @@ +#include "pch.h" + +using namespace std::literals; +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + IAsyncAction Action() + { + co_return; + } + + IAsyncActionWithProgress ActionProgress() + { + co_await 500ms; + auto progress = co_await get_progress_token(); + progress(123); + co_return; + } + + IAsyncOperation Operation() + { + co_return 123; + } + + IAsyncOperationWithProgress OperationProgress() + { + co_await 500ms; + auto progress = co_await get_progress_token(); + progress(123); + co_return 123; + } +} + +TEST_CASE("disconnected") +{ + { + event> source; + + source.add([](auto...) + { + throw hresult_error(RPC_E_DISCONNECTED); + }); + + auto token = source.add([](auto...) + { + throw hresult_error(E_INVALIDARG); + }); + + // Should have two delegates + REQUIRE(source); + + // Should lose the disconnected delegate + source(nullptr, 123); + REQUIRE(source); + + // Fire the remaining delegate + source(nullptr, 123); + REQUIRE(source); + + // Remove the final delegate + source.remove(token); + + // No more delegates + REQUIRE(!source); + + source(nullptr, 123); + } + + { + auto async = Action(); + + async.Completed([](auto&&...) + { + throw hresult_error(RPC_E_DISCONNECTED); + }); + } + + { + auto async = ActionProgress(); + handle signal{ CreateEventW(nullptr, true, false, nullptr) }; + + async.Progress([](auto&&...) + { + throw hresult_error(RPC_E_DISCONNECTED); + }); + + async.Completed([&](auto&&...) + { + SetEvent(signal.get()); + throw hresult_error(RPC_E_DISCONNECTED); + }); + + WaitForSingleObject(signal.get(), INFINITE); + } + + { + auto async = Operation(); + + async.Completed([](auto&&...) + { + throw hresult_error(RPC_E_DISCONNECTED); + }); + } + + { + auto async = OperationProgress(); + handle signal{ CreateEventW(nullptr, true, false, nullptr) }; + + async.Progress([](auto&&...) + { + throw hresult_error(RPC_E_DISCONNECTED); + }); + + async.Completed([&](auto&&...) + { + SetEvent(signal.get()); + throw hresult_error(RPC_E_DISCONNECTED); + }); + + WaitForSingleObject(signal.get(), INFINITE); + } +} diff --git a/test/test_win7/enum.cpp b/test/test_win7/enum.cpp new file mode 100644 index 000000000..48f356a75 --- /dev/null +++ b/test/test_win7/enum.cpp @@ -0,0 +1,24 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace test_component; + +TEST_CASE("enum") +{ + STATIC_REQUIRE(std::is_same_v, int32_t>); + STATIC_REQUIRE(std::is_same_v, uint32_t>); + + STATIC_REQUIRE(name_of() == L"test_component.Signed"sv); + STATIC_REQUIRE(name_of() == L"test_component.Unsigned"sv); + + REQUIRE(((Unsigned::First | Unsigned::Second | Unsigned::Third) & Unsigned::Second) == Unsigned::Second); + + REQUIRE(static_cast(Signed::First) == -1); + REQUIRE(static_cast(Signed::Second) == 0); + REQUIRE(static_cast(Signed::Third) == 1); + + REQUIRE(static_cast(Unsigned::First) == 0); + REQUIRE(static_cast(Unsigned::Second) == 1); + REQUIRE(static_cast(Unsigned::Third) == 2); +} diff --git a/test/test_win7/fast_iterator.cpp b/test/test_win7/fast_iterator.cpp new file mode 100644 index 000000000..5be28fb3d --- /dev/null +++ b/test/test_win7/fast_iterator.cpp @@ -0,0 +1,23 @@ +#include "pch.h" + +TEST_CASE("fast_iterator") +{ + { + auto v = winrt::single_threaded_vector({ 1, 2, 3 }); + + std::vector result; + + std::copy(begin(v), end(v), std::back_inserter(result)); + + REQUIRE((result == std::vector{ 1, 2, 3 })); + } + { + auto v = winrt::single_threaded_vector({ 1, 2, 3 }); + + std::vector result; + + std::copy(rbegin(v), rend(v), std::back_inserter(result)); + + REQUIRE((result == std::vector{ 3, 2, 1 })); + } +} diff --git a/test/test_win7/final_release.cpp b/test/test_win7/final_release.cpp new file mode 100644 index 000000000..c615499cb --- /dev/null +++ b/test/test_win7/final_release.cpp @@ -0,0 +1,66 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + struct Sample : implements + { + hstring ToString() + { + return L"Sample"; + } + + ~Sample() + { + // It's safe to QI/AddRef/Release inside destructor. + IStringable s; + check_hresult(QueryInterface(guid_of(), put_abi(s))); + REQUIRE(s.ToString() == L"Sample"); + + // Weak references are also supported during destruction. + REQUIRE(weak_ref{ s }.get()); + + REQUIRE(released); + REQUIRE(!destroyed); + destroyed = true; + } + + static void final_release(std::unique_ptr ptr) noexcept + { + // It's safe to QI/AddRef/Release inside final_release. + IStringable s; + check_hresult(ptr->QueryInterface(guid_of(), put_abi(s))); + REQUIRE(s.ToString() == L"Sample"); + + // References must be released prior to destroying the unique_ptr. + s = nullptr; + + REQUIRE(!released); + REQUIRE(!destroyed); + released = true; + ptr = nullptr; + REQUIRE(destroyed); + } + + static inline bool released; + static inline bool destroyed; + }; +} + +TEST_CASE("final_release") +{ + { + auto s = make(); + + // Weak references are supported prior to destruction. + REQUIRE(weak_ref{ s }.get()); + + REQUIRE(!Sample::released); + REQUIRE(!Sample::destroyed); + s = nullptr; + REQUIRE(Sample::released); + REQUIRE(Sample::destroyed); + } +} diff --git a/test/test_win7/generic_type_names.cpp b/test/test_win7/generic_type_names.cpp new file mode 100644 index 000000000..580d69ac3 --- /dev/null +++ b/test/test_win7/generic_type_names.cpp @@ -0,0 +1,150 @@ +// Windows.Foundation is intentionally *not* included here to ensure that stable names/guids +// are generated with only the xxx.0.h header. This ensures that indirect declarations produce +// stable identity values. +#define WINRT_LEAN_AND_MEAN +#include "winrt/Windows.Storage.h" + +#include "catch.hpp" +#include "generic_types.h" + +TEST_CASE("generic_type_names") +{ + using A = IIterable; + using B = IKeyValuePair>; + + test_guids(); + + // + // Generated Windows.Foundation names + // + + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IStringable", + IStringable); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IAsyncActionWithProgress`1>", + IAsyncActionWithProgress); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IAsyncOperationWithProgress`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + IAsyncOperationWithProgress); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IAsyncOperation`1>", + IAsyncOperation); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReferenceArray`1>", + IReferenceArray); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1>", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.AsyncActionProgressHandler`1>", + AsyncActionProgressHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.AsyncActionWithProgressCompletedHandler`1>", + AsyncActionWithProgressCompletedHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.AsyncOperationCompletedHandler`1>", + AsyncOperationCompletedHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.AsyncOperationProgressHandler`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + AsyncOperationProgressHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.AsyncOperationWithProgressCompletedHandler`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + AsyncOperationWithProgressCompletedHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.EventHandler`1>", + EventHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.TypedEventHandler`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + TypedEventHandler); + + // + // Generated Windows.Foundation.Collections names + // + + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IIterable`1>", + IIterable); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IIterator`1>", + IIterator); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IKeyValuePair`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + IKeyValuePair); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IMapChangedEventArgs`1>", + IMapChangedEventArgs); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IMapView`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + IMapView); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IMap`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + IMap); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IObservableMap`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + IObservableMap); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IObservableVector`1>", + IObservableVector); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IVectorView`1>", + IVectorView); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IVector`1>", + IVector); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.MapChangedEventHandler`2, Windows.Foundation.Collections.IKeyValuePair`2, Single>>>", + MapChangedEventHandler); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.VectorChangedEventHandler`1>", + VectorChangedEventHandler); + + // + // Generated primitive names + // + + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + + // Enums, structs, IInspectable, classes, and delegates + + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IReference`1", + IReference); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IVector`1", + IVector); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IVector`1", + IVector); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Collections.IVector`1", + IVector); +} diff --git a/test/test_win7/generic_types.cpp b/test/test_win7/generic_types.cpp new file mode 100644 index 000000000..98ffd12eb --- /dev/null +++ b/test/test_win7/generic_types.cpp @@ -0,0 +1,16 @@ +#include "pch.h" +#include "generic_types.h" + +TEST_CASE("generic_types") +{ + test_guids(); + + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Uri", Uri); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.PropertyType", PropertyType); + REQUIRE_EQUAL_NAME(L"Windows.Foundation.Point", Point); + + // Clang 9 doesn't think this is a constant expression. +#ifndef __clang__ + REQUIRE_EQUAL_NAME(L"Windows.Foundation.IStringable", IStringable); +#endif +} diff --git a/test/test_win7/generic_types.h b/test/test_win7/generic_types.h new file mode 100644 index 000000000..c305cd734 --- /dev/null +++ b/test/test_win7/generic_types.h @@ -0,0 +1,165 @@ +#pragma once + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::Foundation::Numerics; +using namespace std::literals; + +#define REQUIRE_EQUAL_GUID(left, ...) STATIC_REQUIRE(equal(make_guid(left), guid_of<__VA_ARGS__>())); +#define REQUIRE_EQUAL_NAME(left, ...) STATIC_REQUIRE(left == name_of<__VA_ARGS__>()); + +namespace +{ + constexpr uint32_t to_uint(char const value) noexcept + { + if (value >= '0' && value <= '9') + { + return value - '0'; + } + + if (value >= 'A' && value <= 'F') + { + return 10 + value - 'A'; + } + + if (value >= 'a' && value <= 'f') + { + return 10 + value - 'a'; + } + + std::terminate(); + } + + constexpr guid make_guid(std::string_view const& value) noexcept + { + if (value.size() != 36 || value[8] != '-' || value[13] != '-' || value[18] != '-' || value[23] != '-') + { + std::terminate(); + } + + return + { + ((to_uint(value[0]) * 16 + to_uint(value[1])) << 24) + + ((to_uint(value[2]) * 16 + to_uint(value[3])) << 16) + + ((to_uint(value[4]) * 16 + to_uint(value[5])) << 8) + + (to_uint(value[6]) * 16 + to_uint(value[7])), + + static_cast(((to_uint(value[9]) * 16 + to_uint(value[10])) << 8) + + (to_uint(value[11]) * 16 + to_uint(value[12]))), + + static_cast(((to_uint(value[14]) * 16 + to_uint(value[15])) << 8) + + (to_uint(value[16]) * 16 + to_uint(value[17]))), + + { + static_cast(to_uint(value[19]) * 16 + to_uint(value[20])), + static_cast(to_uint(value[21]) * 16 + to_uint(value[22])), + + static_cast(to_uint(value[24]) * 16 + to_uint(value[25])), + static_cast(to_uint(value[26]) * 16 + to_uint(value[27])), + static_cast(to_uint(value[28]) * 16 + to_uint(value[29])), + static_cast(to_uint(value[30]) * 16 + to_uint(value[31])), + static_cast(to_uint(value[32]) * 16 + to_uint(value[33])), + static_cast(to_uint(value[34]) * 16 + to_uint(value[35])), + } + }; + } + + constexpr bool equal(guid const& left, guid const& right) noexcept + { + return left.Data1 == right.Data1 && + left.Data2 == right.Data2 && + left.Data3 == right.Data3 && + left.Data4[0] == right.Data4[0] && + left.Data4[1] == right.Data4[1] && + left.Data4[2] == right.Data4[2] && + left.Data4[3] == right.Data4[3] && + left.Data4[4] == right.Data4[4] && + left.Data4[5] == right.Data4[5] && + left.Data4[6] == right.Data4[6] && + left.Data4[7] == right.Data4[7]; + } + + void test_guids() + { + using A = IIterable; + using B = IKeyValuePair>; + + REQUIRE_EQUAL_GUID("96369F54-8EB6-48F0-ABCE-C1B211E627C3"sv, IStringable); + + // + // Generated Windows.Foundation GUIDs + // + + REQUIRE_EQUAL_GUID("DD725452-2DA3-5103-9C7D-22EE9BB14AD3", IAsyncActionWithProgress); + REQUIRE_EQUAL_GUID("94645425-B9E5-5B91-B509-8DA4DF6A8916", IAsyncOperationWithProgress); + REQUIRE_EQUAL_GUID("2BD35EE6-72D9-5C5D-9827-05EBB81487AB", IAsyncOperation); + REQUIRE_EQUAL_GUID("4A33FE03-E8B9-5346-A124-5449913ECA57", IReferenceArray); + REQUIRE_EQUAL_GUID("F9E4006C-6E8C-56DF-811C-61F9990EBFB0", IReference); + REQUIRE_EQUAL_GUID("C261D8D0-71BA-5F38-A239-872342253A18", AsyncActionProgressHandler); + REQUIRE_EQUAL_GUID("9A0D211C-0374-5D23-9E15-EAA3570FAE63", AsyncActionWithProgressCompletedHandler); + REQUIRE_EQUAL_GUID("9D534225-231F-55E7-A6D0-6C938E2D9160", AsyncOperationCompletedHandler); + REQUIRE_EQUAL_GUID("264F1E0C-ABE4-590B-9D37-E1CC118ECC75", AsyncOperationProgressHandler); + REQUIRE_EQUAL_GUID("C2D078D8-AC47-55AB-83E8-123B2BE5BC5A", AsyncOperationWithProgressCompletedHandler); + REQUIRE_EQUAL_GUID("FA0B7D80-7EFA-52DF-9B69-0574CE57ADA4", EventHandler); + REQUIRE_EQUAL_GUID("EDB31843-B4CF-56EB-925A-D4D0CE97A08D", TypedEventHandler); + + // + // Generated Windows.Foundation.Collections GUIDs + // + + REQUIRE_EQUAL_GUID("96565EB9-A692-59C8-BCB5-647CDE4E6C4D", IIterable); + REQUIRE_EQUAL_GUID("3C9B1E27-8357-590B-8828-6E917F172390", IIterator); + REQUIRE_EQUAL_GUID("89336CD9-8B66-50A7-9759-EB88CCB2E1FE", IKeyValuePair); + REQUIRE_EQUAL_GUID("E1AA5138-12BD-51A1-8558-698DFD070ABE", IMapChangedEventArgs); + REQUIRE_EQUAL_GUID("B78F0653-FA89-59CF-BA95-726938AAE666", IMapView); + REQUIRE_EQUAL_GUID("9962CD50-09D5-5C46-B1E1-3C679C1C8FAE", IMap); + REQUIRE_EQUAL_GUID("75F99E2A-137E-537E-A5B1-0B5A6245FC02", IObservableMap); + REQUIRE_EQUAL_GUID("D24C289F-2341-5128-AAA1-292DD0DC1950", IObservableVector); + REQUIRE_EQUAL_GUID("5F07498B-8E14-556E-9D2E-2E98D5615DA9", IVectorView); + REQUIRE_EQUAL_GUID("0E3F106F-A266-50A1-8043-C90FCF3844F6", IVector); + REQUIRE_EQUAL_GUID("19046F0B-CF81-5DEC-BBB2-7CC250DA8B8B", MapChangedEventHandler); + REQUIRE_EQUAL_GUID("A1E9ACD7-E4DF-5A79-AEFA-DE07934AB0FB", VectorChangedEventHandler); + + // + // Generated primitive GUIDs + // + + REQUIRE_EQUAL_GUID("3C00FD60-2950-5939-A21A-2D12C5A01B8A", IReference); + REQUIRE_EQUAL_GUID("95500129-FBF6-5AFC-89DF-70642D741990", IReference); + REQUIRE_EQUAL_GUID("6EC9E41B-6709-5647-9918-A1270110FC4E", IReference); + REQUIRE_EQUAL_GUID("548CEFBD-BC8A-5FA0-8DF2-957440FC8BF4", IReference); + REQUIRE_EQUAL_GUID("4DDA9E24-E69F-5C6A-A0A6-93427365AF2A", IReference); + REQUIRE_EQUAL_GUID("e5198cc8-2873-55f5-b0a1-84ff9e4aad62", IReference); + REQUIRE_EQUAL_GUID("5AB7D2C3-6B62-5E71-A4B6-2D49C4F238FD", IReference); + REQUIRE_EQUAL_GUID("513ef3af-e784-5325-a91e-97c2b8111cf3", IReference); + REQUIRE_EQUAL_GUID("6755e376-53bb-568b-a11d-17239868309e", IReference); + REQUIRE_EQUAL_GUID("719CC2BA-3E76-5DEF-9F1A-38D85A145EA8", IReference); + REQUIRE_EQUAL_GUID("2F2D6C29-5473-5F3E-92E7-96572BB990E2", IReference); + REQUIRE_EQUAL_GUID("FB393EF3-BBAC-5BD5-9144-84F23576F415", IReference); + REQUIRE_EQUAL_GUID("7D50F649-632C-51F9-849A-EE49428933EA", IReference); + REQUIRE_EQUAL_GUID("6FF27A1E-4B6A-59B7-B2C3-D1F2EE474593", IReference); + REQUIRE_EQUAL_GUID("FD416DFB-2A07-52EB-AAE3-DFCE14116C05", IReference); + REQUIRE_EQUAL_GUID("A9B18291-CE2A-5DAE-8A23-B7F7388416DB", IReference); + REQUIRE_EQUAL_GUID("604D0C4C-91DE-5C2A-935F-362F13EAF800", IReference); + REQUIRE_EQUAL_GUID("5541D8A7-497C-5AA4-86FC-7713ADBF2A2C", IReference); + REQUIRE_EQUAL_GUID("84F14C22-A00A-5272-8D3D-82112E66DF00", IReference); + REQUIRE_EQUAL_GUID("80423F11-054F-5EAC-AFD3-63B6CE15E77B", IReference); + REQUIRE_EQUAL_GUID("61723086-8e53-5276-9f36-2a4bb93e2b75", IReference); + REQUIRE_EQUAL_GUID("48F6A69E-8465-57AE-9400-9764087F65AD", IReference); + REQUIRE_EQUAL_GUID("1EE770FF-C954-59CA-A754-6199A9BE282C", IReference); + REQUIRE_EQUAL_GUID("A5E843C9-ED20-5339-8F8D-9FE404CF3654", IReference); + REQUIRE_EQUAL_GUID("76358CFD-2CBD-525B-A49E-90EE18247B71", IReference); + REQUIRE_EQUAL_GUID("DACBFFDC-68EF-5FD0-B657-782D0AC9807E", IReference); + REQUIRE_EQUAL_GUID("B27004BB-C014-5DCE-9A21-799C5A3C1461", IReference); + REQUIRE_EQUAL_GUID("46D542A1-52F7-58E7-ACFC-9A6D364DA022", IReference); + + // Enums, structs, IInspectable, classes, and delegates + + REQUIRE_EQUAL_GUID("ECEBDE54-FAC0-5AEB-9BA9-9E1FE17E31D5", IReference); + REQUIRE_EQUAL_GUID("84F14C22-A00A-5272-8D3D-82112E66DF00", IReference); + REQUIRE_EQUAL_GUID("B32BDCA4-5E52-5B27-BC5D-D66A1A268C2A", IVector); + REQUIRE_EQUAL_GUID("0D82BD8D-FE62-5D67-A7B9-7886DD75BC4E", IVector); + REQUIRE_EQUAL_GUID("5DAFE591-86DC-59AA-BFDA-07F5D59FC708", IVector); + } +} diff --git a/test/test_win7/iid_ppv_args.cpp b/test/test_win7/iid_ppv_args.cpp new file mode 100644 index 000000000..012e1856d --- /dev/null +++ b/test/test_win7/iid_ppv_args.cpp @@ -0,0 +1,39 @@ +#include "pch.h" +#include + +namespace +{ + struct Stringable : winrt::implements + { + winrt::hstring ToString() + { + return L"hello"; + } + }; + + HRESULT GetStringable(GUID const& id, void** object) noexcept + { + *object = nullptr; + + if (id != __uuidof(ABI::Windows::Foundation::IStringable)) + { + return E_NOINTERFACE; + } + + *object = winrt::detach_abi(winrt::make()); + return S_OK; + } +} + +TEST_CASE("iid_ppv_args") +{ + { + winrt::com_ptr ptr; + REQUIRE(S_OK == GetStringable(IID_PPV_ARGS(&ptr))); + REQUIRE(ptr.as().ToString() == L"hello"); + } + { + winrt::com_ptr ptr; + REQUIRE(E_NOINTERFACE == GetStringable(IID_PPV_ARGS(&ptr))); + } +} diff --git a/test/test_win7/in_params.cpp b/test/test_win7/in_params.cpp new file mode 100644 index 000000000..1709f4fe7 --- /dev/null +++ b/test/test_win7/in_params.cpp @@ -0,0 +1,52 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace test_component; + +namespace +{ + struct Value : implements + { + Value(int32_t value) : + m_value(value) + { + } + + hstring ToString() + { + return hstring{ std::to_wstring(m_value) }; + } + + private: + + int32_t m_value{}; + }; +} + +TEST_CASE("in_params") +{ + Class object; + + REQUIRE(object.InInt32(123) == L"123"); + REQUIRE(object.InString(L"123") == L"123"); + REQUIRE(object.InObject(make(123)) == L"123"); + REQUIRE(object.InStringable(make(123)) == L"123"); + REQUIRE(object.InStruct({ L"1", L"2" }) == L"12"); + REQUIRE(object.InStructRef({ L"1", L"2" }) == L"12ref"); + REQUIRE(object.InEnum(Signed::First) == L"First"); + + REQUIRE(object.InInt32Array({ 1,2 }) == L"12"); + REQUIRE(object.InStringArray({ L"1", L"2" }) == L"12"); + REQUIRE(object.InObjectArray({ make(1), make(2) }) == L"12"); + REQUIRE(object.InStringableArray({ make(1), make(2) }) == L"12"); + REQUIRE(object.InStructArray({ {L"1",L"2"}, {L"3",L"4"} }) == L"1234"); + REQUIRE(object.InEnumArray({ Signed::First, Signed::Second }) == L"FirstSecond"); + + // params::hstring optimizations + REQUIRE(object.InString(L"") == L""); + REQUIRE(object.InString({}) == L""); + wchar_t non_const_string[1] = { L'\0' }; + REQUIRE(object.InString(non_const_string) == L""); +} diff --git a/test/test_win7/inspectable_interop.cpp b/test/test_win7/inspectable_interop.cpp new file mode 100644 index 000000000..d693a6062 --- /dev/null +++ b/test/test_win7/inspectable_interop.cpp @@ -0,0 +1,64 @@ +#include +#include "winrt/Windows.Foundation.h" +#include "catch.hpp" + +using namespace winrt; + +namespace +{ + struct __declspec(uuid("ed0dd761-c31e-4803-8cf9-22a2cb20ec47")) IBadInterop : ::IInspectable + { + virtual int32_t __stdcall JustSayNo() noexcept = 0; + }; + + struct Sample : implements + { + Windows::Foundation::IInspectable ActivateInstance() + { + throw hresult_not_implemented(); + } + + hstring GetRuntimeClassName() + { + return L"Sample"; + } + + Windows::Foundation::TrustLevel GetTrustLevel() + { + return Windows::Foundation::TrustLevel::PartialTrust; + } + + int32_t __stdcall JustSayNo() noexcept final + { + return 123; + } + }; +} + +TEST_CASE("inspectable_interop") +{ + Windows::Foundation::IActivationFactory a = make(); + REQUIRE(a != nullptr); + + Windows::Foundation::IActivationFactory b = a.as(); + REQUIRE(b != nullptr); + + com_ptr c = a.as(); + REQUIRE(c != nullptr); + REQUIRE(c->JustSayNo() == 123); + + Windows::Foundation::IActivationFactory d = c.as(); + REQUIRE(a == d); + + Windows::Foundation::IInspectable f = c.as(); + REQUIRE(f != nullptr); + + Windows::Foundation::IInspectable e(c.detach(), take_ownership_from_abi); + + REQUIRE(winrt::get_class_name(e) == L"Sample"); + REQUIRE(winrt::get_trust_level(e) == Windows::Foundation::TrustLevel::PartialTrust); + + auto interfaces = winrt::get_interfaces(e); + REQUIRE(interfaces.size() == 1); + REQUIRE(interfaces[0] == guid_of()); +} diff --git a/test/test_win7/interop.cpp b/test/test_win7/interop.cpp new file mode 100644 index 000000000..9ca4c94ea --- /dev/null +++ b/test/test_win7/interop.cpp @@ -0,0 +1,80 @@ +#include "pch.h" +#include + +struct __declspec(uuid("5040a5f4-796a-42ff-9f06-be89137a518f")) IBase : IUnknown +{ +}; + +struct __declspec(uuid("529fed32-514f-4150-b1ba-15b47df700b7")) IDerived : IBase +{ +}; + +struct __declspec(uuid("b81fb2a2-eab4-488a-96a7-434873c2c20b")) IMoreDerived : IDerived +{ +}; + +namespace winrt +{ + template<> bool is_guid_of(guid const& id) noexcept + { + return is_guid_of(id); + } + + template<> bool is_guid_of(guid const& id) noexcept + { + return is_guid_of(id); + } +} + +using namespace winrt; + +struct MyBase : implements +{ +}; + +struct MyDerived : implements +{ +}; + +struct MyMoreDerived : implements +{ +}; + +Windows::Foundation::IAsyncAction Async() +{ + co_return; +} + +TEST_CASE("interop") +{ + { + Windows::Foundation::IAsyncAction a = Async(); + com_ptr<::IInspectable> b = a.as<::IInspectable>(); + Windows::Foundation::IAsyncAction c = b.as(); + REQUIRE(a == c); + } + { + com_ptr a = make(); + REQUIRE(a); + REQUIRE(a.try_as() != nullptr); + REQUIRE(a.try_as() == nullptr); + REQUIRE(a.try_as() == nullptr); + REQUIRE(a.try_as<::IInspectable>() == nullptr); + } + { + com_ptr a = make(); + REQUIRE(a); + REQUIRE(a.try_as() != nullptr); + REQUIRE(a.try_as() != nullptr); + REQUIRE(a.try_as() == nullptr); + REQUIRE(a.try_as<::IInspectable>() == nullptr); + } + { + com_ptr a = make(); + REQUIRE(a); + REQUIRE(a.try_as() != nullptr); + REQUIRE(a.try_as() != nullptr); + REQUIRE(a.try_as() != nullptr); + REQUIRE(a.try_as<::IInspectable>() != nullptr); + } +} diff --git a/test/test_win7/invalid_events.cpp b/test/test_win7/invalid_events.cpp new file mode 100644 index 000000000..a8f6a69bd --- /dev/null +++ b/test/test_win7/invalid_events.cpp @@ -0,0 +1,63 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +// +// Checks that invalid tokens may be removed harmlessly. +// + +TEST_CASE("invalid_events") +{ + event> event; + int counter{}; + + auto a = event.add([&](auto && ...) + { + counter += 1; + }); + + auto b = event.add([&](auto && ...) + { + counter += 10; + }); + + REQUIRE(counter == 0); + event(0, 0); + REQUIRE(counter == 11); + + // Remove invalid token (with two valids) + event.remove(event_token {1}); + + counter = 0; + event(0, 0); + REQUIRE(counter == 11); + + // Remove valid token + event.remove(b); + + counter = 0; + event(0, 0); + REQUIRE(counter == 1); + + // Remove invalid token (with one valid) + event.remove(event_token {1}); + + counter = 0; + event(0, 0); + REQUIRE(counter == 1); + + // Remove remaining valid token + event.remove(a); + + counter = 0; + event(0, 0); + REQUIRE(counter == 0); + + // Remove invalid token (with no valids) + event.remove(event_token {1}); + + counter = 0; + event(0, 0); + REQUIRE(counter == 0); +} diff --git a/test/test_win7/main.cpp b/test/test_win7/main.cpp new file mode 100644 index 000000000..171bd517f --- /dev/null +++ b/test/test_win7/main.cpp @@ -0,0 +1,17 @@ +#define CATCH_CONFIG_RUNNER +#include "catch.hpp" +#include "winrt/base.h" + +using namespace winrt; + +int main(int const argc, char** argv) +{ + init_apartment(); + std::set_terminate([] { ExitProcess(1); }); + return Catch::Session().run(argc, argv); +} + +CATCH_TRANSLATE_EXCEPTION(hresult_error const& e) +{ + return to_string(e.message()); +} diff --git a/test/test_win7/module_lock_dll.cpp b/test/test_win7/module_lock_dll.cpp new file mode 100644 index 000000000..605142126 --- /dev/null +++ b/test/test_win7/module_lock_dll.cpp @@ -0,0 +1,50 @@ +#include "catch.hpp" + +// The default behavior (no macro) provides the static winrt::get_module_lock implementation for components/DLLs. + +#include "winrt/Windows.Foundation.h" + +namespace +{ + struct Stringable : winrt::implements + { + winrt::hstring ToString() + { + return L"Stringable"; + } + }; +} + +TEST_CASE("module_lock_dll") +{ + uint32_t const count = winrt::get_module_lock(); + + ++winrt::get_module_lock(); + + REQUIRE(winrt::get_module_lock() == count + 1); + + --winrt::get_module_lock(); + + REQUIRE(winrt::get_module_lock() == count); + + { + auto stringable = winrt::make(); + REQUIRE(winrt::get_module_lock() == count + 1); + } + + REQUIRE(winrt::get_module_lock() == count); + + { + winrt::Windows::Foundation::EventHandler delegate = [](auto&&...) {}; + REQUIRE(winrt::get_module_lock() == count + 1); + } + + REQUIRE(winrt::get_module_lock() == count); + + { + winrt::delegate delegate = [] {}; + REQUIRE(winrt::get_module_lock() == count + 1); + } + + REQUIRE(winrt::get_module_lock() == count); +} diff --git a/test/test_win7/names.cpp b/test/test_win7/names.cpp new file mode 100644 index 000000000..7730fe82e --- /dev/null +++ b/test/test_win7/names.cpp @@ -0,0 +1,19 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +void check_terminated(winrt::param::hstring const&) +{ +} + +TEST_CASE("names") +{ + REQUIRE(name_of() == L"{00000000-0000-0000-c000-000000000046}"sv); + STATIC_REQUIRE(name_of() == L"Object"sv); + + check_terminated(name_of()); + check_terminated(name_of()); + check_terminated(name_of>()); + check_terminated(name_of>()); +} diff --git a/test/test_win7/no_make_detection.cpp b/test/test_win7/no_make_detection.cpp new file mode 100644 index 000000000..dada3c69f --- /dev/null +++ b/test/test_win7/no_make_detection.cpp @@ -0,0 +1,48 @@ +// This test validates that defining WINRT_NO_MAKE_DETECTION actually +// allows an implementation to be final and have a private destructor. +// This is *not* recommended as there are no safeguards for direct and +// invalid allocations, but is provided for compatibility. + +#define WINRT_NO_MAKE_DETECTION +#include "catch.hpp" +#include "winrt/Windows.Foundation.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + struct Stringable final : implements + { + hstring ToString() + { + return L"Stringable"; + } + + inline static bool Destroyed{}; + + private: + + ~Stringable() + { + Destroyed = true; + } + }; +} + +TEST_CASE("no_make_detection") +{ + { + IStringable stringable{ (new Stringable())->get_abi(), take_ownership_from_abi }; + REQUIRE(!Stringable::Destroyed); + stringable = nullptr; + REQUIRE(Stringable::Destroyed); + } + { + Stringable::Destroyed = false; + IStringable stringable = make(); + REQUIRE(!Stringable::Destroyed); + stringable = nullptr; + REQUIRE(Stringable::Destroyed); + } +} diff --git a/test/test_win7/noexcept.cpp b/test/test_win7/noexcept.cpp new file mode 100644 index 000000000..1df30689f --- /dev/null +++ b/test/test_win7/noexcept.cpp @@ -0,0 +1,17 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace test_component; + +TEST_CASE("noexcept") +{ + Class c; + + c.NoexceptVoid(); + int32_t a = c.NoexceptInt32(); + hstring b = c.NoexceptString(); + + REQUIRE(a == 123); + REQUIRE(b == L"123"); +} diff --git a/test/test_win7/notify_awaiter.cpp b/test/test_win7/notify_awaiter.cpp new file mode 100644 index 000000000..9b074d2e4 --- /dev/null +++ b/test/test_win7/notify_awaiter.cpp @@ -0,0 +1,217 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + struct free_awaitable + { + }; + bool await_ready(free_awaitable) + { + return true; + } + void await_suspend(free_awaitable, std::experimental::coroutine_handle<>) + { + } + void await_resume(free_awaitable) + { + + } + + struct member_awaitable + { + bool await_ready() + { + return true; + } + void await_suspend(std::experimental::coroutine_handle<>) + { + } + void await_resume() + { + + } + }; + + struct free_operator_awaitable + { + }; + auto operator co_await(free_operator_awaitable) + { + struct awaitable + { + bool await_ready() + { + return true; + } + void await_suspend(std::experimental::coroutine_handle<>) + { + } + void await_resume() + { + } + }; + return awaitable{}; + } + + struct member_operator_awaitable + { + auto operator co_await() + { + struct awaitable + { + bool await_ready() + { + return true; + } + void await_suspend(std::experimental::coroutine_handle<>) + { + } + void await_resume() + { + } + }; + return awaitable{}; + } + }; + + struct no_copy_awaitable + { + no_copy_awaitable() = default; + no_copy_awaitable(no_copy_awaitable const&) = delete; + + bool await_ready() + { + return true; + } + void await_suspend(std::experimental::coroutine_handle<>) + { + } + void await_resume() + { + + } + }; + + IAsyncAction AsyncAction() + { + co_return; + } + IAsyncActionWithProgress AsyncActionWithProgress() + { + co_return; + } + IAsyncOperation AsyncOperation() + { + co_return 0; + } + IAsyncOperationWithProgress AsyncOperationWithProgress() + { + co_return 0; + } + + struct notification + { + uint32_t suspend{}; + uint32_t resume{}; + }; + + static std::map watcher; + static slim_mutex lock; + static handle start_racing{ CreateEventW(nullptr, true, false, nullptr) }; + constexpr size_t test_coroutines = 20; + constexpr size_t test_suspension_points = 12; + + IAsyncAction Async() + { + co_await resume_on_signal(start_racing.get()); + co_await resume_background(); + co_await resume_background(); + co_await free_awaitable{}; + co_await member_awaitable{}; + co_await free_operator_awaitable{}; + co_await member_operator_awaitable{}; + co_await no_copy_awaitable{}; + co_await AsyncAction(); + co_await AsyncActionWithProgress(); + co_await AsyncOperation(); + co_await AsyncOperationWithProgress(); + } +} + +TEST_CASE("notify_awaiter") +{ + // Everything works fine when nobody is watching. + + REQUIRE(!winrt_suspend_handler); + REQUIRE(!winrt_resume_handler); + SetEvent(start_racing.get()); + Async().get(); + ResetEvent(start_racing.get()); + + // Hook up some watchers. + + winrt_suspend_handler = [](void const* token) noexcept + { + slim_lock_guard guard(lock); + watcher[token].suspend += 1; + }; + + winrt_resume_handler = [](void const* token) noexcept + { + slim_lock_guard guard(lock); + watcher[token].resume += 1; + }; + + // Prepare a few coroutines. + + std::vector concurrency; + REQUIRE(watcher.empty()); + + for (size_t i = 0; i != test_coroutines; ++i) + { + concurrency.push_back(Async()); + } + + // Give coroutines a moment to get to the starting line. + + Sleep(1000); + + // Each coroutine should have suspended once. + + REQUIRE(concurrency.size() == test_coroutines); + REQUIRE(watcher.size() == test_coroutines); + + for (auto&& [_, tally] : watcher) + { + REQUIRE(tally.suspend == 1); + REQUIRE(tally.resume == 0); + } + + // And the race is on! + + SetEvent(start_racing.get()); + + for (auto&& async : concurrency) + { + async.get(); + } + + // Each suspension point should have been recorded. + + REQUIRE(watcher.size() == test_coroutines * test_suspension_points); + + for (auto&& [_, tally] : watcher) + { + // And should be be perfectly balanced. + REQUIRE(tally.suspend == 1); + REQUIRE(tally.resume == 1); + } + + // Remove watchers. + + winrt_suspend_handler = nullptr; + winrt_resume_handler = nullptr; +} diff --git a/test/test_win7/numerics.cpp b/test/test_win7/numerics.cpp new file mode 100644 index 000000000..cfc73c0dd --- /dev/null +++ b/test/test_win7/numerics.cpp @@ -0,0 +1,13 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation::Numerics; + +TEST_CASE("numerics") +{ + // Basic smoke test exercising SIMD intrinsics used by numerics. + + auto one = float4::one(); + + REQUIRE(one * one == one); +} diff --git a/test/test_win7/out_params.cpp b/test/test_win7/out_params.cpp new file mode 100644 index 000000000..1beee6b93 --- /dev/null +++ b/test/test_win7/out_params.cpp @@ -0,0 +1,268 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace test_component; + +namespace +{ + struct Stringable : implements + { + hstring ToString() + { + return L"Stringable"; + } + }; +} + +TEST_CASE("out_params") +{ + Class object; + + { + int value; + object.OutInt32(value); + REQUIRE(value == 123); + } + { + hstring value = L"replace"; + object.OutString(value); + REQUIRE(value == L"123"); + } + { + IInspectable value = make(); + object.OutObject(value); + REQUIRE(value.as().ToString() == L"123"); + } + { + IStringable value = make(); + object.OutStringable(value); + REQUIRE(value.ToString() == L"123"); + } + { + Struct value{ L"First", L"Second" }; + object.OutStruct(value); + REQUIRE(value.First == L"1"); + REQUIRE(value.Second == L"2"); + } + { + Signed value; + object.OutEnum(value); + REQUIRE(value == Signed::First); + } + + { + com_array value(10); + object.OutInt32Array(value); + REQUIRE(value.size() == 3); + REQUIRE(value[0] == 1); + REQUIRE(value[1] == 2); + REQUIRE(value[2] == 3); + } + { + com_array value(10); + object.OutStringArray(value); + REQUIRE(value.size() == 3); + REQUIRE(value[0] == L"1"); + REQUIRE(value[1] == L"2"); + REQUIRE(value[2] == L"3"); + } + { + com_array value(10); + object.OutObjectArray(value); + REQUIRE(value.size() == 3); + REQUIRE(value[0].as().ToString() == L"1"); + REQUIRE(value[1].as().ToString() == L"2"); + REQUIRE(value[2].as().ToString() == L"3"); + } + { + com_array value(10); + object.OutStringableArray(value); + REQUIRE(value.size() == 3); + REQUIRE(value[0].ToString() == L"1"); + REQUIRE(value[1].ToString() == L"2"); + REQUIRE(value[2].ToString() == L"3"); + } + { + com_array value(10); + object.OutStructArray(value); + REQUIRE(value.size() == 2); + REQUIRE(value[0].First == L"1"); + REQUIRE(value[0].Second == L"2"); + REQUIRE(value[1].First == L"10"); + REQUIRE(value[1].Second == L"20"); + } + { + com_array value(10); + object.OutEnumArray(value); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == Signed::First); + REQUIRE(value[1] == Signed::Second); + } + + { + std::array value{ 0xCC, 0xCC, 0xCC, 0xCC }; + object.RefInt32Array(value); + REQUIRE(value[0] == 1); + REQUIRE(value[1] == 2); + REQUIRE(value[2] == 3); + REQUIRE(value[3] == 0xCC); + } + { + std::array value{ L"r1", L"r2", L"r3", L"r4" }; + object.RefStringArray(value); + REQUIRE(value[0] == L"1"); + REQUIRE(value[1] == L"2"); + REQUIRE(value[2] == L"3"); + REQUIRE(value[3] == L""); + } + { + std::array value{ make(), make(), make(), make() }; + object.RefObjectArray(value); + REQUIRE(value[0].as().ToString() == L"1"); + REQUIRE(value[1].as().ToString() == L"2"); + REQUIRE(value[2].as().ToString() == L"3"); + REQUIRE(value[3] == nullptr); + } + { + std::array value{ make(), make(), make(), make() }; + object.RefStringableArray(value); + REQUIRE(value[0].ToString() == L"1"); + REQUIRE(value[1].ToString() == L"2"); + REQUIRE(value[2].ToString() == L"3"); + REQUIRE(value[3] == nullptr); + } + { + std::array value{ {L"First", L"Second"} }; + object.RefStructArray(value); + REQUIRE(value[0].First == L"1"); + REQUIRE(value[0].Second == L"2"); + REQUIRE(value[1].First == L"3"); + REQUIRE(value[1].Second == L"4"); + REQUIRE(value[2].First == L""); + REQUIRE(value[2].Second == L""); + } + { + std::array value{}; + object.RefEnumArray(value); + REQUIRE(value.size() == 3); + REQUIRE(value[0] == Signed::First); + REQUIRE(value[1] == Signed::Second); + REQUIRE(value[2] == static_cast(0)); + } + + object.Fail(true); + + { + int value = 0xCC; + REQUIRE_THROWS_AS(object.OutInt32(value), hresult_invalid_argument); + REQUIRE(value == 0xCC); + } + { + hstring value = L"replace"; + REQUIRE_THROWS_AS(object.OutString(value), hresult_invalid_argument); + REQUIRE(value == L""); + } + { + IInspectable value = make(); + REQUIRE_THROWS_AS(object.OutObject(value), hresult_invalid_argument); + REQUIRE(value == nullptr); + } + { + IStringable value = make(); + REQUIRE_THROWS_AS(object.OutStringable(value), hresult_invalid_argument); + REQUIRE(value == nullptr); + } + { + Struct value{ L"First", L"Second" }; + REQUIRE_THROWS_AS(object.OutStruct(value), hresult_invalid_argument); + REQUIRE(value.First == L""); + REQUIRE(value.Second == L""); + } + { + Signed value = static_cast(0xCC); + REQUIRE_THROWS_AS(object.OutEnum(value), hresult_invalid_argument); + REQUIRE(static_cast(value) == 0xCC); + } + + { + com_array value(10); + REQUIRE_THROWS_AS(object.OutInt32Array(value), hresult_invalid_argument); + REQUIRE(value.size() == 0); + } + { + com_array value(10); + REQUIRE_THROWS_AS(object.OutStringArray(value), hresult_invalid_argument); + REQUIRE(value.size() == 0); + } + { + com_array value(10); + REQUIRE_THROWS_AS(object.OutObjectArray(value), hresult_invalid_argument); + REQUIRE(value.size() == 0); + } + { + com_array value(10); + REQUIRE_THROWS_AS(object.OutStringableArray(value), hresult_invalid_argument); + REQUIRE(value.size() == 0); + } + { + com_array value(10); + REQUIRE_THROWS_AS(object.OutStructArray(value), hresult_invalid_argument); + REQUIRE(value.size() == 0); + } + { + com_array value(10); + REQUIRE_THROWS_AS(object.OutEnumArray(value), hresult_invalid_argument); + REQUIRE(value.size() == 0); + } + + { + std::array value{ 0xCC, 0xCC, 0xCC, 0xCC }; + REQUIRE_THROWS_AS(object.RefInt32Array(value), hresult_invalid_argument); + REQUIRE(value[0] == 0xCC); + REQUIRE(value[1] == 0xCC); + REQUIRE(value[2] == 0xCC); + REQUIRE(value[3] == 0xCC); + } + { + std::array value{ L"r1", L"r2", L"r3", L"r4" }; + REQUIRE_THROWS_AS(object.RefStringArray(value), hresult_invalid_argument); + REQUIRE(value[0] == L""); + REQUIRE(value[1] == L""); + REQUIRE(value[2] == L""); + REQUIRE(value[3] == L""); + } + { + std::array value{ make(), make(), make(), make() }; + REQUIRE_THROWS_AS(object.RefObjectArray(value), hresult_invalid_argument); + REQUIRE(value[0] == nullptr); + REQUIRE(value[1] == nullptr); + REQUIRE(value[2] == nullptr); + REQUIRE(value[3] == nullptr); + } + { + std::array value{ make(), make(), make(), make() }; + REQUIRE_THROWS_AS(object.RefStringableArray(value), hresult_invalid_argument); + REQUIRE(value[0] == nullptr); + REQUIRE(value[1] == nullptr); + REQUIRE(value[2] == nullptr); + REQUIRE(value[3] == nullptr); + } + { + std::array value{ {L"First", L"Second"} }; + REQUIRE_THROWS_AS(object.RefStructArray(value), hresult_invalid_argument); + REQUIRE(value[0].First == L""); + REQUIRE(value[0].Second == L""); + REQUIRE(value[1].First == L""); + REQUIRE(value[1].Second == L""); + REQUIRE(value[2].First == L""); + REQUIRE(value[2].Second == L""); + } + { + std::array value{ static_cast(0xCC), static_cast(0xCC) }; + REQUIRE_THROWS_AS(object.RefEnumArray(value), hresult_invalid_argument); + REQUIRE(value[0] == static_cast(0xCC)); + REQUIRE(value[1] == static_cast(0xCC)); + } +} diff --git a/test/test_win7/parent_includes.cpp b/test/test_win7/parent_includes.cpp new file mode 100644 index 000000000..f7dc0d0c8 --- /dev/null +++ b/test/test_win7/parent_includes.cpp @@ -0,0 +1,17 @@ +#include "pch.h" +#include "winrt/test_component.Parent.One.Two.Three.h" + +using namespace winrt::test_component; + +TEST_CASE("parent_includes") +{ + // Including "...Three.h" should include all (available) ancestors, skipping + // any that are empty. In this case, "Three" and "Parent" are not empty while + // the intermediate namespaces are empty. + + Parent::One::Two::Three::ThreeStruct three; + three.Value = 0; + + Parent::ParentStruct parent; + parent.Value = 0; +} diff --git a/test/test_win7/pch.cpp b/test/test_win7/pch.cpp new file mode 100644 index 000000000..1d9f38c57 --- /dev/null +++ b/test/test_win7/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/test/test_win7/pch.h b/test/test_win7/pch.h new file mode 100644 index 000000000..2f7ee3989 --- /dev/null +++ b/test/test_win7/pch.h @@ -0,0 +1,9 @@ +#pragma once + +#define WINRT_LEAN_AND_MEAN +#include +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Foundation.Numerics.h" +#include "catch.hpp" + +using namespace std::literals; diff --git a/test/test_win7/return_params.cpp b/test/test_win7/return_params.cpp new file mode 100644 index 000000000..3ff5026d1 --- /dev/null +++ b/test/test_win7/return_params.cpp @@ -0,0 +1,80 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace test_component; + +namespace +{ + struct Stringable : implements + { + hstring ToString() + { + return L"Stringable"; + } + }; +} + +TEST_CASE("return_params") +{ + Class object; + + { + int value = object.ReturnInt32(); + REQUIRE(value == 123); + } + { + hstring value = object.ReturnString(); + REQUIRE(value == L"123"); + } + { + IInspectable value = object.ReturnObject(); + REQUIRE(value.as().ToString() == L"123"); + } + { + IStringable value = object.ReturnStringable(); + REQUIRE(value.ToString() == L"123"); + } + { + Struct value = object.ReturnStruct(); + REQUIRE(value.First == L"1"); + REQUIRE(value.Second == L"2"); + } + { + com_array value = object.ReturnInt32Array(); + REQUIRE(value.size() == 3); + REQUIRE(value[0] == 1); + REQUIRE(value[1] == 2); + REQUIRE(value[2] == 3); + } + { + com_array value = object.ReturnStringArray(); + REQUIRE(value.size() == 3); + REQUIRE(value[0] == L"1"); + REQUIRE(value[1] == L"2"); + REQUIRE(value[2] == L"3"); + } + { + com_array value = object.ReturnObjectArray(); + REQUIRE(value.size() == 3); + REQUIRE(value[0].as().ToString() == L"1"); + REQUIRE(value[1].as().ToString() == L"2"); + REQUIRE(value[2].as().ToString() == L"3"); + } + { + com_array value = object.ReturnStringableArray(); + REQUIRE(value.size() == 3); + REQUIRE(value[0].ToString() == L"1"); + REQUIRE(value[1].ToString() == L"2"); + REQUIRE(value[2].ToString() == L"3"); + } + { + com_array value = object.ReturnStructArray(); + REQUIRE(value.size() == 2); + REQUIRE(value[0].First == L"1"); + REQUIRE(value[0].Second == L"2"); + REQUIRE(value[1].First == L"10"); + REQUIRE(value[1].Second == L"20"); + } +} diff --git a/test/test_win7/structs.cpp b/test/test_win7/structs.cpp new file mode 100644 index 000000000..3926c06ed --- /dev/null +++ b/test/test_win7/structs.cpp @@ -0,0 +1,18 @@ +#include "pch.h" +#include "winrt/test_component.Structs.Nested.h" +#include "winrt/test_component_no_pch.Peer2.h" + +using namespace winrt; + +TEST_CASE("structs") +{ + test_component::Structs::Nested::Outer outer{}; + outer.Depends.InnerValue = 1; + outer.OuterValue = 2; + + test_component_no_pch::Peer2::B depends{}; + depends.First.Value = 1; + + test_component::Structs::All all{}; + all.H = {}; +} diff --git a/test/test_win7/test_win7.vcxproj b/test/test_win7/test_win7.vcxproj new file mode 100644 index 000000000..1d5cfd058 --- /dev/null +++ b/test/test_win7/test_win7.vcxproj @@ -0,0 +1,380 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {2EF696B9-7F4A-410F-AE5C-5301565C0F08} + unittests + test_win7 + 10.0 + + + + Application + true + + + Application + true + + + Application + true + + + Application + false + true + + + Application + false + true + + + Application + false + true + + + Application + true + + + Application + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(OutDir)temp\$(ProjectName)\ + + + + + $(OutDir)temp\$(ProjectName)\ + + + $(OutDir)temp\$(ProjectName)\ + + + $(OutDir)temp\$(ProjectName)\ + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + Disabled + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + Disabled + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + Disabled + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + Disabled + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + MaxSpeed + true + true + $(OutputPath);Generated Files;..;..\..\cppwinrt + NOMINMAX;_MBCS;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + + + $(OutputPath)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + + + NotUsing + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + NotUsing + + + + + + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/test/test_win7/thread_pool.cpp b/test/test_win7/thread_pool.cpp new file mode 100644 index 000000000..ac75dac18 --- /dev/null +++ b/test/test_win7/thread_pool.cpp @@ -0,0 +1,61 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + struct AsyncQueue + { + thread_pool m_pool; + + AsyncQueue(uint32_t const high, uint32_t const low) + { + m_pool.thread_limits(high, low); + } + + IAsyncAction Async(delegate<> callback) + { + co_await m_pool; + callback(); + } + }; + + uint32_t test(uint32_t const iterations, uint32_t const high, uint32_t const low) + { + AsyncQueue queue(high, low); + std::vector results; + uint32_t counter{}; + + for (uint32_t i = 0; i < iterations; ++i) + { + results.push_back(queue.Async([&] + { + auto value = counter + 1; + Sleep(10); // Induce thread pool to use more threads if available, also force race condition + counter = value; + })); + } + + for (auto&& async : results) + { + async.get(); + } + + return counter; + } +} + +TEST_CASE("thread_pool") +{ + uint32_t const test_iterations = 100; + + uint32_t const stable_counter = test(test_iterations, 1, 1); + uint32_t const unstable_counter = test(test_iterations, 10, 10); + + // This is determinstic since the queue is single-threaded. + REQUIRE(stable_counter == test_iterations); + + // This is unlikely to fail since the pool is multi-threaded. + REQUIRE(unstable_counter < test_iterations); +} diff --git a/test/test_win7/uniform_in_params.cpp b/test/test_win7/uniform_in_params.cpp new file mode 100644 index 000000000..50eb3fde3 --- /dev/null +++ b/test/test_win7/uniform_in_params.cpp @@ -0,0 +1,29 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace test_component; + +TEST_CASE("uniform_in_params") +{ + Class{ single_threaded_vector({ L"test" }).as>(), 0 }; + Class{ single_threaded_map(std::map{ {L"test", L"test" } }).as>>(), 0, 0 }; + Class{ single_threaded_map(std::map{ {L"test", L"test" } }), 0, 0, 0 }; + Class{ single_threaded_map(std::map{ {L"test", L"test" } }).GetView(), 0, 0, 0, 0 }; + Class{ single_threaded_vector({ L"test" }), 0, 0, 0, 0, 0 }; + Class{ single_threaded_vector({ L"test" }).GetView(), 0, 0, 0, 0, 0, 0 }; + + Class c; + REQUIRE(L"test" == c.InIterable({L"test"})); + REQUIRE(L"test" == c.InIterablePair(single_threaded_map(std::map{ {L"test", L"test" } }))); + REQUIRE(L"test" == c.InAsyncIterable({ L"test" }).get()); + REQUIRE(L"test" == c.InAsyncIterablePair(single_threaded_map(std::map{ {L"test", L"test" } })).get()); + REQUIRE(L"test" == c.InMap(single_threaded_map(std::map{ {L"test", L"test" } }))); + REQUIRE(L"test" == c.InMapView(single_threaded_map(std::map{ {L"test", L"test" } }).GetView())); + REQUIRE(L"test" == c.InAsyncMapView(single_threaded_map(std::map{ {L"test", L"test" } }).GetView()).get()); + REQUIRE(L"test" == c.InVector({ L"test" })); + REQUIRE(L"test" == c.InVectorView({ L"test" })); + REQUIRE(L"test" == c.InAsyncVectorView({ L"test" }).get()); +} diff --git a/test/test_win7/velocity.cpp b/test/test_win7/velocity.cpp new file mode 100644 index 000000000..b47a3fdfe --- /dev/null +++ b/test/test_win7/velocity.cpp @@ -0,0 +1,44 @@ +#include "pch.h" +#include "winrt/test_component.Velocity.h" + +using namespace winrt; +using namespace test_component::Velocity; + +TEST_CASE("velocity") +{ + // This interface is always disabled but shows up in the type sytem + // if it is present in the winmd. + IInterface1 a; + REQUIRE(a == nullptr); + + // This interface is always enabled and is naturally available in + // the projection. + IInterface2 b; + REQUIRE(b == nullptr); + + // Class1 is always disabled and thus will not activate. + REQUIRE_THROWS_AS(Class1(), hresult_class_not_available); + + // Class2 is always enabled so should activate just fine. + Class2 c; + c.Class2_Method(); + + // Class3 is always disabled and thus will not activate. + REQUIRE_THROWS_AS(Class3(), hresult_class_not_available); + + // Class4 is not feature-controlled but uses feature interfaces. + Class4 d; + d.Class4_Method(); + + // The single argument constructor is always disabled. + REQUIRE_THROWS_AS(Class4(1), hresult_class_not_available); + + // The Class4_Static1 static is always disabled. + REQUIRE_THROWS_AS(Class4::Class4_Static1(), hresult_class_not_available); + + // The two argument constructor is always enabled. + Class4 e(1, 2); + + // The Class4_Static2 static is always enabled. + Class4::Class4_Static2(); +} diff --git a/test/test_win7/when.cpp b/test/test_win7/when.cpp new file mode 100644 index 000000000..a3b284169 --- /dev/null +++ b/test/test_win7/when.cpp @@ -0,0 +1,74 @@ +#include "pch.h" +#include + +using namespace concurrency; +using namespace winrt; +using namespace Windows::Foundation; + +task ppl(bool& done) +{ + co_await resume_background(); + done = true; +} + +IAsyncAction async(bool& done) +{ + co_await resume_background(); + done = true; +} + +IAsyncOperation when_signaled(int value, handle const& event) +{ + co_await resume_on_signal(event.get()); + co_return value; +} + +IAsyncAction done() +{ + co_return; +} + +TEST_CASE("when") +{ + { + bool ppl_done = false; + bool async_done = false; + + // Ensures that different async types can be aggregated. + when_all(ppl(ppl_done), async(async_done)).get(); + + REQUIRE(ppl_done); + REQUIRE(async_done); + } + { + // Works with IAsyncAction (with no return value). + IAsyncAction result = when_any(done(), done()); + result.get(); + } + { + handle first_event{ check_pointer(CreateEventW(nullptr, true, false, nullptr)) }; + handle second_event{ check_pointer(CreateEventW(nullptr, true, false, nullptr)) }; + + IAsyncOperation first = when_signaled(1, first_event); + IAsyncOperation second = when_signaled(2, second_event); + + IAsyncOperation result = when_any(first, second); + + // Make sure we're still waiting. + Sleep(100); + REQUIRE(result.Status() == AsyncStatus::Started); + REQUIRE(first.Status() == AsyncStatus::Started); + REQUIRE(second.Status() == AsyncStatus::Started); + + // Allow only one of the async objects to complete. + SetEvent(second_event.get()); + + // This should now complete. + REQUIRE(2 == result.get()); + + REQUIRE(first.Status() == AsyncStatus::Started); + REQUIRE(second.Status() == AsyncStatus::Completed); + + SetEvent(first_event.get()); + } +} From 7d59c7f99d259265686824cc38094396fa72a257 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 14 Jan 2020 16:24:16 -0800 Subject: [PATCH 2/3] test --- run_tests.cmd | 1 + 1 file changed, 1 insertion(+) diff --git a/run_tests.cmd b/run_tests.cmd index c60c0fd9b..d63741994 100644 --- a/run_tests.cmd +++ b/run_tests.cmd @@ -9,6 +9,7 @@ if "%target_platform%"=="" set target_platform=x64 if "%target_configuration%"=="" set target_configuration=Debug call :run_test test +call :run_test test_win7 call :run_test test_fast call :run_test test_slow call :run_test test_old From b2a802425238320fe95bbdb076708a10b58b8bc0 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 14 Jan 2020 17:01:01 -0800 Subject: [PATCH 3/3] test --- test/test/test.vcxproj | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index 147f5371d..9511f5de1 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -286,10 +286,40 @@ + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + @@ -308,20 +338,80 @@ NotUsing + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + NotUsing + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + Create + + + + + +