//---------------------------------------------------------------------------------------------------------------------- /// \file /// Test Authoring and Execution Framework native macro definitions // Copyright (c) Microsoft Corporation. All Rights Reserved. //---------------------------------------------------------------------------------------------------------------------- #pragma once #if defined(_MSC_VER) && _MSC_VER < 1910 # error Unsupported version of Visual Studio. WexTestClass.h requires VS 2017 or later. #endif #pragma warning(push) #pragma warning(disable:4481) #if defined(_WIN32) # pragma comment(lib, "TE.Common.lib") # pragma comment(lib, "Wex.Common.lib") # pragma comment(lib, "Wex.Logger.lib") #endif // Allow anyone who has defined an Assert macro to compile with this header file included #pragma push_macro("Assert") #undef Assert #include "Interruption.h" #include "RuntimeParameters.h" #include "TE.Common.h" #include "TestData.h" #include "Verify.h" #include "WexAssert.h" #include "WexDebug.h" #if !defined(_WIN32) #pragma push_macro("_In_") #pragma push_macro("_In_reads_z_") #pragma push_macro("_Outptr_") #pragma push_macro("_Outptr_result_maybenull_z_") #pragma push_macro("_Post_invalid_") #pragma push_macro("_Ret_maybenull_z_") #if !defined(_In_) #define _In_ #endif #if !defined(_In_reads_z_) #define _In_reads_z_(x) #endif #if !defined(_Outptr_) #define _Outptr_ #endif #if !defined(_Outptr_result_maybenull_z_) #define _Outptr_result_maybenull_z_ #endif #if !defined(_Post_invalid_) #define _Post_invalid_ #endif #if !defined(_Ret_maybenull_z_) #define _Ret_maybenull_z_ #endif #endif #if defined(_WIN32) // excpt.h is used for GetExceptionCode, which is used to detect C++ exceptions in a structured exception handler. #include #endif // #if defined(_WIN32) #if defined(__cpp_exceptions) # include "WexException.h" # include #endif /// \internal /// Wide string conversion helper #define TAEF_WIDEN_INT(x) L ## x /// \internal /// Wide string version of "stringizing" operator #define TAEF_WIDEN(x) TAEF_WIDEN_INT(x) /// \internal /// Stringize internal macro #define TAEF_STRINGIZE_INT(x) #x /// \internal /// Stringize macro #define TAEF_STRINGIZE(x) TAEF_STRINGIZE_INT(x) // TAEF_ABI_VERSION contains the version number of the binary interface that the native test loader depends on in order to parse the test module. #if defined(TAEF_ABI_VERSION) // TAEF_ABI_VERSION was user-defined. # if (TAEF_ABI_VERSION != 10) && (TAEF_ABI_VERSION != 11) && (TAEF_ABI_VERSION != 12) && (TAEF_ABI_VERSION != 13) # error TAEF_ABI_VERSION is set to an unsupported value. This version of WexTestClass.h only supports ABI versions 10 through 13. # endif # if (TAEF_ABI_VERSION == 10) && (defined(__clang__) || !defined(_WIN32)) # error TAEF ABI version 10 is no longer supported unless building with MSVC on Windows. Please switch to ABI version 13 or leave TAEF_ABI_VERSION undefined. # endif # if (TAEF_ABI_VERSION == 11) && !defined(_WIN32) # error TAEF ABI version 11 is only supported when building for Windows. Please switch to ABI version 13 or leave TAEF_ABI_VERSION undefined. # endif #else // TAEF_ABI_VERSION is not defined. Set it to the default value. // ARM64EC and ARM64X require ABI version 13; so we default to that for ARM64 and ARM64EC builds. Other architectures still default to 12 for now, // but the default will increase to 13 or greater in an upcoming TAEF version. # if defined(_M_ARM64) || defined(_M_ARM64EC) # define TAEF_ABI_VERSION 13 # else # define TAEF_ABI_VERSION 12 # endif #endif #if (TAEF_ABI_VERSION < 13) && defined(_M_ARM64EC) # error TAEF_ABI_VERSION must be defined to 13 in order to build tests as ARM64EC or ARM64X. #endif #if TAEF_ABI_VERSION == 10 #define TAEF_WIDEN_IF_ABI_10(x) TAEF_WIDEN_INT(x) #else #define TAEF_WIDEN_IF_ABI_10(x) x #endif /// \internal /// The magic number that leads testdata header. This is "TAEF" in ascii. #define TAEF_HEADER_SIGNATURE 0x46454154 // For non-Windows platforms, we force the INLINE_TEST_METHOD_MARKUP setting to be enabled. This is opt-in // for Windows for compatibility reasons. We would prefer to have it be enabled for all tests. #if !defined(_WIN32) && !defined(INLINE_TEST_METHOD_MARKUP) #define INLINE_TEST_METHOD_MARKUP #endif #if defined(INLINE_TEST_METHOD_MARKUP) #define TAEF_TERMINATOR #else #define TAEF_TERMINATOR ; #endif #if defined(__cpp_inline_variables) && __cpp_inline_variables && !defined(TAEF_AVOID_INLINE_STATICS) && !defined(TAEF_USE_INLINE_STATICS) # define TAEF_USE_INLINE_STATICS #endif #if defined(__cpp_constexpr) && __cpp_constexpr && !defined(TAEF_AVOID_CONSTEXPR) && !defined(TAEF_USE_CONSTEXPR) # define TAEF_USE_CONSTEXPR #endif #if defined(TAEF_USE_CONSTEXPR) // We mark the testdata section as read-only memory. The structs we place inside this section need to avoid dynamic initializers for two reasons: // * TAEF reflects over the DLL by reading directly from the testdata section without actually loading the DLL for execution. A dynamic initializer // will make it so the data is not written to the testdata section until the function containing the dynamic initializer is run. // * Since the testdata section is marked as read-only, the dynamic initializer will crash when it tries to write to the testdata section. // The test code can cause a dyanmic initializer to be used by doing something like setting metadata to a string that is the return value of // a function. Using constexpr causes these cases to be compiler errors. When constexpr is not available, we fall back to const although that does // not cause build breaks. Instead, you will get dynamic initializers which may result in crashes when the DLL loads. # define TAEF_CONST_TESTDATA constexpr #else # define TAEF_CONST_TESTDATA const #endif #if defined(_MSC_VER) #define TAEF_MSVC_SUPPRESS_WARNING(warningNumber) __pragma(warning(suppress:warningNumber)) #else #define TAEF_MSVC_SUPPRESS_WARNING(warningNumber) #endif // The WEX::Private namespace contains internal implementation details of TAEF. // Please do not use the contents of this namespace directly. They are referenced // via the public macros that tests should use. namespace WEX { namespace Private { // This function ignores its parameter and returns 0, but the compiler and linker don't // know that since it is a DLL import. Its purpose is to prevent the compiler and linker // from optimizing away any values passed to this function since they will be exposed to // a separate DLL. extern "C" WEXLOGGER_API int WEXLOGGER_STDCALL DoNotDiscard(const void*); #if defined(_WIN32) /// \internal /// /// A SEH filter. /// unsigned long inline DetectMSVCExceptions(unsigned long exceptionCode, bool& isCPlusPlusException) { // If the 'exceptionCode' is 0xE06D7363 (which is the exception code that MSVC uses to implement // C++ exceptions using SEH), then this is a C++ exception. Set isCPlusPlusException to indicate // this. This is used by the catch(...) handler which does a fast-fail if it is not a C++ // exception. The catch(...) will only be invoked for non-C++ exceptions if the /EHa switch is // used on the compiler while compiling the test DLL. isCPlusPlusException = (exceptionCode == 0xE06D7363); #if !defined(__cpp_exceptions) if (isCPlusPlusException) { ::WEX::Logging::Log::Error(L"A C++ exception was throw by a test in a DLL that was compiled with C++ exceptions disabled."); // In this case, if we let the exception continue the search, it would be caught by TAEF code. We want to avoid // catching the exception so the process can crash with the right context. // To fix this in a Visual Studio project, change the Configuration Properties -> C/C++ -> // Code Generation -> Enable C++ Exceptions setting in the project properties from "No" to "Yes (/EHsc)". // To fix this in the build.exe-based build system from the old Windows Driver Kit, add // "USE_NATIVE_EH=1" to the sources file. TAEF_FAST_FAIL; } #endif return EXCEPTION_CONTINUE_SEARCH; } template bool SafeInvoke_Impl(const TFunctor& functor, bool& isCPlusPlusException) { bool result = false; __try { result = functor(); } __except (::WEX::Private::DetectMSVCExceptions(GetExceptionCode(), isCPlusPlusException)) { // This is unreachable because DetectMSVCExceptions returns EXCEPTION_CONTINUE_SEARCH. TAEF_FAST_FAIL; } return result; } #endif // #if defined(_WIN32) #if TAEF_ABI_VERSION < 12 struct StringManager { template _Ret_maybenull_z_ static wchar_t* Allocate(const wchar_t (&szLiteral)[Size]) noexcept { return Allocate(szLiteral, Size - 1); } _Ret_maybenull_z_ static wchar_t* Allocate(const ::WEX::Common::NoThrowString& str) noexcept { if (str.IsValid()) { return Allocate(str, static_cast(str.GetLength())); } return nullptr; } _Ret_maybenull_z_ static wchar_t* Allocate(_In_reads_z_(length+1) const wchar_t* pszString, size_t length) noexcept { WEX_ASSERT(pszString[length] == L'\0', L"The string is not null-terminated."); const size_t allocationSize = sizeof(wchar_t) * (length + 1); wchar_t* pszRet = nullptr; #if defined(__cpp_exceptions) try { #endif // We aren't using "new wchar_t[length+1]" as that caused some linking problems for some existing tests. pszRet = static_cast(::operator new(allocationSize)); #if defined(__cpp_exceptions) } catch (const std::exception&) { // If new fails, pszRet will be null. } #endif if (pszRet) { WEX::TestExecution::Private::Memcpy(pszRet, allocationSize, pszString, allocationSize); } return pszRet; } static void TAEF_CDECL Deallocate(_In_ _Post_invalid_ wchar_t* pszString) { ::operator delete(static_cast(pszString)); } }; #endif // #if TAEF_ABI_VERSION < 12 /// \internal /// /// Identifiers for the structs that make up the native ABI. /// #if !defined(__cplusplus_cli) enum class TaefAbiStructIdentifier : uintptr_t #else enum TaefAbiStructIdentifier : uintptr_t #endif { TestClass = 1, TestMethod, ModuleSetup, ModuleCleanup, TestClassSetup, TestClassCleanup, TestMethodSetup, TestMethodCleanup, ModuleMetadata, TestClassMetadata, TestMethodMetadata, ModuleMetadataNarrow, TestClassMetadataNarrow, TestMethodMetadataNarrow, }; #if !defined(__cplusplus_cli) enum class TaefProcessorArchitecture : uintptr_t #else enum TaefProcessorArchitecture : uintptr_t #endif { Taef_Unrecognized, Taef_X86, Taef_X86_64, Taef_ARM, Taef_ARM64, Taef_ARM64EC, }; template HRESULT SafeInvoke(const TFunctor& functor) { // This is set to false if a structured exception which is not a C++ exception is raised by the test or fixture method. #if defined(_WIN32) bool exceptionIsCPlusPlusException = true; #endif // #if defined(_WIN32) #if defined(__cpp_exceptions) try { #endif #if defined(_WIN32) return ::WEX::Private::SafeInvoke_Impl(functor, exceptionIsCPlusPlusException) #else return functor() #endif ? TAEF_S_OK : TAEF_S_FALSE; #if defined(__cpp_exceptions) } # if !defined(NO_VERIFY_EXCEPTIONS) catch (const ::WEX::TestExecution::VerifyFailureException& e) { return e.ErrorCode(); } # endif catch (...) { # if defined(_WIN32) // exceptionIsCPlusPlusException will always be true if the test is not compiled with /EHa. if (!exceptionIsCPlusPlusException) { // This catch (...) just caught a structured exception which is not a C++ exception. // This means the test DLL was compiled using the /EHa switch on the compiler. // See https://msdn.microsoft.com/en-us/library/1deeycx5.aspx for the /EH documentation. // The use of /EHa is discouraged. Please consider switching to /EHsc, which instructs // the compiler to only catch C++ exceptions with catch (...) and to assume that functions // declared as extern "C" cannot throw. // // To fix this in a Visual Studio project, change the Configuration Properties -> C/C++ -> // Code Generation -> Enable C++ Exceptions setting in the project properties from // "Yes with SEH Exceptions (/EHa)" to "Yes (/EHsc)". // To fix this in the build.exe-based build system from the old Windows Driver Kit, change // "USE_NATIVE_EH=ASYNC" to "USE_NATIVE_EH=1" in the sources file. TAEF_FAST_FAIL; } # endif // #if defined(_WIN32) ::WEX::TestExecution::Private::StringStorage messageBuffer; HRESULT hr = TAEF_S_OK; ::WEX::Logging::Log::Error(::WEX::TestExecution::Private::GetExceptionInfo(messageBuffer, &hr)); return hr; } #endif // if defined(__cpp_exceptions) } #pragma warning(push) #pragma warning(disable:4702) // "unreachable code" #pragma warning(disable:6101) template struct TestClassFactory { /// \internal /// /// Creates an instance of a test class /// static HRESULT TAEF_CDECL CreateInstance(_Outptr_ void** ppInstance, #if TAEF_ABI_VERSION >= 12 _Outptr_result_maybenull_z_ char** ppszMessage) #else _Outptr_result_maybenull_z_ wchar_t** ppszMessage) #endif { *ppszMessage = nullptr; #if defined(_WIN32) // This is set to false if a structured exception which is not a C++ exception is raised by the class constructor. bool exceptionIsCPlusPlusException = true; #endif // #if defined(_WIN32) #if defined(__cpp_exceptions) try { #endif *ppInstance = #if defined(_WIN32) CreateInstanceWithTryExcept(exceptionIsCPlusPlusException); #else CallDefaultConstructor(); #endif if (!*ppInstance) { #if TAEF_ABI_VERSION >= 12 *ppszMessage = ::WEX::TestExecution::Private::StringStorage{"Failed to allocate the test class"}.Detach(); #else *ppszMessage = ::WEX::Private::StringManager::Allocate(L"Failed to allocate the test class"); #endif return TAEF_E_OUTOFMEMORY; } return TAEF_S_OK; #if defined(__cpp_exceptions) } # if TAEF_ABI_VERSION < 12 # if !defined(NO_VERIFY_EXCEPTIONS) catch (const ::WEX::TestExecution::VerifyFailureException&) { *ppszMessage = ::WEX::Private::StringManager::Allocate(L"Verify failure"); return TAEF_E_FAIL; } # endif // #if !defined(NO_VERIFY_EXCEPTIONS) # if defined(_WIN32) catch (const ::WEX::Common::Exception& ex) { *ppszMessage = ::WEX::Private::StringManager::Allocate( ::WEX::Common::NoThrowString().Format(L"Caught WEX::Common::Exception: %s", ex.Message())); return ex.ErrorCode(); } # endif // #if defined(_WIN32) # if defined(__cplusplus_winrt) catch (Platform::Exception^ ex) { *ppszMessage = ::WEX::Private::StringManager::Allocate( ::WEX::Common::NoThrowString().Format(L"Caught Platform::Exception^: %s", ex->Message->Data())); return ex->HResult; } # endif // #if defined(__cplusplus_winrt) # if defined(TAEF_CPPWINRT_SUPPORT) catch (const winrt::hresult_error& ex) { *ppszMessage = ::WEX::Private::StringManager::Allocate( ::WEX::Common::NoThrowString().Format(L"Caught winrt::hresult_error: %s", ex.message().c_str())); return ex.code(); } # endif // #if defined(TAEF_CPPWINRT_SUPPORT) # if defined(TAEF_SYSTEM_ERROR_SUPPORT) catch (const std::system_error& ex) { bool isExactConversion; const HRESULT hr = ::WEX::TestExecution::Private::GetHResultForStdErrorCode(ex.code(), isExactConversion); const char* const pszExceptionMessage = ex.what(); size_t messageLength = ::strlen(pszExceptionMessage); // std::system_error often ends its messages with a new-line on Windows. Exclude that from the length if it is present. while (messageLength > 0 && ::isspace(static_cast(pszExceptionMessage[messageLength - 1]))) { --messageLength; } if (messageLength > INT_MAX) { messageLength = INT_MAX; } // The HRESULT will be included in the final error message, so we only mention the error code if // the HRESULT was not an exact conversion. ::WEX::Common::NoThrowString message; if (isExactConversion) { message.Format(L"Caught std::system_error: %.*S", static_cast(messageLength), pszExceptionMessage); } else { message.Format(L"Caught std::system_error: %.*S [Error code: %d]", static_cast(messageLength), pszExceptionMessage, ex.code().value()); } *ppszMessage = ::WEX::Private::StringManager::Allocate(message); return hr; } # endif // #if defined(TAEF_SYSTEM_ERROR_SUPPORT) catch (const std::exception& ex) { *ppszMessage = ::WEX::Private::StringManager::Allocate( ::WEX::Common::NoThrowString().Format(L"Caught std::exception: %S", ex.what())); return TAEF_E_FAIL; } # endif // TAEF_ABI_VERSION < 12 catch (...) { # if defined(_WIN32) // exceptionIsCPlusPlusException will always be true if the test is not compiled with /EHa. if (!exceptionIsCPlusPlusException) { // This catch (...) just caught a structured exception which is not a C++ exception. // This means the test DLL was compiled using the /EHa switch on the compiler. // See https://msdn.microsoft.com/en-us/library/1deeycx5.aspx for the /EH documentation. // The use of /EHa is discouraged. Please consider switching to /EHsc, which instructs // the compiler to only catch C++ exceptions with catch (...) and to assume that functions // declared as extern "C" cannot throw. // // To fix this in a Visual Studio project, change the Configuration Properties -> C/C++ -> // Code Generation -> Enable C++ Exceptions setting in the project properties from // "Yes with SEH Exceptions (/EHa)" to "Yes (/EHsc)". // To fix this in the build.exe-based build system from the old Windows Driver Kit, change // "USE_NATIVE_EH=ASYNC" to "USE_NATIVE_EH=1" in the sources file. TAEF_FAST_FAIL; } # endif // #if defined(_WIN32) // For TAEF_ABI_VERSION >= 12, we only have a "catch (...)". We call GetExceptionInfo from Verify.h to get the exception details. // For TAEF_ABI_VERSION < 12, we have already caught more specific exception types above. The "catch (...)" is just for unrecognized // exception types in this case. # if TAEF_ABI_VERSION >= 12 ::WEX::TestExecution::Private::StringStorage messageStorage; HRESULT exceptionHResult{}; const char* pszMessage = reinterpret_cast(::WEX::TestExecution::Private::GetExceptionInfo(messageStorage, &exceptionHResult, false /* includeHResultInMessage */)); // Use the message from the messageStorage so it can be freed with the string deallocator in the test data header. // If messageStorage is already populated, pszMessage is already pointing to the buffer within messageStorage; // however, GetExceptionInfo just returns string literals in some cases without populating messageStorage. We need // to use messageStorage all the time. if (!messageStorage.Data()) { messageStorage.Set(pszMessage); } *ppszMessage = messageStorage.Detach(); return exceptionHResult; # else *ppszMessage = ::WEX::Private::StringManager::Allocate(L"Caught an unidentified C++ exception."); return TAEF_E_UNEXPECTED; # endif } #endif // if defined(__cpp_exceptions) } #if defined(_WIN32) /// \internal /// /// Creates an instance of a test class /// static T* TAEF_CDECL CreateInstanceWithTryExcept(bool& exceptionIsCPlusPlusException) { T* pInstance = nullptr; __try { // We have to call the constructor in a separate function to avoid error C2712. pInstance = CallDefaultConstructor(); } __except (::WEX::Private::DetectMSVCExceptions(GetExceptionCode(), exceptionIsCPlusPlusException)) { // This is unreachable because DetectMSVCExceptions returns EXCEPTION_CONTINUE_SEARCH. TAEF_FAST_FAIL; } return pInstance; } #endif // #if !defined(_WIN32) static T* TAEF_CDECL CallDefaultConstructor() { // What the caller links to controls the throwing behavior. We would prefer to use // new (std::nothrow) T(), but that doesn't link for some existing test binaries; so // we just use plain new. return new T(); } /// \internal /// /// Destroys an instance of a test class /// static void TAEF_CDECL DestroyInstance(void* pTestClass) { delete static_cast(pTestClass); } }; #pragma warning(pop) template struct TestInvokeFunctor { typedef void (TAEF_THISCALL T::*TestMethod)(); public: TestInvokeFunctor(T& instance, TestMethod pTestMethod) : m_pTestMethod(pTestMethod) , m_instance(instance) {} bool operator() () const { #if defined(__cpp_exceptions) && !defined(NO_VERIFY_EXCEPTIONS) try { #endif (m_instance.*m_pTestMethod)(); #if defined(__cpp_exceptions) && !defined(NO_VERIFY_EXCEPTIONS) } catch (const ::WEX::TestExecution::VerifyFailureException&) { // Don't re-throw here. The error is already tracked in WEX.Logger. } #endif return true; } private: TestInvokeFunctor(const TestInvokeFunctor&) = delete; TestInvokeFunctor& operator=(const TestInvokeFunctor&) = delete; // Store the pointer-to-member before the reference to avoid warning C4121 for tests // that use #pragma pointers_to_members(full_generality, virtual_inheritance). TestMethod m_pTestMethod; T& m_instance; }; template struct FixtureInvokeFunctor { typedef bool (TAEF_THISCALL T::*FixtureMethod)(); public: FixtureInvokeFunctor(T& instance, FixtureMethod pFixtureMethod) : m_pFixtureMethod(pFixtureMethod) , m_instance(instance) {} bool operator() () const { return (m_instance.*m_pFixtureMethod)(); } private: FixtureInvokeFunctor(const FixtureInvokeFunctor&) = delete; FixtureInvokeFunctor& operator=(const FixtureInvokeFunctor&) = delete; // Store the pointer-to-member before the reference to avoid warning C4121 for tests // that use #pragma pointers_to_members(full_generality, virtual_inheritance). FixtureMethod m_pFixtureMethod; T& m_instance; }; template struct MetadataIdentifierLookup; template struct MetadataIdentifierLookup { static_assert(sizeof(NameCharType) == sizeof(ValueCharType), "Invalid mix of character sizes in TAEF metadata. The name and value must use the same Unicode encoding type."); #if defined(_WIN32) static_assert(sizeof(NameCharType) == 1 || sizeof(NameCharType) == 2, "Unsupported character sizes in TAEF metadata. Metadata strings must be either UTF-8 or UTF-16."); #else static_assert(sizeof(NameCharType) == 1, "Unsupported character sizes in TAEF metadata. Metadata strings must be UTF-8 for non-Windows builds."); #endif static const ::WEX::Private::TaefAbiStructIdentifier c_moduleIdentifier = sizeof(NameCharType) == 1 ? ::WEX::Private::TaefAbiStructIdentifier::ModuleMetadataNarrow : ::WEX::Private::TaefAbiStructIdentifier::ModuleMetadata; static const ::WEX::Private::TaefAbiStructIdentifier c_classIdentifier = sizeof(NameCharType) == 1 ? ::WEX::Private::TaefAbiStructIdentifier::TestClassMetadataNarrow : ::WEX::Private::TaefAbiStructIdentifier::TestClassMetadata; static const ::WEX::Private::TaefAbiStructIdentifier c_methodIdentifier = sizeof(NameCharType) == 1 ? ::WEX::Private::TaefAbiStructIdentifier::TestMethodMetadataNarrow : ::WEX::Private::TaefAbiStructIdentifier::TestMethodMetadata; }; /// \internal /// /// Metadata storage classes /// Store various metadata about test classes in binary's "testdata" section /// /// \internal /// /// Test class related metadata storage class /// Resolves a class name to pointers to class creation and destruction functions /// struct TestClassInfo { using ClassCreatorWithUtf8ErrorString = HRESULT(TAEF_CDECL*)(_Outptr_ void** ppInstance, _Outptr_result_maybenull_z_ char** ppszMessage); using ClassCreatorWithWideErrorString = HRESULT(TAEF_CDECL*)(_Outptr_ void** ppInstance, _Outptr_result_maybenull_z_ wchar_t** ppszMessage); /// \internal /// Pointer to a class 'creation' function #if TAEF_ABI_VERSION >= 12 using ClassCreator = ClassCreatorWithUtf8ErrorString; #else using ClassCreator = ClassCreatorWithWideErrorString; #endif /// \internal /// Pointer to a class 'destroyer' function using ClassDestroyer = void(TAEF_CDECL*)(void*); TaefAbiStructIdentifier m_identifier; #if TAEF_ABI_VERSION >= 11 const char* m_pszClassName; #else const wchar_t* m_pszClassName; #endif ClassCreator m_pfnClassCreator; ClassDestroyer m_pfnClassDestroyer; }; typedef HRESULT (TAEF_CDECL *MethodInvokerFunction)(void* pTestClass); /// \internal /// /// Holds test method information that is relevant to the test loader. /// struct TestMethodInfo { TaefAbiStructIdentifier m_identifier; #if TAEF_ABI_VERSION >= 11 const char* m_pszMethodName; const char* m_pszQualifiedMethodName; #else const wchar_t* m_pszMethodName; const wchar_t* m_pszQualifiedMethodName; #endif uintptr_t m_index; MethodInvokerFunction m_pfnInvoker; }; /// \internal /// /// Holds test or class setup or cleanup method information that is relevant to the test loader. /// struct FixtureMethodInfo { TaefAbiStructIdentifier m_identifier; #if TAEF_ABI_VERSION >= 11 const char* m_pszMethodName; const char* m_pszQualifiedMethodName; #else const wchar_t* m_pszMethodName; const wchar_t* m_pszQualifiedMethodName; #endif MethodInvokerFunction m_pfnInvoker; }; /// \internal /// /// Holds various types of properties /// (module, class or function level) /// pszMetadataIdentifier identifies what kind of property is stored /// struct TestPropertyMetadata { TaefAbiStructIdentifier m_identifier; #if TAEF_ABI_VERSION >= 11 // Either "const wchar_t*" or "const char*", depending on the identifier. const void* m_pszPropertyName; const void* m_pszPropertyValue; #else const wchar_t* m_pszPropertyName; const wchar_t* m_pszPropertyValue; #endif }; /// \internal /// /// Dispatches module setup and cleanup function names to their corresponding pointers /// struct TestGlobalFunctionInfo { using TestModuleInvokerFunction = HRESULT(TAEF_CDECL*)(); TaefAbiStructIdentifier m_identifier; #if TAEF_ABI_VERSION >= 11 const char* m_pszFunctionName; #else const wchar_t* m_pszFunctionName; #endif TestModuleInvokerFunction m_pfnInvoker; }; /// \internal /// /// Holds framework version info /// struct TestVersionInfo { using DeallocatorForUtf8Strings = void(TAEF_CDECL*)(_In_ _Post_invalid_ char* pStr); using DeallocatorForWideStrings = void(TAEF_CDECL*)(_In_ _Post_invalid_ wchar_t* pStr); #if TAEF_ABI_VERSION >= 12 using Deallocator = DeallocatorForUtf8Strings; #else using Deallocator = DeallocatorForWideStrings; #endif uintptr_t m_headerSignature; uintptr_t m_abiVersion; const char* m_pszGenerator; #if TAEF_ABI_VERSION >= 13 TaefProcessorArchitecture m_processorArchitecture; #endif Deallocator m_pfnDeallocator; }; }/* namespace Private */}/* namespace WEX */ // Add types to the WEX namespace that are needed for compatibility. namespace WEX { // TAEF test classes used to be required to inherit from this class. There is no need at all to // use it anymore, but it is declared here to prevent build breaks for tests that still use it. template class TestClass {}; // Some tests take dependencies on internal TAEF implementation details. These definitions are // here to enable those tests to continue to build. Please do not use these in new tests. #if defined(TAEF_INCLUDE_WEX_DECLARATIONS_FOR_COMPAT) namespace TAEF_Identifier { enum Value : uintptr_t { TestClassInfo = 1, TestMethodInfo, ModuleSetup, ModuleCleanup, TestClassSetup, TestClassCleanup, TestMethodSetup, TestMethodCleanup, ModuleMetadata, TestClassMetadata, TestMethodMetadata }; } template using FixtureInvokeFunctor = ::WEX::Private::FixtureInvokeFunctor; using TestClassInfo = ::WEX::Private::TestClassInfo; using TestMethodInfo = ::WEX::Private::TestMethodInfo; using FixtureMethodInfo = ::WEX::Private::FixtureMethodInfo; using TestPropertyMetadata = ::WEX::Private::TestPropertyMetadata; using TestGlobalFunctionInfo = ::WEX::Private::TestGlobalFunctionInfo; using TestVersionInfo = ::WEX::Private::TestVersionInfo; #endif // TAEF__WFUNCTION__ was formerly used by TAEF's implementation. Many tests took a dependency on it; so it is // preserved here to enable those tests to continue to build. If your test was using this just to pass the file, // function, and line number to APIs in the WEX::Logging::Log class, use the WEX_LOGGER_CURRENT_SOURCE_INFO // macro instead. It expands to the file, function, and line number separated by commas so it can be passed to // the Log APIs. The VC++ CRT also has an equivalent macro named __FUNCTIONW__. #if defined(_MSC_VER) && !defined(__clang__) #define TAEF__WFUNCTION__ TAEF_WIDEN(__FUNCTION__) #endif // SafeInvoke is used more frequently than some of the other TAEF internals; so we include it // in the WEX namespace without tests having to opt-in. template HRESULT SafeInvoke(const TFunctor& functor) { return ::WEX::Private::SafeInvoke(functor); } }/* namespace WEX */ #if defined(_WIN32) #pragma section("testdata$a_TDH", read) #pragma section("testdata$b_TCI", read) #pragma section("testdata$c_TMI", read) #pragma section("testdata$d_TGFI", read) #pragma section("testdata$e_FMI", read) #pragma section("testdata$f_MM", read) #pragma section("testdata$g_TCM", read) #pragma section("testdata$h_TMM", read) #define TAEF_TESTDATA_SECTION2(sectionName) __declspec(allocate(#sectionName)) #define TAEF_TESTDATA_SECTION(sectionSuffix) TAEF_TESTDATA_SECTION2(testdata ## sectionSuffix) #else # if defined(__clang__) # define TAEF_TESTDATA_SECTION(sectionSuffix) __attribute__((section("testdata"))) # else /* gcc on Linux*/ # define TAEF_PRIVATE_CAT2(x,y) x##y # define TAEF_PRIVATE_CONCAT(x,y) TAEF_PRIVATE_CAT2(x,y) # define TAEF_TESTDATA_SECTION(sectionsuffix) __attribute__((section(TAEF_STRINGIZE(TAEF_PRIVATE_CONCAT(testdata., __COUNTER__))))) # endif #endif // The IntelliSense compiler reports errors when trying to parse 'TAEF_PIN_FUNCTION_SYMBOL', so it should be skipped. #if defined(__INTELLISENSE__) # define TAEF_PIN_FUNCTION_SYMBOL #elif defined(__clang__) || defined(__GNUC__) /// \internal /// Instruct the compiler not to remove the function from the obj file during the optimization phase # define TAEF_PIN_FUNCTION_SYMBOL __attribute__((used)) #else /// \internal /// Instruct the compiler not to remove the function from the obj file during the optimization phase #if defined(TAEF_USE_INLINE_STATICS) # define TAEF_PIN_FUNCTION_SYMBOL #else # define TAEF_PIN_FUNCTION_SYMBOL __pragma (comment(linker, "/include:" __FUNCDNAME__)) #endif #endif #if defined(_MSC_VER) && !defined(__clang__) # define TAEF_QUALIFIED_METHOD_NAME TAEF_WIDEN_IF_ABI_10(__FUNCTION__) #else # define TAEF_QUALIFIED_METHOD_NAME __PRETTY_FUNCTION__ #endif #if defined(_MSC_VER) // Ignore the following warnings: // * 28651: static initializer causes copy on write pages due to member function pointers // * 4640: construction of local static object is not thread-safe // * 4407: cast between different pointer to member representations, compiler may generate incorrect code #define TAEF_PUSH_IGNORE_WARNINGS __pragma (warning(push)) \ __pragma (warning(disable:28651)) \ __pragma (warning(disable:4640)) \ __pragma (warning(disable:4407)) #define TAEF_POP_IGNORE_WARNINGS __pragma (warning(pop)) #else #define TAEF_PUSH_IGNORE_WARNINGS #define TAEF_POP_IGNORE_WARNINGS #endif #if defined(__INTELLISENSE__) // The IntelliSense compiler reports errors when trying to parse TAEF_REGISTER_FIXTURE_METHOD and TAEF_REGISTER_TEST_METHOD. # define TAEF_REGISTER_FIXTURE_METHOD(methodName, methodType, registrationMethodName) # define TAEF_REGISTER_TEST_METHOD(methodName) #else // TAEF_REGISTER_FIXTURE_METHOD uses the same value for methodName and methodToInvoke. The macros that start with "TAEF_" in this file are not meant // for tests to use directly. However, some tests took a dependency on TAEF internals to enable the use of a separate wrapper function that invokes // the actual fixture. This macro was added so those tests could be updated to use this macro instead of having their own copies of the TAEF macros. # define TAEF_REGISTER_FIXTURE_METHOD_IMPL(methodName, methodToInvoke, methodType, registrationMethodName) \ static TAEF_CPP_EXPORT const ::WEX::Private::FixtureMethodInfo* registrationMethodName() \ { \ struct TAEF_FixtureInvoker \ { \ static HRESULT TAEF_CDECL TAEF_Invoke(void* pTestClass) \ { \ /* TAEF_TestClassType was typedef'd in the TEST_CLASS macro so we can safely cast from \ void* to TAEF_TestClassType* and invoke the correct derived type's member functions */ \ return ::WEX::Private::SafeInvoke(::WEX::Private::FixtureInvokeFunctor( \ *reinterpret_cast(pTestClass), &TAEF_TestClassType::methodToInvoke)); \ } \ }; \ TAEF_PUSH_IGNORE_WARNINGS \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($e_FMI) \ static TAEF_CONST_TESTDATA ::WEX::Private::FixtureMethodInfo s_testInfo = { \ methodType, TAEF_WIDEN_IF_ABI_10(#methodName), TAEF_QUALIFIED_METHOD_NAME, &TAEF_FixtureInvoker::TAEF_Invoke}; \ TAEF_POP_IGNORE_WARNINGS \ return &s_testInfo; /* Return a pointer to s_testInfo in order to pin the struct into the dll so it does not get stripped out by the linker. */ \ } \ TAEF_COMPILER_PIN(registrationMethodName) # define TAEF_REGISTER_FIXTURE_METHOD(methodName, methodType, registrationMethodName) \ TAEF_REGISTER_FIXTURE_METHOD_IMPL(methodName, methodName, methodType, registrationMethodName) // TAEF_REGISTER_TEST_METHOD uses the same value for methodName and methodToInvoke. The macros that start with "TAEF_" in this file are not meant // for tests to use directly. However, some tests took a dependency on TAEF internals to enable the use of a separate wrapper function that invokes // the actual test. This macro was added so those tests could be updated to use this macro instead of having their own copies of the TAEF macros. # define TAEF_REGISTER_TEST_METHOD_IMPL(methodName, methodToInvoke) \ static const TAEF_CPP_EXPORT ::WEX::Private::TestMethodInfo* methodName##_TAEF_PinTestMethodInfo() \ { \ struct TAEF_TestInvoker \ { \ static HRESULT TAEF_CDECL TAEF_Invoke(void* pTestClass) \ { \ /* TAEF_TestClassType was typedef'd in the TEST_CLASS macro so we can safely cast from \ void* to TAEF_TestClassType* and invoke the correct type's member functions */ \ return ::WEX::Private::SafeInvoke(::WEX::Private::TestInvokeFunctor( \ *reinterpret_cast(pTestClass), &TAEF_TestClassType::methodToInvoke)); \ } \ }; \ TAEF_PUSH_IGNORE_WARNINGS \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($c_TMI) \ static TAEF_CONST_TESTDATA ::WEX::Private::TestMethodInfo s_testInfo = { \ ::WEX::Private::TaefAbiStructIdentifier::TestMethod, TAEF_WIDEN_IF_ABI_10(#methodName), TAEF_QUALIFIED_METHOD_NAME, \ __COUNTER__ - TAEF_TestMethodIndexOffset, &TAEF_TestInvoker::TAEF_Invoke }; \ TAEF_POP_IGNORE_WARNINGS \ return &s_testInfo; /* Return a pointer to s_testInfo in order to pin the struct into the dll so it does not get stripped out by the linker. */ \ } \ TAEF_COMPILER_PIN(methodName##_TAEF_PinTestMethodInfo) # define TAEF_REGISTER_TEST_METHOD(methodName) \ TAEF_REGISTER_TEST_METHOD_IMPL(methodName, methodName) #endif #if defined(TAEF_USE_INLINE_STATICS) # define TAEF_COMPILER_PIN(methodName) \ inline static int s_doNotDiscard_##methodName = ::WEX::Private::DoNotDiscard(methodName()); # define TAEF_CPP_EXPORT # define TAEF_STATIC_IF_CALLED_BY_TAEF_COMPILER_PIN static #else # define TAEF_COMPILER_PIN(methodName) # define TAEF_CPP_EXPORT __declspec(dllexport) # define TAEF_STATIC_IF_CALLED_BY_TAEF_COMPILER_PIN #endif // This macro is used directly by some non-TAEF teams. Do not remove it unless those cases are fixed. #define TAEF_TEST_METHOD(methodName) \ TAEF_REGISTER_TEST_METHOD(methodName) \ TAEF_MSVC_SUPPRESS_WARNING(25007) /* Disable warning that member method may be static */ \ void methodName() /// /// Macro for declaring a test method without associating it with any metadata /// /// Example: /// \code /// class TestFeatureClass /// { /// TEST_METHOD(FeatureTestMethod1); /// } /// \endcode #define TEST_METHOD(methodName) \ TAEF_TEST_METHOD(methodName) TAEF_TERMINATOR #if TAEF_ABI_VERSION == 10 #define TAEF_MODULE_METADATA_IDENTIFIER ::WEX::Private::TaefAbiStructIdentifier::ModuleMetadata #define TAEF_TEST_CLASS_METADATA_IDENTIFIER ::WEX::Private::TaefAbiStructIdentifier::TestClassMetadata #define TAEF_TEST_METHOD_METADATA_IDENTIFIER ::WEX::Private::TaefAbiStructIdentifier::TestMethodMetadata #else #define TAEF_MODULE_METADATA_IDENTIFIER ::WEX::Private::TaefAbiStructIdentifier::ModuleMetadataNarrow #define TAEF_TEST_CLASS_METADATA_IDENTIFIER ::WEX::Private::TaefAbiStructIdentifier::TestClassMetadataNarrow #define TAEF_TEST_METHOD_METADATA_IDENTIFIER ::WEX::Private::TaefAbiStructIdentifier::TestMethodMetadataNarrow #endif // Do not use TAEF_TEST_METHOD_METADATA_START/END directly. Instead use // BEGIN_TEST_METHOD/END_TEST_METHOD at the class scope or // BEGIN_TEST_METHOD_PROPERTIES/END_TEST_METHOD_PROPERTIES inside the test method. #define TAEF_TEST_METHOD_METADATA_START \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($h_TMM) \ static TAEF_CONST_TESTDATA ::WEX::Private::TestPropertyMetadata s_Metadata[] = { \ { TAEF_TEST_METHOD_METADATA_IDENTIFIER, TAEF_WIDEN_IF_ABI_10("Name"), TAEF_QUALIFIED_METHOD_NAME}, \ #define TAEF_TEST_METHOD_METADATA_END \ { TAEF_TEST_METHOD_METADATA_IDENTIFIER, nullptr, nullptr}}; \ return s_Metadata; // TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE and TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE // are not intended to be used directly by tests, but some tests do use it; so we preserve it for // compatibility. Use BEGIN_TEST_METHOD/END_TEST_METHOD instead of this. #if defined(TAEF_USE_INLINE_STATICS) # define TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE(methodName) \ struct methodName##TAEF_MetadataStruct \ { \ inline static int s_doNotDiscard_##methodName##_GetTestMethodMetadata = ::WEX::Private::DoNotDiscard([]() \ { \ TAEF_TEST_METHOD_METADATA_START \ # define TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE() \ TAEF_TEST_METHOD_METADATA_END \ }()); \ }; #else # define TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE(methodName) \ static TAEF_CPP_EXPORT const ::WEX::Private::TestPropertyMetadata* methodName ## _GetTestMethodMetadata() \ { \ TAEF_TEST_METHOD_METADATA_START \ # define TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE() \ TAEF_TEST_METHOD_METADATA_END \ } #endif /// /// Macro for declaring a test method and associating it with metadata /// /// Example: /// \code /// class TestFeatureClass /// { /// BEGIN_TEST_METHOD(TestFindNext) /// TEST_METHOD_PROPERTY(L"Priority", L"2") /// END_TEST_METHOD() /// } /// \endcode # define BEGIN_TEST_METHOD(methodName) \ TEST_METHOD(methodName); \ TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE(methodName) /// /// Macro that ends test method declaration. /// Must be used with BEGIN_TEST_METHOD() /// # define END_TEST_METHOD() \ TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE() /// /// Macro for declaring metadata within inline test methods. /// /// Example: /// \code /// class TestFeatureClass /// { /// TEST_METHOD(TestFindNext) /// { /// BEGIN_TEST_METHOD_PROPERTIES() /// TEST_METHOD_PROPERTY(L"Priority", L"2") /// END_TEST_METHOD_PROPERTIES() /// /// // Your test code /// } /// } /// \endcode #define BEGIN_TEST_METHOD_PROPERTIES() \ struct TAEF_TestMethodProperties \ { \ static const ::WEX::Private::TestPropertyMetadata* TAEF_GetTestMethodMetadata() \ { \ TAEF_TEST_METHOD_METADATA_START /// /// Macro that ends inline test method property declaration. /// Must be used with BEGIN_TEST_METHOD_PROPERTIES() /// #define END_TEST_METHOD_PROPERTIES() \ TAEF_TEST_METHOD_METADATA_END \ } \ }; \ ::WEX::Private::DoNotDiscard(TAEF_TestMethodProperties::TAEF_GetTestMethodMetadata()); /// /// Macro for adding a piece of metadata to a test method. /// Must be used with BEGIN_TEST_METHOD() / END_TEST_METHOD() or within BEGIN_TEST_METHOD_PROPERTIES() / END_TEST_METHOD_PROPERTIES() /// #define TEST_METHOD_PROPERTY(propertyName, propertyValue) \ { ::WEX::Private::MetadataIdentifierLookup::c_methodIdentifier, propertyName, propertyValue }, /// /// Macro for defining module properties /// /// Example: /// \code /// BEGIN_MODULE() /// MODULE_PROPERTY(L"Area", L"Desktop Shell") /// MODULE_PROPERTY(L"SubArea", L"Navigation") /// MODULE_PROPERTY(L"Component", L"Start Menu") /// END_MODULE() /// \endcode #define BEGIN_MODULE() \ TAEF_STATIC_IF_CALLED_BY_TAEF_COMPILER_PIN TAEF_CPP_EXPORT const ::WEX::Private::TestPropertyMetadata* TAEF_GetModuleMetadata() \ { \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($f_MM) \ static TAEF_CONST_TESTDATA ::WEX::Private::TestPropertyMetadata s_Metadata[] = { \ { TAEF_MODULE_METADATA_IDENTIFIER, TAEF_WIDEN_IF_ABI_10("Name"), TAEF_WIDEN_IF_ABI_10("___##TestFile##___") }, // BEGIN_MODULE/END_MODULE is intended to be used outside of a class. However, some tests took // advantage of the fact that it happened to work when placed within a class. To prevent build // breaks for those tests, we now use static on the TAEF_GetModuleMetadata function when it is // called within the TAEF_COMPILER_PIN macro. This avoids build breaks when BEGIN_MODULE/END_MODULE // is used within a class. We don't use static when exporting though because that can cause build // breaks for Clang since we would be exporting a function without external linkage. /// /// Macro for adding a piece of metadata (a property) to a test module propeties definition. /// Must be used with BEGIN_MODULE() / END_MODULE() /// #define MODULE_PROPERTY(propertyName, propertyValue) \ { ::WEX::Private::MetadataIdentifierLookup::c_moduleIdentifier, propertyName, propertyValue }, /// /// Macro that ends test module properties definition. /// Must be used with BEGIN_MODULE() /// #define END_MODULE() \ { TAEF_MODULE_METADATA_IDENTIFIER, nullptr, nullptr } \ }; \ return s_Metadata; \ } \ TAEF_COMPILER_PIN(TAEF_GetModuleMetadata) /// /// Macro for declaring a test class without associating it with any metadata /// /// Example: /// \code /// class TestFeatureClass /// { /// TEST_CLASS(TestFeatureClass) /// } /// \endcode #define TEST_CLASS(className) \ typedef className TAEF_TestClassType; \ static const uintptr_t TAEF_TestMethodIndexOffset = __COUNTER__; \ friend struct ::WEX::Private::TestClassFactory; \ typedef bool (className::*TAEF_MemberMaintFunc)(); \ TAEF_MSVC_SUPPRESS_WARNING(25007) /* Disable warning that member method may be static */ \ bool TAEF_DummyMaintFunc() { return true; } \ /* Tests should not call this method. It is an internal TAEF implementation detail that is subject to change. */ \ static TAEF_CPP_EXPORT const ::WEX::Private::TestClassInfo* TAEF_GetTestClassInfo() \ { \ /* this works similarly to WEX::Common::Conversion checking if an expression (&_DummyMaintFunc) \ * is convertible to the MemberMaintFunc type \ */ \ struct TaefClassNameTester { \ /* these functions are only implemented because they're in an local class, so the compiler warns about missing definitions */ \ static char TestType(TAEF_MemberMaintFunc) \ { \ return 'c'; \ } \ static TaefClassNameTester TestType(...) \ { \ return TaefClassNameTester(); \ } \ char member[2]; /* a two element array to ensure a different size from char returned by TestType(MemberMaintFunc) */ \ }; \ static_assert(sizeof(TaefClassNameTester::TestType(&TAEF_TestClassType::TAEF_DummyMaintFunc)) == 1, \ TAEF_STRINGIZE(className) " is not the name of the current class"); \ \ TAEF_PUSH_IGNORE_WARNINGS \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($b_TCI) \ static TAEF_CONST_TESTDATA ::WEX::Private::TestClassInfo s_ClassInfo = \ {::WEX::Private::TaefAbiStructIdentifier::TestClass, TAEF_QUALIFIED_METHOD_NAME, \ &::WEX::Private::TestClassFactory::CreateInstance, &::WEX::Private::TestClassFactory::DestroyInstance }; \ TAEF_POP_IGNORE_WARNINGS \ return &s_ClassInfo; \ } \ TAEF_COMPILER_PIN(TAEF_GetTestClassInfo) /// /// Macro for declaring a test class and associating it with metadata /// /// Example: /// \code /// class FeatureTestClass /// { /// BEGIN_TEST_CLASS(FeatureTestClass) /// TEST_CLASS_PROPERTY(L"BVT", L"TRUE") /// TEST_CLASS_PROPERTY(L"STRESS", L"TRUE") /// END_TEST_CLASS() /// } /// \endcode #define BEGIN_TEST_CLASS(className) \ TEST_CLASS(className) \ \ static TAEF_CPP_EXPORT const ::WEX::Private::TestPropertyMetadata* TAEF_GetClassMetadata() \ { \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($g_TCM) \ static TAEF_CONST_TESTDATA ::WEX::Private::TestPropertyMetadata s_Metadata[] = { \ { TAEF_TEST_CLASS_METADATA_IDENTIFIER, TAEF_WIDEN_IF_ABI_10("Name"), TAEF_QUALIFIED_METHOD_NAME}, \ /// /// Macro for adding a piece of metadata to a test class declaration. /// Must be used with BEGIN_TEST_CLASS() / END_TEST_CLASS() /// #define TEST_CLASS_PROPERTY(propertyName, propertyValue) \ { ::WEX::Private::MetadataIdentifierLookup::c_classIdentifier, propertyName, propertyValue }, /// /// Macro that ends test class declaration. /// Must be used with BEGIN_TEST_CLASS() /// #define END_TEST_CLASS() \ { TAEF_TEST_CLASS_METADATA_IDENTIFIER, nullptr, nullptr} \ }; \ return s_Metadata; \ } \ TAEF_COMPILER_PIN(TAEF_GetClassMetadata) #define TAEF_MODULE_FIXTURE_INFO_IMPL(methodName, moduleFixtureType) \ struct TAEF_ModuleFixtureInvoker \ { \ static HRESULT TAEF_CDECL TAEF_Invoke() \ { \ return ::WEX::Private::SafeInvoke(methodName); \ } \ }; \ \ TAEF_PUSH_IGNORE_WARNINGS \ TAEF_PIN_FUNCTION_SYMBOL TAEF_TESTDATA_SECTION($d_TGFI) \ static TAEF_CONST_TESTDATA ::WEX::Private::TestGlobalFunctionInfo s_moduleFixtureInfo = \ {moduleFixtureType, TAEF_WIDEN_IF_ABI_10(#methodName), &TAEF_ModuleFixtureInvoker::TAEF_Invoke }; \ TAEF_POP_IGNORE_WARNINGS \ return &s_moduleFixtureInfo; #define TAEF_FIXTURE_IMPL(methodName) \ TAEF_MSVC_SUPPRESS_WARNING(25007) /* Disable warning that member method may be static */ \ bool methodName() #if defined(INLINE_TEST_METHOD_MARKUP) #define TAEF_DEFINE_FIXTURE(methodName) \ TAEF_FIXTURE_IMPL(methodName) #else #define TAEF_DEFINE_FIXTURE(methodName) #endif #if defined(INLINE_TEST_METHOD_MARKUP) #define TAEF_DECLARE_FIXTURE(methodName) #else #define TAEF_DECLARE_FIXTURE(methodName) \ __if_not_exists(methodName) \ { \ TAEF_FIXTURE_IMPL(methodName); \ } #endif #define TAEF_DECLARE_OR_DEFINE_FIXTURE(methodName) \ TAEF_DECLARE_FIXTURE(methodName) \ TAEF_DEFINE_FIXTURE(methodName) /// /// Macro for defining global Setup function /// /// The Setup function runs before any test is executed /// Example: /// \code /// /// MODULE_SETUP(AddRegistrySettings) //AddRegistrySettings function adds necessary reg settings for all tests /// /// \endcode #define MODULE_SETUP(methodName) \ TAEF_FIXTURE_IMPL(methodName); \ TAEF_CPP_EXPORT const ::WEX::Private::TestGlobalFunctionInfo* YOU_CAN_ONLY_DESIGNATE_ONE_FUNCTION_TO_BE_A_MODULE_SETUP_FUNCTION() \ { \ TAEF_MODULE_FIXTURE_INFO_IMPL(methodName, ::WEX::Private::TaefAbiStructIdentifier::ModuleSetup) \ } \ TAEF_COMPILER_PIN(YOU_CAN_ONLY_DESIGNATE_ONE_FUNCTION_TO_BE_A_MODULE_SETUP_FUNCTION) \ TAEF_DEFINE_FIXTURE(methodName) /// /// Macro for defining global Teardown function /// /// The Teardown function runs after all the test are executed /// Example: /// \code /// /// MODULE_CLEANUP(CleanupRegistrySettings) //CleanupRegistrySettings restores the registry after all tests /// /// \endcode #define MODULE_CLEANUP(methodName) \ TAEF_FIXTURE_IMPL(methodName); \ TAEF_CPP_EXPORT const ::WEX::Private::TestGlobalFunctionInfo* YOU_CAN_ONLY_DESIGNATE_ONE_FUNCTION_TO_BE_A_MODULE_CLEANUP_FUNCTION() \ { \ TAEF_MODULE_FIXTURE_INFO_IMPL(methodName, ::WEX::Private::TaefAbiStructIdentifier::ModuleCleanup) \ } \ TAEF_COMPILER_PIN(YOU_CAN_ONLY_DESIGNATE_ONE_FUNCTION_TO_BE_A_MODULE_CLEANUP_FUNCTION) \ TAEF_DEFINE_FIXTURE(methodName) /// /// Macro for test setup method declaration /// /// Test setup method gets called before every test method is called /// Example: /// \code /// class NotepadTestClass /// { /// // Declare CopyDownTestFiles class method and designate this method to be a test setup method /// TEST_METHOD_SETUP(CopyDownTestFiles); /// } /// \endcode #define TEST_METHOD_SETUP(methodName) \ TAEF_REGISTER_FIXTURE_METHOD(methodName, ::WEX::Private::TaefAbiStructIdentifier::TestMethodSetup, \ YOU_CAN_ONLY_DESIGNATE_ONE_CLASS_METHOD_TO_BE_A_TEST_METHOD_SETUP_METHOD) \ TAEF_DECLARE_OR_DEFINE_FIXTURE(methodName) /// /// Macros for test cleanup method declaration /// /// Test cleanup method gets called after every test method is called /// Example: /// \code /// class NotepadTestClass /// { /// // Declare DeleteCopiedTestFiles class method and designate this method to be a test cleanup method /// TEST_METHOD_CLEANUP(DeleteCopiedTestFiles); /// } /// \endcode #define TEST_METHOD_CLEANUP(methodName) \ TAEF_REGISTER_FIXTURE_METHOD(methodName, ::WEX::Private::TaefAbiStructIdentifier::TestMethodCleanup, \ YOU_CAN_ONLY_DESIGNATE_ONE_CLASS_METHOD_TO_BE_A_TEST_METHOD_CLEANUP_METHOD) \ TAEF_DECLARE_OR_DEFINE_FIXTURE(methodName) /// /// Macro for test class setup method declaration /// /// Class setup method gets called before the first method in the class is called (after the class constructor) /// Example: /// \code /// class NotepadTestClass /// { /// // Declare InstallNotepad class method and designate this method to be a class setup method /// TEST_CLASS_CLEANUP(InstallNotepad); /// } /// \endcode #define TEST_CLASS_SETUP(methodName) \ TAEF_REGISTER_FIXTURE_METHOD(methodName, ::WEX::Private::TaefAbiStructIdentifier::TestClassSetup, \ YOU_CAN_ONLY_DESIGNATE_ONE_CLASS_METHOD_TO_BE_A_TEST_CLASS_SETUP_METHOD) \ TAEF_DECLARE_OR_DEFINE_FIXTURE(methodName) /// /// Macro for test class cleanup method declaration /// /// Class cleanup method gets called after the last method in the class is called (before the destructor) /// Example: /// \code /// class NotepadTestClass /// { /// // Declare UninstallNotepad class method and designate this method to be a class cleanup method /// TEST_CLASS_CLEANUP(UninstallNotepad); /// } /// \endcode #define TEST_CLASS_CLEANUP(methodName) \ TAEF_REGISTER_FIXTURE_METHOD(methodName, ::WEX::Private::TaefAbiStructIdentifier::TestClassCleanup, \ YOU_CAN_ONLY_DESIGNATE_ONE_CLASS_METHOD_TO_BE_A_TEST_CLASS_CLEANUP_METHOD) \ TAEF_DECLARE_OR_DEFINE_FIXTURE(methodName) // Add the TestVersionInfo header to the testdata section. namespace WEX { namespace Private { // We need to ensure the TestVersionInfo is not discared by the linker. // For Windows binaries, we use a pragma to do this. For non-Windows binaries, we use the Wex.Logger's // DoNotDiscard API. The DoNotDiscard approach would work for Windows too, but the pragma has the advantage // that it avoids adding a Wex.Logger.dll dependency to existing binaries which include WexTestClass.h // without using a test macro. There are many existing cases of that on Windows; so using DoNotDiscard here // would force them to take the Wex.Logger dependency. #if defined(_WIN32) // The TAEF_TESTDATA_SECTION macro will cause MSVC's linker to place the data in the "testdata" section. // The suffix after the dollar sign is used to order the data within the section. A suffix that starts with // "a" will be before any data with a suffix that starts with "b", "c", etc. Using "a_TDH" for the header // ensures the header appears before the rest of the data within the testdata section. extern "C" TAEF_TESTDATA_SECTION($a_TDH) __declspec(selectany) TestVersionInfo s_versionInfo = { TAEF_HEADER_SIGNATURE, TAEF_ABI_VERSION, "WexTestClass.h", # if TAEF_ABI_VERSION >= 13 // Internal detail: Generally we can determine the architecture from the PE file headers, but // ARM64X binaries can have both ARM64 and ARM64EC test data in the same DLL. Having the // architecture in TestVersionInfo enables TAEF to tell which data belongs to which architecture. # if defined(_M_IX86) TaefProcessorArchitecture::Taef_X86, # elif defined(_M_ARM64EC) TaefProcessorArchitecture::Taef_ARM64EC, # elif defined(_M_X64) TaefProcessorArchitecture::Taef_X86_64, # elif defined(_M_ARM64) TaefProcessorArchitecture::Taef_ARM64, # elif defined(_M_ARM) TaefProcessorArchitecture::Taef_ARM, # else TaefProcessorArchitecture::Taef_Unrecognized, # endif # endif // TAEF_ABI_VERSION >= 13 # if TAEF_ABI_VERSION >= 12 ::WEX::TestExecution::Private::StringStorage::TaefAbiStringDeallocator # else StringManager::Deallocate # endif }; # if defined(_M_IX86) # pragma comment(linker, "/include:_s_versionInfo") # else # pragma comment(linker, "/include:s_versionInfo") # endif #else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv inline const TestVersionInfo* TAEF_GetTestVersionInfo() { // On non-Windows platforms, we put the header in a separate section since we can't order data within the // header without using a linker script. __attribute__((section("testdataHeader"))) static TAEF_CONST_TESTDATA TestVersionInfo s_versionInfo = { TAEF_HEADER_SIGNATURE, TAEF_ABI_VERSION, "WexTestClass.h", # if TAEF_ABI_VERSION >= 13 # if defined(__x86_64) TaefProcessorArchitecture::Taef_X86_64, # elif defined(__aarch64__) TaefProcessorArchitecture::Taef_ARM64, # elif defined(__i386) TaefProcessorArchitecture::Taef_X86, # else // TAEF doesn't currently support 32-bit ARM on Linux; so we leave it as Unrecognized for now so we don't // have to decide yet whether to treat Thumb mode differently. TaefProcessorArchitecture::Taef_Unrecognized, # endif # endif // TAEF_ABI_VERSION >= 13 ::WEX::TestExecution::Private::StringStorage::TaefAbiStringDeallocator }; return &s_versionInfo; } # if defined(__cpp_inline_variables) && __cpp_inline_variables inline # else __attribute__((visibility("hidden"))) # endif const int g_doNotDiscardVersionInfo = DoNotDiscard(TAEF_GetTestVersionInfo()); #endif // _WIN32 }/* namespace Private */}/* namespace WEX */ #if !defined(_WIN32) #pragma pop_macro("_In_") #pragma pop_macro("_In_reads_z_") #pragma pop_macro("_Outptr_") #pragma pop_macro("_Outptr_result_maybenull_z_") #pragma pop_macro("_Post_invalid_") #pragma pop_macro("_Ret_maybenull_z_") #endif // #if !defined(_WIN32) #pragma pop_macro("Assert") #pragma warning(pop)