Add tests and other related things needed to get CI builds working (#11)

* Starting on CMake

* Start adding tests

* Yay, tests

* Stashing for now; need to full update the headers

* Update headers

* Last of the tests

* Noexcept tests!

* Build/conformance/etc/etc

* Add cppwinrt tests

* Exit code for scripts

* Will this compile now?

* Configurable CI build options

* oops

* Work around sadness

* minor update

* Hmm

* Just testing...

* Conditionally enable test

* Add nuspec

* hmm

* Add targets file

* Move back to license file to see what was wrong with it

* Take 2?

* Build nupkg with CMake since build servers have an old nuget version

* Update scripts for build machine purposes

* Case insensitive compare

* I've made this change so many times now...

* Revert str_raw_ptr change & add more in depth tests

* Don't build nuget by default

* Turns out reliance on path was a bad thing...

* Some cleanup
This commit is contained in:
Duncan Horn 2019-04-30 10:26:35 -07:00 коммит произвёл GitHub
Родитель 5a6e8df546
Коммит fbcd1d2abb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
57 изменённых файлов: 31942 добавлений и 347 удалений

3
.gitignore поставляемый
Просмотреть файл

@ -331,3 +331,6 @@ ASALocalRun/
# Visual Studio Code directory
.vscode/
# CMake/Build output
build/

11
CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.11)
# Set by build server to speed up build/reduce file/object size
option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF)
if (NOT DEFINED WIL_BUILD_VERSION)
set(WIL_BUILD_VERSION "0.0.0")
endif()
add_subdirectory(packaging)
add_subdirectory(tests)

Просмотреть файл

