ReportingHandlerTests.cpp (5360B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 4 #include "ReportingHandler.h" 5 6 #include <catch2/catch_test_macros.hpp> 7 8 #include <chrono> 9 #include <mutex> 10 #include <optional> 11 #include <thread> 12 13 #define TEST(...) TEST_CASE("[ReportingHandlerTests] " __VA_ARGS__) 14 15 using namespace SFS; 16 using namespace SFS::details; 17 18 TEST("Testing SetLoggingCallback()") 19 { 20 ReportingHandler handler; 21 22 bool called = false; 23 auto handling = [&](const LogData&) { called = true; }; 24 25 handler.SetLoggingCallback(handling); 26 REQUIRE_FALSE(called); 27 28 LOG_INFO(handler, "Test"); 29 30 called = false; 31 handler.SetLoggingCallback(nullptr); 32 33 LOG_INFO(handler, "Test"); 34 REQUIRE_FALSE(called); 35 } 36 37 TEST("Testing Severities") 38 { 39 ReportingHandler handler; 40 41 std::optional<LogSeverity> severity; 42 auto handling = [&](const LogData& data) { severity = data.severity; }; 43 44 handler.SetLoggingCallback(handling); 45 46 REQUIRE(!severity.has_value()); 47 48 LOG_INFO(handler, "Test"); 49 REQUIRE(severity.has_value()); 50 REQUIRE(*severity == LogSeverity::Info); 51 severity.reset(); 52 53 LOG_WARNING(handler, "Test"); 54 REQUIRE(severity.has_value()); 55 REQUIRE(*severity == LogSeverity::Warning); 56 severity.reset(); 57 58 LOG_ERROR(handler, "Test"); 59 REQUIRE(severity.has_value()); 60 REQUIRE(*severity == LogSeverity::Error); 61 severity.reset(); 62 63 LOG_VERBOSE(handler, "Test"); 64 REQUIRE(severity.has_value()); 65 REQUIRE(*severity == LogSeverity::Verbose); 66 67 handler.SetLoggingCallback(nullptr); 68 } 69 70 TEST("Testing file/line/function") 71 { 72 ReportingHandler handler; 73 74 std::string file; 75 int line = 0; 76 std::string function; 77 auto handling = [&](const LogData& data) { 78 file = std::string(data.file); 79 line = data.line; 80 function = std::string(data.function); 81 }; 82 83 handler.SetLoggingCallback(handling); 84 85 LOG_INFO(handler, "Test"); 86 CHECK(file.find("ReportingHandlerTests.cpp") != std::string::npos); 87 CHECK(line == (__LINE__ - 2)); 88 CHECK(function == "CATCH2_INTERNAL_TEST_4"); 89 90 LOG_WARNING(handler, "Test"); 91 CHECK(file.find("ReportingHandlerTests.cpp") != std::string::npos); 92 CHECK(line == (__LINE__ - 2)); 93 CHECK(function == "CATCH2_INTERNAL_TEST_4"); 94 } 95 96 TEST("Testing LogFormatting") 97 { 98 ReportingHandler handler; 99 100 std::string message; 101 auto handling = [&](const LogData& data) { message = data.message; }; 102 103 handler.SetLoggingCallback(handling); 104 105 REQUIRE(message.empty()); 106 107 LOG_INFO(handler, "Test %s", "Test"); 108 REQUIRE(message == "Test Test"); 109 110 LOG_WARNING(handler, "Test %s %s", "Test1", "Test2"); 111 REQUIRE(message == "Test Test1 Test2"); 112 113 LOG_ERROR(handler, "Test %s %s %s", "Test1", "Test2", "Test3"); 114 REQUIRE(message == "Test Test1 Test2 Test3"); 115 116 LOG_INFO(handler, "Test %d %d", 1, true); 117 REQUIRE(message == "Test 1 1"); 118 119 LOG_INFO(handler, "Test %d %s", 2, false ? "true" : "false"); 120 REQUIRE(message == "Test 2 false"); 121 122 handler.SetLoggingCallback(nullptr); 123 } 124 125 TEST("Testing setting another logging callback waits for an existing call to finish") 126 { 127 ReportingHandler handler; 128 129 // Set a callback that will be blocked by a mutex 130 std::mutex mutex; 131 bool called = false; 132 std::chrono::time_point<std::chrono::system_clock> time1; 133 bool startedCall = false; 134 auto handling = [&](const LogData& logData) { 135 startedCall = true; 136 std::lock_guard guard(mutex); 137 called = true; 138 time1 = logData.time; 139 }; 140 141 handler.SetLoggingCallback(handling); 142 143 // Make sure the callback is blocked 144 std::unique_lock lock(mutex); 145 146 // Spawn a thread that will be blocked by the callback 147 std::thread t([&]() { LOG_INFO(handler, "Test"); }); 148 149 LoggingCallbackFn anotherHandling; 150 SECTION("Setting another callback") 151 { 152 anotherHandling = [&](const LogData&) {}; 153 } 154 SECTION("Setting a nullptr callback") 155 { 156 anotherHandling = nullptr; 157 } 158 159 // Spawn a second thread that tries to set another callback 160 std::chrono::time_point<std::chrono::system_clock> time2; 161 std::thread t2([&]() { 162 // Make sure the callback has started and is now blocked 163 while (!startedCall) 164 { 165 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 166 } 167 168 // Now setting another callback should be blocked until we unlock the mutex 169 handler.SetLoggingCallback(std::move(anotherHandling)); 170 INFO("The first callback should have been called at this point"); 171 REQUIRE(called); 172 time2 = std::chrono::system_clock::now(); 173 }); 174 175 // Unlocking the mutex will allow the threads to continue 176 lock.unlock(); 177 t.join(); 178 t2.join(); 179 180 INFO("The first callback should have been called before the second callback was set"); 181 REQUIRE(time1 < time2); 182 } 183 184 TEST("Testing ToString(LogSeverity)") 185 { 186 REQUIRE(SFS::ToString(LogSeverity::Info) == "Info"); 187 REQUIRE(SFS::ToString(LogSeverity::Warning) == "Warning"); 188 REQUIRE(SFS::ToString(LogSeverity::Error) == "Error"); 189 REQUIRE(SFS::ToString(LogSeverity::Verbose) == "Verbose"); 190 }