Adds StringTraits::Literal variants that work with strings. Names the workspace CPPUtils to match the import name in TinyTest. This resolves the dependency issue.
528 lines
22 KiB
C++
528 lines
22 KiB
C++
/**********************************************************************************************************************
|
|
* *
|
|
* @file windows_logger_test.cpp *
|
|
* *
|
|
* @brief Defines tests for the WindowsLogger logging destination class declared in windows_logger.h. *
|
|
* This logging destination works with the Logger class declared in logger.h and logs messages by creating a modal *
|
|
* windows message box *
|
|
* @copyright Copyright (C) 2023 by Tom Hicks <headhunter3@gmail.com> *
|
|
* *
|
|
* Licensed under the MIT license see below for details. *
|
|
* *
|
|
* MIT License *
|
|
* *
|
|
* Copyright (c) 2023 Tom Hicks <headhunter3@gmail.com> *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated *
|
|
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation *
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and *
|
|
* to permit persons to whom the Software is furnished to do so, subject to the following conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of *
|
|
* the Software. *
|
|
* *
|
|
* 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. IN NO EVENT SHALL THE *
|
|
* 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. *
|
|
* *
|
|
**********************************************************************************************************************/
|
|
#include "windows_logger.h"
|
|
#ifdef _WIN32
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <tuple>
|
|
#include <winuser.h>
|
|
|
|
#include "logger.h"
|
|
#include "tinytest.h"
|
|
|
|
namespace {
|
|
using CPPUtils::Logger;
|
|
using CPPUtils::WindowsLogger;
|
|
using std::cout;
|
|
using std::endl;
|
|
using std::get;
|
|
using std::hex;
|
|
using std::make_shared;
|
|
using std::make_tuple;
|
|
using std::ostream;
|
|
using std::ostringstream;
|
|
using std::shared_ptr;
|
|
using std::string;
|
|
using std::tuple;
|
|
using std::vector;
|
|
using TinyTest::ExecuteSuite;
|
|
using TinyTest::MakeTest;
|
|
using TinyTest::MakeTestSuite;
|
|
using TinyTest::TestResults;
|
|
string no_errors = "no errors";
|
|
typedef tuple<string, string, UINT> MessageBoxEvent;
|
|
|
|
/// @brief
|
|
class WindowsLoggerSpy : public WindowsLogger {
|
|
public:
|
|
/// @brief
|
|
mutable vector<MessageBoxEvent> log;
|
|
|
|
protected:
|
|
/// @brief
|
|
/// @param body
|
|
/// @param title
|
|
/// @param u_type
|
|
virtual void ShowMessageBox(const string &body, const string &title, UINT u_type) const override {
|
|
log.push_back(make_tuple(body, title, u_type));
|
|
}
|
|
};
|
|
|
|
/// @brief
|
|
/// @param errors
|
|
/// @param expected
|
|
/// @param spy
|
|
void ExpectLogSize(ostream &errors, size_t expected, const shared_ptr<WindowsLoggerSpy> &spy) {
|
|
size_t actual = spy->log.size();
|
|
if (actual != expected) {
|
|
errors << "Log size mismatch. Expected: " << expected << ", Actual: " << actual << endl;
|
|
}
|
|
}
|
|
|
|
/// @brief
|
|
/// @param errors
|
|
/// @param expected
|
|
/// @param event
|
|
void ExpectMessage(ostream &errors, const string &expected, const MessageBoxEvent &event) {
|
|
string actual = get<0>(event);
|
|
if (actual != expected) {
|
|
errors << "Message mismatch. Expected: " << expected << ", Actual: " << actual << endl;
|
|
}
|
|
}
|
|
|
|
/// @brief
|
|
/// @param errors
|
|
/// @param expected
|
|
/// @param event
|
|
void ExpectTitle(ostream &errors, const string &expected, const MessageBoxEvent &event) {
|
|
string actual = get<1>(event);
|
|
if (actual != expected) {
|
|
errors << "Title mismatch. Expected: \"" << expected << "\", Actual: \"" << actual << "\"" << endl;
|
|
}
|
|
}
|
|
|
|
/// @brief
|
|
/// @param parts
|
|
/// @param separator
|
|
/// @return
|
|
string Join(vector<string> parts, const string &separator) {
|
|
ostringstream os;
|
|
|
|
bool first = true;
|
|
;
|
|
for_each(parts.begin(), parts.end(), [&os, &first, separator](string part) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
os << separator;
|
|
}
|
|
os << part;
|
|
});
|
|
return os.str();
|
|
}
|
|
|
|
/// @brief
|
|
/// @param u_type
|
|
/// @param ignore_defaults
|
|
/// @return
|
|
string ConvertUTypeToString(UINT u_type, bool ignore_defaults = true) {
|
|
UINT u_button = u_type & MB_TYPEMASK;
|
|
UINT u_icon = u_type & MB_ICONMASK;
|
|
UINT u_default_button = u_type & MB_DEFMASK;
|
|
UINT u_mode = u_type & MB_MODEMASK;
|
|
UINT u_misc = u_type & MB_MISCMASK;
|
|
|
|
vector<string> parts;
|
|
|
|
// MB_OK = 0x00000000L
|
|
if (u_button == MB_OK) {
|
|
parts.push_back("MB_OK");
|
|
}
|
|
// MB_OKCANCEL = 0x00000001L
|
|
if (u_button == MB_OKCANCEL) {
|
|
parts.push_back("MB_OKCANCEL");
|
|
}
|
|
// MB_ABORTRETRYIGNORE = 0x00000002L
|
|
if (u_button == MB_ABORTRETRYIGNORE) {
|
|
parts.push_back("MB_ABORTRETRYIGNORE");
|
|
}
|
|
// MB_YESNOCANCEL = 0x00000003L
|
|
if (u_button == MB_YESNOCANCEL) {
|
|
parts.push_back("MB_YESNOCANCEL");
|
|
}
|
|
// MB_YESNO = 0x00000004L
|
|
if (u_button == MB_YESNO) {
|
|
parts.push_back("MB_YESNO");
|
|
}
|
|
// MB_RETRYCANCEL = 0x00000005L
|
|
if (u_button == MB_RETRYCANCEL) {
|
|
parts.push_back("MB_RETRYCANCEL");
|
|
}
|
|
// MB_CANCELTRYCONTINUE = 0x00000006L
|
|
if (u_button == MB_CANCELTRYCONTINUE) {
|
|
parts.push_back("MB_CANCELTRYCONTINUE");
|
|
}
|
|
|
|
// MB_ICONHAND = 0x00000010L
|
|
if (u_icon == MB_ICONHAND) {
|
|
parts.push_back("MB_ICONHAND");
|
|
}
|
|
// MB_ICONQUESTION = 0x00000020L
|
|
if (u_icon == MB_ICONQUESTION) {
|
|
parts.push_back("MB_ICONQUESTION");
|
|
}
|
|
// MB_ICONEXCLAMATION = 0x00000030L
|
|
if (u_icon == MB_ICONEXCLAMATION) {
|
|
parts.push_back("MB_ICONEXCLAMATION");
|
|
}
|
|
// MB_ICONASTERISK = 0x00000040L
|
|
if (u_icon == MB_ICONASTERISK) {
|
|
parts.push_back("MB_ICONASTERISK");
|
|
}
|
|
// MB_USERICON = 0x00000080L
|
|
if (u_icon == MB_USERICON) {
|
|
parts.push_back("MB_USERICON");
|
|
}
|
|
|
|
// MB_DEFBUTTON1 = 0x00000000L
|
|
if (u_default_button == MB_DEFBUTTON1 && !ignore_defaults) {
|
|
parts.push_back("MB_DEFBUTTON1");
|
|
}
|
|
// MB_DEFBUTTON2 = 0x00000100L
|
|
if (u_default_button == MB_DEFBUTTON2) {
|
|
parts.push_back("MB_DEFBUTTON2");
|
|
}
|
|
// MB_DEFBUTTON3 = 0x00000200L
|
|
if (u_default_button == MB_DEFBUTTON3) {
|
|
parts.push_back("MB_DEFBUTTON3");
|
|
}
|
|
// MB_DEFBUTTON4 = 0x00000300L
|
|
if (u_default_button == MB_DEFBUTTON4) {
|
|
parts.push_back("MB_DEFBUTTON4");
|
|
}
|
|
|
|
// MB_APPLMODAL = 0x00000000L
|
|
if (u_mode == MB_APPLMODAL && !ignore_defaults) {
|
|
parts.push_back("MB_APPLMODAL");
|
|
}
|
|
// MB_SYSTEMMODAL = 0x00001000L
|
|
if (u_mode == MB_SYSTEMMODAL) {
|
|
parts.push_back("MB_SYSTEMMODAL");
|
|
}
|
|
// MB_TASKMODAL = 0x00002000L
|
|
if (u_mode == MB_TASKMODAL) {
|
|
parts.push_back("MB_TASKMODAL");
|
|
}
|
|
// MB_HELP = 0x00004000L
|
|
if (u_mode == MB_HELP) {
|
|
parts.push_back("MB_HELP");
|
|
}
|
|
// MB_NOFOCUS = 0x00008000L
|
|
if (u_mode == MB_NOFOCUS) {
|
|
parts.push_back("MB_NOFOCUS");
|
|
}
|
|
|
|
// MB_SETFOREGROUND = 0x00010000L
|
|
if (u_misc == MB_SETFOREGROUND) {
|
|
parts.push_back("MB_SETFOREGROUND");
|
|
}
|
|
// MB_DEFAULT_DESKTOP_ONLY = 0x00020000L
|
|
if (u_misc == MB_DEFAULT_DESKTOP_ONLY) {
|
|
parts.push_back("MB_DEFAULT_DESKTOP_ONLY");
|
|
}
|
|
// MB_TOPMOST = 0x00040000L
|
|
if (u_misc == MB_TOPMOST) {
|
|
parts.push_back("MB_TOPMOST");
|
|
}
|
|
// MB_RIGHT = 0x00080000L
|
|
if (u_misc == MB_RIGHT) {
|
|
parts.push_back("MB_RIGHT");
|
|
}
|
|
|
|
// MB_RTLREADING = 0x00100000L
|
|
if (u_misc == MB_RTLREADING) {
|
|
parts.push_back("MB_RTLREADING");
|
|
}
|
|
// MB_SERVICE_NOTIFICATION = 0x00200000L
|
|
// MB_SERVICE_NOTIFICATION = 0x00040000L
|
|
if ((u_misc == MB_SERVICE_NOTIFICATION) || (u_misc == MB_SERVICE_NOTIFICATION)) {
|
|
parts.push_back("MB_SERVICE_NOTIFICATION");
|
|
}
|
|
|
|
return Join(parts, " | ");
|
|
}
|
|
|
|
/// @brief
|
|
/// @param errors
|
|
/// @param expected
|
|
/// @param event
|
|
void ExpectUType(ostream &errors, UINT expected, const MessageBoxEvent &event) {
|
|
UINT actual = get<2>(event);
|
|
if (actual != expected) {
|
|
errors << "u_type mismatch. Expected: " << hex << expected << " (" << ConvertUTypeToString(expected)
|
|
<< "), Actual: " << hex << actual << " (" << ConvertUTypeToString(actual) << ")" << endl;
|
|
}
|
|
}
|
|
|
|
/// @brief
|
|
/// @param error_messages
|
|
/// @return
|
|
string GetErrors(ostringstream &error_messages) {
|
|
string errors = error_messages.str();
|
|
if (errors.size() > 0) {
|
|
return errors;
|
|
}
|
|
return no_errors;
|
|
}
|
|
} // namespace
|
|
|
|
TestResults test_WindowsLogger_LogMessage() {
|
|
auto log_message = [](const Logger::MessageType &type,
|
|
const string &message,
|
|
const string &body,
|
|
const string &title,
|
|
UINT u_type) -> string {
|
|
ostringstream errors;
|
|
auto destination = make_shared<WindowsLoggerSpy>();
|
|
destination->LogMessage(type, message);
|
|
ExpectLogSize(errors, 1, destination);
|
|
if (destination->log.size() > 0) {
|
|
auto event = destination->log.at(0);
|
|
ExpectMessage(errors, body, event);
|
|
ExpectTitle(errors, title, event);
|
|
ExpectUType(errors, u_type, event);
|
|
}
|
|
return GetErrors(errors);
|
|
};
|
|
return ExecuteSuite(MakeTestSuite(
|
|
"CPPUtils::WindowsLogger::LogMessage(cosnt std::string&)",
|
|
log_message,
|
|
{
|
|
MakeTest("should log \"this is a message\" with title \"Debug\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Debug,
|
|
"this is a message",
|
|
"this is a message",
|
|
"Debug",
|
|
MB_OK | MB_ICONEXCLAMATION)),
|
|
MakeTest(
|
|
"should log \"this is an error\" with title \"ERROR\"",
|
|
no_errors,
|
|
make_tuple(
|
|
Logger::MessageType::Error, "this is an error", "this is an error", "ERROR", MB_OK | MB_ICONHAND)),
|
|
MakeTest("should log \"what a terrible failure\" with title \"How did you let this happen?\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Wtf,
|
|
"what a terrible failure",
|
|
"what a terrible failure",
|
|
"How did you let this happen?",
|
|
MB_OK | MB_ICONHAND)),
|
|
MakeTest("should log \"this is some information\" with title \"Information\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Info,
|
|
"this is some information",
|
|
"this is some information",
|
|
"Information",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
MakeTest("should log \"this is a warning\" with title \"Warning\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Warning,
|
|
"this is a warning",
|
|
"this is a warning",
|
|
"Warning",
|
|
MB_OK | MB_ICONEXCLAMATION)),
|
|
MakeTest("should log \"this is verbose\" with title \"Verbose\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Verbose,
|
|
"this is verbose",
|
|
"this is verbose",
|
|
"Verbose",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
MakeTest("should log \"unclassified message\" with title \"Unclassified\"",
|
|
no_errors,
|
|
make_tuple((Logger::MessageType)1000,
|
|
"unclassified message",
|
|
"unclassified message",
|
|
"Unclassified",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
}));
|
|
}
|
|
|
|
TestResults test_WindowsLogger_LogErrorWithMessage() {
|
|
auto log_error = [](const Logger::MessageType &type,
|
|
const std::exception &ex,
|
|
const string &expected_body,
|
|
const string &expected_title,
|
|
UINT expected_utype) -> string {
|
|
ostringstream errors;
|
|
auto destination = make_shared<WindowsLoggerSpy>();
|
|
destination->LogError(type, ex);
|
|
ExpectLogSize(errors, 1, destination);
|
|
if (destination->log.size() > 0) {
|
|
auto event = destination->log.at(0);
|
|
ExpectMessage(errors, expected_body, event);
|
|
ExpectTitle(errors, expected_title, event);
|
|
ExpectUType(errors, expected_utype, event);
|
|
}
|
|
return GetErrors(errors);
|
|
};
|
|
return ExecuteSuite(MakeTestSuite(
|
|
"CPPUtils::WindowsLogger::LogError(const std::exception&)",
|
|
log_error,
|
|
{
|
|
MakeTest("should log \"Exception: this is an exception\" with title \"Debug\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Debug,
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"Debug",
|
|
MB_OK | MB_ICONEXCLAMATION)),
|
|
MakeTest("should log \"Exception: this is an error\" with title \"ERROR\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Error,
|
|
std::runtime_error("this is an error"),
|
|
"Exception: this is an error",
|
|
"ERROR",
|
|
MB_OK | MB_ICONHAND)),
|
|
MakeTest("should log \"Exception: what a terrible failure\" with title \"How did you let this happen?\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Wtf,
|
|
std::runtime_error("what a terrible failure"),
|
|
"Exception: what a terrible failure",
|
|
"How did you let this happen?",
|
|
MB_OK | MB_ICONHAND)),
|
|
MakeTest("should log \"Exception: this is some information\" with title \"Information\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Info,
|
|
std::runtime_error("this is some information"),
|
|
"Exception: this is some information",
|
|
"Information",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
MakeTest("should log \"Exception: this is a warning\" with title \"Warning\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Warning,
|
|
std::runtime_error("this is a warning"),
|
|
"Exception: this is a warning",
|
|
"Warning",
|
|
MB_OK | MB_ICONEXCLAMATION)),
|
|
MakeTest("should log \"Exception: this is verbose\" with title \"Verbose\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Verbose,
|
|
std::runtime_error("this is verbose"),
|
|
"Exception: this is verbose",
|
|
"Verbose",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
MakeTest("should log \"Exception: unclassified message\" with title \"Unclassified\"",
|
|
no_errors,
|
|
make_tuple((Logger::MessageType)1000,
|
|
std::runtime_error("unclassified message"),
|
|
"Exception: unclassified message",
|
|
"Unclassified",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
}));
|
|
}
|
|
|
|
TestResults test_WindowsLogger_LogErrorWithoutMessage() {
|
|
auto log_error = [](const Logger::MessageType &type,
|
|
const std::string &message,
|
|
const std::exception &ex,
|
|
const string &expected_body,
|
|
const string &expected_title,
|
|
UINT expected_utype) -> string {
|
|
ostringstream errors;
|
|
auto destination = make_shared<WindowsLoggerSpy>();
|
|
destination->LogError(type, ex);
|
|
ExpectLogSize(errors, 1, destination);
|
|
if (destination->log.size() > 0) {
|
|
auto event = destination->log.at(0);
|
|
ExpectMessage(errors, expected_body, event);
|
|
ExpectTitle(errors, expected_title, event);
|
|
ExpectUType(errors, expected_utype, event);
|
|
}
|
|
return GetErrors(errors);
|
|
};
|
|
return ExecuteSuite(MakeTestSuite(
|
|
"CPPUtils::WindowsLogger::LogError(const std::string&, const std::exception&)",
|
|
log_error,
|
|
{
|
|
MakeTest("should log \"Exception: this is an exception\" with title \"Debug\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Debug,
|
|
"this is a message",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"Debug",
|
|
MB_OK | MB_ICONEXCLAMATION)),
|
|
MakeTest("should log \"Exception: this is an exception\" with title \"ERROR\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Error,
|
|
"this is an error",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"ERROR",
|
|
MB_OK | MB_ICONHAND)),
|
|
MakeTest("should log \"Exception: this is an exception\" with title \"How did you let this happen?\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Wtf,
|
|
"what a terrible failure",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"How did you let this happen?",
|
|
MB_OK | MB_ICONHAND)),
|
|
MakeTest("should log \"Exception this is an exception\" with title \"Information\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Info,
|
|
"this is some information",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"Information",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
MakeTest("should log \"Exception: this is a warning with caught exception this is an exception\" with title "
|
|
"\"Warning\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Warning,
|
|
"this is a warning",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"Warning",
|
|
MB_OK | MB_ICONEXCLAMATION)),
|
|
MakeTest("should log \"Exception: this is an exception\" with title \"Verbose\"",
|
|
no_errors,
|
|
make_tuple(Logger::MessageType::Verbose,
|
|
"this is verbose",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"Verbose",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
MakeTest("should log \"Exception: this is an exception\" with title \"Unclassified\"",
|
|
no_errors,
|
|
make_tuple((Logger::MessageType)1000,
|
|
"unclassified message",
|
|
std::runtime_error("this is an exception"),
|
|
"Exception: this is an exception",
|
|
"Unclassified",
|
|
MB_OK | MB_ICONASTERISK)),
|
|
}));
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
TestResults results;
|
|
|
|
results += test_WindowsLogger_LogMessage();
|
|
results += test_WindowsLogger_LogErrorWithMessage();
|
|
results += test_WindowsLogger_LogErrorWithoutMessage();
|
|
|
|
PrintResults(cout, results);
|
|
|
|
return results.Failed() + results.Errors();
|
|
}
|
|
#endif // End defined(_WIN32)
|