@ -32,3 +32,29 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Catch2
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,62 @@
# E.g. replace_cxx_flag("/W[0-4]", "/W4")
macro(replace_cxx_flag pattern text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REGEX REPLACE "${pattern}" "${text}" ${flag} "${${flag}}")
endforeach()
endmacro()
macro(append_cxx_flag text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(APPEND ${flag} " ${text}")
endforeach()
endmacro()
# Fixup default compiler settings
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
replace_cxx_flag("/W[0-4]" "/W4")
append_cxx_flag("/WX")
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
append_cxx_flag("/permissive-")
# wistd::function has padding due to alignment. This is expected
append_cxx_flag("/wd4324")
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
# Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed
append_cxx_flag("-Wno-switch")
append_cxx_flag("-Wno-invalid-noreturn")
append_cxx_flag("-Wno-c++17-compat-mangling")
append_cxx_flag("-Wno-missing-field-initializers")
# For tests, we want to be able to test self assignment, so disable this warning
append_cxx_flag("-Wno-self-assign-overloaded")
# clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar
# results through the following flags.
# TODO: https://github.com/Microsoft/wil/issues/10 - not yet clean enough to have this on by default
# append_cxx_flag("-fno-delayed-template-parsing")
# NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That
# said, errors that originate from WIL headers may benefit
# append_cxx_flag("-fno-ms-compatibility")
else()
# Flags that are either ignored or unrecognized by clang-cl
# TODO: https://github.com/Microsoft/wil/issues/6
# append_cxx_flag("/experimental:preprocessor")
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
# append_cxx_flag("/Wv:18")
append_cxx_flag("/bigobj")
endif()

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_COM_INCLUDED
#define __WIL_COM_INCLUDED

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_COMMON_INCLUDED
#define __WIL_COMMON_INCLUDED
@ -165,25 +174,6 @@ Three exception modes are available:
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
//! This macro is used to invoke another function-like macro with the specified argument list. This is primarily useful
//! when the invocation does not begin with a '(' character, e.g. when using the `WI_FLATTEN` macro. For example:
//! ~~~
//! #define CALL_WITH_BAR_SUFFIX(prefix, ...) WI_PASTE(prefix, Bar) WI_FLATTEN((__VA_ARGS__))
//!
//! // The macro invocation gets expanded to 'FooBar(x)' and does not get processed any further
//! #define FooBar(x) foo_bar(x)
//! CALL_WITH_BAR_SUFFIX(Foo, x);
//! ~~~
//! Instead, you should do:
//! ~~~
//! #define CALL_WITH_BAR_SUFFIX(prefix, ...) WI_MACRO_INVOKE(WI_PASTE(prefix, Bar), WI_FLATTEN((__VA_ARGS__)))
//!
//! // The macro invocation gets expanded to 'foo_bar(x)'
//! #define FooBar(x) foo_bar(x)
//! CALL_WITH_BAR_SUFFIX(Foo, x);
//! ~~~
#define WI_MACRO_INVOKE(fn, argsList) fn argsList
/// @cond
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
@ -200,108 +190,108 @@ Three exception modes are available:
/// @cond
#define __WI_FOR_imp0( fn)
#define __WI_FOR_imp1( fn, arg, ...) __WI_FOR_impN( 0, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp2( fn, arg, ...) __WI_FOR_impN( 1, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp3( fn, arg, ...) __WI_FOR_impN( 2, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp4( fn, arg, ...) __WI_FOR_impN( 3, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp5( fn, arg, ...) __WI_FOR_impN( 4, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp6( fn, arg, ...) __WI_FOR_impN( 5, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp7( fn, arg, ...) __WI_FOR_impN( 6, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp8( fn, arg, ...) __WI_FOR_impN( 7, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp9( fn, arg, ...) __WI_FOR_impN( 8, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp10(fn, arg, ...) __WI_FOR_impN( 9, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp11(fn, arg, ...) __WI_FOR_impN(10, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp12(fn, arg, ...) __WI_FOR_impN(11, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp13(fn, arg, ...) __WI_FOR_impN(12, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp14(fn, arg, ...) __WI_FOR_impN(13, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp15(fn, arg, ...) __WI_FOR_impN(14, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp16(fn, arg, ...) __WI_FOR_impN(15, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp17(fn, arg, ...) __WI_FOR_impN(16, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp18(fn, arg, ...) __WI_FOR_impN(17, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp19(fn, arg, ...) __WI_FOR_impN(18, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp20(fn, arg, ...) __WI_FOR_impN(19, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp21(fn, arg, ...) __WI_FOR_impN(20, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp22(fn, arg, ...) __WI_FOR_impN(21, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp23(fn, arg, ...) __WI_FOR_impN(22, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp24(fn, arg, ...) __WI_FOR_impN(23, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp25(fn, arg, ...) __WI_FOR_impN(24, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp26(fn, arg, ...) __WI_FOR_impN(25, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp27(fn, arg, ...) __WI_FOR_impN(26, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp28(fn, arg, ...) __WI_FOR_impN(27, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp29(fn, arg, ...) __WI_FOR_impN(28, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp30(fn, arg, ...) __WI_FOR_impN(29, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp31(fn, arg, ...) __WI_FOR_impN(30, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp32(fn, arg, ...) __WI_FOR_impN(31, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp33(fn, arg, ...) __WI_FOR_impN(32, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp34(fn, arg, ...) __WI_FOR_impN(33, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp35(fn, arg, ...) __WI_FOR_impN(34, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp36(fn, arg, ...) __WI_FOR_impN(35, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp37(fn, arg, ...) __WI_FOR_impN(36, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp38(fn, arg, ...) __WI_FOR_impN(37, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp39(fn, arg, ...) __WI_FOR_impN(38, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp40(fn, arg, ...) __WI_FOR_impN(39, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp41(fn, arg, ...) __WI_FOR_impN(40, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp42(fn, arg, ...) __WI_FOR_impN(41, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp43(fn, arg, ...) __WI_FOR_impN(42, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp44(fn, arg, ...) __WI_FOR_impN(43, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp45(fn, arg, ...) __WI_FOR_impN(44, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp46(fn, arg, ...) __WI_FOR_impN(45, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp47(fn, arg, ...) __WI_FOR_impN(46, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp48(fn, arg, ...) __WI_FOR_impN(47, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp49(fn, arg, ...) __WI_FOR_impN(48, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp50(fn, arg, ...) __WI_FOR_impN(49, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp51(fn, arg, ...) __WI_FOR_impN(50, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp52(fn, arg, ...) __WI_FOR_impN(51, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp53(fn, arg, ...) __WI_FOR_impN(52, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp54(fn, arg, ...) __WI_FOR_impN(53, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp55(fn, arg, ...) __WI_FOR_impN(54, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp56(fn, arg, ...) __WI_FOR_impN(55, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp57(fn, arg, ...) __WI_FOR_impN(56, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp58(fn, arg, ...) __WI_FOR_impN(57, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp59(fn, arg, ...) __WI_FOR_impN(58, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp60(fn, arg, ...) __WI_FOR_impN(59, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp61(fn, arg, ...) __WI_FOR_impN(60, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp62(fn, arg, ...) __WI_FOR_impN(61, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp63(fn, arg, ...) __WI_FOR_impN(62, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp64(fn, arg, ...) __WI_FOR_impN(63, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp65(fn, arg, ...) __WI_FOR_impN(64, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp66(fn, arg, ...) __WI_FOR_impN(65, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp67(fn, arg, ...) __WI_FOR_impN(66, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp68(fn, arg, ...) __WI_FOR_impN(67, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp69(fn, arg, ...) __WI_FOR_impN(68, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp70(fn, arg, ...) __WI_FOR_impN(69, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp71(fn, arg, ...) __WI_FOR_impN(70, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp72(fn, arg, ...) __WI_FOR_impN(71, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp73(fn, arg, ...) __WI_FOR_impN(72, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp74(fn, arg, ...) __WI_FOR_impN(73, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp75(fn, arg, ...) __WI_FOR_impN(74, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp76(fn, arg, ...) __WI_FOR_impN(75, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp77(fn, arg, ...) __WI_FOR_impN(76, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp78(fn, arg, ...) __WI_FOR_impN(77, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp79(fn, arg, ...) __WI_FOR_impN(78, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp80(fn, arg, ...) __WI_FOR_impN(79, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp81(fn, arg, ...) __WI_FOR_impN(80, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp82(fn, arg, ...) __WI_FOR_impN(81, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp83(fn, arg, ...) __WI_FOR_impN(82, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp84(fn, arg, ...) __WI_FOR_impN(83, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp85(fn, arg, ...) __WI_FOR_impN(84, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp86(fn, arg, ...) __WI_FOR_impN(85, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp87(fn, arg, ...) __WI_FOR_impN(86, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp88(fn, arg, ...) __WI_FOR_impN(87, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp89(fn, arg, ...) __WI_FOR_impN(88, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp90(fn, arg, ...) __WI_FOR_impN(89, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp91(fn, arg, ...) __WI_FOR_impN(90, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp92(fn, arg, ...) __WI_FOR_impN(91, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp93(fn, arg, ...) __WI_FOR_impN(92, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp94(fn, arg, ...) __WI_FOR_impN(93, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp95(fn, arg, ...) __WI_FOR_impN(94, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp96(fn, arg, ...) __WI_FOR_impN(95, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp97(fn, arg, ...) __WI_FOR_impN(96, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp98(fn, arg, ...) __WI_FOR_impN(97, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp99(fn, arg, ...) __WI_FOR_impN(98, fn, arg, fn, ##__VA_ARGS__)
#define __WI_FOR_imp1( fn, arg, ...) __WI_FOR_impN( 0, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp2( fn, arg, ...) __WI_FOR_impN( 1, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp3( fn, arg, ...) __WI_FOR_impN( 2, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp4( fn, arg, ...) __WI_FOR_impN( 3, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp5( fn, arg, ...) __WI_FOR_impN( 4, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp6( fn, arg, ...) __WI_FOR_impN( 5, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp7( fn, arg, ...) __WI_FOR_impN( 6, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp8( fn, arg, ...) __WI_FOR_impN( 7, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp9( fn, arg, ...) __WI_FOR_impN( 8, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp10(fn, arg, ...) __WI_FOR_impN( 9, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp11(fn, arg, ...) __WI_FOR_impN(10, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp12(fn, arg, ...) __WI_FOR_impN(11, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp13(fn, arg, ...) __WI_FOR_impN(12, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp14(fn, arg, ...) __WI_FOR_impN(13, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp15(fn, arg, ...) __WI_FOR_impN(14, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp16(fn, arg, ...) __WI_FOR_impN(15, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp17(fn, arg, ...) __WI_FOR_impN(16, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp18(fn, arg, ...) __WI_FOR_impN(17, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp19(fn, arg, ...) __WI_FOR_impN(18, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp20(fn, arg, ...) __WI_FOR_impN(19, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp21(fn, arg, ...) __WI_FOR_impN(20, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp22(fn, arg, ...) __WI_FOR_impN(21, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp23(fn, arg, ...) __WI_FOR_impN(22, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp24(fn, arg, ...) __WI_FOR_impN(23, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp25(fn, arg, ...) __WI_FOR_impN(24, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp26(fn, arg, ...) __WI_FOR_impN(25, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp27(fn, arg, ...) __WI_FOR_impN(26, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp28(fn, arg, ...) __WI_FOR_impN(27, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp29(fn, arg, ...) __WI_FOR_impN(28, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp30(fn, arg, ...) __WI_FOR_impN(29, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp31(fn, arg, ...) __WI_FOR_impN(30, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp32(fn, arg, ...) __WI_FOR_impN(31, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp33(fn, arg, ...) __WI_FOR_impN(32, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp34(fn, arg, ...) __WI_FOR_impN(33, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp35(fn, arg, ...) __WI_FOR_impN(34, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp36(fn, arg, ...) __WI_FOR_impN(35, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp37(fn, arg, ...) __WI_FOR_impN(36, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp38(fn, arg, ...) __WI_FOR_impN(37, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp39(fn, arg, ...) __WI_FOR_impN(38, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp40(fn, arg, ...) __WI_FOR_impN(39, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp41(fn, arg, ...) __WI_FOR_impN(40, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp42(fn, arg, ...) __WI_FOR_impN(41, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp43(fn, arg, ...) __WI_FOR_impN(42, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp44(fn, arg, ...) __WI_FOR_impN(43, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp45(fn, arg, ...) __WI_FOR_impN(44, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp46(fn, arg, ...) __WI_FOR_impN(45, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp47(fn, arg, ...) __WI_FOR_impN(46, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp48(fn, arg, ...) __WI_FOR_impN(47, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp49(fn, arg, ...) __WI_FOR_impN(48, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp50(fn, arg, ...) __WI_FOR_impN(49, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp51(fn, arg, ...) __WI_FOR_impN(50, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp52(fn, arg, ...) __WI_FOR_impN(51, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp53(fn, arg, ...) __WI_FOR_impN(52, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp54(fn, arg, ...) __WI_FOR_impN(53, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp55(fn, arg, ...) __WI_FOR_impN(54, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp56(fn, arg, ...) __WI_FOR_impN(55, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp57(fn, arg, ...) __WI_FOR_impN(56, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp58(fn, arg, ...) __WI_FOR_impN(57, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp59(fn, arg, ...) __WI_FOR_impN(58, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp60(fn, arg, ...) __WI_FOR_impN(59, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp61(fn, arg, ...) __WI_FOR_impN(60, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp62(fn, arg, ...) __WI_FOR_impN(61, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp63(fn, arg, ...) __WI_FOR_impN(62, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp64(fn, arg, ...) __WI_FOR_impN(63, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp65(fn, arg, ...) __WI_FOR_impN(64, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp66(fn, arg, ...) __WI_FOR_impN(65, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp67(fn, arg, ...) __WI_FOR_impN(66, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp68(fn, arg, ...) __WI_FOR_impN(67, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp69(fn, arg, ...) __WI_FOR_impN(68, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp70(fn, arg, ...) __WI_FOR_impN(69, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp71(fn, arg, ...) __WI_FOR_impN(70, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp72(fn, arg, ...) __WI_FOR_impN(71, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp73(fn, arg, ...) __WI_FOR_impN(72, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp74(fn, arg, ...) __WI_FOR_impN(73, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp75(fn, arg, ...) __WI_FOR_impN(74, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp76(fn, arg, ...) __WI_FOR_impN(75, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp77(fn, arg, ...) __WI_FOR_impN(76, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp78(fn, arg, ...) __WI_FOR_impN(77, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp79(fn, arg, ...) __WI_FOR_impN(78, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp80(fn, arg, ...) __WI_FOR_impN(79, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp81(fn, arg, ...) __WI_FOR_impN(80, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp82(fn, arg, ...) __WI_FOR_impN(81, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp83(fn, arg, ...) __WI_FOR_impN(82, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp84(fn, arg, ...) __WI_FOR_impN(83, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp85(fn, arg, ...) __WI_FOR_impN(84, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp86(fn, arg, ...) __WI_FOR_impN(85, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp87(fn, arg, ...) __WI_FOR_impN(86, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp88(fn, arg, ...) __WI_FOR_impN(87, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp89(fn, arg, ...) __WI_FOR_impN(88, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp90(fn, arg, ...) __WI_FOR_impN(89, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp91(fn, arg, ...) __WI_FOR_impN(90, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp92(fn, arg, ...) __WI_FOR_impN(91, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp93(fn, arg, ...) __WI_FOR_impN(92, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp94(fn, arg, ...) __WI_FOR_impN(93, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp95(fn, arg, ...) __WI_FOR_impN(94, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp96(fn, arg, ...) __WI_FOR_impN(95, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp97(fn, arg, ...) __WI_FOR_impN(96, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp98(fn, arg, ...) __WI_FOR_impN(97, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_imp99(fn, arg, ...) __WI_FOR_impN(98, fn, arg, fn, __VA_ARGS__)
#define __WI_FOR_impN(n, fn, arg, ...) \
fn(arg) \
WI_MACRO_INVOKE(WI_PASTE(__WI_FOR_imp, n), WI_FLATTEN((__VA_ARGS__)))
WI_PASTE(__WI_FOR_imp, n) WI_FLATTEN((__VA_ARGS__))
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
/// @endcond
@ -372,14 +362,14 @@ Three exception modes are available:
//! Set a single compile-time constant `flag` in the variable `var`.
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while (0, 0)
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
//! Clear a single compile-time constant `flag` from the variable `var`.
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while (0, 0)
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
@ -435,7 +425,6 @@ WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
{
g_pfnGetModuleName = GetCurrentModuleName;
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
g_pfnRtlNtStatusToDosErrorNoTeb = RtlNtStatusToDosErrorNoTeb;
return 1;
});
#endif
@ -457,7 +446,7 @@ doing it with global function pointers and header initialization allows a runtim
#endif
/** All Windows Internal Library classes and functions are located within the "wil" namespace.
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
@ -578,7 +567,7 @@ namespace wil
}
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
__forceinline bool verify_bool(T val)
__forceinline bool verify_bool(T /*val*/)
{
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
}

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_INCLUDED
#define __WIL_CPPWINRT_INCLUDED
@ -28,7 +37,7 @@
#define WINRT_EXTERNAL_CATCH_CLAUSE \
catch (const wil::ResultException& e) \
{ \
return hresult_error(e.GetErrorCode(), to_hstring(e.what())).to_abi(); \
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
}
namespace wil::details

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_FILESYSTEM_INCLUDED
#define __WIL_FILESYSTEM_INCLUDED
@ -477,7 +486,7 @@ namespace wil
struct folder_change_reader_state
{
folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
: m_isRecursive(isRecursive), m_filter(filter), m_callback(wistd::move(callback))
: m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter)
{
}
@ -525,7 +534,7 @@ namespace wil
{
// This operation does not have the usual semantic of returning
// ERROR_IO_PENDING.
// NT_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
// WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
// If the operation failed for whatever reason, ensure the TP
// ref counts are accurate.
@ -585,7 +594,7 @@ namespace wil
ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */)
{
auto readerState = static_cast<details::folder_change_reader_state *>(context);
// NT_ASSERT(overlapped == &readerState->m_overlapped);
// WI_ASSERT(overlapped == &readerState->m_overlapped);
bool requeue = true;
if (result == ERROR_SUCCESS)

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_REGISTRY_INCLUDED
#define __WIL_REGISTRY_INCLUDED
@ -54,7 +63,7 @@ namespace wil
struct registry_watcher_state
{
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
: m_keyToWatch(wistd::move(keyToWatch)), m_callback(wistd::move(callback)), m_isRecursive(isRecursive)
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
{
}
wistd::function<void(RegistryChangeKind)> m_callback;

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#include "result_macros.h"
#include "wistd_functional.h"
#include "wistd_memory.h"
@ -461,7 +470,7 @@ namespace wil
}
lambda_call_log(lambda_call_log&& other) WI_NOEXCEPT :
m_info(other.m_info), m_address(other.m_address), m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call)
m_address(other.m_address), m_info(other.m_info), m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call)
{
other.m_call = false;
}
@ -1783,7 +1792,7 @@ namespace std
{
size_t operator()(wil::unique_any_t<storage_t> const &val) const
{
return (hash<wil::unique_any_t<storage_t>::pointer>()(val.get()));
return (hash<typename wil::unique_any_t<storage_t>::pointer>()(val.get()));
}
};
}
@ -1884,7 +1893,7 @@ namespace wil {
void reset(wistd::nullptr_t) WI_NOEXCEPT
{
static_assert(wistd::is_same<policy::pointer_invalid, wistd::nullptr_t>::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value");
static_assert(wistd::is_same<typename policy::pointer_invalid, wistd::nullptr_t>::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value");
reset();
}
@ -1909,14 +1918,9 @@ namespace wil {
return m_ptr.use_count();
}
bool unique() const WI_NOEXCEPT
{
return m_ptr.unique();
}
private:
template <typename storage_t>
friend class weak_any;
friend class ::wil::weak_any;
std::shared_ptr<unique_t> m_ptr;
};
@ -1945,7 +1949,7 @@ namespace wil {
shared_any_t(wistd::nullptr_t) WI_NOEXCEPT
{
static_assert(wistd::is_same<policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value");
static_assert(wistd::is_same<typename policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value");
}
shared_any_t(shared_any_t &&other) WI_NOEXCEPT :
@ -1986,7 +1990,7 @@ namespace wil {
shared_any_t& operator=(wistd::nullptr_t) WI_NOEXCEPT
{
static_assert(wistd::is_same<policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value");
static_assert(wistd::is_same<typename policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value");
storage_t::reset();
return (*this);
}
@ -2005,14 +2009,14 @@ namespace wil {
pointer_storage *operator&()
{
static_assert(wistd::is_same<policy::pointer_access, details::pointer_access_all>::value, "operator & is not available for this handle");
static_assert(wistd::is_same<typename policy::pointer_access, details::pointer_access_all>::value, "operator & is not available for this handle");
storage_t::reset();
return storage_t::addressof();
}
pointer get() const WI_NOEXCEPT
{
static_assert(!wistd::is_same<policy::pointer_access, details::pointer_access_none>::value, "get(): the raw handle value is not available for this resource class");
static_assert(!wistd::is_same<typename policy::pointer_access, details::pointer_access_none>::value, "get(): the raw handle value is not available for this resource class");
return storage_t::get();
}
@ -2038,14 +2042,14 @@ namespace wil {
template <typename unique_t>
bool operator==(const shared_any_t<unique_t>& left, wistd::nullptr_t) WI_NOEXCEPT
{
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
return !left;
}
template <typename unique_t>
bool operator==(wistd::nullptr_t, const shared_any_t<unique_t>& right) WI_NOEXCEPT
{
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
return !right;
}
@ -2058,14 +2062,14 @@ namespace wil {
template <typename unique_t>
bool operator!=(const shared_any_t<unique_t>& left, wistd::nullptr_t) WI_NOEXCEPT
{
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
return !!left;
}
template <typename unique_t>
bool operator!=(wistd::nullptr_t, const shared_any_t<unique_t>& right) WI_NOEXCEPT
{
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
return !!right;
}
@ -2108,7 +2112,7 @@ namespace wil {
}
weak_any(const shared_t &other) WI_NOEXCEPT :
m_weakPtr(other.m_ptr)
m_weakPtr(other.m_ptr)
{
}
@ -2175,7 +2179,7 @@ namespace std
{
size_t operator()(wil::shared_any_t<storage_t> const &val) const
{
return (hash<wil::shared_any_t<storage_t>::pointer>()(val.get()));
return (hash<typename wil::shared_any_t<storage_t>::pointer>()(val.get()));
}
};
}
@ -2660,11 +2664,9 @@ namespace wil
class slim_event_t
{
public:
slim_event_t()
{
}
slim_event_t() WI_NOEXCEPT = default;
slim_event_t(bool isSignaled) :
slim_event_t(bool isSignaled) WI_NOEXCEPT :
m_isSignaled(isSignaled ? TRUE : FALSE)
{
}
@ -3465,7 +3467,6 @@ namespace wil
unique_event_nothrow m_event;
// The thread pool must be last to ensure that the other members are valid
// when it is destructed as it will reference them.
// See http://osgvsowi/2224623
unique_threadpool_wait m_threadPoolWait;
};

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_RESULT_INCLUDED
#define __WIL_RESULT_INCLUDED

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_RESULTMACROS_INCLUDED
#define __WIL_RESULTMACROS_INCLUDED
@ -91,6 +100,15 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "1")
#else
WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#endif
// It would appear as though the C++17 "noexcept is part of the type system" update in MSVC has "infected" the behavior
// when compiling with C++14 (the default...), however the updated behavior for decltype understanding noexcept is _not_
// present... So, work around it
#if __WI_LIBCPP_STD_VER >= 17
#define WI_PFN_NOEXCEPT WI_NOEXCEPT
#else
#define WI_PFN_NOEXCEPT
#endif
/// @endcond
#if defined(__cplusplus) && !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE)
@ -544,23 +562,23 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#endif
// end-of-repeated fail-fast handling macros
// Helpers for return macros
#define __RETURN_HR_MSG(hr, str, fmt, ...) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); } return __hr; } while (0, 0)
#define __RETURN_HR_MSG_FAIL(hr, str, fmt, ...) do { HRESULT __hr = (hr); __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); return __hr; } while (0, 0)
#define __RETURN_WIN32_MSG(err, str, fmt, ...) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } return S_OK; } while (0, 0)
#define __RETURN_WIN32_MSG_FAIL(err, str, fmt, ...) do { DWORD __err = (err); return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } while (0, 0)
#define __RETURN_HR_MSG(hr, str, fmt, ...) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); } return __hr; } while ((void)0, 0)
#define __RETURN_HR_MSG_FAIL(hr, str, fmt, ...) do { HRESULT __hr = (hr); __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); return __hr; } while ((void)0, 0)
#define __RETURN_WIN32_MSG(err, str, fmt, ...) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } return S_OK; } while ((void)0, 0)
#define __RETURN_WIN32_MSG_FAIL(err, str, fmt, ...) do { DWORD __err = (err); return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } while ((void)0, 0)
#define __RETURN_GLE_MSG_FAIL(str, fmt, ...) return __R_FN(Return_GetLastErrorMsg)(__R_INFO(str) fmt, __VA_ARGS__)
#define __RETURN_NTSTATUS_MSG(status, str, fmt, ...) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } return S_OK; } while (0, 0)
#define __RETURN_NTSTATUS_MSG_FAIL(status, str, fmt, ...) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } while (0, 0)
#define __RETURN_HR(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO(str) __hr); } return __hr; } while (0, 0)
#define __RETURN_HR_NOFILE(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); } return __hr; } while (0, 0)
#define __RETURN_HR_FAIL(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO(str) __hr); return __hr; } while (0, 0)
#define __RETURN_HR_FAIL_NOFILE(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); return __hr; } while (0, 0)
#define __RETURN_WIN32(err, str) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32)(__R_INFO(str) __err); } return S_OK; } while (0, 0)
#define __RETURN_WIN32_FAIL(err, str) do { DWORD __err = (err); return __R_FN(Return_Win32)(__R_INFO(str) __err); } while (0, 0)
#define __RETURN_NTSTATUS_MSG(status, str, fmt, ...) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } return S_OK; } while ((void)0, 0)
#define __RETURN_NTSTATUS_MSG_FAIL(status, str, fmt, ...) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } while ((void)0, 0)
#define __RETURN_HR(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO(str) __hr); } return __hr; } while ((void)0, 0)
#define __RETURN_HR_NOFILE(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); } return __hr; } while ((void)0, 0)
#define __RETURN_HR_FAIL(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO(str) __hr); return __hr; } while ((void)0, 0)
#define __RETURN_HR_FAIL_NOFILE(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); return __hr; } while ((void)0, 0)
#define __RETURN_WIN32(err, str) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32)(__R_INFO(str) __err); } return S_OK; } while ((void)0, 0)
#define __RETURN_WIN32_FAIL(err, str) do { DWORD __err = (err); return __R_FN(Return_Win32)(__R_INFO(str) __err); } while ((void)0, 0)
#define __RETURN_GLE_FAIL(str) return __R_FN(Return_GetLastError)(__R_INFO_ONLY(str))
#define __RETURN_GLE_FAIL_NOFILE(str) return __R_FN(Return_GetLastError)(__R_INFO_NOFILE_ONLY(str))
#define __RETURN_NTSTATUS(status, str) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return S_OK; } while (0, 0)
#define __RETURN_NTSTATUS_FAIL(status, str) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } while (0, 0)
#define __RETURN_NTSTATUS(status, str) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return S_OK; } while ((void)0, 0)
#define __RETURN_NTSTATUS_FAIL(status, str) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } while ((void)0, 0)
/// @endcond
//*****************************************************************************
@ -574,15 +592,15 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#define RETURN_NTSTATUS(status) __RETURN_NTSTATUS(status, #status)
// Conditionally returns failures (HRESULT) - always logs failures
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} while (0, 0)
#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} while (0, 0)
#define RETURN_IF_WIN32_ERROR(win32err) do { DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_FAIL(__errRet, #win32err); }} while (0, 0)
#define RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL(E_OUTOFMEMORY, #ptr); }} while (0, 0)
#define RETURN_HR_IF(hr, condition) do { if (wil::verify_bool(condition)) { __RETURN_HR(wil::verify_hresult(hr), #condition); }} while (0, 0)
#define RETURN_HR_IF_NULL(hr, ptr) do { if ((ptr) == nullptr) { __RETURN_HR(wil::verify_hresult(hr), #ptr); }} while (0, 0)
#define RETURN_LAST_ERROR_IF(condition) do { if (wil::verify_bool(condition)) { __RETURN_GLE_FAIL(#condition); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} while (0, 0)
#define RETURN_IF_NTSTATUS_FAILED(status) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_FAIL(__statusRet, #status); }} while (0, 0)
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} while ((void)0, 0)
#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} while ((void)0, 0)
#define RETURN_IF_WIN32_ERROR(win32err) do { DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_FAIL(__errRet, #win32err); }} while ((void)0, 0)
#define RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL(E_OUTOFMEMORY, #ptr); }} while ((void)0, 0)
#define RETURN_HR_IF(hr, condition) do { if (wil::verify_bool(condition)) { __RETURN_HR(wil::verify_hresult(hr), #condition); }} while ((void)0, 0)
#define RETURN_HR_IF_NULL(hr, ptr) do { if ((ptr) == nullptr) { __RETURN_HR(wil::verify_hresult(hr), #ptr); }} while ((void)0, 0)
#define RETURN_LAST_ERROR_IF(condition) do { if (wil::verify_bool(condition)) { __RETURN_GLE_FAIL(#condition); }} while ((void)0, 0)
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} while ((void)0, 0)
#define RETURN_IF_NTSTATUS_FAILED(status) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_FAIL(__statusRet, #status); }} while ((void)0, 0)
// Always returns a known failure (HRESULT) - always logs a var-arg message on failure
#define RETURN_HR_MSG(hr, fmt, ...) __RETURN_HR_MSG(wil::verify_hresult(hr), #hr, fmt, __VA_ARGS__)
@ -591,26 +609,26 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#define RETURN_NTSTATUS_MSG(status, fmt, ...) __RETURN_NTSTATUS_MSG(status, #status, fmt, __VA_ARGS__)
// Conditionally returns failures (HRESULT) - always logs a var-arg message on failure
#define RETURN_IF_FAILED_MSG(hr, fmt, ...) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_MSG_FAIL(__hrRet, #hr, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) do { if (!wil::verify_BOOL(win32BOOL)) { __RETURN_GLE_MSG_FAIL(#win32BOOL, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_IF_WIN32_ERROR_MSG(win32err, fmt, ...) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_MSG_FAIL(__errRet, #win32err, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_IF_NULL_ALLOC_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG_FAIL(E_OUTOFMEMORY, #ptr, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_HR_IF_MSG(hr, condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_HR_MSG(wil::verify_hresult(hr), #condition, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_HR_IF_NULL_MSG(hr, ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG(wil::verify_hresult(hr), #ptr, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_MSG(condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_GLE_MSG_FAIL(#condition, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_GLE_MSG_FAIL(#ptr, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_MSG_FAIL(__statusRet, #status, fmt, __VA_ARGS__); }} while (0, 0)
#define RETURN_IF_FAILED_MSG(hr, fmt, ...) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_MSG_FAIL(__hrRet, #hr, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) do { if (!wil::verify_BOOL(win32BOOL)) { __RETURN_GLE_MSG_FAIL(#win32BOOL, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_IF_WIN32_ERROR_MSG(win32err, fmt, ...) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_MSG_FAIL(__errRet, #win32err, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_IF_NULL_ALLOC_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG_FAIL(E_OUTOFMEMORY, #ptr, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_HR_IF_MSG(hr, condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_HR_MSG(wil::verify_hresult(hr), #condition, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_HR_IF_NULL_MSG(hr, ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG(wil::verify_hresult(hr), #ptr, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_LAST_ERROR_IF_MSG(condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_GLE_MSG_FAIL(#condition, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_GLE_MSG_FAIL(#ptr, fmt, __VA_ARGS__); }} while ((void)0, 0)
#define RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_MSG_FAIL(__statusRet, #status, fmt, __VA_ARGS__); }} while ((void)0, 0)
// Conditionally returns failures (HRESULT) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern
#define RETURN_IF_FAILED_EXPECTED(hr) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { return __hrRet; }} while (0, 0)
#define RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(win32BOOL) do { if (!wil::verify_BOOL(win32BOOL)) { return wil::details::GetLastErrorFailHr(); }} while (0, 0)
#define RETURN_IF_WIN32_ERROR_EXPECTED(win32err) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { return HRESULT_FROM_WIN32(__errRet); }} while (0, 0)
#define RETURN_IF_NULL_ALLOC_EXPECTED(ptr) do { if ((ptr) == nullptr) { return E_OUTOFMEMORY; }} while (0, 0)
#define RETURN_HR_IF_EXPECTED(hr, condition) do { if (wil::verify_bool(condition)) { return wil::verify_hresult(hr); }} while (0, 0)
#define RETURN_HR_IF_NULL_EXPECTED(hr, ptr) do { if ((ptr) == nullptr) { return wil::verify_hresult(hr); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_EXPECTED(condition) do { if (wil::verify_bool(condition)) { return wil::details::GetLastErrorFailHr(); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) do { if ((ptr) == nullptr) { return wil::details::GetLastErrorFailHr(); }} while (0, 0)
#define RETURN_IF_NTSTATUS_FAILED_EXPECTED(status) do { auto __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { return wil::details::NtStatusToHr(__statusRet); }} while (0, 0)
#define RETURN_IF_FAILED_EXPECTED(hr) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { return __hrRet; }} while ((void)0, 0)
#define RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(win32BOOL) do { if (!wil::verify_BOOL(win32BOOL)) { return wil::details::GetLastErrorFailHr(); }} while ((void)0, 0)
#define RETURN_IF_WIN32_ERROR_EXPECTED(win32err) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { return HRESULT_FROM_WIN32(__errRet); }} while ((void)0, 0)
#define RETURN_IF_NULL_ALLOC_EXPECTED(ptr) do { if ((ptr) == nullptr) { return E_OUTOFMEMORY; }} while ((void)0, 0)
#define RETURN_HR_IF_EXPECTED(hr, condition) do { if (wil::verify_bool(condition)) { return wil::verify_hresult(hr); }} while ((void)0, 0)
#define RETURN_HR_IF_NULL_EXPECTED(hr, ptr) do { if ((ptr) == nullptr) { return wil::verify_hresult(hr); }} while ((void)0, 0)
#define RETURN_LAST_ERROR_IF_EXPECTED(condition) do { if (wil::verify_bool(condition)) { return wil::details::GetLastErrorFailHr(); }} while ((void)0, 0)
#define RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) do { if ((ptr) == nullptr) { return wil::details::GetLastErrorFailHr(); }} while ((void)0, 0)
#define RETURN_IF_NTSTATUS_FAILED_EXPECTED(status) do { auto __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { return wil::details::NtStatusToHr(__statusRet); }} while ((void)0, 0)
#define __WI_OR_IS_EXPECTED_HRESULT(e) || (__hrRet == wil::verify_hresult(e))
#define RETURN_IF_FAILED_WITH_EXPECTED(hr, hrExpected, ...) \
@ -626,7 +644,7 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
__RETURN_HR_FAIL(__hrRet, #hr); \
} \
} \
while (0, 0)
while ((void)0, 0)
//*****************************************************************************
// Macros for logging failures (ignore or pass-through)
@ -744,7 +762,7 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#define FAIL_FAST_IMMEDIATE_IF_NTSTATUS_FAILED(status) __RFF_FN(FailFastImmediate_IfNtStatusFailed)(status)
// Specializations
#define FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT() do { if (wil::details::g_pfnFailFastInLoaderCallout != nullptr) { wil::details::g_pfnFailFastInLoaderCallout(); } } while (0, 0)
#define FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT() do { if (wil::details::g_pfnFailFastInLoaderCallout != nullptr) { wil::details::g_pfnFailFastInLoaderCallout(); } } while ((void)0, 0)
//*****************************************************************************
@ -837,14 +855,14 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#define WI_USAGE_ASSERT_STOP(condition) WI_ASSERT(condition)
#endif
#ifdef RESULT_DEBUG
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_ReplaceMsg(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_ReplaceMsg(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
#else
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_Hr(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_Hr(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
#endif
#define WI_USAGE_VERIFY(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR(msg, __VA_ARGS__); }} while (0, 0)
#define WI_USAGE_VERIFY_FORWARD(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR_FORWARD(msg, __VA_ARGS__); }} while (0, 0)
#define WI_USAGE_VERIFY(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR(msg, __VA_ARGS__); }} while ((void)0, 0)
#define WI_USAGE_VERIFY_FORWARD(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR_FORWARD(msg, __VA_ARGS__); }} while ((void)0, 0)
#ifdef RESULT_DEBUG
#define WI_USAGE_ASSERT(condition, msg, ...) WI_USAGE_VERIFY(condition, msg, __VA_ARGS__)
#else
@ -866,12 +884,12 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
#define __WIL_PRIVATE_FAIL_FAST_HR(hr) FAIL_FAST_HR(hr)
#define __WIL_PRIVATE_LOG_HR(hr) LOG_HR(hr)
#else
#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL_NOFILE(__hrRet, #hr); }} while (0, 0)
#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) do { if (wil::verify_bool(cond)) { __RETURN_HR_NOFILE(wil::verify_hresult(hr), #cond); }} while (0, 0)
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) do { if (wil::verify_bool(cond)) { __RETURN_GLE_FAIL_NOFILE(#cond); }} while (0, 0)
#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL_NOFILE(#win32BOOL); }} while (0, 0)
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL_NOFILE(#ptr); }} while (0, 0)
#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL_NOFILE(E_OUTOFMEMORY, #ptr); }} while (0, 0)
#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL_NOFILE(__hrRet, #hr); }} while ((void)0, 0)
#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) do { if (wil::verify_bool(cond)) { __RETURN_HR_NOFILE(wil::verify_hresult(hr), #cond); }} while ((void)0, 0)
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) do { if (wil::verify_bool(cond)) { __RETURN_GLE_FAIL_NOFILE(#cond); }} while ((void)0, 0)
#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL_NOFILE(#win32BOOL); }} while ((void)0, 0)
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL_NOFILE(#ptr); }} while ((void)0, 0)
#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL_NOFILE(E_OUTOFMEMORY, #ptr); }} while ((void)0, 0)
#define __WIL_PRIVATE_RETURN_LAST_ERROR() __RETURN_GLE_FAIL_NOFILE(nullptr)
#define __WIL_PRIVATE_FAIL_FAST_HR_IF(hr, condition) __RFF_FN(FailFast_HrIf)(__RFF_INFO_NOFILE(#condition) wil::verify_hresult(hr), wil::verify_bool(condition))
#define __WIL_PRIVATE_FAIL_FAST_HR(hr) __RFF_FN(FailFast_Hr)(__RFF_INFO_NOFILE(#hr) wil::verify_hresult(hr))
@ -971,7 +989,7 @@ namespace wil
// [optionally] Plug in error logging
// Note: This callback is deprecated. Please use SetResultTelemetryFallback for telemetry or
// SetResultLoggingCallback for observation.
extern "C" __declspec(selectany) void(__stdcall *g_pfnResultLoggingCallback)(_Inout_ wil::FailureInfo *pFailure, _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage, _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) WI_NOEXCEPT = nullptr;
extern "C" __declspec(selectany) void(__stdcall *g_pfnResultLoggingCallback)(_Inout_ wil::FailureInfo *pFailure, _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage, _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) WI_PFN_NOEXCEPT = nullptr;
// [optional]
// This can be explicitly set to control whether or not error messages will be output to OutputDebugString. It can also
@ -981,13 +999,13 @@ namespace wil
// [optionally] Allows application to specify a debugger to detect whether a debugger is present.
// Useful for processes that can only be debugged under kernel debuggers where IsDebuggerPresent returns
// false.
__declspec(selectany) bool(__stdcall *g_pfnIsDebuggerPresent)() WI_NOEXCEPT = nullptr;
__declspec(selectany) bool(__stdcall *g_pfnIsDebuggerPresent)() WI_PFN_NOEXCEPT = nullptr;
// [optionally] Allows forcing WIL to believe a debugger is present. Useful for when a kernel debugger is attached and ::IsDebuggerPresent returns false
__declspec(selectany) bool g_fIsDebuggerPresent = false;
// [optionally] Plug in additional exception-type support (return S_OK when *unable* to remap the exception)
__declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException)() WI_NOEXCEPT = nullptr;
__declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException)() WI_PFN_NOEXCEPT = nullptr;
// [optionally] Use to configure fast fail of unknown exceptions (turn them off).
__declspec(selectany) bool g_fResultFailFastUnknownExceptions = true;
@ -1002,7 +1020,7 @@ namespace wil
__declspec(selectany) bool g_fBreakOnFailure = false;
// [optionally] customize failfast behavior
__declspec(selectany) bool(__stdcall *g_pfnWilFailFast)(const wil::FailureInfo& info) WI_NOEXCEPT = nullptr;
__declspec(selectany) bool(__stdcall *g_pfnWilFailFast)(const wil::FailureInfo& info) WI_PFN_NOEXCEPT = nullptr;
/// @cond
namespace details
@ -1139,31 +1157,34 @@ namespace wil
};
// Fallback telemetry provider callback (set with wil::SetResultTelemetryFallback)
__declspec(selectany) void(__stdcall *g_pfnTelemetryCallback)(bool alreadyReported, wil::FailureInfo const &failure) WI_NOEXCEPT = nullptr;
__declspec(selectany) void(__stdcall *g_pfnTelemetryCallback)(bool alreadyReported, wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr;
// Result.h plug-in (WIL use only)
__declspec(selectany) void(__stdcall *g_pfnGetContextAndNotifyFailure)(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_NOEXCEPT = nullptr;
__declspec(selectany) void(__stdcall *g_pfnGetContextAndNotifyFailure)(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_PFN_NOEXCEPT = nullptr;
// Observe all errors flowing through the system with this callback (set with wil::SetResultLoggingCallback); use with custom logging
__declspec(selectany) void(__stdcall *g_pfnLoggingCallback)(wil::FailureInfo const &failure) WI_NOEXCEPT = nullptr;
__declspec(selectany) void(__stdcall *g_pfnLoggingCallback)(wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr;
// Desktop/System Only: Module fetch function (automatically setup)
__declspec(selectany) PCSTR(__stdcall *g_pfnGetModuleName)() WI_NOEXCEPT = nullptr;
__declspec(selectany) PCSTR(__stdcall *g_pfnGetModuleName)() WI_PFN_NOEXCEPT = nullptr;
// Desktop/System Only: Retrieve address offset and modulename
__declspec(selectany) bool(__stdcall *g_pfnGetModuleInformation)(void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_NOEXCEPT = nullptr;
__declspec(selectany) bool(__stdcall *g_pfnGetModuleInformation)(void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_PFN_NOEXCEPT = nullptr;
// Desktop/System Only: Private module load fail fast function (automatically setup)
__declspec(selectany) void(__stdcall *g_pfnFailFastInLoaderCallout)() WI_NOEXCEPT = nullptr;
// Called with the expectation that the program will terminate when called inside of a loader callout.
// Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined)
__declspec(selectany) void(__stdcall *g_pfnFailFastInLoaderCallout)() WI_PFN_NOEXCEPT = nullptr;
// Desktop/System Only: Private module load convert NtStatus to HResult (automatically setup)
__declspec(selectany) ULONG(__stdcall *g_pfnRtlNtStatusToDosErrorNoTeb)(NTSTATUS) WI_NOEXCEPT = nullptr;
// Called to translate an NTSTATUS value to a Win32 error code
// Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined)
__declspec(selectany) ULONG(__stdcall *g_pfnRtlNtStatusToDosErrorNoTeb)(NTSTATUS) WI_PFN_NOEXCEPT = nullptr;
// Desktop/System Only: Private module load to call debug break
__declspec(selectany) void(__stdcall *g_pfnDebugBreak)() WI_NOEXCEPT = nullptr;
// Desktop/System Only: Call to DebugBreak
__declspec(selectany) void(__stdcall *g_pfnDebugBreak)() WI_PFN_NOEXCEPT = nullptr;
// Private API to determine whether or not termination is happening
__declspec(selectany) BOOLEAN(__stdcall *g_pfnRtlDllShutdownInProgress)() WI_NOEXCEPT = nullptr;
// Called to determine whether or not termination is happening
// Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined)
__declspec(selectany) BOOLEAN(__stdcall *g_pfnDllShutdownInProgress)() WI_PFN_NOEXCEPT = nullptr;
__declspec(selectany) bool g_processShutdownInProgress = false;
// On Desktop/System WINAPI family: dynalink RaiseFailFastException because we may encounter modules
@ -1174,15 +1195,15 @@ namespace wil
__declspec(selectany) HRESULT(__stdcall *g_pfnRunFunctorWithExceptionFilter)(IFunctor& functor, IFunctorHost& host, void* returnAddress) = nullptr;
__declspec(selectany) void(__stdcall *g_pfnRethrow)() = nullptr;
__declspec(selectany) void(__stdcall *g_pfnThrowResultException)(const FailureInfo& failure) = nullptr;
extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtExceptionInternal)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT = nullptr;
extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtExceptionInternal)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr;
// C++/cx compiled additions
extern "C" __declspec(selectany) void(__stdcall *g_pfnThrowPlatformException)(FailureInfo const &failure, PCWSTR debugString) = nullptr;
extern "C" __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromCaughtException_WinRt)(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT = nullptr;
extern "C" __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromCaughtException_WinRt)(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr;
__declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromKnownExceptions_WinRt)(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) = nullptr;
// Plugin to call RoOriginateError (WIL use only)
__declspec(selectany) void(__stdcall *g_pfnOriginateCallback)(wil::FailureInfo const& failure) WI_NOEXCEPT = nullptr;
__declspec(selectany) void(__stdcall *g_pfnOriginateCallback)(wil::FailureInfo const& failure) WI_PFN_NOEXCEPT = nullptr;
enum class ReportFailureOptions
{
@ -1202,7 +1223,7 @@ namespace wil
TFunctor&& functor;
functor_wrapper_void(TFunctor&& functor_) : functor(wistd::forward<TFunctor>(functor_)) { }
#pragma warning(push)
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
HRESULT Run() override
{
functor();
@ -1229,7 +1250,7 @@ namespace wil
TReturn& retVal;
functor_wrapper_other(TFunctor& functor_, TReturn& retval_) : functor(wistd::forward<TFunctor>(functor_)), retVal(retval_) { }
#pragma warning(push)
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
HRESULT Run() override
{
retVal = functor();
@ -1316,8 +1337,8 @@ namespace wil
//*****************************************************************************
/// @cond
#define __FAIL_FAST_ASSERT__(condition) do { if (!(condition)) { __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); } } while (0, 0)
#define __FAIL_FAST_IMMEDIATE_ASSERT__(condition) do { if (!(condition)) { wil::FailureInfo failure {}; wil::details::WilFailFast(failure); } } while (0, 0)
#define __FAIL_FAST_ASSERT__(condition) do { if (!(condition)) { __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); } } while ((void)0, 0)
#define __FAIL_FAST_IMMEDIATE_ASSERT__(condition) do { if (!(condition)) { wil::FailureInfo failure {}; wil::details::WilFailFast(failure); } } while ((void)0, 0)
#define __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(condition) __RFF_FN(FailFast_IfWin32BoolFalse)(__RFF_INFO(#condition) wil::verify_BOOL(condition))
// A simple ref-counted buffer class. The interface is very similar to shared_ptr<>, only it manages
@ -1719,7 +1740,7 @@ namespace wil
// All successful status codes have only one hresult equivalent, S_OK
return S_OK;
}
if (status == STATUS_NO_MEMORY)
if (status == static_cast<NTSTATUS>(STATUS_NO_MEMORY))
{
// RtlNtStatusToDosErrorNoTeb maps STATUS_NO_MEMORY to the less popular of two Win32 no memory error codes resulting in an unexpected mapping
return E_OUTOFMEMORY;
@ -1843,9 +1864,7 @@ namespace wil
}
#pragma warning(pop)
#pragma warning(push)
#pragma warning(disable : 4100) // Unused parameter (pszDest)
_Post_satisfies_(cchDest > 0 && cchDest <= cchMax) static STRSAFEAPI WilStringValidateDestA(_In_reads_opt_(cchDest) STRSAFE_PCNZCH pszDest, _In_ size_t cchDest, _In_ const size_t cchMax)
_Post_satisfies_(cchDest > 0 && cchDest <= cchMax) static STRSAFEAPI WilStringValidateDestA(_In_reads_opt_(cchDest) STRSAFE_PCNZCH /*pszDest*/, _In_ size_t cchDest, _In_ const size_t cchMax)
{
HRESULT hr = S_OK;
if ((cchDest == 0) || (cchDest > cchMax))
@ -1854,7 +1873,6 @@ namespace wil
}
return hr;
}
#pragma warning(pop)
static STRSAFEAPI WilStringVPrintfWorkerA(_Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchDest, _Always_(_Out_opt_ _Deref_out_range_(<=, cchDest - 1)) size_t* pcchNewDestLength, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, _In_ va_list argList)
{
@ -1909,7 +1927,7 @@ namespace wil
return hr;
}
STRSAFEAPI StringCchPrintfA( _Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ size_t cchDest, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, ...)
__inline HRESULT StringCchPrintfA( _Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ size_t cchDest, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, ...)
{
HRESULT hr;
hr = wil::details::WilStringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
@ -2030,7 +2048,7 @@ namespace wil
//! Call this method to determine if process shutdown is in progress (allows avoiding work during dll unload).
inline bool ProcessShutdownInProgress()
{
return (details::g_processShutdownInProgress || (details::g_pfnRtlDllShutdownInProgress ? details::g_pfnRtlDllShutdownInProgress() : false));
return (details::g_processShutdownInProgress || (details::g_pfnDllShutdownInProgress ? details::g_pfnDllShutdownInProgress() : false));
}
/** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down,
@ -2067,7 +2085,7 @@ namespace wil
}
private:
unsigned char m_raw[sizeof(T)];
alignas(T) unsigned char m_raw[sizeof(T)];
};
/** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down.
@ -2123,11 +2141,11 @@ namespace wil
}
private:
unsigned char m_raw[sizeof(T)];
alignas(T) unsigned char m_raw[sizeof(T)];
};
/** Forward your DLLMain to this function so that WIL can have visibility into whether a DLL unload is because
of termination or normal unload. Note that when private API usage is enabled, WIL attempts to make this
of termination or normal unload. Note that when g_pfnDllShutdownInProgress is set, WIL attempts to make this
determination on its own without this callback. Suppressing private APIs requires use of this. */
inline void DLLMain(HINSTANCE, DWORD reason, _In_opt_ LPVOID reserved)
{
@ -3143,8 +3161,7 @@ namespace wil
// Returns true if a debugger should be considered to be connected.
// Modules can force this on through setting g_fIsDebuggerPresent explicitly (useful for live debugging),
// they can provide a callback function by setting g_pfnIsDebuggerPresent (useful for kernel debbugging),
// and finally the user-mode check (IsDebuggerPrsent) is checked. IsDebuggerPresent is a fast call as it
// returns NtCurrentPeb()->BeingDebugged.
// and finally the user-mode check (IsDebuggerPrsent) is checked. IsDebuggerPresent is a fast call
inline bool IsDebuggerPresent()
{
return g_fIsDebuggerPresent || ((g_pfnIsDebuggerPresent != nullptr) ? g_pfnIsDebuggerPresent() : (::IsDebuggerPresent() != FALSE));
@ -3299,7 +3316,6 @@ namespace wil
er.ExceptionInformation[0] = FAST_FAIL_FATAL_APP_EXIT; // see winnt.h, generated from minkernel\published\base\ntrtl_x.w
if (failure.returnAddress == 0) // FailureInfo does not have _ReturnAddress, have RaiseFailFastException generate it
{
// http://osgvsowi/17364039 - confirm with !analyze team that this is the best we can do in this case
// passing ExceptionCode 0xC0000409 and one param with FAST_FAIL_APP_EXIT will use existing
// !analyze functionality to crawl the stack looking for the HRESULT
// don't pass a 0 HRESULT in param 1 because that will result in worse bucketing.

Просмотреть файл

@ -1,3 +1,14 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_SAFECAST_INCLUDED
#define __WIL_SAFECAST_INCLUDED
@ -291,9 +300,9 @@ namespace wil
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT var)
NewT safe_cast_nothrow(const OldT /*var*/)
{
static_assert(false, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
}
// This conversion is always safe, therefore a static_cast is fine.

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_STL_INCLUDED
#define __WIL_STL_INCLUDED

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TOKEN_HELPERS_INCLUDED
#define __WIL_TOKEN_HELPERS_INCLUDED

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WIN32_HELPERS_INCLUDED
#define __WIL_WIN32_HELPERS_INCLUDED
@ -158,7 +167,7 @@ namespace wil
// This function does not work beyond the default stack buffer size (255).
// Needs to to retry in a loop similar to wil::GetModuleFileNameExW
// These updates and unit tests are tracked by //osgvsowi/14483919
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
template <typename string_type, size_t stackBufferLength = 256>
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
{
@ -401,7 +410,7 @@ namespace wil
}
#pragma warning(push)
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
void success()
{
m_flags = 0;

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WINRT_INCLUDED
#define __WIL_WINRT_INCLUDED
@ -75,68 +84,6 @@ namespace wil
template <bool InhibitStringArrays, bool IgnoreCase>
struct hstring_compare
{
template <typename LhsT, typename RhsT>
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
decltype(get_buffer(lhs, wistd::declval<UINT32*>()), get_buffer(rhs, wistd::declval<UINT32*>()), int())
{
UINT32 lhsLength;
UINT32 rhsLength;
auto lhsBuffer = get_buffer(wistd::forward<LhsT>(lhs), &lhsLength);
auto rhsBuffer = get_buffer(wistd::forward<RhsT>(rhs), &rhsLength);
const auto result = ::CompareStringOrdinal(
lhsBuffer,
lhsLength,
rhsBuffer,
rhsLength,
IgnoreCase ? TRUE : FALSE);
NT_ASSERT(result != 0);
return result;
}
template <typename LhsT, typename RhsT>
static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_EQUAL;
}
template <typename LhsT, typename RhsT>
static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_EQUAL;
}
template <typename LhsT, typename RhsT>
static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_LESS_THAN;
}
template <typename LhsT, typename RhsT>
static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_GREATER_THAN;
}
template <typename LhsT, typename RhsT>
static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_GREATER_THAN;
}
template <typename LhsT, typename RhsT>
static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_LESS_THAN;
}
// get_buffer returns the string buffer and length for the supported string types
static const wchar_t* get_buffer(HSTRING hstr, UINT32* length) WI_NOEXCEPT
{
@ -208,6 +155,68 @@ namespace wil
return str.c_str();
}
#endif
template <typename LhsT, typename RhsT>
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
decltype(get_buffer(lhs, wistd::declval<UINT32*>()), get_buffer(rhs, wistd::declval<UINT32*>()), int())
{
UINT32 lhsLength;
UINT32 rhsLength;
auto lhsBuffer = get_buffer(wistd::forward<LhsT>(lhs), &lhsLength);
auto rhsBuffer = get_buffer(wistd::forward<RhsT>(rhs), &rhsLength);
const auto result = ::CompareStringOrdinal(
lhsBuffer,
lhsLength,
rhsBuffer,
rhsLength,
IgnoreCase ? TRUE : FALSE);
WI_ASSERT(result != 0);
return result;
}
template <typename LhsT, typename RhsT>
static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_EQUAL;
}
template <typename LhsT, typename RhsT>
static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_EQUAL;
}
template <typename LhsT, typename RhsT>
static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_LESS_THAN;
}
template <typename LhsT, typename RhsT>
static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_GREATER_THAN;
}
template <typename LhsT, typename RhsT>
static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_GREATER_THAN;
}
template <typename LhsT, typename RhsT>
static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
{
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_LESS_THAN;
}
};
}
/// @endcond
@ -426,7 +435,7 @@ namespace wil
template<typename T, typename Enable = void> struct MapToSmartType
{
#pragma warning(push)
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
struct type // T holder
{
type() {};
@ -439,6 +448,7 @@ namespace wil
// In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen
T&& Get() { return wistd::move(m_value); }
HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; }
T* GetAddressOf() { return &m_value; }
T* ReleaseAndGetAddressOf() { return &m_value; }
T* operator&() { return &m_value; }
@ -727,8 +737,8 @@ namespace wil
vector_range_nothrow& operator=(const vector_range_nothrow&) = delete;
vector_range_nothrow(vector_range_nothrow&& other) :
m_v(other.m_v), m_result(other.m_result), m_resultStorage(other.m_resultStorage),
m_size(other.m_size), m_currentElement(wistd::move(other.m_currentElement))
m_v(other.m_v), m_size(other.m_size), m_result(other.m_result), m_resultStorage(other.m_resultStorage),
m_currentElement(wistd::move(other.m_currentElement))
{
}
@ -897,7 +907,7 @@ namespace wil
}
// for end()
iterable_iterator(int currentIndex) : m_i(-1)
iterable_iterator(int /*currentIndex*/) : m_i(-1)
{
}
@ -1244,6 +1254,7 @@ namespace details
return wistd::forward<TFunc>(func)(wistd::forward<Args>(args)...);
}
#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TResult, typename TFunc, typename ...Args,
typename wistd::enable_if<wistd::is_same<void, TResult>::value, int>::type = 0>
HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args)
@ -1255,6 +1266,7 @@ namespace details
CATCH_RETURN();
return S_OK;
}
#endif
template <typename TFunc, typename ...Args>
HRESULT CallAndHandleErrors(TFunc&& func, Args&&... args)
@ -2041,8 +2053,8 @@ namespace details
#define WI_MakeUniqueWinRtEventToken(_event, _object, _handler) \
wil::details::make_unique_winrt_event_token<wil::err_exception_policy>( \
_object, \
&wistd::remove_pointer<decltype(##_object##)>::type::add_##_event, \
&wistd::remove_pointer<decltype(##_object##)>::type::remove_##_event, \
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
_handler)
#endif // WIL_ENABLE_EXCEPTIONS
@ -2050,16 +2062,16 @@ namespace details
#define WI_MakeUniqueWinRtEventTokenNoThrow(_event, _object, _handler, _token_reference) \
wil::details::make_unique_winrt_event_token( \
_object, \
&wistd::remove_pointer<decltype(##_object##)>::type::add_##_event, \
&wistd::remove_pointer<decltype(##_object##)>::type::remove_##_event, \
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
_handler, \
_token_reference)
#define WI_MakeUniqueWinRtEventTokenFailFast(_event, _object, _handler) \
wil::details::make_unique_winrt_event_token<wil::err_failfast_policy>( \
_object, \
&wistd::remove_pointer<decltype(##_object##)>::type::add_##_event, \
&wistd::remove_pointer<decltype(##_object##)>::type::remove_##_event, \
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
_handler)
#pragma endregion // EventRegistrationToken RAII wrapper

Просмотреть файл

@ -1,3 +1,13 @@
// -*- C++ -*-
//===--------------------------- __config ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless

Просмотреть файл

@ -37,6 +37,7 @@
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include "wistd_memory.h"
#include <intrin.h> // For __fastfail
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
@ -128,7 +129,7 @@ namespace wistd // ("Windows Implementation" std)
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
void __throw_bad_function_call()
{
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
}
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
@ -191,7 +192,7 @@ namespace wistd // ("Windows Implementation" std)
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
virtual void __clone(__base*) const = 0;
virtual void __move(__base*) WI_NOEXCEPT = 0;
virtual void __move(__base*) = 0;
virtual void destroy() WI_NOEXCEPT = 0;
virtual _Rp operator()(_ArgTypes&& ...) = 0;
};
@ -213,7 +214,7 @@ namespace wistd // ("Windows Implementation" std)
: __f_(__f) {}
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
virtual void __move(__base<_Rp(_ArgTypes...)>*) WI_NOEXCEPT;
virtual void __move(__base<_Rp(_ArgTypes...)>*);
virtual void destroy() WI_NOEXCEPT;
virtual _Rp operator()(_ArgTypes&& ... __arg);
};
@ -227,7 +228,7 @@ namespace wistd // ("Windows Implementation" std)
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p) WI_NOEXCEPT
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
{
::new (__p) __func(wistd::move(__f_));
}
@ -301,12 +302,12 @@ namespace wistd // ("Windows Implementation" std)
__WI_LIBCPP_INLINE_VISIBILITY
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
function(const function&);
function(function&&) WI_NOEXCEPT;
function(function&&);
template<class _Fp, class = _EnableIfCallable<_Fp>>
function(_Fp);
function& operator=(const function&);
function& operator=(function&&) WI_NOEXCEPT;
function& operator=(function&&);
function& operator=(nullptr_t) WI_NOEXCEPT;
template<class _Fp, class = _EnableIfCallable<_Fp>>
function& operator=(_Fp&&);
@ -314,7 +315,7 @@ namespace wistd // ("Windows Implementation" std)
~function();
// function modifiers:
void swap(function&) WI_NOEXCEPT;
void swap(function&);
// function capacity:
__WI_LIBCPP_INLINE_VISIBILITY
@ -346,7 +347,7 @@ namespace wistd // ("Windows Implementation" std)
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>::function(function&& __f) WI_NOEXCEPT
function<_Rp(_ArgTypes...)>::function(function&& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
@ -388,7 +389,7 @@ namespace wistd // ("Windows Implementation" std)
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(function&& __f) WI_NOEXCEPT
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
{
*this = nullptr;
if (__f.__f_)
@ -438,7 +439,7 @@ namespace wistd // ("Windows Implementation" std)
template<class _Rp, class ..._ArgTypes>
void
function<_Rp(_ArgTypes...)>::swap(function& __f) WI_NOEXCEPT
function<_Rp(_ArgTypes...)>::swap(function& __f)
{
if (wistd::addressof(__f) == this)
return;
@ -506,13 +507,13 @@ namespace wistd // ("Windows Implementation" std)
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) WI_NOEXCEPT
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) WI_NOEXCEPT
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
#else // __WI_LIBCPP_CXX03_LANG

Просмотреть файл

@ -1,4 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WRL_INCLUDED
#define __WIL_WRL_INCLUDED

2
packaging/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,2 @@
add_subdirectory(nuget)

Просмотреть файл

@ -0,0 +1,20 @@
file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/build_tools/nuget.exe" nuget_exe)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" nupkg_dir)
file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_BUILD_VERSION}.nupkg" wil_nupkg)
# The build servers don't have an up-to-date version of nuget, so pull it down ourselves...
file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe})
file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h)
add_custom_command(OUTPUT ${wil_nupkg}
COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets
${wil_headers}
${CMAKE_SOURCE_DIR}/LICENSE
${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt)
add_custom_target(make_wil_nupkg DEPENDS ${wil_nupkg})

Просмотреть файл

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Microsoft.Windows.ImplementationLibrary</id>
<version>0.0.0</version>
<title>Windows Implementation Library</title>
<authors>Microsoft</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>The Windows Implementation Libraries (wil) were created to improve productivity and solve problems commonly seen by Windows developers.</description>
<tags>windows utility wil native</tags>
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
<license type="file">LICENSE</license>
<projectUrl>https://github.com/Microsoft/wil</projectUrl>
</metadata>
<files>
<file src="..\..\LICENSE"/>
<file src="..\..\ThirdPartyNotices.txt"/>
<file src="..\..\include\**" target="include\" />
<file src="Microsoft.Windows.ImplementationLibrary.targets" target="build\native\" />
</files>
</package>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>

51
scripts/build_all.cmd Normal file
Просмотреть файл

@ -0,0 +1,51 @@
@echo off
setlocal EnableDelayedExpansion
set BUILD_ROOT=%~dp0\..\build
if "%Platform%"=="x64" (
set BUILD_ARCH=64
) else if "%Platform%"=="x86" (
set BUILD_ARCH=32
) else if [%Platform%]==[] (
echo ERROR: The build_all.cmd script must be run from a Visual Studio command window
exit /B 1
) else (
echo ERROR: Unrecognized/unsupported platform %Platform%
exit /B 1
)
call :build clang debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
echo All build completed successfully!
goto :eof
:: build [compiler] [type]
:build
set BUILD_DIR=%BUILD_ROOT%\%1%BUILD_ARCH%%2
if not exist %BUILD_DIR% (
goto :eof
)
pushd %BUILD_DIR%
echo Building from %CD%
ninja
popd
goto :eof

154
scripts/init.cmd Normal file
Просмотреть файл

@ -0,0 +1,154 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
:: Globals
set BUILD_ROOT=%~dp0\..\build
goto :init
:usage
echo USAGE:
echo init.cmd [--help] [-c^|--compiler ^<clang^|msvc^>] [-g^|--generator ^<ninja^|msbuild^>] [--fast]
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-v^|--version X.Y.Z]
goto :eof
:init
:: Initialize values as empty so that we can identify if we are using defaults later for error purposes
set COMPILER=
set GENERATOR=
set BUILD_TYPE=
set CMAKE_ARGS=
set BITNESS=
set VERSION=
set FAST_BUILD=0
:parse
if /I "%~1"=="" goto :execute
if /I "%~1"=="--help" call :usage & goto :eof
set COMPILER_SET=0
if /I "%~1"=="-c" set COMPILER_SET=1
if /I "%~1"=="--compiler" set COMPILER_SET=1
if %COMPILER_SET%==1 (
if "%COMPILER%" NEQ "" echo ERROR: Compiler already specified & exit /B 1
if /I "%~2"=="clang" set COMPILER=clang
if /I "%~2"=="msvc" set COMPILER=msvc
if "!COMPILER!"=="" echo ERROR: Unrecognized/missing compiler %~2 & exit /B 1
shift
shift
goto :parse
)
set GENERATOR_SET=0
if /I "%~1"=="-g" set GENERATOR_SET=1
if /I "%~1"=="--generator" set GENERATOR_SET=1
if %GENERATOR_SET%==1 (
if "%GENERATOR%" NEQ "" echo ERROR: Generator already specified & exit /B 1
if /I "%~2"=="ninja" set GENERATOR=ninja
if /I "%~2"=="msbuild" set GENERATOR=msbuild
if "!GENERATOR!"=="" echo ERROR: Unrecognized/missing generator %~2 & exit /B 1
shift
shift
goto :parse
)
set BUILD_TYPE_SET=0
if /I "%~1"=="-b" set BUILD_TYPE_SET=1
if /I "%~1"=="--build-type" set BUILD_TYPE_SET=1
if %BUILD_TYPE_SET%==1 (
if "%BUILD_TYPE%" NEQ "" echo ERROR: Build type already specified & exit /B 1
if /I "%~2"=="debug" set BUILD_TYPE=debug
if /I "%~2"=="release" set BUILD_TYPE=release
if /I "%~2"=="relwithdebinfo" set BUILD_TYPE=relwithdebinfo
if /I "%~2"=="minsizerel" set BUILD_TYPE=minsizerel
if "!BUILD_TYPE!"=="" echo ERROR: Unrecognized/missing build type %~2 & exit /B 1
shift
shift
goto :parse
)
set VERSION_SET=0
if /I "%~1"=="-v" set VERSION_SET=1
if /I "%~1"=="--version" set VERSION_SET=1
if %VERSION_SET%==1 (
if "%VERSION%" NEQ "" echo ERROR: Version alread specified & exit /B 1
if /I "%~2"=="" echo ERROR: Version string missing & exit /B 1
set VERSION=%~2
shift
shift
goto :parse
)
if /I "%~1"=="--fast" (
if %FAST_BUILD% NEQ 0 echo ERROR: Fast build already specified
set FAST_BUILD=1
shift
goto :parse
)
echo ERROR: Unrecognized argument %~1
exit /B 1
:execute
:: Check for conflicting arguments
if "%COMPILER%"=="clang" (
if "%GENERATOR%"=="msbuild" echo ERROR: Cannot use Clang with MSBuild & exit /B 1
)
:: Select defaults
if "%GENERATOR%"=="" set GENERATOR=ninja
if %GENERATOR%==msbuild set COMPILER=msvc
if "%COMPILER%"=="" set COMPILER=clang
if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug
:: Formulate CMake arguments
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja
if %COMPILER%==clang set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
if %COMPILER%==msvc set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl
if %BUILD_TYPE%==debug set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Debug
if %BUILD_TYPE%==release set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Release
if %BUILD_TYPE%==relwithdebinfo set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=RelWithDebInfo
if %BUILD_TYPE%==minsizerel set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=MinSizeRel
if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION%
if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON
:: Figure out the platform
if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1
if "%Platform%"=="x86" (
set BITNESS=32
if %COMPILER%==clang set CFLAGS=-m32 & set CXXFLAGS=-m32
)
if "%Platform%"=="x64" set BITNESS=64
if "%BITNESS%"=="" echo ERROR: Unrecognized/unsupported platform %Platform% & exit /B 1
:: Set up the build directory
set BUILD_DIR=%BUILD_ROOT%\%COMPILER%%BITNESS%%BUILD_TYPE%
mkdir %BUILD_DIR% > NUL 2>&1
:: Run CMake
pushd %BUILD_DIR%
echo Using compiler....... %COMPILER%
echo Using architecture... %Platform%
echo Using build type..... %BUILD_TYPE%
echo Using build root..... %CD%
echo.
cmake %CMAKE_ARGS% ..\..
popd
goto :eof

17
scripts/init_all.cmd Normal file
Просмотреть файл

@ -0,0 +1,17 @@
@echo off
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
:: TODO: https://github.com/Microsoft/wil/issues/7 - There's currently a bug where Clang and/or the linker chokes when
:: trying to compile the tests for 32-bit debug, so skip for now
if "%Platform%"=="x86" goto :skip_clang_x86_debug
call %~dp0\init.cmd -c clang -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
:skip_clang_x86_debug
call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )

67
scripts/runtests.cmd Normal file
Просмотреть файл

@ -0,0 +1,67 @@
@echo off
setlocal EnableDelayedExpansion
set BUILD_ROOT=%~dp0\..\build
:: Unlike building, we don't need to limit ourselves to the Platform of the command window
call :execute_tests clang64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
goto :eof
:execute_tests
set BUILD_DIR=%BUILD_ROOT%\%1
if not exist %BUILD_DIR% ( goto :eof )
pushd %BUILD_DIR%
echo Running tests from %CD%
call :execute_test app witest.app.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test cpplatest witest.cpplatest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test noexcept witest.noexcept.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test normal witest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
popd
goto :eof
:execute_test
if not exist tests\%1\%2 ( goto :eof )
echo Running %1 tests...
tests\%1\%2
goto :eof

19
tests/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,19 @@
include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake)
# All projects need to reference the WIL headers
include_directories(${CMAKE_SOURCE_DIR}/include)
# TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that
include_directories(BEFORE SYSTEM ./workarounds/wrl)
# The build pipelines have limitations that local development environments do not, so turn a few knobs
if (${FAST_BUILD})
replace_cxx_flag("/GR" "/GR-") # Disables RTTI
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
endif()
add_subdirectory(app)
add_subdirectory(cpplatest)
add_subdirectory(noexcept)
add_subdirectory(normal)

2676
tests/ComTests.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

243
tests/CommonTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,243 @@
#include <wil/common.h>
#include <wil/resource.h>
#include <wrl/client.h>
#include "common.h"
TEST_CASE("CommonTests::OutParamHelpers", "[common]")
{
int i = 2;
int *pOutTest = &i;
int *pNullTest = nullptr;
SECTION("Value type")
{
wil::assign_to_opt_param(pNullTest, 3);
wil::assign_to_opt_param(pOutTest, 3);
REQUIRE(*pOutTest == 3);
}
SECTION("Pointer to value type")
{
int **ppOutTest = &pOutTest;
int **ppNullTest = nullptr;
wil::assign_null_to_opt_param(ppNullTest);
wil::assign_null_to_opt_param(ppOutTest);
REQUIRE(*ppOutTest == nullptr);
}
SECTION("COM out pointer")
{
Microsoft::WRL::ComPtr<IUnknown> spUnk;
IUnknown **ppunkNull = nullptr;
IUnknown *pUnk = reinterpret_cast<IUnknown *>(1);
IUnknown **ppUnkValid = &pUnk;
wil::detach_to_opt_param(ppunkNull, spUnk);
wil::detach_to_opt_param(ppUnkValid, spUnk);
REQUIRE(*ppUnkValid == nullptr);
}
}
TEST_CASE("CommonTests::TypeValidation", "[common]")
{
std::unique_ptr<BYTE> boolCastClass;
std::vector<int> noBoolCastClass;
HRESULT hr = S_OK;
BOOL bigBool = true;
bool smallBool = true;
DWORD dword = 1;
Microsoft::WRL::ComPtr<IUnknown> comPtr;
(void)dword;
// NOTE: The commented out verify* calls should give compilation errors
SECTION("verify_bool")
{
REQUIRE(wil::verify_bool(smallBool));
REQUIRE(wil::verify_bool(bigBool));
REQUIRE_FALSE(wil::verify_bool(boolCastClass));
REQUIRE_FALSE(wil::verify_bool(comPtr));
//wil::verify_bool(noBoolCastClass);
//wil::verify_bool(dword);
//wil::verify_bool(hr);
}
SECTION("verify_hresult")
{
//wil::verify_hresult(smallBool);
//wil::verify_hresult(bigBool);
//wil::verify_hresult(boolCastClass);
//wil::verify_hresult(noBoolCastClass);
//wil::verify_hresult(dword);
//wil::verify_hresult(comPtr);
REQUIRE(wil::verify_hresult(hr) == S_OK);
}
SECTION("verify_BOOL")
{
//wil::verify_BOOL(smallBool);
REQUIRE(wil::verify_BOOL(bigBool));
//wil::verify_BOOL(boolCastClass);
//wil::verify_BOOL(noBoolCastClass);
//wil::verify_BOOL(dword);
//wil::verify_BOOL(comPtr);
//wil::verify_BOOL(hr);
}
}
template <typename T>
static void FlagsMacrosNonStatic(T none, T one, T two, T three, T four)
{
T eval = one | four;
REQUIRE(WI_AreAllFlagsSet(MDEC(eval), MDEC(one | four)));
REQUIRE_FALSE(WI_AreAllFlagsSet(eval, one | three));
REQUIRE_FALSE(WI_AreAllFlagsSet(eval, three | two));
REQUIRE(WI_AreAllFlagsSet(eval, none));
REQUIRE(WI_IsAnyFlagSet(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsAnyFlagSet(eval, one | four | three));
REQUIRE_FALSE(WI_IsAnyFlagSet(eval, two));
REQUIRE(WI_AreAllFlagsClear(MDEC(eval), MDEC(three)));
REQUIRE(WI_AreAllFlagsClear(eval, three | two));
REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | four));
REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | three));
REQUIRE(WI_IsAnyFlagClear(MDEC(eval), MDEC(three)));
REQUIRE(WI_IsAnyFlagClear(eval, three | two));
REQUIRE(WI_IsAnyFlagClear(eval, four | three));
REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one));
REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one | four));
REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eval)));
REQUIRE(WI_IsSingleFlagSet(eval & one));
REQUIRE(WI_IsSingleFlagSetInMask(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsSingleFlagSetInMask(eval, one | three));
REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, three));
REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, one | four));
REQUIRE_FALSE(WI_IsClearOrSingleFlagSet(MDEC(eval)));
REQUIRE(WI_IsClearOrSingleFlagSet(eval & one));
REQUIRE(WI_IsClearOrSingleFlagSet(none));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, one | three));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, three));
REQUIRE_FALSE(WI_IsClearOrSingleFlagSetInMask(eval, one | four));
eval = none;
WI_SetAllFlags(MDEC(eval), MDEC(one));
REQUIRE(eval == one);
WI_SetAllFlags(eval, one | two);
REQUIRE(eval == (one | two));
eval = one | two;
WI_ClearAllFlags(MDEC(eval), one);
REQUIRE(eval == two);
WI_ClearAllFlags(eval, two);
REQUIRE(eval == none);
eval = one | two;
WI_UpdateFlagsInMask(MDEC(eval), MDEC(two | three), MDEC(three | four));
REQUIRE(eval == (one | three));
eval = one;
WI_ToggleAllFlags(MDEC(eval), MDEC(one | two));
REQUIRE(eval == two);
}
enum class EClassTest
{
None = 0x0,
One = 0x1,
Two = 0x2,
Three = 0x4,
Four = 0x8,
};
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);
enum ERawTest
{
ER_None = 0x0,
ER_One = 0x1,
ER_Two = 0x2,
ER_Three = 0x4,
ER_Four = 0x8,
};
DEFINE_ENUM_FLAG_OPERATORS(ERawTest);
TEST_CASE("CommonTests::FlagsMacros", "[common]")
{
SECTION("Integral types")
{
FlagsMacrosNonStatic<char>(static_cast<char>(0), static_cast<char>(0x1), static_cast<char>(0x2), static_cast<char>(0x4), static_cast<char>(0x40));
FlagsMacrosNonStatic<unsigned char>(0, 0x1, 0x2, 0x4, 0x80u);
FlagsMacrosNonStatic<short>(0, 0x1, 0x2, 0x4, 0x4000);
FlagsMacrosNonStatic<unsigned short>(0, 0x1, 0x2, 0x4, 0x8000u);
FlagsMacrosNonStatic<long>(0, 0x1, 0x2, 0x4, 0x80000000ul);
FlagsMacrosNonStatic<unsigned long>(0, 0x1, 0x2, 0x4, 0x80000000ul);
FlagsMacrosNonStatic<long long>(0, 0x1, 0x2, 0x4, 0x8000000000000000ull);
FlagsMacrosNonStatic<unsigned long long>(0, 0x1, 0x2, 0x4, 0x8000000000000000ull);
}
SECTION("Raw enum")
{
FlagsMacrosNonStatic<ERawTest>(ER_None, ER_One, ER_Two, ER_Three, ER_Four);
}
SECTION("Enum class")
{
FlagsMacrosNonStatic<EClassTest>(EClassTest::None, EClassTest::One, EClassTest::Two, EClassTest::Three, EClassTest::Four);
EClassTest eclass = EClassTest::One | EClassTest::Two;
REQUIRE(WI_IsFlagSet(MDEC(eclass), EClassTest::One));
REQUIRE(WI_IsFlagSet(eclass, EClassTest::Two));
REQUIRE_FALSE(WI_IsFlagSet(eclass, EClassTest::Three));
REQUIRE(WI_IsFlagClear(MDEC(eclass), EClassTest::Three));
REQUIRE_FALSE(WI_IsFlagClear(eclass, EClassTest::One));
REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eclass)));
REQUIRE(WI_IsSingleFlagSet(eclass & EClassTest::One));
eclass = EClassTest::None;
WI_SetFlag(MDEC(eclass), EClassTest::One);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::None;
WI_SetFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
WI_SetFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::None;
WI_SetFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
WI_SetFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::One | EClassTest::Two;
WI_ClearFlag(eclass, EClassTest::Two);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::One | EClassTest::Two;
WI_ClearFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == (EClassTest::One | EClassTest::Two));
WI_ClearFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::Two);
eclass = EClassTest::None;
WI_UpdateFlag(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
WI_UpdateFlag(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
eclass = EClassTest::One;
WI_ToggleFlag(eclass, EClassTest::One);
WI_ToggleFlag(eclass, EClassTest::Two);
REQUIRE(eclass == EClassTest::Two);
}
}

