UnhandledException.Tests.cpp
1 #include "pch.h" 2 #include "TestHelpers.h" 3 #include <UnhandledExceptionHandler.h> 4 5 using namespace Microsoft::VisualStudio::CppUnitTestFramework; 6 7 namespace UnitTestsCommonUtils 8 { 9 TEST_CLASS(UnhandledExceptionTests) 10 { 11 public: 12 // exceptionDescription tests 13 TEST_METHOD(ExceptionDescription_AccessViolation_ReturnsDescription) 14 { 15 auto result = exceptionDescription(EXCEPTION_ACCESS_VIOLATION); 16 Assert::IsTrue(result && *result != '\0'); 17 // Should contain meaningful description 18 std::string desc{ result }; 19 Assert::IsTrue(desc.find("ACCESS") != std::string::npos || 20 desc.find("access") != std::string::npos || 21 desc.find("violation") != std::string::npos || 22 desc.length() > 0); 23 } 24 25 TEST_METHOD(ExceptionDescription_StackOverflow_ReturnsDescription) 26 { 27 auto result = exceptionDescription(EXCEPTION_STACK_OVERFLOW); 28 Assert::IsTrue(result && *result != '\0'); 29 } 30 31 TEST_METHOD(ExceptionDescription_DivideByZero_ReturnsDescription) 32 { 33 auto result = exceptionDescription(EXCEPTION_INT_DIVIDE_BY_ZERO); 34 Assert::IsTrue(result && *result != '\0'); 35 } 36 37 TEST_METHOD(ExceptionDescription_IllegalInstruction_ReturnsDescription) 38 { 39 auto result = exceptionDescription(EXCEPTION_ILLEGAL_INSTRUCTION); 40 Assert::IsTrue(result && *result != '\0'); 41 } 42 43 TEST_METHOD(ExceptionDescription_ArrayBoundsExceeded_ReturnsDescription) 44 { 45 auto result = exceptionDescription(EXCEPTION_ARRAY_BOUNDS_EXCEEDED); 46 Assert::IsTrue(result && *result != '\0'); 47 } 48 49 TEST_METHOD(ExceptionDescription_Breakpoint_ReturnsDescription) 50 { 51 auto result = exceptionDescription(EXCEPTION_BREAKPOINT); 52 Assert::IsTrue(result && *result != '\0'); 53 } 54 55 TEST_METHOD(ExceptionDescription_SingleStep_ReturnsDescription) 56 { 57 auto result = exceptionDescription(EXCEPTION_SINGLE_STEP); 58 Assert::IsTrue(result && *result != '\0'); 59 } 60 61 TEST_METHOD(ExceptionDescription_FloatDivideByZero_ReturnsDescription) 62 { 63 auto result = exceptionDescription(EXCEPTION_FLT_DIVIDE_BY_ZERO); 64 Assert::IsTrue(result && *result != '\0'); 65 } 66 67 TEST_METHOD(ExceptionDescription_FloatOverflow_ReturnsDescription) 68 { 69 auto result = exceptionDescription(EXCEPTION_FLT_OVERFLOW); 70 Assert::IsTrue(result && *result != '\0'); 71 } 72 73 TEST_METHOD(ExceptionDescription_FloatUnderflow_ReturnsDescription) 74 { 75 auto result = exceptionDescription(EXCEPTION_FLT_UNDERFLOW); 76 Assert::IsTrue(result && *result != '\0'); 77 } 78 79 TEST_METHOD(ExceptionDescription_FloatInvalidOperation_ReturnsDescription) 80 { 81 auto result = exceptionDescription(EXCEPTION_FLT_INVALID_OPERATION); 82 Assert::IsTrue(result && *result != '\0'); 83 } 84 85 TEST_METHOD(ExceptionDescription_PrivilegedInstruction_ReturnsDescription) 86 { 87 auto result = exceptionDescription(EXCEPTION_PRIV_INSTRUCTION); 88 Assert::IsTrue(result && *result != '\0'); 89 } 90 91 TEST_METHOD(ExceptionDescription_InPageError_ReturnsDescription) 92 { 93 auto result = exceptionDescription(EXCEPTION_IN_PAGE_ERROR); 94 Assert::IsTrue(result && *result != '\0'); 95 } 96 97 TEST_METHOD(ExceptionDescription_UnknownCode_ReturnsDescription) 98 { 99 auto result = exceptionDescription(0x12345678); 100 // Should return something (possibly "Unknown exception" or similar) 101 Assert::IsTrue(result && *result != '\0'); 102 } 103 104 TEST_METHOD(ExceptionDescription_ZeroCode_ReturnsDescription) 105 { 106 auto result = exceptionDescription(0); 107 // Should handle zero gracefully 108 Assert::IsTrue(result && *result != '\0'); 109 } 110 111 // GetFilenameStart tests (if accessible) 112 TEST_METHOD(GetFilenameStart_ValidPath_ReturnsFilename) 113 { 114 wchar_t path[] = L"C:\\folder\\subfolder\\file.exe"; 115 int start = GetFilenameStart(path); 116 117 Assert::IsTrue(start >= 0); 118 Assert::AreEqual(std::wstring(L"file.exe"), std::wstring(path + start)); 119 } 120 121 TEST_METHOD(GetFilenameStart_NoPath_ReturnsOriginal) 122 { 123 wchar_t path[] = L"file.exe"; 124 int start = GetFilenameStart(path); 125 126 Assert::IsTrue(start >= 0); 127 Assert::AreEqual(std::wstring(L"file.exe"), std::wstring(path + start)); 128 } 129 130 TEST_METHOD(GetFilenameStart_TrailingBackslash_ReturnsEmpty) 131 { 132 wchar_t path[] = L"C:\\folder\\"; 133 int start = GetFilenameStart(path); 134 135 // Should point to empty string after last backslash 136 Assert::IsTrue(start >= 0); 137 } 138 139 TEST_METHOD(GetFilenameStart_NullPath_HandlesGracefully) 140 { 141 // This might crash or return null depending on implementation 142 // Just document the behavior 143 int start = GetFilenameStart(nullptr); 144 (void)start; 145 // Result is implementation-defined for null input 146 Assert::IsTrue(true); 147 } 148 149 // Thread safety tests 150 TEST_METHOD(ExceptionDescription_ThreadSafe) 151 { 152 std::vector<std::thread> threads; 153 std::atomic<int> successCount{ 0 }; 154 155 for (int i = 0; i < 10; ++i) 156 { 157 threads.emplace_back([&successCount]() { 158 for (int j = 0; j < 10; ++j) 159 { 160 auto desc = exceptionDescription(EXCEPTION_ACCESS_VIOLATION); 161 if (desc && *desc != '\0') 162 { 163 successCount++; 164 } 165 } 166 }); 167 } 168 169 for (auto& t : threads) 170 { 171 t.join(); 172 } 173 174 Assert::AreEqual(100, successCount.load()); 175 } 176 177 // All exception codes test 178 TEST_METHOD(ExceptionDescription_AllCommonCodes_ReturnDescriptions) 179 { 180 std::vector<DWORD> codes = { 181 EXCEPTION_ACCESS_VIOLATION, 182 EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 183 EXCEPTION_BREAKPOINT, 184 EXCEPTION_DATATYPE_MISALIGNMENT, 185 EXCEPTION_FLT_DENORMAL_OPERAND, 186 EXCEPTION_FLT_DIVIDE_BY_ZERO, 187 EXCEPTION_FLT_INEXACT_RESULT, 188 EXCEPTION_FLT_INVALID_OPERATION, 189 EXCEPTION_FLT_OVERFLOW, 190 EXCEPTION_FLT_STACK_CHECK, 191 EXCEPTION_FLT_UNDERFLOW, 192 EXCEPTION_ILLEGAL_INSTRUCTION, 193 EXCEPTION_IN_PAGE_ERROR, 194 EXCEPTION_INT_DIVIDE_BY_ZERO, 195 EXCEPTION_INT_OVERFLOW, 196 EXCEPTION_INVALID_DISPOSITION, 197 EXCEPTION_NONCONTINUABLE_EXCEPTION, 198 EXCEPTION_PRIV_INSTRUCTION, 199 EXCEPTION_SINGLE_STEP, 200 EXCEPTION_STACK_OVERFLOW 201 }; 202 203 for (DWORD code : codes) 204 { 205 auto desc = exceptionDescription(code); 206 Assert::IsTrue(desc && *desc != '\0', (L"Empty description for code: " + std::to_wstring(code)).c_str()); 207 } 208 } 209 }; 210 }