diff --git a/build/premake/premake4.lua b/build/premake/premake4.lua
index 8bf879c543..80522f8866 100644
--- a/build/premake/premake4.lua
+++ b/build/premake/premake4.lua
@@ -711,7 +711,8 @@ function setup_all_libs ()
"maths",
"maths/scripting",
"i18n",
- "i18n/scripting"
+ "i18n/scripting",
+ "third_party/cppformat",
}
extern_libs = {
"spidermonkey",
diff --git a/source/graphics/TextRenderer.cpp b/source/graphics/TextRenderer.cpp
index 3149ed69fb..ae5ac69849 100644
--- a/source/graphics/TextRenderer.cpp
+++ b/source/graphics/TextRenderer.cpp
@@ -144,6 +144,14 @@ void CTextRenderer::Put(float x, float y, const wchar_t* buf)
PutString(x, y, new std::wstring(buf), true);
}
+void CTextRenderer::Put(float x, float y, const char* buf)
+{
+ if (buf[0] == 0)
+ return; // empty string; don't bother storing
+
+ PutString(x, y, new std::wstring(wstring_from_utf8(buf)), true);
+}
+
void CTextRenderer::Put(float x, float y, const std::wstring* buf)
{
if (buf->empty())
diff --git a/source/graphics/TextRenderer.h b/source/graphics/TextRenderer.h
index ac4d3b1d83..0c096f99dc 100644
--- a/source/graphics/TextRenderer.h
+++ b/source/graphics/TextRenderer.h
@@ -88,6 +88,13 @@ public:
*/
void Put(float x, float y, const wchar_t* buf);
+ /**
+ * Print text at (x,y) under the current transform.
+ * Does not alter the current transform.
+ * @p buf must be a UTF-8 string.
+ */
+ void Put(float x, float y, const char* buf);
+
/**
* Print text at (x,y) under the current transform.
* Does not alter the current transform.
diff --git a/source/graphics/tests/test_MeshManager.h b/source/graphics/tests/test_MeshManager.h
index 4e59ba1cb3..a35d3b3f32 100644
--- a/source/graphics/tests/test_MeshManager.h
+++ b/source/graphics/tests/test_MeshManager.h
@@ -190,7 +190,7 @@ public:
CModelDefPtr modeldef = meshManager->GetMesh(testDAE);
TS_ASSERT(! modeldef);
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"parser error");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "parser error");
}
void test_invalid_dae()
@@ -205,7 +205,7 @@ public:
CModelDefPtr modeldef = meshManager->GetMesh(testDAE);
TS_ASSERT(! modeldef);
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"parser error");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "parser error");
}
void test_load_nonexistent_pmd()
diff --git a/source/lib/self_test.h b/source/lib/self_test.h
index 27e3870bbc..4b68be034d 100644
--- a/source/lib/self_test.h
+++ b/source/lib/self_test.h
@@ -269,7 +269,10 @@ namespace CxxTest
#define TS_ASSERT_PATH_EQUALS(path1, path2) TS_ASSERT_EQUALS((path1).string(), (path2).string())
#define TSM_ASSERT_PATH_EQUALS(m, path1, path2) TSM_ASSERT_EQUALS(m, (path1).string(), (path2).string())
+bool ts_str_contains(const std::string& str1, const std::string& str2); // defined in test_setup.cpp
bool ts_str_contains(const std::wstring& str1, const std::wstring& str2); // defined in test_setup.cpp
+#define TS_ASSERT_STR_CONTAINS(str1, str2) TSM_ASSERT(str1, ts_str_contains(str1, str2))
+#define TS_ASSERT_STR_NOT_CONTAINS(str1, str2) TSM_ASSERT(str1, !ts_str_contains(str1, str2))
#define TS_ASSERT_WSTR_CONTAINS(str1, str2) TSM_ASSERT(str1, ts_str_contains(str1, str2))
#define TS_ASSERT_WSTR_NOT_CONTAINS(str1, str2) TSM_ASSERT(str1, !ts_str_contains(str1, str2))
diff --git a/source/ps/CLogger.cpp b/source/ps/CLogger.cpp
index 1a68943e6d..16c8ff03bd 100644
--- a/source/ps/CLogger.cpp
+++ b/source/ps/CLogger.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -140,16 +140,15 @@ CLogger::~CLogger()
}
}
-static std::string ToHTML(const wchar_t* message)
+static std::string ToHTML(const char* message)
{
- Status err;
- std::string cmessage = utf8_from_wstring(message, &err);
+ std::string cmessage = message;
boost::algorithm::replace_all(cmessage, "&", "&");
boost::algorithm::replace_all(cmessage, "<", "<");
return cmessage;
}
-void CLogger::WriteMessage(const wchar_t* message, bool doRender = false)
+void CLogger::WriteMessage(const char* message, bool doRender = false)
{
std::string cmessage = ToHTML(message);
@@ -157,20 +156,20 @@ void CLogger::WriteMessage(const wchar_t* message, bool doRender = false)
++m_NumberOfMessages;
// if (m_UseDebugPrintf)
-// debug_printf(L"MESSAGE: %ls\n", message);
+// debug_printf(L"MESSAGE: %hs\n", message);
*m_MainLog << "
" << cmessage << "
\n";
m_MainLog->flush();
if (doRender)
{
- if (g_Console) g_Console->InsertMessage(L"INFO: %ls", message);
+ if (g_Console) g_Console->InsertMessage(L"INFO: %hs", message);
PushRenderMessage(Normal, message);
}
}
-void CLogger::WriteError(const wchar_t* message)
+void CLogger::WriteError(const char* message)
{
std::string cmessage = ToHTML(message);
@@ -178,9 +177,9 @@ void CLogger::WriteError(const wchar_t* message)
++m_NumberOfErrors;
if (m_UseDebugPrintf)
- debug_printf(L"ERROR: %ls\n", message);
+ debug_printf(L"ERROR: %hs\n", message);
- if (g_Console) g_Console->InsertMessage(L"ERROR: %ls", message);
+ if (g_Console) g_Console->InsertMessage(L"ERROR: %hs", message);
*m_InterestingLog << "ERROR: " << cmessage << "
\n";
m_InterestingLog->flush();
@@ -190,7 +189,7 @@ void CLogger::WriteError(const wchar_t* message)
PushRenderMessage(Error, message);
}
-void CLogger::WriteWarning(const wchar_t* message)
+void CLogger::WriteWarning(const char* message)
{
std::string cmessage = ToHTML(message);
@@ -198,9 +197,9 @@ void CLogger::WriteWarning(const wchar_t* message)
++m_NumberOfWarnings;
if (m_UseDebugPrintf)
- debug_printf(L"WARNING: %ls\n", message);
+ debug_printf(L"WARNING: %hs\n", message);
- if (g_Console) g_Console->InsertMessage(L"WARNING: %ls", message);
+ if (g_Console) g_Console->InsertMessage(L"WARNING: %hs", message);
*m_InterestingLog << "WARNING: " << cmessage << "
\n";
m_InterestingLog->flush();
@@ -210,70 +209,6 @@ void CLogger::WriteWarning(const wchar_t* message)
PushRenderMessage(Warning, message);
}
-void CLogger::LogMessage(const wchar_t* fmt, ...)
-{
- va_list argp;
- wchar_t buffer[BUFFER_SIZE] = {0};
-
- va_start(argp, fmt);
- if (sys_vswprintf(buffer, ARRAY_SIZE(buffer), fmt, argp) == -1)
- {
- // Buffer too small - ensure the string is nicely terminated
- wcscpy_s(buffer+ARRAY_SIZE(buffer)-4, 4, L"...");
- }
- va_end(argp);
-
- WriteMessage(buffer);
-}
-
-void CLogger::LogMessageRender(const wchar_t* fmt, ...)
-{
- va_list argp;
- wchar_t buffer[BUFFER_SIZE] = {0};
-
- va_start(argp, fmt);
- if (sys_vswprintf(buffer, ARRAY_SIZE(buffer), fmt, argp) == -1)
- {
- // Buffer too small - ensure the string is nicely terminated
- wcscpy_s(buffer+ARRAY_SIZE(buffer)-4, 4, L"...");
- }
- va_end(argp);
-
- WriteMessage(buffer, true);
-}
-
-void CLogger::LogWarning(const wchar_t* fmt, ...)
-{
- va_list argp;
- wchar_t buffer[BUFFER_SIZE] = {0};
-
- va_start(argp, fmt);
- if (sys_vswprintf(buffer, ARRAY_SIZE(buffer), fmt, argp) == -1)
- {
- // Buffer too small - ensure the string is nicely terminated
- wcscpy_s(buffer+ARRAY_SIZE(buffer)-4, 4, L"...");
- }
- va_end(argp);
-
- WriteWarning(buffer);
-}
-
-void CLogger::LogError(const wchar_t* fmt, ...)
-{
- va_list argp;
- wchar_t buffer[BUFFER_SIZE] = {0};
-
- va_start(argp, fmt);
- if (sys_vswprintf(buffer, ARRAY_SIZE(buffer), fmt, argp) == -1)
- {
- // Buffer too small - ensure the string is nicely terminated
- wcscpy_s(buffer+ARRAY_SIZE(buffer)-4, 4, L"...");
- }
- va_end(argp);
-
- WriteError(buffer);
-}
-
void CLogger::Render()
{
PROFILE3_GPU("logger");
@@ -300,26 +235,26 @@ void CLogger::Render()
for (std::deque::iterator it = m_RenderMessages.begin(); it != m_RenderMessages.end(); ++it)
{
- const wchar_t* type;
+ const char* type;
if (it->method == Normal)
{
- type = L"info";
+ type = "info";
textRenderer.Color(0.0f, 0.8f, 0.0f);
}
else if (it->method == Warning)
{
- type = L"warning";
+ type = "warning";
textRenderer.Color(1.0f, 1.0f, 0.0f);
}
else
{
- type = L"error";
+ type = "error";
textRenderer.Color(1.0f, 0.0f, 0.0f);
}
CMatrix3D savedTransform = textRenderer.GetTransform();
- textRenderer.PrintfAdvance(L"[%8.3f] %ls: ", it->time, type);
+ textRenderer.PrintfAdvance(L"[%8.3f] %hs: ", it->time, type);
// Display the actual message in white so it's more readable
textRenderer.Color(1.0f, 1.0f, 1.0f);
textRenderer.Put(0.0f, 0.0f, it->message.c_str());
@@ -334,26 +269,26 @@ void CLogger::Render()
textTech->EndPass();
}
-void CLogger::PushRenderMessage(ELogMethod method, const wchar_t* message)
+void CLogger::PushRenderMessage(ELogMethod method, const char* message)
{
double now = timer_Time();
// Add each message line separately
- const wchar_t* pos = message;
- const wchar_t* eol;
- while ((eol = wcschr(pos, L'\n')) != NULL)
+ const char* pos = message;
+ const char* eol;
+ while ((eol = strchr(pos, '\n')) != NULL)
{
if (eol != pos)
{
- RenderedMessage r = { method, now, std::wstring(pos, eol) };
+ RenderedMessage r = { method, now, std::string(pos, eol) };
m_RenderMessages.push_back(r);
}
pos = eol + 1;
}
// Add the last line, if we didn't end on a \n
- if (*pos != L'\0')
+ if (*pos != '\0')
{
- RenderedMessage r = { method, now, std::wstring(pos) };
+ RenderedMessage r = { method, now, std::string(pos) };
m_RenderMessages.push_back(r);
}
}
@@ -398,10 +333,9 @@ TestLogger::~TestLogger()
g_Logger = m_OldLogger;
}
-std::wstring TestLogger::GetOutput()
+std::string TestLogger::GetOutput()
{
- Status err;
- return wstring_from_utf8(m_Stream.str(), &err);
+ return m_Stream.str();
}
TestStdoutLogger::TestStdoutLogger()
diff --git a/source/ps/CLogger.h b/source/ps/CLogger.h
index 78e9ad8f1f..ba239767c4 100644
--- a/source/ps/CLogger.h
+++ b/source/ps/CLogger.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -24,15 +24,16 @@
#include
#include "ps/ThreadUtil.h"
+#include "third_party/cppformat/format.h"
class CLogger;
extern CLogger* g_Logger;
-#define LOGMESSAGE g_Logger->LogMessage
-#define LOGMESSAGERENDER g_Logger->LogMessageRender
-#define LOGWARNING g_Logger->LogWarning
-#define LOGERROR g_Logger->LogError
+#define LOGMESSAGE(...) g_Logger->WriteMessage(fmt::sprintf(__VA_ARGS__).c_str(), false)
+#define LOGMESSAGERENDER(...) g_Logger->WriteMessage(fmt::sprintf(__VA_ARGS__).c_str(), true)
+#define LOGWARNING(...) g_Logger->WriteWarning(fmt::sprintf(__VA_ARGS__).c_str())
+#define LOGERROR(...) g_Logger->WriteError (fmt::sprintf(__VA_ARGS__).c_str())
/**
* Error/warning/message logging class.
@@ -64,23 +65,17 @@ public:
// Functions to write different message types (Errors and warnings are placed
// both in mainLog and intrestingLog.)
- void WriteMessage(const wchar_t* message, bool doRender);
- void WriteError (const wchar_t* message);
- void WriteWarning(const wchar_t* message);
+ void WriteMessage(const char* message, bool doRender);
+ void WriteError (const char* message);
+ void WriteWarning(const char* message);
- // Functions to write a message, warning or error to file.
- void LogMessage(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
- void LogMessageRender(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
- void LogWarning(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
- void LogError(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
-
// Render recent log messages onto the screen
void Render();
private:
void Init();
- void PushRenderMessage(ELogMethod method, const wchar_t* message);
+ void PushRenderMessage(ELogMethod method, const char* message);
// Delete old timed-out entries from the list of text to render
void CleanupRenderQueue();
@@ -104,7 +99,7 @@ private:
{
ELogMethod method;
double time;
- std::wstring message;
+ std::string message;
};
std::deque m_RenderMessages;
double m_RenderLastEraseTime;
@@ -123,7 +118,7 @@ class TestLogger
public:
TestLogger();
~TestLogger();
- std::wstring GetOutput();
+ std::string GetOutput();
private:
CLogger* m_OldLogger;
std::stringstream m_Stream;
diff --git a/source/ps/XML/tests/test_RelaxNG.h b/source/ps/XML/tests/test_RelaxNG.h
index 0cb11c69d3..3b1098672c 100644
--- a/source/ps/XML/tests/test_RelaxNG.h
+++ b/source/ps/XML/tests/test_RelaxNG.h
@@ -44,13 +44,13 @@ public:
{
TestLogger logger;
TS_ASSERT(!v.Validate(L"doc", L""));
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"Parse error: doc:1: Expecting element test, got bogus");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "Parse error: doc:1: Expecting element test, got bogus");
}
{
TestLogger logger;
TS_ASSERT(!v.Validate(L"doc", L"bogus"));
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"RelaxNGValidator: Failed to parse document");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "RelaxNGValidator: Failed to parse document");
}
TS_ASSERT(v.Validate(L"doc", L""));
@@ -82,7 +82,7 @@ public:
TestLogger logger;
TS_ASSERT(!v.LoadGrammar("whoops"));
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"RelaxNGValidator: Failed to compile schema");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "RelaxNGValidator: Failed to compile schema");
TS_ASSERT(!v.Validate(L"doc", L""));
}
diff --git a/source/ps/tests/test_CLogger.h b/source/ps/tests/test_CLogger.h
index 285abd6d64..7b7623e1c4 100644
--- a/source/ps/tests/test_CLogger.h
+++ b/source/ps/tests/test_CLogger.h
@@ -24,8 +24,8 @@ class TestCLogger : public CxxTest::TestSuite
public:
void test_basic()
{
- logger->LogMessage(L"Test 1");
- logger->LogMessage(L"Test 2");
+ logger->WriteMessage("Test 1", false);
+ logger->WriteMessage("Test 2", false);
ParseOutput();
@@ -34,65 +34,10 @@ public:
TS_ASSERT_EQUALS(lines[1], "Test 2");
}
- void test_overflow()
- {
- const int buflen = 1024;
-
- std::string msg0 (buflen-2, '*');
- std::string msg1 (buflen-1, '*');
- std::string msg2 (buflen, '*');
- std::string msg3 (buflen+1, '*');
-
- std::string clipped (buflen-4, '*');
- clipped += "...";
-
- logger->LogMessage(L"%hs", msg0.c_str());
- logger->LogMessage(L"%hs", msg1.c_str());
- logger->LogMessage(L"%hs", msg2.c_str());
- logger->LogMessage(L"%hs", msg3.c_str());
-
- logger->LogMessageRender(L"%hs", msg0.c_str());
- logger->LogMessageRender(L"%hs", msg1.c_str());
- logger->LogMessageRender(L"%hs", msg2.c_str());
- logger->LogMessageRender(L"%hs", msg3.c_str());
-
- logger->LogWarning(L"%hs", msg0.c_str());
- logger->LogWarning(L"%hs", msg1.c_str());
- logger->LogWarning(L"%hs", msg2.c_str());
- logger->LogWarning(L"%hs", msg3.c_str());
-
- logger->LogError(L"%hs", msg0.c_str());
- logger->LogError(L"%hs", msg1.c_str());
- logger->LogError(L"%hs", msg2.c_str());
- logger->LogError(L"%hs", msg3.c_str());
-
- ParseOutput();
-
- TS_ASSERT_EQUALS((int)lines.size(), 4*4);
- TS_ASSERT_EQUALS(lines[0], msg0);
- TS_ASSERT_EQUALS(lines[1], msg1);
- TS_ASSERT_EQUALS(lines[2], clipped);
- TS_ASSERT_EQUALS(lines[3], clipped);
-
- TS_ASSERT_EQUALS(lines[4], msg0);
- TS_ASSERT_EQUALS(lines[5], msg1);
- TS_ASSERT_EQUALS(lines[6], clipped);
- TS_ASSERT_EQUALS(lines[7], clipped);
-
- TS_ASSERT_EQUALS(lines[8], "WARNING: "+msg0);
- TS_ASSERT_EQUALS(lines[9], "WARNING: "+msg1);
- TS_ASSERT_EQUALS(lines[10], "WARNING: "+clipped);
- TS_ASSERT_EQUALS(lines[11], "WARNING: "+clipped);
-
- TS_ASSERT_EQUALS(lines[12], "ERROR: "+msg0);
- TS_ASSERT_EQUALS(lines[13], "ERROR: "+msg1);
- TS_ASSERT_EQUALS(lines[14], "ERROR: "+clipped);
- TS_ASSERT_EQUALS(lines[15], "ERROR: "+clipped);
- }
-
void test_unicode()
{
- logger->LogMessage(L"%lc %lc", 226, 295);
+ wchar_t str[] = { 226, 32, 295, 0 };
+ logger->WriteMessage(utf8_from_wstring(str).c_str(), false);
ParseOutput();
@@ -102,7 +47,7 @@ public:
void test_html()
{
- logger->LogMessage(L"Testc");
+ logger->WriteMessage("Testc", false);
ParseOutput();
diff --git a/source/scriptinterface/tests/test_ScriptInterface.h b/source/scriptinterface/tests/test_ScriptInterface.h
index 3089ea6af2..e966ee4a4f 100644
--- a/source/scriptinterface/tests/test_ScriptInterface.h
+++ b/source/scriptinterface/tests/test_ScriptInterface.h
@@ -31,8 +31,8 @@ public:
ScriptInterface script("Test", "Test", g_ScriptRuntime);
TestLogger logger;
TS_ASSERT(script.LoadScript(L"test.js", "var x = 1+1;"));
- TS_ASSERT_WSTR_NOT_CONTAINS(logger.GetOutput(), L"JavaScript error");
- TS_ASSERT_WSTR_NOT_CONTAINS(logger.GetOutput(), L"JavaScript warning");
+ TS_ASSERT_STR_NOT_CONTAINS(logger.GetOutput(), "JavaScript error");
+ TS_ASSERT_STR_NOT_CONTAINS(logger.GetOutput(), "JavaScript warning");
}
void test_loadscript_error()
@@ -40,7 +40,7 @@ public:
ScriptInterface script("Test", "Test", g_ScriptRuntime);
TestLogger logger;
TS_ASSERT(!script.LoadScript(L"test.js", "1+"));
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"JavaScript error: test.js line 1\nSyntaxError: syntax error");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "JavaScript error: test.js line 1\nSyntaxError: syntax error");
}
void test_loadscript_strict_warning()
@@ -49,7 +49,7 @@ public:
TestLogger logger;
// in strict mode, this inside a function doesn't point to the global object
TS_ASSERT(script.LoadScript(L"test.js", "var isStrict = (function() { return !this; })();warn('isStrict is '+isStrict);"));
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"WARNING: isStrict is true");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "WARNING: isStrict is true");
}
void test_loadscript_strict_error()
@@ -57,7 +57,7 @@ public:
ScriptInterface script("Test", "Test", g_ScriptRuntime);
TestLogger logger;
TS_ASSERT(!script.LoadScript(L"test.js", "with(1){}"));
- TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements");
+ TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements");
}
void test_clone_basic()
diff --git a/source/simulation2/tests/test_ComponentManager.h b/source/simulation2/tests/test_ComponentManager.h
index c84fe34208..9c92b4bb5b 100644
--- a/source/simulation2/tests/test_ComponentManager.h
+++ b/source/simulation2/tests/test_ComponentManager.h
@@ -109,13 +109,13 @@ public:
{
TestLogger log;
TS_ASSERT(! man.AddComponent(hnd1, 12345, noParam));
- TS_ASSERT_WSTR_CONTAINS(log.GetOutput(), L"ERROR: Invalid component id 12345");
+ TS_ASSERT_STR_CONTAINS(log.GetOutput(), "ERROR: Invalid component id 12345");
}
{
TestLogger log;
TS_ASSERT(! man.AddComponent(hnd1, CID_Test1B, noParam));
- TS_ASSERT_WSTR_CONTAINS(log.GetOutput(), L"ERROR: Multiple components for interface ");
+ TS_ASSERT_STR_CONTAINS(log.GetOutput(), "ERROR: Multiple components for interface ");
}
}
@@ -347,7 +347,7 @@ public:
// In SpiderMonkey 1.6, JS_ReportError calls the error reporter even if it's inside
// a try{} in the script; in recent versions (not sure when it changed) it doesn't
// so the error here won't get reported.
- TS_ASSERT_WSTR_NOT_CONTAINS(log.GetOutput(), L"ERROR: JavaScript error: simulation/components/error.js line 4\nInvalid interface id");
+ TS_ASSERT_STR_NOT_CONTAINS(log.GetOutput(), "ERROR: JavaScript error: simulation/components/error.js line 4\nInvalid interface id");
}
}
diff --git a/source/test_setup.cpp b/source/test_setup.cpp
index 448a650df9..c75f43a4f0 100644
--- a/source/test_setup.cpp
+++ b/source/test_setup.cpp
@@ -100,6 +100,11 @@ static MiscSetup miscSetup;
// Definition of functions from lib/self_test.h
+bool ts_str_contains(const std::string& str1, const std::string& str2)
+{
+ return str1.find(str2) != str1.npos;
+}
+
bool ts_str_contains(const std::wstring& str1, const std::wstring& str2)
{
return str1.find(str2) != str1.npos;