74
tests/CppWinRTTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,74 @@
#include "catch.hpp"
#include <wil/cppwinrt.h>
TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
THROW_HR(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};
test(E_UNEXPECTED);
test(E_ACCESSDENIED);
test(E_INVALIDARG);
test(E_HANDLE);
test(E_OUTOFMEMORY);
}
TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
winrt::check_hresult(hr);
}
catch (...)
{
REQUIRE(hr == wil::ResultFromCaughtException());
}
};
test(E_UNEXPECTED);
test(E_ACCESSDENIED);
test(E_INVALIDARG);
test(E_HANDLE);
test(E_OUTOFMEMORY);
}
TEST_CASE("CppWinRTTests::ResultFromExceptionDebugTest", "[cppwinrt]")
{
auto test = [](HRESULT hr, wil::SupportedExceptions supportedExceptions)
{
auto result = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, supportedExceptions, [&]()
{
winrt::check_hresult(hr);
});
REQUIRE(hr == result);
};
// Anything from SupportedExceptions::Known or SupportedExceptions::All should give back the same HRESULT
test(E_UNEXPECTED, wil::SupportedExceptions::Known);
test(E_ACCESSDENIED, wil::SupportedExceptions::Known);
test(E_INVALIDARG, wil::SupportedExceptions::All);
test(E_HANDLE, wil::SupportedExceptions::All);
// OOM gets translated to bad_alloc, which should always give back E_OUTOFMEMORY
test(E_OUTOFMEMORY, wil::SupportedExceptions::All);
test(E_OUTOFMEMORY, wil::SupportedExceptions::Known);
test(E_OUTOFMEMORY, wil::SupportedExceptions::ThrownOrAlloc);
// Uncomment any of the following to validate SEH failfast
//test(E_UNEXPECTED, wil::SupportedExceptions::None);
//test(E_ACCESSDENIED, wil::SupportedExceptions::Thrown);
//test(E_INVALIDARG, wil::SupportedExceptions::ThrownOrAlloc);
}

