From b7cffa5e82d70aec265830dff5102f45d1990424 Mon Sep 17 00:00:00 2001 From: Nicolas Hake Date: Sat, 23 Jan 2016 03:17:06 +0100 Subject: [PATCH] Aul tests: Assert that Translate() warns when a translation is missing I'm not a huge fan of testing for warnings by hijacking the logging routines, but right now there's no way to exfiltrate warnings from Aul any other way, so it'll have to do. Overriding the logging functions from C4SimpleLog.cpp has the nice additional advantage that expected runtime errors no longer get written to stdout - this is okay because we're already checking that an exception is thrown. --- tests/CMakeLists.txt | 10 +++- tests/TestLog.cpp | 69 +++++++++++++++++++++++++ tests/TestLog.h | 59 +++++++++++++++++++++ tests/aul/AulPredefinedFunctionTest.cpp | 10 ++++ 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 tests/TestLog.cpp create mode 100644 tests/TestLog.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c62a8bea4..2a9d75be2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,9 +88,15 @@ function(create_test testName) set(oneValueArgs "") set(multiValueArgs SOURCES LIBRARIES) CMAKE_PARSE_ARGUMENTS(CT "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - add_executable("${testName}" EXCLUDE_FROM_ALL ${CT_SOURCES} main.cpp) + add_executable("${testName}" EXCLUDE_FROM_ALL + ${CT_SOURCES} + TestLog.cpp + TestLog.h + main.cpp + ) + target_include_directories("${testName}" PRIVATE "${CMAKE_CURRENT_LIST_DIR}") set_property(TARGET "${testName}" PROPERTY FOLDER "Testing") - target_link_libraries("${testName}" gtest ${CT_LIBRARIES}) + target_link_libraries("${testName}" gtest gmock ${CT_LIBRARIES}) add_test(NAME "${testName}" COMMAND "${testName}") endfunction() diff --git a/tests/TestLog.cpp b/tests/TestLog.cpp new file mode 100644 index 000000000..77d5ab336 --- /dev/null +++ b/tests/TestLog.cpp @@ -0,0 +1,69 @@ +/* +* OpenClonk, http://www.openclonk.org +* +* Copyright (c) 2016, The OpenClonk Team and contributors +* +* Distributed under the terms of the ISC license; see accompanying file +* "COPYING" for details. +* +* "Clonk" is a registered trademark of Matthes Bender, used with permission. +* See accompanying file "TRADEMARK" for details. +* +* To redistribute this file separately, substitute the full license texts +* for the above references. +*/ + +// Proxies the logging functions into a class so we can test that something +// gets logged + +#include "TestLog.h" + +// Override ALL of the C4SimpleLog.cpp functions here because otherwise MSVC +// pulls the .obj in and will end up with multiple definitions of a symbol. +#define FORWARD_UNFORMATTED(func) bool func(const char *msg) { return TestLog::handler ? TestLog::handler->func(msg) : true; } +FORWARD_UNFORMATTED(Log) +FORWARD_UNFORMATTED(DebugLog) +FORWARD_UNFORMATTED(LogSilent) +FORWARD_UNFORMATTED(LogFatal) +#undef FORWARD_UNFORMATTED + +#define FORWARD_FORMATTED(func) bool func(const char *msg, ...) \ + { \ + va_list args; va_start(args, msg); \ + bool result = TestLog::handler ? TestLog::handler->func(msg, args) : true; \ + va_end(args); \ + return result; \ + } +FORWARD_FORMATTED(DebugLogF) +FORWARD_FORMATTED(LogF) +FORWARD_FORMATTED(LogSilentF) +#undef FORWARD_FORMATTED + +TestLog *TestLog::handler = 0; + +void TestLog::setHandler(TestLog *new_handler) +{ + handler = new_handler; +} + +TestLog::TestLog() +{ + setHandler(this); +} + +TestLog::~TestLog() +{ + // Make sure there's no deleted logging handler set + if (handler == this) + handler = 0; +} + +// Default implementation does nothing +bool TestLog::Log(const char * /* msg */) { return true; } +bool TestLog::DebugLog(const char * /* msg */) { return true; } +bool TestLog::LogFatal(const char * /* msg */) { return true; } +bool TestLog::LogSilent(const char * /* msg */) { return true; } + +bool TestLog::LogF(const char * /* msg */, va_list /* args */) { return true; } +bool TestLog::DebugLogF(const char * /* msg */, va_list /* args */) { return true; } +bool TestLog::LogSilentF(const char * /* msg */, va_list /* args */) { return true; } diff --git a/tests/TestLog.h b/tests/TestLog.h new file mode 100644 index 000000000..992f26747 --- /dev/null +++ b/tests/TestLog.h @@ -0,0 +1,59 @@ +/* +* OpenClonk, http://www.openclonk.org +* +* Copyright (c) 2016, The OpenClonk Team and contributors +* +* Distributed under the terms of the ISC license; see accompanying file +* "COPYING" for details. +* +* "Clonk" is a registered trademark of Matthes Bender, used with permission. +* See accompanying file "TRADEMARK" for details. +* +* To redistribute this file separately, substitute the full license texts +* for the above references. +*/ + +#include +#include + +class TestLog +{ +public: + virtual ~TestLog(); + static void setHandler(TestLog *new_handler); + +protected: + virtual bool Log(const char *msg) = 0; + virtual bool DebugLog(const char *msg) = 0; + virtual bool LogFatal(const char *msg) = 0; + virtual bool LogSilent(const char *msg) = 0; + + virtual bool LogF(const char *format, va_list args) = 0; + virtual bool DebugLogF(const char *format, va_list args) = 0; + virtual bool LogSilentF(const char *format, va_list args) = 0; + + TestLog(); + +private: + static TestLog *handler; + + friend bool Log(const char*); + friend bool DebugLog(const char*); + friend bool LogFatal(const char*); + friend bool LogSilent(const char*); + friend bool LogF(const char*, ...); + friend bool DebugLogF(const char*, ...); + friend bool LogSilentF(const char*, ...); +}; + +class LogMock : public TestLog +{ +public: + MOCK_METHOD1(Log, bool(const char*)); + MOCK_METHOD1(DebugLog, bool(const char*)); + MOCK_METHOD1(LogFatal, bool(const char*)); + MOCK_METHOD1(LogSilent, bool(const char*)); + MOCK_METHOD2(LogF, bool(const char*, va_list)); + MOCK_METHOD2(DebugLogF, bool(const char*, va_list)); + MOCK_METHOD2(LogSilentF, bool(const char*, va_list)); +}; diff --git a/tests/aul/AulPredefinedFunctionTest.cpp b/tests/aul/AulPredefinedFunctionTest.cpp index b3b52d729..a0527b97f 100644 --- a/tests/aul/AulPredefinedFunctionTest.cpp +++ b/tests/aul/AulPredefinedFunctionTest.cpp @@ -17,14 +17,24 @@ #include "C4Include.h" #include "AulTest.h" +#include "TestLog.h" #include "script/C4Aul.h" class AulPredefFunctionTest : public AulTest {}; +using ::testing::StartsWith; +using ::testing::AnyNumber; +using ::testing::_; + TEST_F(AulPredefFunctionTest, Translate) { + // Expect the engine to warn when it can't find a translation + LogMock log; + EXPECT_CALL(log, DebugLogF(R"(WARNING: Translate: no translation for string "%s")", _)); + EXPECT_CALL(log, DebugLog(StartsWith(" by: "))).Times(AnyNumber()); // ignore stack trace + EXPECT_EQ(C4VString("a"), RunExpr("Translate(\"a\")")); }