400
tests/FakeWinRTTypes.h Normal file
Просмотреть файл

@ -0,0 +1,400 @@
#pragma once
#include <wil/resource.h>
#include <windows.foundation.h>
#include <wrl/implements.h>
#include <wrl/wrappers/corewrappers.h>
template <typename T>
struct WinRTStorage
{
T value = {};
HRESULT CopyTo(T* result)
{
*result = value;
return S_OK;
}
HRESULT Set(T val)
{
value = val;
return S_OK;
}
static void Destroy(T&)
{
}
void Reset()
{
}
bool Equals(T val)
{
// NOTE: Padding can through this off, but this isn't intended to be a robust solution...
return memcmp(&value, &val, sizeof(T)) == 0;
}
};
template <>
struct WinRTStorage<HSTRING>
{
Microsoft::WRL::Wrappers::HString value;
HRESULT CopyTo(HSTRING* result)
{
return value.CopyTo(result);
}
HRESULT Set(HSTRING val)
{
return value.Set(val);
}
static void Destroy(HSTRING& val)
{
::WindowsDeleteString(val);
val = nullptr;
}
void Reset()
{
value = {};
}
bool Equals(HSTRING val)
{
return value == val;
}
};
template <typename T>
struct WinRTStorage<T*>
{
Microsoft::WRL::ComPtr<T> value;
HRESULT CopyTo(T** result)
{
*result = Microsoft::WRL::ComPtr<T>(value).Detach();
return S_OK;
}
HRESULT Set(T* val)
{
value = val;
return S_OK;
}
static void Destroy(T*& val)
{
val->Release();
val = nullptr;
}
void Reset()
{
value.Reset();
}
bool Equals(T* val)
{
return value.Get() == val;
}
};
// Very minimal IAsyncOperation implementation that gives calling tests control over when it completes
template <typename Logical, typename Abi = Logical>
struct FakeAsyncOperation : Microsoft::WRL::RuntimeClass<
ABI::Windows::Foundation::IAsyncInfo,
ABI::Windows::Foundation::IAsyncOperation<Logical>>
{
using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler<Logical>;
// IAsyncInfo
IFACEMETHODIMP get_Id(unsigned int*) override
{
return E_NOTIMPL;
}
IFACEMETHODIMP get_Status(AsyncStatus* status) override
{
auto lock = m_lock.lock_shared();
*status = m_status;
return S_OK;
}
IFACEMETHODIMP get_ErrorCode(HRESULT* errorCode) override
{
auto lock = m_lock.lock_shared();
*errorCode = m_result;
return S_OK;
}
IFACEMETHODIMP Cancel() override
{
return E_NOTIMPL;
}
IFACEMETHODIMP Close() override
{
return E_NOTIMPL;
}
// IAsyncOperation
IFACEMETHODIMP put_Completed(Handler* handler) override
{
bool invoke = false;
{
auto lock = m_lock.lock_exclusive();
if (m_handler)
{
return E_FAIL;
}
m_handler = handler;
invoke = m_status != ABI::Windows::Foundation::AsyncStatus::Started;
}
if (invoke)
{
handler->Invoke(this, m_status);
}
return S_OK;
}
IFACEMETHODIMP get_Completed(Handler** handler) override
{
auto lock = m_lock.lock_shared();
*handler = Microsoft::WRL::ComPtr<Handler>(m_handler).Detach();
return S_OK;
}
IFACEMETHODIMP GetResults(Abi* results) override
{
return m_storage.CopyTo(results);
}
// Test functions
void Complete(HRESULT hr, Abi result)
{
using namespace ABI::Windows::Foundation;
Handler* handler = nullptr;
{
auto lock = m_lock.lock_exclusive();
if (m_status == AsyncStatus::Started)
{
m_result = hr;
m_storage.Set(result);
m_status = SUCCEEDED(hr) ? AsyncStatus::Completed : AsyncStatus::Error;
handler = m_handler.Get();
}
}
if (handler)
{
handler->Invoke(this, m_status);
}
}
private:
wil::srwlock m_lock;
Microsoft::WRL::ComPtr<Handler> m_handler;
ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
HRESULT m_result = S_OK;
WinRTStorage<Abi> m_storage;
};
template <typename Logical, typename Abi = Logical, size_t MaxSize = 42>
struct FakeVector : Microsoft::WRL::RuntimeClass<
ABI::Windows::Foundation::Collections::IVector<Logical>,
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
{
// IVector
IFACEMETHODIMP GetAt(unsigned index, Abi* item) override
{
if (index >= m_size)
{
return E_BOUNDS;
}
return m_data[index].CopyTo(item);
}
IFACEMETHODIMP get_Size(unsigned* size) override
{
*size = static_cast<unsigned>(m_size);
return S_OK;
}
IFACEMETHODIMP GetView(ABI::Windows::Foundation::Collections::IVectorView<Logical>** view) override
{
this->AddRef();
*view = this;
return S_OK;
}
IFACEMETHODIMP IndexOf(Abi value, unsigned* index, boolean* found) override
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i].Equals(value))
{
*index = static_cast<unsigned>(i);
*found = true;
return S_OK;
}
}
*index = 0;
*found = false;
return S_OK;
}
IFACEMETHODIMP SetAt(unsigned index, Abi item) override
{
if (index >= m_size)
{
return E_BOUNDS;
}
return m_data[index].Set(item);
}
IFACEMETHODIMP InsertAt(unsigned index, Abi item) override
{
// Insert at the end and swap it into place
if (index > m_size)
{
return E_BOUNDS;
}
auto hr = Append(item);
if (SUCCEEDED(hr))
{
for (size_t i = m_size - 1; i > index; --i)
{
wistd::swap_wil(m_data[i], m_data[i - 1]);
}
}
return hr;
}
IFACEMETHODIMP RemoveAt(unsigned index) override
{
if (index >= m_size)
{
return E_BOUNDS;
}
for (size_t i = index + 1; i < m_size; ++i)
{
wistd::swap_wil(m_data[i - 1], m_data[i]);
}
m_data[--m_size].Reset();
return S_OK;
}
IFACEMETHODIMP Append(Abi item) override
{
if (m_size > MaxSize)
{
return E_OUTOFMEMORY;
}
auto hr = m_data[m_size].Set(item);
if (SUCCEEDED(hr))
{
++m_size;
}
return hr;
}
IFACEMETHODIMP RemoveAtEnd() override
{
if (m_size == 0)
{
return E_BOUNDS;
}
m_data[--m_size].Reset();
return S_OK;
}
IFACEMETHODIMP Clear() override
{
for (size_t i = 0; i < m_size; ++i)
{
m_data[i].Reset();
}
m_size = 0;
return S_OK;
}
IFACEMETHODIMP GetMany(unsigned startIndex, unsigned capacity, Abi* value, unsigned* actual) override
{
*actual = 0;
if (startIndex >= m_size)
{
return S_OK;
}
auto count = m_size - startIndex;
count = (count > capacity) ? capacity : count;
HRESULT hr = S_OK;
unsigned i = 0;
for (; (i < count) && SUCCEEDED(hr); ++i)
{
hr = m_data[startIndex + i].CopyTo(value + i);
}
if (SUCCEEDED(hr))
{
*actual = static_cast<unsigned>(count);
}
else
{
while (i--)
{
WinRTStorage<Abi>::Destroy(value[i]);
}
}
return hr;
}
IFACEMETHODIMP ReplaceAll(unsigned count, Abi* value) override
{
if (count > MaxSize)
{
return E_OUTOFMEMORY;
}
Clear();
HRESULT hr = S_OK;
for (size_t i = 0; (i < count) && SUCCEEDED(hr); ++i)
{
hr = m_data[i].Set(value[i]);
}
if (FAILED(hr))
{
Clear();
}
return hr;
}
private:
size_t m_size = 0;
WinRTStorage<Abi> m_data[MaxSize];
};

448
tests/FileSystemTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,448 @@
#include <windows.h>
#include <winstring.h> // For wil::unique_hstring
#include <wil/common.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <string>
#endif
// TODO: str_raw_ptr is not two-phase name lookup clean (https://github.com/Microsoft/wil/issues/8)
namespace wil
{
PCWSTR str_raw_ptr(HSTRING);
#ifdef WIL_ENABLE_EXCEPTIONS
PCWSTR str_raw_ptr(const std::wstring&);
#endif
}
#include <wil/filesystem.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <wil/stl.h> // For std::wstring string_maker
#endif
#include "common.h"
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
bool DirectoryExists(_In_ PCWSTR path)
{
DWORD dwAttrib = GetFileAttributesW(path);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
{
wchar_t basePath[MAX_PATH];
REQUIRE(GetTempPathW(ARRAYSIZE(basePath), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(basePath, ARRAYSIZE(basePath), L"FileSystemTests"));
REQUIRE_FALSE(DirectoryExists(basePath));
REQUIRE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath)));
REQUIRE(DirectoryExists(basePath));
auto scopeGuard = wil::scope_exit([&]
{
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(basePath));
});
PCWSTR relativeTestPath = L"folder1\\folder2\\folder3\\folder4\\folder5\\folder6\\folder7\\folder8";
wchar_t absoluteTestPath[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath, ARRAYSIZE(absoluteTestPath), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath, ARRAYSIZE(absoluteTestPath), relativeTestPath));
REQUIRE_FALSE(DirectoryExists(absoluteTestPath));
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteTestPath));
PCWSTR invalidCharsPath = L"Bad?Char|";
wchar_t absoluteInvalidPath[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), invalidCharsPath));
REQUIRE_FALSE(DirectoryExists(absoluteInvalidPath));
REQUIRE_FALSE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteInvalidPath)));
PCWSTR testPath3 = L"folder1\\folder2\\folder3";
wchar_t absoluteTestPath3[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), testPath3));
REQUIRE(DirectoryExists(absoluteTestPath3));
PCWSTR testPath4 = L"folder1\\folder2\\folder3\\folder4";
wchar_t absoluteTestPath4[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), testPath4));
REQUIRE(DirectoryExists(absoluteTestPath4));
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(absoluteTestPath3, wil::RemoveDirectoryOptions::KeepRootDirectory));
REQUIRE(DirectoryExists(absoluteTestPath3));
REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
}
#ifdef WIL_ENABLE_EXCEPTIONS
// Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
// This test verifies the ability of RemoveDirectoryRecursive to be able to delete files
// that are in the non-normalized form.
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteFoldersWithNonNormalizedNames", "[filesystem]")
{
// Extended length paths can access files with non-normalized names.
// This function creates a path with that ability.
auto CreatePathThatCanAccessNonNormalizedNames = [](PCWSTR root, PCWSTR name)
{
wil::unique_hlocal_string path;
THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, &path));
REQUIRE(wil::is_extended_length_path(path.get()));
return path;
};
// Regular paths are normalized in the Win32 APIs thus can't address files in the non-normalized form.
// This function creates a regular path form but preserves the non-normalized parts of the input (for testing)
auto CreateRegularPath = [](PCWSTR root, PCWSTR name)
{
wil::unique_hlocal_string path;
THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &path));
REQUIRE_FALSE(wil::is_extended_length_path(path.get()));
return path;
};
struct TestCases
{
PCWSTR CreateWithName;
PCWSTR DeleteWithName;
wil::unique_hlocal_string (*CreatePathFunction)(PCWSTR root, PCWSTR name);
HRESULT ExpectedResult;
};
PCWSTR NormalizedName = L"Foo";
PCWSTR NonNormalizedName = L"Foo."; // The dot at the end is what makes this non-normalized.
const auto PathNotFoundError = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
TestCases tests[] =
{
{ NormalizedName, NormalizedName, CreateRegularPath, S_OK },
{ NonNormalizedName, NormalizedName, CreateRegularPath, PathNotFoundError },
{ NormalizedName, NonNormalizedName, CreateRegularPath, S_OK },
{ NonNormalizedName, NonNormalizedName, CreateRegularPath, PathNotFoundError },
{ NormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
{ NonNormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
{ NormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
{ NonNormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
};
auto folderRoot = wil::ExpandEnvironmentStringsW(LR"(%TEMP%)");
REQUIRE_FALSE(wil::is_extended_length_path(folderRoot.get()));
auto EnsureFolderWithNonCanonicalNameAndContentsExists = [&](const TestCases& test)
{
const auto enableNonNormalized = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS;
wil::unique_hlocal_string targetFolder;
// Create a folder for testing using the extended length form to enable
// access to non-normalized forms of the path
THROW_IF_FAILED(PathAllocCombine(folderRoot.get(), test.CreateWithName, enableNonNormalized, &targetFolder));
// This ensures the folder is there and won't fail if it already exists (common when testing).
wil::CreateDirectoryDeep(targetFolder.get());
// Create a file in that folder with a non-normalized name (with the dot at the end).
wil::unique_hlocal_string extendedFilePath;
THROW_IF_FAILED(PathAllocCombine(targetFolder.get(), L"NonNormalized.", enableNonNormalized, &extendedFilePath));
wil::unique_hfile fileHandle(CreateFileW(extendedFilePath.get(), FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
THROW_LAST_ERROR_IF(!fileHandle);
};
for (auto const& test : tests)
{
// remove remnants from previous test that will cause failures
wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NormalizedName).get());
wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NonNormalizedName).get());
EnsureFolderWithNonCanonicalNameAndContentsExists(test);
auto deleteWithPath = test.CreatePathFunction(folderRoot.get(), test.DeleteWithName);
const auto hr = wil::RemoveDirectoryRecursiveNoThrow(deleteWithPath.get());
REQUIRE(test.ExpectedResult == hr);
}
}
#endif
// real paths to test
const wchar_t c_variablePath[] = L"%systemdrive%\\Windows\\System32\\Windows.Storage.dll";
const wchar_t c_expandedPath[] = L"c:\\Windows\\System32\\Windows.Storage.dll";
// // paths that should not exist on the system
const wchar_t c_missingVariable[] = L"%doesnotexist%\\doesnotexist.dll";
const wchar_t c_missingPath[] = L"c:\\Windows\\System32\\doesnotexist.dll";
const int c_stackBufferLimitTest = 5;
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("FileSystemTests::VerifyGetCurrentDirectory", "[filesystem]")
{
auto pwd = wil::GetCurrentDirectoryW();
REQUIRE(*pwd.get() != L'\0');
}
TEST_CASE("FileSystemTests::VerifyGetFullPathName", "[filesystem]")
{
PCWSTR fileName = L"ReadMe.txt";
auto result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, nullptr);
PCWSTR fileNameResult;
result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, &fileNameResult);
REQUIRE(wcscmp(fileName, fileNameResult) == 0);
auto result2 = wil::GetFullPathNameW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileName, &fileNameResult);
REQUIRE(wcscmp(fileName, fileNameResult) == 0);
REQUIRE(wcscmp(result.get(), result2.get()) == 0);
// The only negative test case I've found is a path > 32k.
std::wstring big(1024 * 32, L'a');
wil::unique_hstring output;
auto hr = wil::GetFullPathNameW(big.c_str(), output, nullptr);
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE));
}
TEST_CASE("FileSystemTests::VerifyGetFinalPathNameByHandle", "[filesystem]")
{
wil::unique_hfile fileHandle(CreateFileW(c_expandedPath, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr));
THROW_LAST_ERROR_IF(!fileHandle);
auto name = wil::GetFinalPathNameByHandleW(fileHandle.get());
auto name2 = wil::GetFinalPathNameByHandleW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileHandle.get());
REQUIRE(wcscmp(name.get(), name2.get()) == 0);
std::wstring path;
auto hr = wil::GetFinalPathNameByHandleW(nullptr, path);
REQUIRE(hr == E_HANDLE); // should be a usage error so be a fail fast.
// A more legitimate case is a non file handler like a drive volume.
wil::unique_hfile volumeHandle(CreateFileW(LR"(\\?\C:)", FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr));
THROW_LAST_ERROR_IF(!volumeHandle);
const auto hr2 = wil::GetFinalPathNameByHandleW(volumeHandle.get(), path);
REQUIRE(hr2 == HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
TEST_CASE("FileSystemTests::VerifyTrySearchPathW", "[filesystem]")
{
auto pathToTest = wil::TrySearchPathW(nullptr, c_expandedPath, nullptr);
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
pathToTest = wil::TrySearchPathW(nullptr, c_missingPath, nullptr);
REQUIRE(wil::string_get_not_null(pathToTest)[0] == L'\0');
}
#endif
// Simple test to expand an environmental string
TEST_CASE("FileSystemTests::VerifyExpandEnvironmentStringsW", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_variablePath, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
// This should effectively be a no-op
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_expandedPath, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
// Environment variable does not exist, but the call should still succeed
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_missingVariable, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_missingVariable, -1, TRUE) == CSTR_EQUAL);
}
TEST_CASE("FileSystemTests::VerifySearchPathW", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::SearchPathW(nullptr, c_expandedPath, nullptr, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::SearchPathW(nullptr, c_missingPath, nullptr, pathToTest));
}
TEST_CASE("FileSystemTests::VerifyExpandEnvAndSearchPath", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::ExpandEnvAndSearchPath(c_variablePath, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
// This test will exercise the case where AdaptFixedSizeToAllocatedResult will need to
// reallocate the initial buffer to fit the final string.
// This test is sufficient to test both wil::ExpandEnvironmentStringsW and wil::SeachPathW
REQUIRE_SUCCEEDED((wil::ExpandEnvAndSearchPath<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(c_variablePath, pathToTest)));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
pathToTest.reset();
REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::ExpandEnvAndSearchPath(c_missingVariable, pathToTest));
REQUIRE(pathToTest.get() == nullptr);
}
TEST_CASE("FileSystemTests::VerifyGetSystemDirectoryW", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::GetSystemDirectoryW(pathToTest));
// allocate based on the string that wil::GetSystemDirectoryW returned
size_t length = wcslen(pathToTest.get()) + 1;
auto trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
// Force AdaptFixed* to realloc. Test stack boundary with small initial buffer limit, c_stackBufferLimitTest
REQUIRE_SUCCEEDED((wil::GetSystemDirectoryW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(pathToTest)));
// allocate based on the string that wil::GetSystemDirectoryW returned
length = wcslen(pathToTest.get()) + 1;
trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
}
struct has_operator_pcwstr
{
PCWSTR value;
operator PCWSTR() const
{
return value;
}
};
struct has_operator_pwstr
{
PWSTR value;
operator PWSTR() const
{
return value;
}
};
#ifdef WIL_ENABLE_EXCEPTIONS
struct has_operator_wstr
{
std::wstring value;
operator const std::wstring&() const
{
return value;
}
};
#endif
TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]")
{
SECTION("Concat with multiple strings")
{
PCWSTR test1 = L"Test1";
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring test2 = L"Test2";
#else
PCWSTR test2 = L"Test2";
#endif
WCHAR test3[6] = L"Test3";
wil::unique_cotaskmem_string test4 = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"test4");
wil::unique_hstring test5 = wil::make_unique_string_nothrow<wil::unique_hstring>(L"test5");
has_operator_pcwstr test6{ L"Test6" };
WCHAR test7Buffer[] = L"Test7";
has_operator_pwstr test7{ test7Buffer };
#ifdef WIL_ENABLE_EXCEPTIONS
has_operator_wstr test8{ L"Test8" };
#else
PCWSTR test8 = L"Test8";
#endif
PCWSTR expectedStr = L"Test1Test2Test3Test4Test5Test6Test7Test8";
#ifdef WIL_ENABLE_EXCEPTIONS
auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8);
REQUIRE(CompareStringOrdinal(combinedString.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
#endif
wil::unique_cotaskmem_string combinedStringNT;
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1, test2, test3, test4, test5, test6, test7, test8));
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8);
REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
}
SECTION("Concat with single string")
{
PCWSTR test1 = L"Test1";
#ifdef WIL_ENABLE_EXCEPTIONS
auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1);
REQUIRE(CompareStringOrdinal(combinedString.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
#endif
wil::unique_cotaskmem_string combinedStringNT;
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1));
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1);
REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
}
SECTION("Concat with existing string")
{
std::wstring test2 = L"Test2";
WCHAR test3[6] = L"Test3";
PCWSTR expectedStr = L"Test1Test2Test3";
wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"Test1");
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test2.c_str(), test3));
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
}
}
TEST_CASE("FileSystemTests::VerifyStrPrintf", "[filesystem]")
{
#ifdef WIL_ENABLE_EXCEPTIONS
auto formattedString = wil::str_printf<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
REQUIRE(CompareStringOrdinal(formattedString.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
#endif
wil::unique_cotaskmem_string formattedStringNT;
REQUIRE_SUCCEEDED(wil::str_printf_nothrow(formattedStringNT, L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28));
REQUIRE(CompareStringOrdinal(formattedStringNT.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
auto formattedStringFF = wil::str_printf_failfast<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
REQUIRE(CompareStringOrdinal(formattedStringFF.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
}
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]")
{
wil::unique_cotaskmem_string path;
REQUIRE_SUCCEEDED(wil::GetModuleFileNameW(nullptr, path));
auto len = wcslen(path.get());
REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
// Call again, but force multiple retries through a small initial buffer
wil::unique_cotaskmem_string path2;
REQUIRE_SUCCEEDED((wil::GetModuleFileNameW<wil::unique_cotaskmem_string, 4>(nullptr, path2)));
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
}
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
{
wil::unique_cotaskmem_string path;
REQUIRE_SUCCEEDED(wil::GetModuleFileNameExW(nullptr, nullptr, path));
auto len = wcslen(path.get());
REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
// Call again, but force multiple retries through a small initial buffer
wil::unique_cotaskmem_string path2;
REQUIRE_SUCCEEDED((wil::GetModuleFileNameExW<wil::unique_cotaskmem_string, 4>(nullptr, nullptr, path2)));
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

145
tests/MallocSpy.h Normal file
Просмотреть файл

@ -0,0 +1,145 @@
#pragma once
#include "catch.hpp"
#include <objbase.h>
#include <wil/wistd_functional.h>
#include <wrl/implements.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
// IMallocSpy requires you to implement all methods, but we often only want one or two...
struct MallocSpy : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IMallocSpy>
{
wistd::function<SIZE_T(SIZE_T)> PreAllocCallback;
virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T requestSize) override
{
if (PreAllocCallback)
{
return PreAllocCallback(requestSize);
}
return requestSize;
}
wistd::function<void*(void*)> PostAllocCallback;
virtual void* STDMETHODCALLTYPE PostAlloc(void* ptr) override
{
if (PostAllocCallback)
{
return PostAllocCallback(ptr);
}
return ptr;
}
wistd::function<void*(void*)> PreFreeCallback;
virtual void* STDMETHODCALLTYPE PreFree(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreFreeCallback)
{
return PreFreeCallback(ptr);
}
return ptr;
}
virtual void STDMETHODCALLTYPE PostFree(BOOL /*wasSpyed*/) override
{
}
wistd::function<SIZE_T(void*, SIZE_T, void**)> PreReallocCallback;
virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void* ptr, SIZE_T requestSize, void** newPtr, BOOL wasSpyed) override
{
*newPtr = ptr;
if (wasSpyed && PreReallocCallback)
{
return PreReallocCallback(ptr, requestSize, newPtr);
}
return requestSize;
}
wistd::function<void*(void*)> PostReallocCallback;
virtual void* STDMETHODCALLTYPE PostRealloc(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PostReallocCallback)
{
return PostReallocCallback(ptr);
}
return ptr;
}
wistd::function<void*(void*)> PreGetSizeCallback;
virtual void* STDMETHODCALLTYPE PreGetSize(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreGetSizeCallback)
{
return PreGetSizeCallback(ptr);
}
return ptr;
}
wistd::function<SIZE_T(SIZE_T)> PostGetSizeCallback;
virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T size, BOOL wasSpyed) override
{
if (wasSpyed && PostGetSizeCallback)
{
return PostGetSizeCallback(size);
}
return size;
}
wistd::function<void*(void*)> PreDidAllocCallback;
virtual void* STDMETHODCALLTYPE PreDidAlloc(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreDidAllocCallback)
{
return PreDidAllocCallback(ptr);
}
return ptr;
}
virtual int STDMETHODCALLTYPE PostDidAlloc(void* /*ptr*/, BOOL /*wasSpyed*/, int result) override
{
return result;
}
virtual void STDMETHODCALLTYPE PreHeapMinimize() override
{
}
virtual void STDMETHODCALLTYPE PostHeapMinimize() override
{
}
};
Microsoft::WRL::ComPtr<MallocSpy> MakeSecureDeleterMallocSpy()
{
using namespace Microsoft::WRL;
auto result = Make<MallocSpy>();
REQUIRE(result);
result->PreFreeCallback = [](void* ptr)
{
ComPtr<IMalloc> malloc;
if (SUCCEEDED(::CoGetMalloc(1, &malloc)))
{
auto size = malloc->GetSize(ptr);
auto buffer = static_cast<byte*>(ptr);
for (size_t i = 0; i < size; ++i)
{
REQUIRE(buffer[i] == 0);
}
}
return ptr;
};
return result;
}
#endif

598
tests/ResourceTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,598 @@
// Included first and then again later to ensure that we're able to "light up" new functionality based off new includes
#include <wil/resource.h>
// Headers to "light up" functionality in resource.h
#include <memory>
#include <roapi.h>
#include <winstring.h>
#include <wil/com.h>
#include <wil/resource.h>
#include <wil/stl.h>
#include <wrl/implements.h>
#include "common.h"
TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]")
{
int count = 0;
auto validate = [&](int expected) { REQUIRE(count == expected); count = 0; };
{
auto foo = wil::scope_exit([&] { count++; });
}
validate(1);
{
auto foo = wil::scope_exit([&] { count++; });
foo.release();
foo.reset();
}
validate(0);
{
auto foo = wil::scope_exit([&] { count++; });
foo.reset();
foo.reset();
validate(1);
}
validate(0);
#ifdef WIL_ENABLE_EXCEPTIONS
{
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
}
validate(1);
{
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
foo.release();
foo.reset();
}
validate(0);
{
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
foo.reset();
foo.reset();
validate(1);
}
validate(0);
#endif // WIL_ENABLE_EXCEPTIONS
}
interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00"))
ITest : public IUnknown
{
STDMETHOD_(void, Test)() = 0;
};
class PointerTestObject : witest::AllocatedObject,
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, ITest>
{
public:
STDMETHOD_(void, Test)() {};
};
TEST_CASE("ResourceTests::TestOperationsOnGenericSmartPointerClasses", "[resource]")
{
#ifdef WIL_ENABLE_EXCEPTIONS
{
// wil::unique_any_t example
wil::unique_event ptr2(wil::EventOptions::ManualReset);
// wil::com_ptr
wil::com_ptr<PointerTestObject> ptr3 = Microsoft::WRL::Make<PointerTestObject>();
// wil::shared_any_t example
wil::shared_event ptr4(wil::EventOptions::ManualReset);
// wistd::unique_ptr example
auto ptr5 = wil::make_unique_failfast<POINT>();
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr2)>::pointer, HANDLE>::value, "type-mismatch");
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr3)>::pointer, PointerTestObject*>::value, "type-mismatch");
auto p2 = wil::detach_from_smart_pointer(ptr2);
auto p3 = wil::detach_from_smart_pointer(ptr3);
// auto p4 = wil::detach_from_smart_pointer(ptr4); // wil::shared_any_t and std::shared_ptr do not support release().
HANDLE p4{};
auto p5 = wil::detach_from_smart_pointer(ptr5);
REQUIRE((!ptr2 && !ptr3));
REQUIRE((p2 && p3));
wil::attach_to_smart_pointer(ptr2, p2);
wil::attach_to_smart_pointer(ptr3, p3);
wil::attach_to_smart_pointer(ptr4, p4);
wil::attach_to_smart_pointer(ptr5, p5);
p2 = nullptr;
p3 = nullptr;
p4 = nullptr;
p5 = nullptr;
wil::detach_to_opt_param(&p2, ptr2);
wil::detach_to_opt_param(&p3, ptr3);
REQUIRE((!ptr2 && !ptr3));
REQUIRE((p2 && p3));
wil::attach_to_smart_pointer(ptr2, p2);
wil::attach_to_smart_pointer(ptr3, p3);
p2 = nullptr;
p3 = nullptr;
wil::detach_to_opt_param(&p2, ptr2);
wil::detach_to_opt_param(&p3, ptr3);
REQUIRE((!ptr2 && !ptr3));
REQUIRE((p2 && p3));
[&](decltype(p2)* ptr) { *ptr = p2; } (wil::out_param(ptr2));
[&](decltype(p3)* ptr) { *ptr = p3; } (wil::out_param(ptr3));
[&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4));
[&](decltype(p5)* ptr) { *ptr = p5; } (wil::out_param(ptr5));
REQUIRE((ptr2 && ptr3));
// Validate R-Value compilation
wil::detach_to_opt_param(&p2, decltype(ptr2){});
wil::detach_to_opt_param(&p3, decltype(ptr3){});
}
#endif
std::unique_ptr<int> ptr1(new int(1));
Microsoft::WRL::ComPtr<PointerTestObject> ptr4 = Microsoft::WRL::Make<PointerTestObject>();
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr1)>::pointer, int*>::value, "type-mismatch");
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr4)>::pointer, PointerTestObject*>::value, "type-mismatch");
auto p1 = wil::detach_from_smart_pointer(ptr1);
auto p4 = wil::detach_from_smart_pointer(ptr4);
REQUIRE((!ptr1 && !ptr4));
REQUIRE((p1 && p4));
wil::attach_to_smart_pointer(ptr1, p1);
wil::attach_to_smart_pointer(ptr4, p4);
REQUIRE((ptr1 && ptr4));
p1 = nullptr;
p4 = nullptr;
int** pNull = nullptr;
wil::detach_to_opt_param(pNull, ptr1);
REQUIRE(ptr1);
wil::detach_to_opt_param(&p1, ptr1);
wil::detach_to_opt_param(&p4, ptr4);
REQUIRE((!ptr1 && !ptr4));
REQUIRE((p1 && p4));
[&](decltype(p1)* ptr) { *ptr = p1; } (wil::out_param(ptr1));
[&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4));
REQUIRE((ptr1 && ptr4));
p1 = wil::detach_from_smart_pointer(ptr1);
[&](int** ptr) { *ptr = p1; } (wil::out_param_ptr<int **>(ptr1));
REQUIRE(ptr1);
}
// Compilation only test...
void StlAdlTest()
{
// This test has exposed some Argument Dependent Lookup issues in wistd / stl. Primarily we're
// just looking for clean compilation.
std::vector<wistd::unique_ptr<int>> v;
v.emplace_back(new int{ 1 });
v.emplace_back(new int{ 2 });
v.emplace_back(new int{ 3 });
std::rotate(begin(v), begin(v) + 1, end(v));
REQUIRE(*v[0] == 1);
REQUIRE(*v[1] == 3);
REQUIRE(*v[2] == 2);
decltype(v) v2;
v2 = std::move(v);
REQUIRE(*v2[0] == 1);
REQUIRE(*v2[1] == 3);
REQUIRE(*v2[2] == 2);
decltype(v) v3;
std::swap(v2, v3);
REQUIRE(*v3[0] == 1);
REQUIRE(*v3[1] == 3);
REQUIRE(*v3[2] == 2);
}
// Compilation only test...
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
void UniqueProcessInfo()
{
wil::unique_process_information process;
CreateProcessW(nullptr, nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &process);
ResumeThread(process.hThread);
WaitForSingleObject(process.hProcess, INFINITE);
wil::unique_process_information other(wistd::move(process));
}
#endif
struct FakeComInterface
{
void AddRef()
{
refs++;
}
void Release()
{
refs--;
}
HRESULT __stdcall Close()
{
closes++;
return S_OK;
}
size_t refs = 0;
size_t closes = 0;
bool called()
{
auto old = closes;
closes = 0;
return (old > 0);
}
bool has_ref()
{
return (refs > 0);
}
};
static void __stdcall CloseFakeComInterface(FakeComInterface* fake)
{
fake->Close();
}
using unique_fakeclose_call = wil::unique_com_call<FakeComInterface, decltype(&CloseFakeComInterface), CloseFakeComInterface>;
TEST_CASE("ResourceTests::VerifyUniqueComCall", "[resource][unique_com_call]")
{
unique_fakeclose_call call1;
unique_fakeclose_call call2;
// intentional compilation errors
// unique_fakeclose_call call3 = call1;
// call2 = call1;
FakeComInterface fake1;
unique_fakeclose_call call4(&fake1);
REQUIRE(fake1.has_ref());
unique_fakeclose_call call5(wistd::move(call4));
REQUIRE(!call4);
REQUIRE(call5);
REQUIRE(fake1.has_ref());
call4 = wistd::move(call5);
REQUIRE(call4);
REQUIRE(!call5);
REQUIRE(fake1.has_ref());
REQUIRE(!fake1.called());
FakeComInterface fake2;
{
unique_fakeclose_call scoped(&fake2);
}
REQUIRE(!fake2.has_ref());
REQUIRE(fake2.called());
call4.reset(&fake2);
REQUIRE(fake1.called());
REQUIRE(!fake1.has_ref());
call4.reset();
REQUIRE(!fake2.has_ref());
REQUIRE(fake2.called());
call1.reset(&fake1);
call2.swap(call1);
REQUIRE((call2 && !call1));
call2.release();
REQUIRE(!fake1.called());
REQUIRE(!fake1.has_ref());
REQUIRE(!call2);
REQUIRE(*call1.addressof() == nullptr);
call1.reset(&fake1);
fake2.closes = 0;
fake2.refs = 1;
*(&call1) = &fake2;
REQUIRE(!fake1.has_ref());
REQUIRE(fake1.called());
REQUIRE(fake2.has_ref());
call1.reset();
REQUIRE(!fake2.has_ref());
REQUIRE(fake2.called());
}
static bool g_called = false;
static bool called()
{
auto call = g_called;
g_called = false;
return (call);
}
static void __stdcall FakeCall()
{
g_called = true;
}
using unique_fake_call = wil::unique_call<decltype(&FakeCall), FakeCall>;
TEST_CASE("ResourceTests::VerifyUniqueCall", "[resource][unique_call]")
{
unique_fake_call call1;
unique_fake_call call2;
// intentional compilation errors
// unique_fake_call call3 = call1;
// call2 = call1;
unique_fake_call call4;
REQUIRE(!called());
unique_fake_call call5(wistd::move(call4));
REQUIRE(!call4);
REQUIRE(call5);
call4 = wistd::move(call5);
REQUIRE(call4);
REQUIRE(!call5);
REQUIRE(!called());
{
unique_fake_call scoped;
}
REQUIRE(called());
call4.reset();
REQUIRE(called());
call4.reset();
REQUIRE(!called());
call1.release();
REQUIRE((!call1 && call2));
call2.swap(call1);
REQUIRE((call1 && !call2));
call2.release();
REQUIRE(!called());
REQUIRE(!call2);
#ifdef __WIL__ROAPI_H_APPEXCEPTIONAL
{
auto call = wil::RoInitialize();
}
#endif
#ifdef __WIL__ROAPI_H_APP
{
wil::unique_rouninitialize_call uninit;
uninit.release();
auto call = wil::RoInitialize_failfast();
}
#endif
#ifdef __WIL__COMBASEAPI_H_APPEXCEPTIONAL
{
auto call = wil::CoInitializeEx();
}
#endif
#ifdef __WIL__COMBASEAPI_H_APP
{
wil::unique_couninitialize_call uninit;
uninit.release();
auto call = wil::CoInitializeEx_failfast();
}
#endif
}
void UniqueCallCompilationTest()
{
#ifdef __WIL__COMBASEAPI_H_EXCEPTIONAL
{
auto call = wil::CoImpersonateClient();
}
#endif
#ifdef __WIL__COMBASEAPI_H_
{
wil::unique_coreverttoself_call uninit;
uninit.release();
auto call = wil::CoImpersonateClient_failfast();
}
#endif
}
template<typename StringType, typename VerifyContents>
static void TestStringMaker(VerifyContents&& verifyContents)
{
PCWSTR values[] =
{
L"",
L"value",
// 300 chars
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
};
for (const auto& value : values)
{
auto const valueLength = wcslen(value);
// Direct construction case.
wil::details::string_maker<StringType> maker;
THROW_IF_FAILED(maker.make(value, valueLength));
auto result = maker.release();
verifyContents(value, valueLength, result);
// Two phase construction case.
THROW_IF_FAILED(maker.make(nullptr, valueLength));
REQUIRE(maker.buffer() != nullptr);
// In the case of the wil::unique_hstring and the empty string the buffer is in a read only
// section and can't be written to, so StringCchCopy(maker.buffer(), valueLength + 1, value) will fault adding the nul terminator.
// Use memcpy_s specifying exact size that will be zero in this case instead.
memcpy_s(maker.buffer(), valueLength * sizeof(*value), value, valueLength * sizeof(*value));
result = maker.release();
verifyContents(value, valueLength, result);
{
// no promote, ensure no leaks (not tested here, inspect in the debugger)
wil::details::string_maker<StringType> maker2;
THROW_IF_FAILED(maker2.make(value, valueLength));
}
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
template <typename StringType>
static void VerifyMakeUniqueString(bool nullValueSupported = true)
{
if (nullValueSupported)
{
auto value0 = wil::make_unique_string<StringType>(nullptr, 5);
}
struct
{
PCWSTR expectedValue;
PCWSTR testValue;
// this is an optional parameter
size_t testLength = static_cast<size_t>(-1);
}
const testCaseEntries[] =
{
{ L"value", L"value", 5 },
{ L"value", L"value" },
{ L"va", L"va\0ue", 5 },
{ L"v", L"value", 1 },
{ L"\0", L"", 5 },
{ L"\0", nullptr, 5 },
};
using maker = wil::details::string_maker<StringType>;
for (auto const &entry : testCaseEntries)
{
bool shouldSkipNullString = ((wcscmp(entry.expectedValue, L"\0") == 0) && !nullValueSupported);
if (!shouldSkipNullString)
{
auto desiredValue = wil::make_unique_string<StringType>(entry.expectedValue);
auto stringValue = wil::make_unique_string<StringType>(entry.testValue, entry.testLength);
auto stringValueNoThrow = wil::make_unique_string_nothrow<StringType>(entry.testValue, entry.testLength);
auto stringValueFailFast = wil::make_unique_string_failfast<StringType>(entry.testValue, entry.testLength);
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValue)) == 0);
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueNoThrow)) == 0);
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueFailFast)) == 0);
}
}
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerCoTaskMem", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_cotaskmem_string>();
TestStringMaker<wil::unique_cotaskmem_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_cotaskmem_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerLocalAlloc", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_hlocal_string>();
TestStringMaker<wil::unique_hlocal_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_hlocal_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerGlobalAlloc", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_hglobal_string>();
TestStringMaker<wil::unique_hglobal_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_hglobal_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerProcessHeap", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_process_heap_string>();
TestStringMaker<wil::unique_process_heap_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_process_heap_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
#endif
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerHString", "[resource][string_maker]")
{
wil::unique_hstring value;
value.reset(static_cast<HSTRING>(nullptr));
VerifyMakeUniqueString<wil::unique_hstring>(false);
TestStringMaker<wil::unique_hstring>(
[](PCWSTR value, size_t valueLength, const wil::unique_hstring& result)
{
UINT32 length;
REQUIRE(wcscmp(value, WindowsGetStringRawBuffer(result.get(), &length)) == 0);
REQUIRE(valueLength == length);
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerStdWString", "[resource][string_maker]")
{
std::string s;
wil::details::string_maker<std::wstring> maker;
TestStringMaker<std::wstring>(
[](PCWSTR value, size_t valueLength, const std::wstring& result)
{
REQUIRE(wcscmp(value, result.c_str()) == 0);
REQUIRE(result == value);
REQUIRE(result.size() == valueLength);
});
}
#endif
TEST_CASE("UniqueStringAndStringMakerTests::VerifyLegacySTringMakers", "[resource][string_maker]")
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
auto l = wil::make_hlocal_string(L"value");
l = wil::make_hlocal_string_nothrow(L"value");
l = wil::make_hlocal_string_failfast(L"value");
auto p = wil::make_process_heap_string(L"value");
p = wil::make_process_heap_string_nothrow(L"value");
p = wil::make_process_heap_string_failfast(L"value");
#endif
auto c = wil::make_cotaskmem_string(L"value");
c = wil::make_cotaskmem_string_nothrow(L"value");
c = wil::make_cotaskmem_string_failfast(L"value");
}
#endif

574
tests/ResultTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,574 @@
#include <roerrorapi.h>
#include <wil/com.h>
#include <wil/result.h>
#include <wil/result_originate.h>
#include "common.h"
static volatile long objectCount = 0;
struct SharedObject
{
SharedObject()
{
::InterlockedIncrement(&objectCount);
}
~SharedObject()
{
::InterlockedDecrement(&objectCount);
}
void ProcessShutdown()
{
}
int value;
};
TEST_CASE("ResultTests::SemaphoreValue", "[result]")
{
auto TestValue = [&](auto start, auto end)
{
wil::details_abi::SemaphoreValue semaphore;
for (auto index = start; index <= end; index++)
{
semaphore.Destroy();
REQUIRE(SUCCEEDED(semaphore.CreateFromValue(L"test", index)));
auto num1 = index;
auto num2 = index;
REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num1)));
REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num2)));
REQUIRE(num1 == index);
REQUIRE(num2 == index);
}
};
// Test 32-bit values (edge cases)
TestValue(0u, 10u);
TestValue(250u, 260u);
TestValue(0x7FFFFFF0u, 0x7FFFFFFFu);
// Test 64-bit values (edge cases)
TestValue(0ull, 10ull);
TestValue(250ull, 260ull);
TestValue(0x000000007FFFFFF0ull, 0x000000008000000Full);
TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
TestValue(0x3FFFFFFFFFFFFFF0ull, 0x3FFFFFFFFFFFFFFFull);
// Test pointer values
wil::details_abi::SemaphoreValue semaphore;
void* address = &semaphore;
REQUIRE(SUCCEEDED(semaphore.CreateFromPointer(L"test", address)));
void* ptr;
REQUIRE(SUCCEEDED(semaphore.TryGetPointer(L"test", &ptr)));
REQUIRE(ptr == address);
}
TEST_CASE("ResultTests::ProcessLocalStorage", "[result]")
{
// Test process local storage memory and ref-counting
{
wil::details_abi::ProcessLocalStorage<SharedObject> obj1("ver1");
wil::details_abi::ProcessLocalStorage<SharedObject> obj2("ver1");
auto& o1 = *obj1.GetShared();
auto& o2 = *obj2.GetShared();
REQUIRE(o1.value == 0);
REQUIRE(o2.value == 0);
o1.value = 42;
REQUIRE(o2.value == 42);
REQUIRE(objectCount == 1);
wil::details_abi::ProcessLocalStorage<SharedObject> obj3("ver3");
auto& o3 = *obj3.GetShared();
REQUIRE(o3.value == 0);
REQUIRE(objectCount == 2);
}
REQUIRE(objectCount == 0);
}
#ifdef WIL_ENABLE_EXCEPTIONS
#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code
TEST_CASE("ResultTests::ExceptionHandling", "[result]")
{
witest::TestFailureCache failures;
SECTION("Test 'what()' implementation on ResultException")
{
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
try
{
THROW_HR(E_INVALIDARG);
FAIL("Expected an exception");
}
catch (const std::exception& exception)
{
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_INVALIDARG);
auto what = exception.what();
REQUIRE((what && *what));
REQUIRE(strstr(what, "Exception") != nullptr);
}
}
failures.clear();
SECTION("Test messaging from an unhandled std exception")
{
// #pragma warning(suppress: 28931) // unused assignment -- it IS being used... seems like a tool issue.
auto hr = []()
{
try
{
throw std::runtime_error("runtime");
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
}();
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
REQUIRE(wcsstr(failures[0].pszMessage, L"runtime") != nullptr); // should get the exception what() string...
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
}
failures.clear();
SECTION("Test messaging from bad_alloc")
{
auto hr = []() -> HRESULT
{
try
{
throw std::bad_alloc();
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
}();
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Test messaging from a WIL exception")
{
auto hr = []() -> HRESULT
{
try
{
THROW_HR(E_INVALIDARG);
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
return S_OK;
}();
REQUIRE(failures.size() == 2);
REQUIRE(failures[0].hr == E_INVALIDARG);
REQUIRE(failures[0].pszMessage == nullptr);
REQUIRE(failures[1].hr == E_INVALIDARG);
REQUIRE(wcsstr(failures[1].pszMessage, L"Exception") != nullptr); // should get the exception debug string...
REQUIRE(hr == E_INVALIDARG);
}
failures.clear();
SECTION("Fail fast an unknown exception")
{
REQUIRE(witest::DoesCodeCrash([]()
{
try
{
throw E_INVALIDARG; // bad throw... (long)
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
}));
}
failures.clear();
SECTION("Log test (returns hr)")
{
HRESULT hr = S_OK;
try
{
throw std::bad_alloc();
}
catch (...)
{
hr = LOG_CAUGHT_EXCEPTION();
auto hrDirect = wil::ResultFromCaughtException();
REQUIRE(hr == hrDirect);
}
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Fail-fast test")
{
REQUIRE_CRASH([]()
{
try
{
throw std::bad_alloc();
}
catch (...)
{
FAIL_FAST_CAUGHT_EXCEPTION();
}
}());
}
failures.clear();
SECTION("Exception test (different exception type thrown...)")
{
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
size_t line = 0;
try
{
try
{
throw std::bad_alloc();
}
catch (...)
{
line = __LINE__; THROW_NORMALIZED_CAUGHT_EXCEPTION();
}
}
catch (const wil::ResultException& exception)
{
REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have thrown new, so we should have the rethrow line number
REQUIRE(exception.GetErrorCode() == E_OUTOFMEMORY);
}
catch (...)
{
FAIL();
}
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
}
failures.clear();
SECTION("Exception test (rethrow same exception type...)")
{
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
size_t line = 0;
try
{
try
{
line = __LINE__; THROW_HR(E_OUTOFMEMORY);
}
catch (...)
{
THROW_NORMALIZED_CAUGHT_EXCEPTION();
}
}
catch (const wil::ResultException& exception)
{
REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have re-thrown the original exception (with the original line number)
}
catch (...)
{
FAIL();
}
}
failures.clear();
SECTION("Test catch message")
{
try
{
throw std::bad_alloc();
}
catch (...)
{
LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42);
}
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
REQUIRE(wcsstr(failures[0].pszMessage, L"train") != nullptr); // should *also* get the message...
REQUIRE(wcsstr(failures[0].pszMessage, L"42") != nullptr);
}
failures.clear();
SECTION("Test messaging from a WIL exception")
{
auto hr = []() -> HRESULT
{
try
{
throw std::bad_alloc();
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION_EXPECTED();
}
}();
REQUIRE(failures.size() == 0);
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Test ResultFromException...")
{
auto hrOk = wil::ResultFromException([&]
{
});
REQUIRE(hrOk == S_OK);
auto hr = wil::ResultFromException([&]
{
throw std::bad_alloc();
});
REQUIRE(failures.size() == 0);
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Explicit failfast for unrecognized")
{
REQUIRE_CRASH(wil::ResultFromException([&]
{
throw E_FAIL;
}));
}
failures.clear();
SECTION("Manual debug-only validation of the SEH failfast")
{
auto hr1 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]()
{
// Uncomment to test SEH fail-fast
// throw E_FAIL;
});
REQUIRE(hr1 == S_OK);
auto hr2 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::Thrown, [&]
{
// Uncomment to test SEH fail-fast
// throw std::range_error("range");
});
REQUIRE(hr2 == S_OK);
wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
{
// Uncomment to test SEH fail-fast
// THROW_HR(E_FAIL);
});
}
failures.clear();
SECTION("Standard")
{
auto line = __LINE__; auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
{
THROW_HR(E_INVALIDARG);
});
REQUIRE(failures.size() == 2);
REQUIRE(static_cast<decltype(line)>(failures[1].uLineNumber) == line);
REQUIRE(hr == E_INVALIDARG);
}
failures.clear();
SECTION("bad_alloc")
{
auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
{
throw std::bad_alloc();
});
REQUIRE(failures.size() == 1);
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("std::exception")
{
auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
{
throw std::range_error("range");
});
REQUIRE(failures.size() == 1);
REQUIRE(wcsstr(failures[0].pszMessage, L"range") != nullptr);
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
}
}
void ExceptionHandlingCompilationTest()
{
[]{ try { throw std::bad_alloc(); } CATCH_RETURN(); }();
[]{ try { throw std::bad_alloc(); } CATCH_RETURN_MSG("train: %d", 42); }();
[]{ try { throw std::bad_alloc(); } CATCH_RETURN_EXPECTED(); }();
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION(); } }();
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_MSG("train: %d", 42); } }();
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }();
try { throw std::bad_alloc(); } CATCH_LOG();
try { throw std::bad_alloc(); } CATCH_LOG_MSG("train: %d", 42);
try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION(); }
try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
try { throw std::bad_alloc(); } CATCH_FAIL_FAST();
try { throw std::bad_alloc(); } CATCH_FAIL_FAST_MSG("train: %d", 42);
try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); }
try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED(); } catch (...) {}
try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED_MSG("train: %d", 42); } catch (...) {}
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
{
THROW_HR(E_FAIL);
});
hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
{
});
hr = wil::ResultFromException([&]
{
});
wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
{
});
}
#pragma warning(pop)
#endif
TEST_CASE("ResultTests::ErrorMacros", "[result]")
{
REQUIRE_ERROR(FAIL_FAST());
REQUIRE_ERROR(FAIL_FAST_IF(true));
REQUIRE_ERROR(FAIL_FAST_IF_NULL(nullptr));
REQUIRE_NOERROR(FAIL_FAST_IF(false));
REQUIRE_NOERROR(FAIL_FAST_IF_NULL(_ReturnAddress()));
REQUIRE_ERROR(FAIL_FAST_MSG("%d", 42));
REQUIRE_ERROR(FAIL_FAST_IF_MSG(true, "%d", 42));
REQUIRE_ERROR(FAIL_FAST_IF_NULL_MSG(nullptr, "%d", 42));
REQUIRE_NOERROR(FAIL_FAST_IF_MSG(false, "%d", 42));
REQUIRE_NOERROR(FAIL_FAST_IF_NULL_MSG(_ReturnAddress(), "%d", 42));
//wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;
SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
REQUIRE_ERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(FALSE));
REQUIRE_NOERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(TRUE));
}
// The originate helper isn't compatible with CX so don't test it in that mode.
#ifndef __cplusplus_winrt
TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
{
::wil::SetOriginateErrorCallback(nullptr);
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
// We can't guarantee test order, so clear the error payload prior to starting
SetRestrictedErrorInfo(nullptr);
[]() -> HRESULT
{
RETURN_HR(S_OK);
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
#ifdef WIL_ENABLE_EXCEPTIONS
try
{
THROW_HR(E_FAIL);
}
catch (...) {}
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
#endif // WIL_ENABLE_EXCEPTIONS
[]() -> HRESULT
{
RETURN_HR(E_FAIL);
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
[]() -> HRESULT
{
RETURN_IF_FAILED_EXPECTED(E_ACCESSDENIED);
return S_OK;
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
}
TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
{
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
// Make sure we don't start with an error payload
SetRestrictedErrorInfo(nullptr);
// Success codes shouldn't originate.
[]()
{
RETURN_HR(S_OK);
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
auto validateOriginatedError = [&](HRESULT hrExpected)
{
wil::unique_bstr descriptionUnused;
HRESULT existingHr = S_OK;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
REQUIRE_SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused));
REQUIRE(hrExpected == existingHr);
};
#ifdef WIL_ENABLE_EXCEPTIONS
// Throwing an error should originate.
constexpr HRESULT thrownErrorCode = TYPE_E_ELEMENTNOTFOUND;
try
{
THROW_HR(thrownErrorCode);
}
catch (...) {}
REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
validateOriginatedError(thrownErrorCode);
#endif // WIL_ENABLE_EXCEPTIONS
// Returning an error code should originate.
static constexpr HRESULT returnedErrorCode = REGDB_E_CLASSNOTREG;
[]()
{
RETURN_HR(returnedErrorCode);
}();
REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
validateOriginatedError(returnedErrorCode);
// _EXPECTED errors should NOT originate.
static constexpr HRESULT expectedErrorCode = E_ACCESSDENIED;
[]()
{
RETURN_IF_FAILED_EXPECTED(expectedErrorCode);
return S_OK;
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
}
#endif // __cplusplus_winrt

571
tests/SafeCastTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,571 @@
#include "common.h"
#include <wil/safecast.h>
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("SafeCastTests::SafeCastThrowsTemplateCheck", "[safecast]")
{
// In all cases, a value of '1' should be cast-able to any signed or unsigned integral type without error
SECTION("Unqualified char")
{
char orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
// wil::safe_cast<unsigned char> (orig); // No available conversion in intsafe
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
// wil::safe_cast<unsigned short> (orig); // No available conversion in intsafe
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
// wil::safe_cast<unsigned int> (orig); // No available conversion in intsafe
wil::safe_cast<long> (orig);
// wil::safe_cast<unsigned long> (orig); // No available conversion in intsafe
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
// wil::safe_cast<unsigned __int64> (orig); // No available conversion in intsafe
wil::safe_cast<__int3264> (orig);
// wil::safe_cast<unsigned __int3264>(orig); // No available conversion in intsafe
// wil::safe_cast<wchar_t> (orig); // No available conversion in intsafe
}
SECTION("Signed char")
{
signed char orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned char")
{
unsigned char orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified short")
{
short orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Signed short")
{
signed short orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned short")
{
unsigned short orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified int")
{
int orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Signed int")
{
signed int orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned int")
{
unsigned int orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified long")
{
long orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned log")
{
unsigned long orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified int64")
{
__int64 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Signed int64")
{
signed __int64 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned int64")
{
unsigned __int64 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified int3264")
{
__int3264 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned int3264")
{
unsigned __int3264 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("wchar_t")
{
wchar_t orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
}
#endif
TEST_CASE("SafeCastTests::SafeCastFailFastSyntaxCheck", "[safecast]")
{
SECTION("safe_cast_failfast safe")
{
INT i = INT_MAX;
LONG l = wil::safe_cast_failfast<LONG>(i);
REQUIRE(l == INT_MAX);
}
SECTION("safe_cast_failfast unsafe")
{
INT i = 0;
SHORT s = wil::safe_cast_failfast<SHORT>(i);
REQUIRE(s == 0);
}
SECTION("safe_cast_failfast unsafe to wchar_t")
{
INT i = 0;
wchar_t wc = wil::safe_cast_failfast<wchar_t>(i);
REQUIRE(wc == 0);
}
SECTION("safe_cast_failfast unsafe from wchar_t")
{
wchar_t wc = 0;
unsigned char uc = wil::safe_cast_failfast<unsigned char>(wc);
REQUIRE(uc == 0);
}
}
TEST_CASE("SafeCastTests::SafeCastNoThrowSyntaxCheck", "[safecast]")
{
SECTION("safe_cast_nothrow safe")
{
INT i = INT_MAX;
LONG l = wil::safe_cast_nothrow<LONG>(i);
REQUIRE(l == INT_MAX);
}
// safe_cast_nothrow one parameter unsafe, throws compiler error as expected
// {
// __int64 i64 = 0;
// int i = wil::safe_cast_nothrow<int>(i64);
// }
SECTION("safe_cast_nothrow two parameter potentially unsafe due to usage of variable sized types")
{
SIZE_T st = 0;
UINT ui;
auto result = wil::safe_cast_nothrow<UINT>(st, &ui);
REQUIRE_SUCCEEDED(result);
REQUIRE(ui == 0);
}
// safe_cast_nothrow two parameter known safe, throws compiler error as expected
// {
// unsigned char uc = 0;
// unsigned short us;
// auto result = wil::safe_cast_nothrow(uc, &us);
// }
SECTION("safe_cast_nothrow unsafe")
{
INT i = 0;
SHORT s;
auto result = wil::safe_cast_nothrow<SHORT>(i, &s);
REQUIRE_SUCCEEDED(result);
REQUIRE(s == 0);
}
SECTION("safe_cast_nothrow unsafe to wchar_t")
{
INT i = 0;
wchar_t wc;
auto result = wil::safe_cast_nothrow<wchar_t>(i, &wc);
REQUIRE_SUCCEEDED(result);
REQUIRE(wc == 0);
}
SECTION("safe_cast_nothrow unsafe from wchar_t")
{
wchar_t wc = 0;
unsigned char uc;
auto result = wil::safe_cast_nothrow<unsigned char>(wc, &uc);
REQUIRE_SUCCEEDED(result);
REQUIRE(uc == 0);
}
}
TEST_CASE("SafeCastTests::SafeCastNoFailures", "[safecast]")
{
SECTION("INT -> LONG")
{
INT i = INT_MAX;
LONG l = wil::safe_cast_nothrow<LONG>(i);
REQUIRE(l == INT_MAX);
}
SECTION("LONG -> INT")
{
LONG l = LONG_MAX;
INT i = wil::safe_cast_nothrow<INT>(l);
REQUIRE(i == LONG_MAX);
}
SECTION("INT -> UINT")
{
INT i = INT_MAX;
UINT ui = wil::safe_cast_failfast<UINT>(i);
REQUIRE(ui == INT_MAX);
}
SECTION("SIZE_T -> SIZE_T")
{
SIZE_T st = SIZE_T_MAX;
SIZE_T st2 = wil::safe_cast_failfast<SIZE_T>(st);
REQUIRE(st2 == SIZE_T_MAX);
}
SECTION("wchar_t -> uint")
{
wchar_t wc = 0;
UINT ui = wil::safe_cast_failfast<UINT>(wc);
REQUIRE(ui == 0);
}
SECTION("wchar_t -> unsigned char")
{
wchar_t wc = 0;
unsigned char uc = wil::safe_cast_failfast<unsigned char>(wc);
REQUIRE(uc == 0);
auto result = wil::safe_cast_nothrow<unsigned char>(wc, &uc);
REQUIRE_SUCCEEDED(result);
}
SECTION("uint -> wchar_t")
{
UINT ui = 0;
wchar_t wc = wil::safe_cast_failfast<wchar_t>(ui);
REQUIRE(wc == 0);
auto result = wil::safe_cast_nothrow<wchar_t>(ui, &wc);
REQUIRE_SUCCEEDED(result);
}
#ifndef _WIN64
SECTION("SIZE_T -> UINT")
{
SIZE_T st = SIZE_T_MAX;
UINT ui = wil::safe_cast_nothrow<UINT>(st);
REQUIRE(ui == SIZE_T_MAX);
}
#endif
}
TEST_CASE("SafeCastTests::SafeCastNoThrowFail", "[safecast]")
{
SECTION("size_t -> short")
{
size_t st = SIZE_T_MAX;
short s;
REQUIRE_FAILED(wil::safe_cast_nothrow(st, &s));
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("SafeCastTests::SafeCastExpectFailFast", "[safecast]")
{
// Template for safe_cast fail fast tests, fill out more instances needed
witest::TestFailureCache failures;
failures.clear();
{
size_t st = SIZE_T_MAX;
REQUIRE_CRASH(wil::safe_cast_failfast<short>(st));
REQUIRE(failures.size() == 1);
}
failures.clear();
{
size_t st = SIZE_T_MAX;
REQUIRE_THROWS(wil::safe_cast<short>(st));
REQUIRE(failures.size() == 1);
}
}
#endif

47
tests/StlTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,47 @@
#include <wil/stl.h>
#include "common.h"
#ifndef WIL_ENABLE_EXCEPTIONS
#error STL tests require exceptions
#endif
struct dummy
{
char value;
};
// Specialize std::allocator<> so that we don't actually allocate/deallocate memory
dummy g_memoryBuffer[256];
namespace std
{
template <>
struct allocator<dummy>
{
using value_type = dummy;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
dummy* allocate(std::size_t count)
{
REQUIRE(count <= std::size(g_memoryBuffer));
return g_memoryBuffer;
}
void deallocate(dummy* ptr, std::size_t count)
{
for (std::size_t i = 0; i < count; ++i)
{
REQUIRE(ptr[i].value == 0);
}
}
};
}
TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]")
{
{
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
}
}

288
tests/TokenHelpersTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,288 @@
#include <wil/token_helpers.h>
#include "common.h"
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessTokenNoThrow", "[token_helpers]")
{
// Open current thread/process access token
wil::unique_handle token;
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token));
REQUIRE(token != nullptr);
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ));
REQUIRE(token != nullptr);
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Current));
REQUIRE(token != nullptr);
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Self));
REQUIRE(token != nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]")
{
// Open current thread/process access token
wil::unique_handle token(wil::open_current_access_token());
REQUIRE(token != nullptr);
token = wil::open_current_access_token(TOKEN_READ);
REQUIRE(token != nullptr);
token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Current);
REQUIRE(token != nullptr);
token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Self);
REQUIRE(token != nullptr);
}
#endif
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
{
SECTION("Passing a null token")
{
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, nullptr));
REQUIRE(tokenInfo != nullptr);
}
SECTION("Passing a non null token, since it a fake token there is no tokenInfo and hence should fail, code path is correct")
{
HANDLE faketoken = GetStdHandle(STD_INPUT_HANDLE);
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, faketoken));
}
}
// Pseudo tokens can be passed to token APIs and avoid the handle allocations
// making use more efficient.
TEST_CASE("TokenHelpersTests::DemonstrateUseWithPseudoTokens", "[token_helpers]")
{
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentProcessToken()));
REQUIRE(tokenInfo != nullptr);
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadEffectiveToken()));
REQUIRE(tokenInfo != nullptr);
// No thread token by default, this should fail
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadToken()));
REQUIRE(tokenInfo == nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation", "[token_helpers]")
{
// Passing a null token
wistd::unique_ptr<TOKEN_USER> tokenInfo(wil::get_token_information<TOKEN_USER>(nullptr));
REQUIRE(tokenInfo != nullptr);
}
#endif
// This fails with 'ERROR_NO_SUCH_LOGON_SESSION' on the CI machines, so disable
#ifndef WIL_FAST_BUILD
TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]")
{
wil::unique_token_linked_token theToken;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(theToken, nullptr));
#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE_NOTHROW(wil::get_linked_token_information());
#endif
}
#endif
bool IsImpersonating()
{
wil::unique_handle token;
if (!::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token))
{
WI_ASSERT(::GetLastError() == ERROR_NO_TOKEN);
return false;
}
return true;
}
wil::unique_handle GetTokenToImpersonate()
{
wil::unique_handle processToken;
FAIL_FAST_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken));
wil::unique_handle impersonateToken;
FAIL_FAST_IF_WIN32_BOOL_FALSE(::DuplicateToken(processToken.get(), SecurityImpersonation, &impersonateToken));
return impersonateToken;
}
TEST_CASE("TokenHelpersTests::VerifyResetThreadTokenNoThrow", "[token_helpers]")
{
auto impersonateToken = GetTokenToImpersonate();
// Set the thread into a known state - no token.
wil::unique_token_reverter clearThreadToken;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadToken));
REQUIRE_FALSE(IsImpersonating());
// Set a token on the thread - the process token, guaranteed to be friendly
wil::unique_token_reverter setThreadToken1;
REQUIRE_SUCCEEDED(wil::impersonate_token_nothrow(impersonateToken.get(), setThreadToken1));
REQUIRE(IsImpersonating());
SECTION("Clear the token again, should be not impersonating, explicit reset")
{
wil::unique_token_reverter clearThreadAgain;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
REQUIRE_FALSE(IsImpersonating());
clearThreadAgain.reset();
REQUIRE(IsImpersonating());
}
SECTION("Clear the token again, should be not impersonating, dtor reset")
{
wil::unique_token_reverter clearThreadAgain;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
REQUIRE_FALSE(IsImpersonating());
}
REQUIRE(IsImpersonating());
// Clear what we were impersonating
setThreadToken1.reset();
REQUIRE_FALSE(IsImpersonating());
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]")
{
auto impersonateToken = GetTokenToImpersonate();
// Set the thread into a known state - no token.
auto clearThreadToken = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
// Set a token on the thread - the process token, guaranteed to be friendly
auto setThreadToken1 = wil::impersonate_token(impersonateToken.get());
REQUIRE(IsImpersonating());
SECTION("Clear the token again, should be not impersonating, explicit reset")
{
auto clearThreadAgain = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
clearThreadAgain.reset();
REQUIRE(IsImpersonating());
}
SECTION("Clear the token again, should be not impersonating, dtor reset")
{
auto clearThreadAgain = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
}
REQUIRE(IsImpersonating());
// Clear what we were impersonating
setThreadToken1.reset();
REQUIRE_FALSE(IsImpersonating());
}
#endif // WIL_ENABLE_EXCEPTIONS
template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
wistd::unique_ptr<T> tokenInfo;
const auto hr = wil::get_token_information_nothrow(tokenInfo, nullptr);
REQUIRE(S_OK == hr);
}
template <typename T, wistd::enable_if_t<wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
T tokenInfo{};
const auto hr = wil::get_token_information_nothrow(&tokenInfo, nullptr);
REQUIRE(S_OK == hr);
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation2", "[token_helpers]")
{
// Variable sized cases
TestGetTokenInfoForCurrentThread<TOKEN_ACCESS_INFORMATION>();
TestGetTokenInfoForCurrentThread<TOKEN_APPCONTAINER_INFORMATION>();
TestGetTokenInfoForCurrentThread<TOKEN_DEFAULT_DACL>();
TestGetTokenInfoForCurrentThread<TOKEN_GROUPS_AND_PRIVILEGES>();
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_LABEL>();
TestGetTokenInfoForCurrentThread<TOKEN_OWNER>();
TestGetTokenInfoForCurrentThread<TOKEN_PRIMARY_GROUP>();
TestGetTokenInfoForCurrentThread<TOKEN_PRIVILEGES>();
TestGetTokenInfoForCurrentThread<TOKEN_USER>();
// Fixed size and reports size using ERROR_INSUFFICIENT_BUFFER (perf opportunity, ignore second allocation)
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION_TYPE>();
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_POLICY>();
TestGetTokenInfoForCurrentThread<TOKEN_ORIGIN>();
TestGetTokenInfoForCurrentThread<TOKEN_SOURCE>();
TestGetTokenInfoForCurrentThread<TOKEN_STATISTICS>();
TestGetTokenInfoForCurrentThread<TOKEN_TYPE>();
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationBadLength", "[token_helpers]")
{
// Fixed size and reports size using ERROR_BAD_LENGTH (bug)
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION>();
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLevelErrorCases", "[token_helpers]")
{
SECURITY_IMPERSONATION_LEVEL tokenInfo{};
// SECURITY_IMPERSONATION_LEVEL does not support the effective token when it is implicit.
// Demonstrate the error return in that case.
REQUIRE(E_INVALIDARG == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadEffectiveToken()));
// Using an explicit token is supported but returns ERROR_NO_TOKEN when there is no
// impersonation token be sure to use RETURN_IF_FAILED_EXPECTED() and don't use
// the exception forms if this case is not expected.
REQUIRE(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));
// Setup the impersonation token that SECURITY_IMPERSONATION_LEVEL requires.
FAIL_FAST_IF_WIN32_BOOL_FALSE(ImpersonateSelf(SecurityIdentification));
TestGetTokenInfoForCurrentThread<SECURITY_IMPERSONATION_LEVEL>();
REQUIRE(S_OK == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));
RevertToSelf();
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyGetTokenInfo", "[token_helpers]")
{
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_APPCONTAINER_INFORMATION>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION_TYPE>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_MANDATORY_POLICY>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ORIGIN>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_STATISTICS>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION>());
// check a non-pointer size value to make sure the whole struct is returned.
DWORD resultSize{};
TOKEN_SOURCE ts{};
auto tokenSource = wil::get_token_information<TOKEN_SOURCE>();
GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenSource, &ts, sizeof(ts), &resultSize);
REQUIRE(memcmp(&ts, &tokenSource, sizeof(ts)) == 0);
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInfoFailFast", "[token_helpers]")
{
// fixed size
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_APPCONTAINER_INFORMATION>());
// variable size
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_OWNER>());
}
TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]")
{
auto impersonationToken = wil::impersonate_token();
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
}
#endif // WIL_ENABLE_EXCEPTIONS

Просмотреть файл

@ -0,0 +1,266 @@
#include <wil/winrt.h>
#include <wrl/implements.h>
#include "common.h"
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
namespace wiltest
{
class AbiTestEventSender WrlFinal : public RuntimeClass<
RuntimeClassFlags<RuntimeClassType::WinRtClassicComMix>,
IClosable,
IMemoryBufferReference,
FtmBase>
{
public:
// IMemoryBufferReference
IFACEMETHODIMP get_Capacity(_Out_ UINT32* value)
{
*value = 0;
return S_OK;
}
IFACEMETHODIMP add_Closed(
_In_ ITypedEventHandler<IMemoryBufferReference*, IInspectable*>* handler,
_Out_ ::EventRegistrationToken* token)
{
return m_closedEvent.Add(handler, token);
}
IFACEMETHODIMP remove_Closed(::EventRegistrationToken token)
{
return m_closedEvent.Remove(token);
}
// IClosable
IFACEMETHODIMP Close()
{
RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr));
return S_OK;
}
private:
Microsoft::WRL::EventSource<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>> m_closedEvent;
};
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
{
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
token.reset();
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> outerToken;
REQUIRE_FALSE(static_cast<bool>(outerToken));
{
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
outerToken = std::move(token);
REQUIRE_FALSE(static_cast<bool>(token));
REQUIRE(static_cast<bool>(outerToken));
}
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 2);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
REQUIRE(static_cast<bool>(firstToken));
closable->Close();
REQUIRE(timesInvoked == 1);
wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(std::move(firstToken));
REQUIRE_FALSE(static_cast<bool>(firstToken));
REQUIRE(static_cast<bool>(secondToken));
closable->Close();
REQUIRE(timesInvoked == 2);
firstToken.reset();
closable->Close();
REQUIRE(timesInvoked == 3);
secondToken.reset();
closable->Close();
REQUIRE(timesInvoked == 3);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
REQUIRE(static_cast<bool>(firstToken));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
::EventRegistrationToken rawToken = firstToken.release();
REQUIRE_FALSE(static_cast<bool>(firstToken));
REQUIRE(rawToken.value != 0);
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 2);
wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(
rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed);
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 3);
secondToken.reset();
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 3);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
{
#ifdef WIL_ENABLE_EXCEPTIONS
auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get());
REQUIRE(static_cast<bool>(exceptionPolicyToken));
#endif
auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get());
REQUIRE(static_cast<bool>(failFastPolicyToken));
REQUIRE_SUCCEEDED(closable->Close());
#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE(timesInvoked == 2);
#else
REQUIRE(timesInvoked == 1);
#endif
}
REQUIRE_SUCCEEDED(closable->Close());
#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE(timesInvoked == 2);
#else
REQUIRE(timesInvoked == 1);
#endif
}

516
tests/WatcherTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,516 @@
#include <memory> // For shared_event_watcher
#include <windows.h> // filesystem.h includes PathCch.h which includes winnt.h, which will complain about missing target architecture
#include <wil/filesystem.h>
#include <wil/registry.h>
#include <wil/resource.h>
#include "common.h"
TEST_CASE("EventWatcherTests::Construction", "[resource][event_watcher]")
{
SECTION("Create unique_event_watcher_nothrow without event")
{
auto watcher = wil::make_event_watcher_nothrow([]{});
REQUIRE(watcher != nullptr);
}
SECTION("Create unique_event_watcher_nothrow with unique_event_nothrow")
{
wil::unique_event_nothrow eventToPass;
FAIL_FAST_IF_FAILED(eventToPass.create(wil::EventOptions::None));
auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
REQUIRE(watcher != nullptr);
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher_nothrow with handle")
{
wil::unique_event_nothrow eventToDupe;
FAIL_FAST_IF_FAILED(eventToDupe.create(wil::EventOptions::None));
auto watcher = wil::make_event_watcher_nothrow(eventToDupe.get(), []{});
REQUIRE(watcher != nullptr);
REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create unique_event_watcher_nothrow with unique_event")
{
wil::unique_event eventToPass(wil::EventOptions::None);
auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
REQUIRE(watcher != nullptr);
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher without event")
{
auto watcher = wil::make_event_watcher([]{});
}
SECTION("Create unique_event_watcher with unique_event_nothrow")
{
wil::unique_event_nothrow eventToPass;
THROW_IF_FAILED(eventToPass.create(wil::EventOptions::None));
auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher with unique_event")
{
wil::unique_event eventToPass(wil::EventOptions::None);
auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher with handle")
{
wil::unique_event eventToDupe(wil::EventOptions::None);
auto watcher = wil::make_event_watcher(eventToDupe.get(), []{});
REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
}
SECTION("Create unique_event_watcher shared watcher")
{
wil::shared_event_watcher sharedWatcher = wil::make_event_watcher([]{});
}
#endif
}
static auto make_event(wil::EventOptions options = wil::EventOptions::None)
{
wil::unique_event_nothrow result;
FAIL_FAST_IF_FAILED(result.create(options));
return result;
}
TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]")
{
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto watcher = wil::make_event_watcher_nothrow([&]
{
countObserved++;
notificationReceived.SetEvent();
});
REQUIRE(watcher != nullptr);
watcher.SetEvent();
REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
watcher.SetEvent();
REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
REQUIRE(countObserved == 2);
}
TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watcher]")
{
wil::EventOptions const eventOptions[] =
{
wil::EventOptions::None,
wil::EventOptions::ManualReset,
wil::EventOptions::Signaled,
wil::EventOptions::ManualReset | wil::EventOptions::Signaled,
};
for (auto const &eventOption : eventOptions)
{
auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset to avoid hang in case where 2 callbacks are generated (a test failure).
auto processedChange = make_event();
DWORD volatile stateToObserve = 0;
DWORD volatile lastObservedState = 0;
int volatile countObserved = 0;
auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
{
allChangesMade.wait();
countObserved++;
lastObservedState = stateToObserve;
processedChange.SetEvent();
});
REQUIRE(watcher != nullptr);
stateToObserve = 1;
watcher.SetEvent();
stateToObserve = 2;
watcher.SetEvent();
allChangesMade.SetEvent();
REQUIRE(processedChange.wait(5000));
REQUIRE((countObserved == 1 || countObserved == 2)); // ensure the race worked how we wanted it to
REQUIRE(lastObservedState == stateToObserve);
}
}
#define ROOT_KEY_PAIR HKEY_CURRENT_USER, L"Software\\Microsoft\\RegistryWatcherTest"
TEST_CASE("RegistryWatcherTests::Construction", "[registry][registry_watcher]")
{
SECTION("Create unique_registry_watcher_nothrow with string")
{
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){});
REQUIRE(watcher);
}
SECTION("Create unique_registry_watcher_nothrow with unique_hkey")
{
wil::unique_hkey keyToMove;
REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
auto watcher = wil::make_registry_watcher_nothrow(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){});
REQUIRE(watcher);
REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
}
SECTION("Create unique_registry_watcher_nothrow with handle")
{
// construct with just an open registry key
wil::unique_hkey rootKey;
REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &rootKey, nullptr)));
auto watcher = wil::make_registry_watcher_nothrow(rootKey.get(), L"", true, [&](wil::RegistryChangeKind){});
REQUIRE(watcher);
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create unique_registry_watcher with string")
{
REQUIRE_NOTHROW(wil::make_registry_watcher(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){}));
}
SECTION("Create unique_registry_watcher with unique_hkey")
{
wil::unique_hkey keyToMove;
THROW_IF_FAILED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
REQUIRE_NOTHROW(wil::make_registry_watcher(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){}));
REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
}
#endif
}
void SetRegistryValue(
_In_ HKEY hKey,
_In_opt_ LPCWSTR lpSubKey,
_In_opt_ LPCWSTR lpValueName,
_In_ DWORD dwType,
_In_reads_bytes_opt_(cbData) LPCVOID lpData,
_In_ DWORD cbData)
{
wil::unique_hkey key;
REQUIRE(RegOpenKeyExW(hKey, lpSubKey, 0, KEY_WRITE, &key) == ERROR_SUCCESS);
REQUIRE(RegSetValueExW(key.get(), lpValueName, 0, dwType, static_cast<BYTE const*>(lpData), cbData) == ERROR_SUCCESS);
}
TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]")
{
RegDeleteTreeW(ROOT_KEY_PAIR); // So that we get the 'Modify' event
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
REQUIRE(watcher);
DWORD value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
REQUIRE(notificationReceived.wait(5000));
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
value++;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
REQUIRE(notificationReceived.wait(5000));
REQUIRE(countObserved == 2);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
}
TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry_watcher]")
{
RegDeleteTreeW(ROOT_KEY_PAIR);
auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset for the case where both registry operations result in a callback.
auto processedChange = make_event();
DWORD volatile stateToObserve = 0;
DWORD volatile lastObservedState = 0;
DWORD volatile lastObservedValue = 0;
int volatile countObserved = 0;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&, called = false](wil::RegistryChangeKind) mutable
{
// This callback may be called more than once (since we modify the key twice), but we're holding references to
// local variables. Therefore, bail out if this is not the first time we're called
if (called)
{
return;
}
called = true;
allChangesMade.wait();
countObserved++;
lastObservedState = stateToObserve;
DWORD value, cbValue = sizeof(value);
RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
lastObservedValue = value;
processedChange.SetEvent();
});
REQUIRE(watcher);
DWORD value;
// make 2 changes and verify that only the last gets observed
stateToObserve = 1;
value = 0;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
stateToObserve = 2;
value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
allChangesMade.SetEvent();
REQUIRE(processedChange.wait(5000));
REQUIRE(countObserved >= 1); // Sometimes 2 events are observed, see if this can be eliminated.
REQUIRE(lastObservedState == stateToObserve);
REQUIRE(lastObservedValue == 1);
}
TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
REQUIRE(watcher);
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
REQUIRE(notificationReceived.wait(5000));
REQUIRE(countObserved == 1);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
}
TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
{
watcher.reset();
DWORD value = 2;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.SetEvent();
});
REQUIRE(watcher);
DWORD value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
REQUIRE(notificationReceived.wait(5000));
}
// Stress test, disabled by default
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]")
{
for (DWORD value = 0; value < 10000; ++value)
{
wil::srwlock lock;
auto notificationReceived = make_event();
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
{
{
auto al = lock.lock_exclusive();
watcher.reset(); // get m_refCount to 1 to ensure the Release happens on the background thread
}
++value;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.SetEvent();
});
REQUIRE(watcher);
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.wait();
{
auto al = lock.lock_exclusive();
watcher.reset();
}
}
}
TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
REQUIRE(watcher);
});
REQUIRE(watcher);
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
notificationReceived.wait();
REQUIRE(countObserved == 1);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
// wait for the reset to finish. The constructor creates the registry key
notificationReceived.wait(300);
DWORD value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.wait();
REQUIRE(countObserved == 2);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
}
TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
auto deleteNotification = make_event();
int volatile deleteObserved = 0;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind)
{
notificationReceived.SetEvent();
// ensure that the callback is still being executed while the watcher is reset().
deleteNotification.wait(200);
deleteObserved++;
notificationReceived.SetEvent();
});
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
REQUIRE(notificationReceived.wait(5000));
watcher.reset();
deleteNotification.SetEvent();
REQUIRE(notificationReceived.wait(5000));
REQUIRE(deleteObserved == 1);
}
TEST_CASE("FileSystemWatcherTests::Construction", "[resource][folder_watcher]")
{
SECTION("Create unique_folder_watcher_nothrow with valid path")
{
auto watcher = wil::make_folder_watcher_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{});
REQUIRE(watcher);
}
SECTION("Create unique_folder_watcher_nothrow with invalid path")
{
auto watcher = wil::make_folder_watcher_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{});
REQUIRE(!watcher);
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create unique_folder_watcher with valid path")
{
REQUIRE_NOTHROW(wil::make_folder_watcher(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{}));
}
SECTION("Create unique_folder_watcher with invalid path")
{
REQUIRE_THROWS(wil::make_folder_watcher(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{}));
}
#endif
}
TEST_CASE("FileSystemWatcherTests::VerifyDelivery", "[resource][folder_watcher]")
{
witest::TestFolder folder;
REQUIRE(folder);
auto notificationEvent = make_event();
int observedCount = 0;
auto watcher = wil::make_folder_watcher_nothrow(folder.Path(), true, wil::FolderChangeEvents::All, [&]
{
++observedCount;
notificationEvent.SetEvent();
});
REQUIRE(watcher);
witest::TestFile file(folder.Path(), L"file.txt");
REQUIRE(file);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedCount == 1);
witest::TestFile file2(folder.Path(), L"file2.txt");
REQUIRE(file2);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedCount == 2);
}
TEST_CASE("FolderChangeReaderTests::Construction", "[resource][folder_change_reader]")
{
SECTION("Create folder_change_reader_nothrow with valid path")
{
auto reader = wil::make_folder_change_reader_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {});
REQUIRE(reader);
}
SECTION("Create folder_change_reader_nothrow with invalid path")
{
auto reader = wil::make_folder_change_reader_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {});
REQUIRE(!reader);
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create folder_change_reader with valid path")
{
REQUIRE_NOTHROW(wil::make_folder_change_reader(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
}
SECTION("Create folder_change_reader with invalid path")
{
REQUIRE_THROWS(wil::make_folder_change_reader(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
}
#endif
}
TEST_CASE("FolderChangeReaderTests::VerifyDelivery", "[resource][folder_change_reader]")
{
witest::TestFolder folder;
REQUIRE(folder);
auto notificationEvent = make_event();
wil::FolderChangeEvent observedEvent;
wchar_t observedFileName[MAX_PATH] = L"";
auto reader = wil::make_folder_change_reader_nothrow(folder.Path(), true, wil::FolderChangeEvents::All,
[&](wil::FolderChangeEvent event, PCWSTR fileName)
{
observedEvent = event;
StringCchCopyW(observedFileName, ARRAYSIZE(observedFileName), fileName);
notificationEvent.SetEvent();
});
REQUIRE(reader);
witest::TestFile testFile(folder.Path(), L"file.txt");
REQUIRE(testFile);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
REQUIRE(wcscmp(observedFileName, L"file.txt") == 0);
witest::TestFile testFile2(folder.Path(), L"file2.txt");
REQUIRE(testFile2);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
REQUIRE(wcscmp(observedFileName, L"file2.txt") == 0);
}

842
tests/WinRTTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,842 @@
#include <time.h>
#include <wil/winrt.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <map>
#include <string>
#endif
#include "common.h"
#include "FakeWinRTTypes.h"
#include "test_objects.h"
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Storage;
using namespace ABI::Windows::System;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
TEST_CASE("WinRTTests::VerifyTraitsTypes", "[winrt]")
{
static_assert(wistd::is_same_v<bool, typename wil::details::LastType<int, bool>::type>, "");
static_assert(wistd::is_same_v<int, typename wil::details::LastType<int>::type>, "");
static_assert(wistd::is_same_v<IAsyncAction*, decltype(wil::details::GetReturnParamPointerType(&IFileIOStatics::WriteTextAsync))>, "");
static_assert(wistd::is_same_v<IAsyncOperation<bool>*, decltype(wil::details::GetReturnParamPointerType(&ILauncherStatics::LaunchUriAsync))>, "");
static_assert(wistd::is_same_v<void, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncAction*>(nullptr)))>, "");
static_assert(wistd::is_same_v<boolean, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncOperation<bool>*>(nullptr)))>, "");
static_assert(wistd::is_same_v<IStorageFile*, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncOperation<StorageFile*>*>(nullptr)))>, "");
}
template <bool InhibitArrayReferences, bool IgnoreCase, typename LhsT, typename RhsT>
void DoHStringComparisonTest(LhsT&& lhs, RhsT&& rhs, int relation)
{
using compare = wil::details::hstring_compare<InhibitArrayReferences, IgnoreCase>;
// == and !=
REQUIRE(compare::equals(lhs, rhs) == (relation == 0));
REQUIRE(compare::not_equals(lhs, rhs) == (relation != 0));
REQUIRE(compare::equals(rhs, lhs) == (relation == 0));
REQUIRE(compare::not_equals(rhs, lhs) == (relation != 0));
// < and >=
REQUIRE(compare::less(lhs, rhs) == (relation < 0));
REQUIRE(compare::greater_equals(lhs, rhs) == (relation >= 0));
REQUIRE(compare::less(rhs, lhs) == (relation > 0));
REQUIRE(compare::greater_equals(rhs, lhs) == (relation <= 0));
// > and <=
REQUIRE(compare::greater(lhs, rhs) == (relation > 0));
REQUIRE(compare::less_equals(lhs, rhs) == (relation <= 0));
REQUIRE(compare::greater(rhs, lhs) == (relation < 0));
REQUIRE(compare::less_equals(rhs, lhs) == (relation >= 0));
// We wish to test with both const and non-const values. We can do this for free here so long as the type is
// not an array since changing the const-ness of an array may change the expected results
#pragma warning(suppress: 4127)
if (!wistd::is_array<wistd::remove_reference_t<LhsT>>::value &&
!wistd::is_const<wistd::remove_reference_t<LhsT>>::value)
{
const wistd::remove_reference_t<LhsT>& constLhs = lhs;
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(constLhs, rhs, relation);
}
#pragma warning(suppress: 4127)
if (!wistd::is_array<wistd::remove_reference_t<RhsT>>::value &&
!wistd::is_const<wistd::remove_reference_t<RhsT>>::value)
{
const wistd::remove_reference_t<RhsT>& constRhs = rhs;
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, constRhs, relation);
}
}
// The two string arguments are expected to compare equal to one another using the specified IgnoreCase argument and
// contain at least one embedded null character
template <bool InhibitArrayReferences, bool IgnoreCase, size_t Size>
void DoHStringSameValueComparisonTest(const wchar_t (&lhs)[Size], const wchar_t (&rhs)[Size])
{
wchar_t lhsNonConstArray[Size + 5];
wchar_t rhsNonConstArray[Size + 5];
wcsncpy_s(lhsNonConstArray, lhs, Size);
wcsncpy_s(rhsNonConstArray, rhs, Size);
// For non-const arrays, we should never deduce length, so even though we append different values to each string, we
// do so after the last null character, so they should never be read
wcsncpy_s(lhsNonConstArray + Size + 1, 4, L"foo", 3);
wcsncpy_s(rhsNonConstArray + Size + 1, 4, L"bar", 3);
const wchar_t* lhsCstr = lhs;
const wchar_t* rhsCstr = rhs;
HStringReference lhsRef(lhs);
HStringReference rhsRef(rhs);
HString lhsStr;
HString rhsStr;
REQUIRE_SUCCEEDED(lhsStr.Set(lhs));
REQUIRE_SUCCEEDED(rhsStr.Set(rhs));
auto lhsHstr = lhsStr.Get();
auto rhsHstr = rhsStr.Get();
wil::unique_hstring lhsUniqueStr;
wil::unique_hstring rhsUniqueStr;
REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr));
REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr));
// Const array - embedded nulls are included only if InhibitArrayReferences is false
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhs, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsNonConstArray, InhibitArrayReferences ? 0 : 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsCstr, InhibitArrayReferences ? 0 : 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsRef, InhibitArrayReferences ? -1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsStr, InhibitArrayReferences ? -1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsHstr, InhibitArrayReferences ? -1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsUniqueStr, InhibitArrayReferences ? -1 : 0);
// Non-const array - *never* deduce length
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsNonConstArray, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsCstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsRef, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsStr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsHstr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsUniqueStr, -1);
// C string - impossible to deduce length
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsCstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsRef, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsStr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsHstr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsUniqueStr, -1);
// HStringReference
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsRef, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsStr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsUniqueStr, 0);
// HString
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsStr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsUniqueStr, 0);
// Raw HSTRING
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsUniqueStr, 0);
// wil::unique_hstring
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 0);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring lhsWstr(lhs, 7);
std::wstring rhsWstr(rhs, 7);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, InhibitArrayReferences ? 1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsRef, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsStr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsUniqueStr, 0);
#endif
}
// It's expected that the first argument (lhs) compares greater than the second argument (rhs)
template <bool InhibitArrayReferences, bool IgnoreCase, size_t LhsSize, size_t RhsSize>
void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const wchar_t (&rhs)[RhsSize])
{
wchar_t lhsNonConstArray[LhsSize];
wchar_t rhsNonConstArray[RhsSize];
wcsncpy_s(lhsNonConstArray, lhs, LhsSize);
wcsncpy_s(rhsNonConstArray, rhs, RhsSize);
const wchar_t* lhsCstr = lhs;
const wchar_t* rhsCstr = rhs;
HStringReference lhsRef(lhs);
HStringReference rhsRef(rhs);
HString lhsStr;
HString rhsStr;
REQUIRE_SUCCEEDED(lhsStr.Set(lhs));
REQUIRE_SUCCEEDED(rhsStr.Set(rhs));
auto lhsHstr = lhsStr.Get();
auto rhsHstr = rhsStr.Get();
wil::unique_hstring lhsUniqueStr;
wil::unique_hstring rhsUniqueStr;
REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr));
REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr));
// Const array
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhs, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsUniqueStr, 1);
// Non-const array
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsUniqueStr, 1);
// C string
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsUniqueStr, 1);
// HStringReference
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsUniqueStr, 1);
// HString
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsUniqueStr, 1);
// Raw HSTRING
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsUniqueStr, 1);
// wil::unique_hstring
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring lhsWstr(lhs, 7);
std::wstring rhsWstr(rhs, 7);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsUniqueStr, 1);
#endif
}
TEST_CASE("WinRTTests::HStringComparison", "[winrt][hstring_compare]")
{
SECTION("Don't inhibit arrays")
{
DoHStringSameValueComparisonTest<false, false>(L"foo\0bar", L"foo\0bar");
DoHStringDifferentValueComparisonTest<false, false>(L"foo", L"bar");
}
SECTION("Inhibit arrays")
{
DoHStringSameValueComparisonTest<true, false>(L"foo\0bar", L"foo\0bar");
DoHStringDifferentValueComparisonTest<true, false>(L"foo", L"bar");
}
SECTION("Ignore case")
{
DoHStringSameValueComparisonTest<true, true>(L"foo\0bar", L"FoO\0bAR");
DoHStringDifferentValueComparisonTest<true, true>(L"Foo", L"baR");
}
SECTION("Empty string")
{
const wchar_t constArray[] = L"";
wchar_t nonConstArray[] = L"";
const wchar_t* cstr = constArray;
const wchar_t* nullCstr = nullptr;
// str may end up referencing a null HSTRING. That's fine; we'll just test null HSTRING twice
HString str;
REQUIRE_SUCCEEDED(str.Set(constArray));
HSTRING nullHstr = nullptr;
// Const array - impossible to use null value
DoHStringComparisonTest<false, false>(constArray, constArray, 0);
DoHStringComparisonTest<false, false>(constArray, nonConstArray, 0);
DoHStringComparisonTest<false, false>(constArray, cstr, 0);
DoHStringComparisonTest<false, false>(constArray, nullCstr, 0);
DoHStringComparisonTest<false, false>(constArray, str.Get(), 0);
DoHStringComparisonTest<false, false>(constArray, nullHstr, 0);
// Non-const array - impossible to use null value
DoHStringComparisonTest<false, false>(nonConstArray, nonConstArray, 0);
DoHStringComparisonTest<false, false>(nonConstArray, cstr, 0);
DoHStringComparisonTest<false, false>(nonConstArray, nullCstr, 0);
DoHStringComparisonTest<false, false>(nonConstArray, str.Get(), 0);
DoHStringComparisonTest<false, false>(nonConstArray, nullHstr, 0);
// Non-null c-string
DoHStringComparisonTest<false, false>(cstr, cstr, 0);
DoHStringComparisonTest<false, false>(cstr, nullCstr, 0);
DoHStringComparisonTest<false, false>(cstr, str.Get(), 0);
DoHStringComparisonTest<false, false>(cstr, nullHstr, 0);
// Null c-string
DoHStringComparisonTest<false, false>(nullCstr, nullCstr, 0);
DoHStringComparisonTest<false, false>(nullCstr, str.Get(), 0);
DoHStringComparisonTest<false, false>(nullCstr, nullHstr, 0);
// (Possibly) non-null HSTRING
DoHStringComparisonTest<false, false>(str.Get(), str.Get(), 0);
DoHStringComparisonTest<false, false>(str.Get(), nullHstr, 0);
// Null HSTRING
DoHStringComparisonTest<false, false>(nullHstr, nullHstr, 0);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring wstr;
DoHStringComparisonTest<false, false>(wstr, wstr, 0);
DoHStringComparisonTest<false, false>(wstr, constArray, 0);
DoHStringComparisonTest<false, false>(wstr, nonConstArray, 0);
DoHStringComparisonTest<false, false>(wstr, cstr, 0);
DoHStringComparisonTest<false, false>(wstr, nullCstr, 0);
DoHStringComparisonTest<false, false>(wstr, str.Get(), 0);
DoHStringComparisonTest<false, false>(wstr, nullHstr, 0);
#endif
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WinRTTests::HStringMapTest", "[winrt][hstring_compare]")
{
int nextValue = 0;
std::map<std::wstring, int> wstringMap;
wstringMap.emplace(L"foo", nextValue++);
wstringMap.emplace(L"bar", nextValue++);
wstringMap.emplace(std::wstring(L"foo\0bar", 7), nextValue++);
wstringMap.emplace(L"adding", nextValue++);
wstringMap.emplace(L"quite", nextValue++);
wstringMap.emplace(L"a", nextValue++);
wstringMap.emplace(L"few", nextValue++);
wstringMap.emplace(L"more", nextValue++);
wstringMap.emplace(L"values", nextValue++);
wstringMap.emplace(L"for", nextValue++);
wstringMap.emplace(L"testing", nextValue++);
wstringMap.emplace(L"", nextValue++);
std::map<HString, int> hstringMap;
for (auto& pair : wstringMap)
{
HString str;
THROW_IF_FAILED(str.Set(pair.first.c_str(), static_cast<UINT>(pair.first.length())));
hstringMap.emplace(std::move(str), pair.second);
}
// Order should be the same as the map of wstring
auto itr = hstringMap.begin();
for (auto& pair : wstringMap)
{
REQUIRE(itr != hstringMap.end());
REQUIRE(itr->first == HStringReference(pair.first.c_str(), static_cast<UINT>(pair.first.length())));
// Should also be able to find the value
REQUIRE(hstringMap.find(pair.first) != hstringMap.end());
++itr;
}
REQUIRE(itr == hstringMap.end());
const wchar_t constArray[] = L"foo\0bar";
wchar_t nonConstArray[] = L"foo\0bar";
const wchar_t* cstr = constArray;
HString key;
wil::unique_hstring uniqueHstr;
THROW_IF_FAILED(key.Set(constArray));
THROW_IF_FAILED(key.CopyTo(&uniqueHstr));
HStringReference ref(constArray);
std::wstring wstr(constArray, 7);
auto verifyFunc = [&](int expectedValue, auto&& keyValue)
{
auto itr = hstringMap.find(std::forward<decltype(keyValue)>(keyValue));
REQUIRE(itr != hstringMap.end());
REQUIRE(expectedValue == itr->second);
};
// The following should find "foo\0bar"
auto expectedValue = wstringMap[wstr];
verifyFunc(expectedValue, uniqueHstr);
verifyFunc(expectedValue, key);
verifyFunc(expectedValue, key.Get());
verifyFunc(expectedValue, ref);
verifyFunc(expectedValue, wstr);
// Arrays/strings should not deduce length and should therefore find "foo"
expectedValue = wstringMap[L"foo"];
verifyFunc(expectedValue, constArray);
verifyFunc(expectedValue, nonConstArray);
verifyFunc(expectedValue, cstr);
// Should not ignore case
REQUIRE(hstringMap.find(L"FOO") == hstringMap.end());
// Should also be able to find empty values
const wchar_t constEmptyArray[] = L"";
wchar_t nonConstEmptyArray[] = L"";
const wchar_t* emptyCstr = constEmptyArray;
const wchar_t* nullCstr = nullptr;
HString emptyStr;
HSTRING nullHstr = nullptr;
std::wstring emptyWstr;
expectedValue = wstringMap[L""];
verifyFunc(expectedValue, constEmptyArray);
verifyFunc(expectedValue, nonConstEmptyArray);
verifyFunc(expectedValue, emptyCstr);
verifyFunc(expectedValue, nullCstr);
verifyFunc(expectedValue, emptyStr);
verifyFunc(expectedValue, nullHstr);
verifyFunc(expectedValue, emptyWstr);
}
TEST_CASE("WinRTTests::HStringCaseInsensitiveMapTest", "[winrt][hstring_compare]")
{
std::map<HString, int, wil::hstring_insensitive_less> hstringMap;
auto emplaceFunc = [&](auto&& key, int value)
{
HString str;
THROW_IF_FAILED(str.Set(std::forward<decltype(key)>(key)));
hstringMap.emplace(std::move(str), value);
};
int nextValue = 0;
int fooValue = nextValue++;
emplaceFunc(L"foo", fooValue);
emplaceFunc(L"bar", nextValue++);
int foobarValue = nextValue++;
emplaceFunc(L"foo\0bar", foobarValue);
emplaceFunc(L"foobar", nextValue++);
emplaceFunc(L"adding", nextValue++);
emplaceFunc(L"some", nextValue++);
emplaceFunc(L"more", nextValue++);
emplaceFunc(L"values", nextValue++);
emplaceFunc(L"for", nextValue++);
emplaceFunc(L"testing", nextValue++);
WI_ASSERT(static_cast<size_t>(nextValue) == hstringMap.size());
const wchar_t constArray[] = L"FoO\0BAr";
wchar_t nonConstArray[] = L"fOo\0baR";
const wchar_t* cstr = constArray;
HString key;
wil::unique_hstring uniqueHstr;
THROW_IF_FAILED(key.Set(constArray));
THROW_IF_FAILED(key.CopyTo(&uniqueHstr));
HStringReference ref(constArray);
std::wstring wstr(constArray, 7);
auto verifyFunc = [&](int expectedValue, auto&& key)
{
auto itr = hstringMap.find(std::forward<decltype(key)>(key));
REQUIRE(itr != std::end(hstringMap));
REQUIRE(expectedValue == itr->second);
};
// The following should find "foo\0bar"
verifyFunc(foobarValue, uniqueHstr);
verifyFunc(foobarValue, key);
verifyFunc(foobarValue, key.Get());
verifyFunc(foobarValue, ref);
verifyFunc(foobarValue, wstr);
// Arrays/strings should not deduce length and should therefore find "foo"
verifyFunc(fooValue, constArray);
verifyFunc(fooValue, nonConstArray);
verifyFunc(fooValue, cstr);
}
#endif
// This is not a test method, nor should it be called. This is a compilation-only test.
#ifdef WIL_ENABLE_EXCEPTIONS
void RunWhenCompleteCompilationTest()
{
{
ComPtr<IAsyncOperation<HSTRING>> stringOp;
wil::run_when_complete(stringOp.Get(), [](HRESULT /* result */, HSTRING /* value */) {});
auto result = wil::wait_for_completion(stringOp.Get());
}
{
ComPtr<IAsyncOperationWithProgress<HSTRING, UINT64>> stringOpWithProgress;
wil::run_when_complete(stringOpWithProgress.Get(), [](HRESULT /* result */, HSTRING /* value */) {});
auto result = wil::wait_for_completion(stringOpWithProgress.Get());
}
}
#endif
TEST_CASE("WinRTTests::RunWhenCompleteMoveOnlyTest", "[winrt][run_when_complete]")
{
auto op = Make<FakeAsyncOperation<int>>();
REQUIRE(op);
bool gotEvent = false;
auto hr = wil::run_when_complete_nothrow(op.Get(), [&gotEvent, enforce = cannot_copy{}](HRESULT hr, int result)
{
(void)enforce;
REQUIRE_SUCCEEDED(hr);
REQUIRE(result == 42);
gotEvent = true;
return S_OK;
});
REQUIRE_SUCCEEDED(hr);
op->Complete(S_OK, 42);
REQUIRE(gotEvent);
}
TEST_CASE("WinRTTests::WaitForCompletionTimeout", "[winrt][wait_for_completion]")
{
auto op = Make<FakeAsyncOperation<bool, boolean>>();
REQUIRE(op);
// The wait_for_completion* functions don't properly deduce the "decayed" async type, so force it here
auto asyncOp = static_cast<IAsyncOperation<bool>*>(op.Get());
bool timedOut = false;
REQUIRE_SUCCEEDED(wil::wait_for_completion_or_timeout_nothrow(asyncOp, 1, &timedOut));
REQUIRE(timedOut);
}
// This is not a test method, nor should it be called. This is a compilation-only test.
#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code
void WaitForCompletionCompilationTest()
{
// Ensure the wait_for_completion variants compile
FAIL_FAST_HR_MSG(E_UNEXPECTED, "This is a compilation test, and should not be called");
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
// inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
IAsyncAction* action = nullptr;
wil::wait_for_completion_nothrow(action);
wil::wait_for_completion_nothrow(action, COWAIT_DEFAULT);
// template <typename TResult>
// HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
// _Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
// COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
IAsyncOperation<bool>* operation = nullptr;
wil::wait_for_completion_nothrow(operation);
wil::wait_for_completion_nothrow(operation, COWAIT_DEFAULT);
// template <typename TResult, typename TProgress>
// HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
// _Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
// COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
ComPtr<IAsyncOperation<bool>> operationWithResult;
boolean result = false;
wil::wait_for_completion_nothrow(operationWithResult.Get(), &result);
wil::wait_for_completion_nothrow(operationWithResult.Get(), &result, COWAIT_DEFAULT);
DWORD timeoutValue = 1000; // arbitrary
bool timedOut = false;
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
// inline HRESULT wait_for_completion_or_timeout_nothrow(_In_ TAsync* operation,
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut);
wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut, COWAIT_DEFAULT);
// template <typename TResult>
// HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
// _Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut);
wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut, COWAIT_DEFAULT);
// template <typename TResult, typename TProgress>
// HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
// _Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut);
wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut, COWAIT_DEFAULT);
#ifdef WIL_ENABLE_EXCEPTIONS
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
// inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
wil::wait_for_completion(action);
wil::wait_for_completion(action, COWAIT_DEFAULT);
// template <typename TResult, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
// TReturn
// wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
wil::wait_for_completion(operation);
wil::wait_for_completion(operation, COWAIT_DEFAULT);
// template <typename TResult, typename TProgress, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
// TReturn
// wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
result = wil::wait_for_completion(operationWithResult.Get());
result = wil::wait_for_completion(operationWithResult.Get(), COWAIT_DEFAULT);
#endif
}
#pragma warning(pop)
TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]")
{
// Verifying that converting DateTime variable set as the date that means 0 as time_t works
DateTime time1 = { wil::SecondsToStartOf1970 * wil::HundredNanoSecondsInSecond };
__time64_t time_t1 = wil::DateTime_to_time_t(time1);
REQUIRE(time_t1 == 0);
// Verifying that converting back to DateTime would return the same value
DateTime time2 = wil::time_t_to_DateTime(time_t1);
REQUIRE(time1.UniversalTime == time2.UniversalTime);
// Verifying that converting to time_t for non-zero value also works
time2.UniversalTime += wil::HundredNanoSecondsInSecond * 123;
__time64_t time_t2 = wil::DateTime_to_time_t(time2);
REQUIRE(time_t2 - time_t1 == 123);
// Verifying that converting back to DateTime for non-zero value also works
time1 = wil::time_t_to_DateTime(time_t2);
REQUIRE(time1.UniversalTime == time2.UniversalTime);
}
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
{
auto result = Make<FakeVector<IInspectable*>>();
REQUIRE(result);
ComPtr<IPropertyValueStatics> propStatics;
REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics));
for (UINT32 i = 0; i < 5; ++i)
{
ComPtr<IInspectable> myProp;
REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp));
REQUIRE_SUCCEEDED(result->Append(myProp.Get()));
}
return result;
}
ComPtr<IVector<HSTRING>> MakeSampleStringVector()
{
auto result = Make<FakeVector<HSTRING>>();
REQUIRE(result);
const HStringReference items[] = { HStringReference(L"one"), HStringReference(L"two"), HStringReference(L"three") };
for (const auto& i : items)
{
REQUIRE_SUCCEEDED(result->Append(i.Get()));
}
return result;
}
ComPtr<IVector<Point>> MakeSamplePointVector()
{
auto result = Make<FakeVector<Point>>();
REQUIRE(result);
for (int i = 0; i < 5; ++i)
{
auto value = static_cast<float>(i);
REQUIRE_SUCCEEDED(result->Append(Point{ value, value }));
}
return result;
}
TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]")
{
auto uninit = wil::RoInitialize_failfast();
auto inspectables = MakeSampleInspectableVector();
unsigned count = 0;
REQUIRE_SUCCEEDED(inspectables->get_Size(&count));
unsigned idx = 0;
HRESULT success = S_OK;
for (const auto& i : wil::get_range_nothrow(inspectables.Get(), &success))
{
// Duplications are not a typo - they verify the thing is callable twice
UINT32 value;
ComPtr<IReference<UINT32>> intRef;
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
++idx;
HString rtc;
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
}
REQUIRE_SUCCEEDED(success);
REQUIRE(count == idx);
auto strings = MakeSampleStringVector();
for (const auto& i : wil::get_range_nothrow(strings.Get(), &success))
{
REQUIRE(i.Get());
REQUIRE(i.Get());
}
REQUIRE_SUCCEEDED(success);
int index = 0;
auto points = MakeSamplePointVector();
for (auto value : wil::get_range_nothrow(points.Get(), &success))
{
REQUIRE(index++ == value.Get().X);
}
REQUIRE_SUCCEEDED(success);
// operator-> should not clear out the pointer
auto inspRange = wil::get_range_nothrow(inspectables.Get());
for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
auto strRange = wil::get_range_nothrow(strings.Get());
for (auto itr = strRange.begin(); itr != strRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
index = 0;
auto pointRange = wil::get_range_nothrow(points.Get());
for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr)
{
REQUIRE(index++ == itr->Get().X);
}
#if (defined WIL_ENABLE_EXCEPTIONS)
idx = 0;
for (const auto& i : wil::get_range(inspectables.Get()))
{
// Duplications are not a typo - they verify the thing is callable twice
UINT32 value;
ComPtr<IReference<UINT32>> intRef;
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
++idx;
HString rtc;
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
}
REQUIRE(count == idx);
for (const auto& i : wil::get_range(strings.Get()))
{
REQUIRE(i.Get());
REQUIRE(i.Get());
}
index = 0;
for (auto value : wil::get_range(points.Get()))
{
REQUIRE(index++ == value.Get().X);
}
// operator-> should not clear out the pointer
for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
for (auto itr = strRange.begin(); itr != strRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
index = 0;
for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr)
{
REQUIRE(index++ == itr->Get().X);
}
#endif
}
unsigned long GetComObjectRefCount(IUnknown* unk) { unk->AddRef(); return unk->Release(); }
TEST_CASE("WinRTTests::VectorRangeLeakTest", "[winrt][vector_range]")
{
auto uninit = wil::RoInitialize_failfast();
auto inspectables = MakeSampleInspectableVector();
ComPtr<IInspectable> verifyNotLeaked;
HRESULT hr = S_OK;
for (const auto& ptr : wil::get_range_nothrow(inspectables.Get(), &hr))
{
if (!verifyNotLeaked)
{
verifyNotLeaked = ptr;
}
}
inspectables = nullptr; // clear all refs to verifyNotLeaked
REQUIRE_SUCCEEDED(hr);
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
inspectables = MakeSampleInspectableVector();
for (const auto& ptr : wil::get_range_failfast(inspectables.Get()))
{
if (!verifyNotLeaked)
{
verifyNotLeaked = ptr;
}
}
inspectables = nullptr; // clear all refs to verifyNotLeaked
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
#if (defined WIL_ENABLE_EXCEPTIONS)
inspectables = MakeSampleInspectableVector();
for (const auto& ptr : wil::get_range(inspectables.Get()))
{
if (!verifyNotLeaked)
{
verifyNotLeaked = ptr;
}
}
inspectables = nullptr; // clear all refs to verifyNotLeaked
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
#endif
}

209
tests/WistdTests.cpp Normal file
Просмотреть файл

@ -0,0 +1,209 @@
#include <wil/wistd_functional.h>
#include "common.h"
#include "test_objects.h"
// Test methods/objects
int GetValue()
{
return 42;
}
int GetOtherValue()
{
return 8;
}
int Negate(int value)
{
return -value;
}
int Add(int lhs, int rhs)
{
return lhs + rhs;
}
TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]")
{
wistd::function<int()> getValue = GetValue;
REQUIRE(GetValue() == getValue());
wistd::function<int(int)> negate = Negate;
REQUIRE(Negate(42) == negate(42));
wistd::function<int(int, int)> add = Add;
REQUIRE(Add(42, 8) == add(42, 8));
}
TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]")
{
wistd::function<int()> fn = GetValue;
REQUIRE(GetValue() == fn());
fn = GetOtherValue;
REQUIRE(GetOtherValue() == fn());
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]")
{
// We should be able to capture a std::function in a wistd::function
wistd::function<int()> fn;
{
value_holder holder{ 42 };
std::function<int()> stdFn = [holder]()
{
return holder.value;
};
fn = stdFn;
}
REQUIRE(42 == fn());
}
#endif
TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == copyFrom());
auto copyTo = copyFrom;
REQUIRE(1 == copyTo());
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> copyTo;
{
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == copyFrom());
copyTo = copyFrom;
}
REQUIRE(1 == copyTo());
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == moveFrom());
auto moveTo = std::move(moveFrom);
REQUIRE(0 == moveTo());
// Because we move the underlying function object, we _must_ invalidate the moved from function
REQUIRE_FALSE(moveFrom != nullptr);
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> moveTo;
{
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == moveFrom());
moveTo = std::move(moveFrom);
}
REQUIRE(0 == moveTo());
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> first;
wistd::function<int()> second;
first.swap(second);
REQUIRE_FALSE(first != nullptr);
REQUIRE_FALSE(second != nullptr);
first = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
first.swap(second);
REQUIRE_FALSE(first != nullptr);
REQUIRE(second != nullptr);
REQUIRE(0 == second());
first.swap(second);
REQUIRE(first != nullptr);
REQUIRE_FALSE(second != nullptr);
REQUIRE(0 == first());
second = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
first.swap(second);
REQUIRE(first != nullptr);
REQUIRE(second != nullptr);
REQUIRE(0 == first());
}
REQUIRE(0 == state.instance_count());
}
// MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a
// function that accepts the arguments by value. This test exercises the workaround that we have in place. Note
// that this of course requires building with optimizations enabled
void ForwardingTest(wistd::function<int()> getValue, wistd::function<int(int)> negate, wistd::function<int(int, int)> add)
{
// Previously, this would cause a runtime crash
REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8)));
}
template <typename... Args>
void CallForwardingTest(Args&&... args)
{
ForwardingTest(wistd::forward<Args>(args)...);
}
TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]")
{
CallForwardingTest(
wistd::function<int()>(GetValue),
wistd::function<int(int)>(Negate),
wistd::function<int(int, int)>(Add));
}

20
tests/app/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,20 @@
project(witest.app)
add_executable(witest.app)
add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
target_sources(witest.app PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

14934
tests/catch.hpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

507
tests/common.h Normal file
Просмотреть файл

@ -0,0 +1,507 @@
#pragma once
#include <windows.h>
#include <PathCch.h>
#include "catch.hpp"
#include <wil/filesystem.h>
#include <wil/result.h>
#define REPORTS_ERROR(expr) witest::ReportsError(wistd::is_same<HRESULT, decltype(expr)>{}, [&]() { return expr; })
#define REQUIRE_ERROR(expr) REQUIRE(REPORTS_ERROR(expr))
#define REQUIRE_NOERROR(expr) REQUIRE_FALSE(REPORTS_ERROR(expr))
#define CRASHES(expr) witest::DoesCodeCrash([&]() { return expr; })
#define REQUIRE_CRASH(expr) REQUIRE(CRASHES(expr))
#define REQUIRE_NOCRASH(expr) REQUIRE_FALSE(CRASHES(expr))
// NOTE: SUCCEEDED/FAILED macros not used here since Catch2 can give us better diagnostics if it knows the HRESULT value
#define REQUIRE_SUCCEEDED(expr) REQUIRE((HRESULT)(expr) >= 0)
#define REQUIRE_FAILED(expr) REQUIRE((HRESULT)(expr) < 0)
// MACRO double evaluation check.
// The following macro illustrates a common problem with writing macros:
// #define MY_MAX(a, b) (((a) > (b)) ? (a) : (b))
// The issue is that whatever code is being used for both a and b is being executed twice.
// This isn't harmful when thinking of constant numerics, but consider this example:
// MY_MAX(4, InterlockedIncrement(&cCount))
// This evaluates the (B) parameter twice and results in incrementing the counter twice.
// We use MDEC in unit tests to verify that this kind of pattern is not present. A test
// of this kind:
// MY_MAX(MDEC(4), MDEC(InterlockedIncrement(&cCount))
// will verify that the parameters are not evaluated more than once.
#define MDEC(PARAM) (witest::details::MacroDoubleEvaluationCheck(__LINE__, #PARAM), PARAM)
// There's some functionality that we need for testing that's not available for the app partition. Since those tests are
// primarily compilation tests, declare what's needed here
extern "C" {
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
WINBASEAPI _Ret_maybenull_
PVOID WINAPI AddVectoredExceptionHandler(_In_ ULONG First, _In_ PVECTORED_EXCEPTION_HANDLER Handler);
WINBASEAPI
ULONG WINAPI RemoveVectoredExceptionHandler(_In_ PVOID Handle);
#endif
}
#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code
namespace witest
{
namespace details
{
inline void MacroDoubleEvaluationCheck(size_t uLine, _In_ const char* pszCode)
{
struct SEval
{
size_t uLine;
const char* pszCode;
};
static SEval rgEval[15] = {};
static size_t nOffset = 0;
for (auto& eval : rgEval)
{
if ((eval.uLine == uLine) && (eval.pszCode != nullptr) && (0 == strcmp(pszCode, eval.pszCode)))
{
// This verification indicates that macro-double-evaluation check is firing for a particular usage of MDEC().
FAIL("Expression '" << pszCode << "' double evaluated in macro on line " << uLine);
}
}
rgEval[nOffset].uLine = uLine;
rgEval[nOffset].pszCode = pszCode;
nOffset = (nOffset + 1) % ARRAYSIZE(rgEval);
}
template <typename T>
class AssignTemporaryValueCleanup
{
public:
AssignTemporaryValueCleanup(_In_ AssignTemporaryValueCleanup const &) = delete;
AssignTemporaryValueCleanup & operator=(_In_ AssignTemporaryValueCleanup const &) = delete;
explicit AssignTemporaryValueCleanup(_Inout_ T *pVal, T val) WI_NOEXCEPT :
m_pVal(pVal),
m_valOld(*pVal)
{
*pVal = val;
}
AssignTemporaryValueCleanup(_Inout_ AssignTemporaryValueCleanup && other) WI_NOEXCEPT :
m_pVal(other.m_pVal),
m_valOld(other.m_valOld)
{
other.m_pVal = nullptr;
}
~AssignTemporaryValueCleanup() WI_NOEXCEPT
{
operator()();
}
void operator()() WI_NOEXCEPT
{
if (m_pVal != nullptr)
{
*m_pVal = m_valOld;
m_pVal = nullptr;
}
}
void Dismiss() WI_NOEXCEPT
{
m_pVal = nullptr;
}
private:
T *m_pVal;
T m_valOld;
};
}
// Use the following routine to allow for a variable to be swapped with another and automatically revert the
// assignment at the end of the scope.
// Example:
// int nFoo = 10
// {
// auto revert = witest::AssignTemporaryValue(&nFoo, 12);
// // nFoo will now be 12 within this scope...
// }
// // and nFoo is back to 10 within the outer scope
template <typename T>
inline witest::details::AssignTemporaryValueCleanup<T> AssignTemporaryValue(_Inout_ T *pVal, T val) WI_NOEXCEPT
{
return witest::details::AssignTemporaryValueCleanup<T>(pVal, val);
}
//! Global class which tracks objects that derive from @ref AllocatedObject.
//! Use `witest::g_objectCount.Leaked()` to determine if an object deriving from `AllocatedObject` has been leaked.
class GlobalCount
{
public:
int m_count = 0;
//! Returns `true` if there are any objects that derive from @ref AllocatedObject still in memory.
bool Leaked() const
{
return (m_count != 0);
}
~GlobalCount()
{
if (Leaked())
{
// NOTE: This runs when no test is active, but will still cause an assert failure to notify
FAIL("GlobalCount is non-zero; there is a leak somewhere");
}
}
};
__declspec(selectany) GlobalCount g_objectCount;
//! Derive an allocated test object from witest::AllocatedObject to ensure that those objects aren't leaked in the test.
//! Note that you can call g_objectCount.Leaked() at any point to determine if a leak has already occurred (assuming that
//! all objects should have been destroyed at that point.
class AllocatedObject
{
public:
AllocatedObject() { g_objectCount.m_count++; }
~AllocatedObject() { g_objectCount.m_count--; }
};
template <typename Lambda>
bool DoesCodeThrow(Lambda&& callOp)
{
#ifdef WIL_ENABLE_EXCEPTIONS
try
#endif
{
callOp();
}
#ifdef WIL_ENABLE_EXCEPTIONS
catch (...)
{
return true;
}
#endif
return false;
}
[[noreturn]]
inline void __stdcall TranslateFailFastException(PEXCEPTION_RECORD rec, PCONTEXT, DWORD)
{
// RaiseFailFastException cannot be continued or handled. By instead calling RaiseException, it allows us to
// handle exceptions
::RaiseException(rec->ExceptionCode, rec->ExceptionFlags, rec->NumberParameters, rec->ExceptionInformation);
}
constexpr DWORD msvc_exception_code = 0xE06D7363;
// This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below -
// that interprets a set of exception codes as fatal. We don't want this behavior since we may be expecting such
// crashes, so instead translate all exception codes to something not fatal
inline LONG WINAPI TranslateExceptionCodeHandler(PEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode != witest::msvc_exception_code)
{
info->ExceptionRecord->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
}
return EXCEPTION_CONTINUE_SEARCH;
}
namespace details
{
inline bool DoesCodeCrash(wistd::function<void()>& callOp)
{
bool result = false;
__try
{
callOp();
}
// Let C++ exceptions pass through
__except ((::GetExceptionCode() != msvc_exception_code) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
result = true;
}
return result;
}
}
inline bool DoesCodeCrash(wistd::function<void()> callOp)
{
// See above; we don't want to actually fail fast, so make sure we raise a different exception instead
auto restoreHandler = AssignTemporaryValue(&wil::details::g_pfnRaiseFailFastException, TranslateFailFastException);
auto handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler);
auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); });
return details::DoesCodeCrash(callOp);
}
template <typename Lambda>
bool ReportsError(wistd::false_type, Lambda&& callOp)
{
bool doesThrow = false;
bool doesCrash = DoesCodeCrash([&]()
{
doesThrow = DoesCodeThrow(callOp);
});
return doesThrow || doesCrash;
}
template <typename Lambda>
bool ReportsError(wistd::true_type, Lambda&& callOp)
{
return FAILED(callOp());
}
#ifdef WIL_ENABLE_EXCEPTIONS
class TestFailureCache final :
public wil::details::IFailureCallback
{
public:
TestFailureCache() :
m_callbackHolder(this)
{
}
void clear()
{
m_failures.clear();
}
size_t size() const
{
return m_failures.size();
}
bool empty() const
{
return m_failures.empty();
}
const wil::FailureInfo& operator[](size_t pos) const
{
return m_failures.at(pos).GetFailureInfo();
}
// IFailureCallback
bool NotifyFailure(wil::FailureInfo const & failure) WI_NOEXCEPT override
{
m_failures.emplace_back(failure);
return false;
}
private:
std::vector<wil::StoredFailureInfo> m_failures;
wil::details::ThreadFailureCallbackHolder m_callbackHolder;
};
#endif
inline HRESULT GetTempFileName(wchar_t (&result)[MAX_PATH])
{
wchar_t dir[MAX_PATH];
RETURN_LAST_ERROR_IF(::GetTempPathW(MAX_PATH, dir) == 0);
RETURN_LAST_ERROR_IF(::GetTempFileNameW(dir, L"wil", 0, result) == 0);
return S_OK;
}
inline HRESULT CreateUniqueFolderPath(wchar_t (&buffer)[MAX_PATH], PCWSTR root = nullptr)
{
if (root)
{
RETURN_LAST_ERROR_IF(::GetTempFileNameW(root, L"wil", 0, buffer) == 0);
}
else
{
wchar_t tempPath[MAX_PATH];
RETURN_LAST_ERROR_IF(::GetTempPathW(ARRAYSIZE(tempPath), tempPath) == 0);
RETURN_LAST_ERROR_IF(::GetLongPathNameW(tempPath, tempPath, ARRAYSIZE(tempPath)) == 0);
RETURN_LAST_ERROR_IF(::GetTempFileNameW(tempPath, L"wil", 0, buffer) == 0);
}
RETURN_IF_WIN32_BOOL_FALSE(DeleteFileW(buffer));
PathCchRemoveExtension(buffer, ARRAYSIZE(buffer));
return S_OK;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
struct TestFolder
{
TestFolder()
{
if (SUCCEEDED(CreateUniqueFolderPath(m_path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path)))
{
m_valid = true;
}
}
TestFolder(PCWSTR path)
{
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path)))
{
m_valid = true;
}
}
TestFolder(const TestFolder&) = delete;
TestFolder& operator=(const TestFolder&) = delete;
TestFolder(TestFolder&& other)
{
if (other.m_valid)
{
m_valid = true;
other.m_valid = false;
wcscpy_s(m_path, other.m_path);
}
}
~TestFolder()
{
if (m_valid)
{
wil::RemoveDirectoryRecursiveNoThrow(m_path);
}
}
operator bool() const
{
return m_valid;
}
operator PCWSTR() const
{
return m_path;
}
PCWSTR Path() const
{
return m_path;
}
private:
bool m_valid = false;
wchar_t m_path[MAX_PATH] = L"";
};
struct TestFile
{
TestFile(PCWSTR path)
{
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)))
{
Create();
}
}
TestFile(PCWSTR dirPath, PCWSTR fileName)
{
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), dirPath)) && SUCCEEDED(PathCchAppend(m_path, ARRAYSIZE(m_path), fileName)))
{
Create();
}
}
TestFile(const TestFile&) = delete;
TestFile& operator=(const TestFile&) = delete;
TestFile(TestFile&& other)
{
if (other.m_valid)
{
m_valid = true;
m_deleteDir = other.m_deleteDir;
other.m_valid = other.m_deleteDir = false;
wcscpy_s(m_path, other.m_path);
}
}
~TestFile()
{
// Best effort on all of these
if (m_valid)
{
::DeleteFileW(m_path);
}
if (m_deleteDir)
{
size_t parentLength;
if (wil::try_get_parent_path_range(m_path, &parentLength))
{
m_path[parentLength] = L'\0';
::RemoveDirectoryW(m_path);
m_path[parentLength] = L'\\';
}
}
}
operator bool() const
{
return m_valid;
}
operator PCWSTR() const
{
return m_path;
}
PCWSTR Path() const
{
return m_path;
}
private:
HRESULT Create()
{
WI_ASSERT(!m_valid && !m_deleteDir);
wil::unique_hfile fileHandle(::CreateFileW(m_path,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
if (!fileHandle)
{
auto err = ::GetLastError();
size_t parentLength;
if ((err == ERROR_PATH_NOT_FOUND) && wil::try_get_parent_path_range(m_path, &parentLength))
{
m_path[parentLength] = L'\0';
RETURN_IF_FAILED(wil::CreateDirectoryDeepNoThrow(m_path));
m_deleteDir = true;
m_path[parentLength] = L'\\';
fileHandle.reset(::CreateFileW(m_path,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
RETURN_LAST_ERROR_IF(!fileHandle);
}
else
{
RETURN_WIN32(err);
}
}
m_valid = true;
return S_OK;
}
bool m_valid = false;
bool m_deleteDir = false;
wchar_t m_path[MAX_PATH] = L"";
};
#endif
}
#pragma warning(pop)

Просмотреть файл

@ -0,0 +1,25 @@
# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
# on compiler) as new standards are ratified/support is available
set(CMAKE_CXX_STANDARD 17)
project(witest.cpplatest)
add_executable(witest.cpplatest)
target_sources(witest.cpplatest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

7
tests/main.cpp Normal file
Просмотреть файл

@ -0,0 +1,7 @@
#pragma comment(lib, "Pathcch.lib")
#pragma comment(lib, "RuntimeObject.lib")
#pragma comment(lib, "Synchronization.lib")
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

Просмотреть файл

@ -0,0 +1,26 @@
project(witest.noexcept)
add_executable(witest.noexcept)
# Turn off exceptions for this test
replace_cxx_flag("/EHsc" "")
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)
# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
# statements... Thankfully MSVC just gives us a warning that we can disable
append_cxx_flag("/wd4530")
target_sources(witest.noexcept PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp)

Просмотреть файл

@ -0,0 +1,20 @@
project(witest)
add_executable(witest)
target_sources(witest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

107
tests/test_objects.h Normal file
Просмотреть файл

@ -0,0 +1,107 @@
#pragma once
#include "catch.hpp"
// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that
// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that
// require CopyConstructible (e.g. for wistd::function)
struct fail_on_copy
{
fail_on_copy() = default;
fail_on_copy(const fail_on_copy&)
{
FAIL("Copy constructor invoked for fail_on_copy type");
}
fail_on_copy(fail_on_copy&&) = default;
fail_on_copy& operator=(const fail_on_copy&)
{
FAIL("Copy assignment operator invoked for fail_on_copy type");
return *this;
}
fail_on_copy& operator=(fail_on_copy&&) = default;
};
// Useful for validating that objects get copied e.g. as opposed to capturing a reference
struct value_holder
{
int value = 0xbadf00d;
~value_holder()
{
value = 0xbadf00d;
}
};
// Useful for validating that functions, etc. are callable with move-only types
// Example real type that is move only is Microsoft::WRL::Wrappers::HString
struct cannot_copy
{
cannot_copy(const cannot_copy&) = delete;
cannot_copy& operator=(const cannot_copy&) = delete;
cannot_copy(cannot_copy&&) = default;
cannot_copy& operator=(cannot_copy&&) = default;
};
// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in
// contexts that require a default constructible type, but has the nice property that it allows for tests to run
// concurrently
struct object_counter_state
{
volatile LONG constructed_count = 0;
volatile LONG destructed_count = 0;
volatile LONG copy_count = 0;
volatile LONG move_count = 0;
LONG instance_count()
{
return constructed_count - destructed_count;
}
};
struct object_counter
{
object_counter_state* state;
object_counter(object_counter_state& s) :
state(&s)
{
::InterlockedIncrement(&state->constructed_count);
}
object_counter(const object_counter& other) :
state(other.state)
{
::InterlockedIncrement(&state->constructed_count);
::InterlockedIncrement(&state->copy_count);
}
object_counter(object_counter&& other) :
state(other.state)
{
::InterlockedIncrement(&state->constructed_count);
::InterlockedIncrement(&state->move_count);
}
~object_counter()
{
::InterlockedIncrement(&state->destructed_count);
state = nullptr;
}
object_counter& operator=(const object_counter&)
{
::InterlockedIncrement(&state->copy_count);
return *this;
}
object_counter& operator=(object_counter&&)
{
::InterlockedIncrement(&state->move_count);
return *this;
}
};

3448
tests/wiTest.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,2 @@
We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу