aboutsummaryrefslogtreecommitdiffstats
path: root/tests/UnitTest++
diff options
context:
space:
mode:
authorAdam Domurad <[email protected]>2012-11-21 12:37:38 -0500
committerAdam Domurad <[email protected]>2012-11-21 12:37:38 -0500
commit47e24eece15121c917a30166037cbb072bb6a443 (patch)
treefe7730603d16164711ff8db7b9dea283888aae90 /tests/UnitTest++
parent86bfc5f740524bdd7f341f9e1b90e39369ad7e8f (diff)
Add UnitTest++ source code into ITW, without integration.
Diffstat (limited to 'tests/UnitTest++')
-rw-r--r--tests/UnitTest++/COPYING20
-rw-r--r--tests/UnitTest++/Makefile96
-rw-r--r--tests/UnitTest++/README68
-rw-r--r--tests/UnitTest++/src/AssertException.cpp34
-rw-r--r--tests/UnitTest++/src/AssertException.h28
-rw-r--r--tests/UnitTest++/src/CheckMacros.h122
-rw-r--r--tests/UnitTest++/src/Checks.cpp50
-rw-r--r--tests/UnitTest++/src/Checks.h158
-rw-r--r--tests/UnitTest++/src/Config.h31
-rw-r--r--tests/UnitTest++/src/CurrentTest.cpp18
-rw-r--r--tests/UnitTest++/src/CurrentTest.h17
-rw-r--r--tests/UnitTest++/src/DeferredTestReporter.cpp29
-rw-r--r--tests/UnitTest++/src/DeferredTestReporter.h28
-rw-r--r--tests/UnitTest++/src/DeferredTestResult.cpp25
-rw-r--r--tests/UnitTest++/src/DeferredTestResult.h29
-rw-r--r--tests/UnitTest++/src/ExecuteTest.h46
-rw-r--r--tests/UnitTest++/src/MemoryOutStream.cpp149
-rw-r--r--tests/UnitTest++/src/MemoryOutStream.h67
-rw-r--r--tests/UnitTest++/src/Posix/SignalTranslator.cpp46
-rw-r--r--tests/UnitTest++/src/Posix/SignalTranslator.h42
-rw-r--r--tests/UnitTest++/src/Posix/TimeHelpers.cpp33
-rw-r--r--tests/UnitTest++/src/Posix/TimeHelpers.h28
-rw-r--r--tests/UnitTest++/src/ReportAssert.cpp10
-rw-r--r--tests/UnitTest++/src/ReportAssert.h10
-rw-r--r--tests/UnitTest++/src/Test.cpp41
-rw-r--r--tests/UnitTest++/src/Test.h34
-rw-r--r--tests/UnitTest++/src/TestDetails.cpp22
-rw-r--r--tests/UnitTest++/src/TestDetails.h24
-rw-r--r--tests/UnitTest++/src/TestList.cpp39
-rw-r--r--tests/UnitTest++/src/TestList.h32
-rw-r--r--tests/UnitTest++/src/TestMacros.h113
-rw-r--r--tests/UnitTest++/src/TestReporter.cpp10
-rw-r--r--tests/UnitTest++/src/TestReporter.h20
-rw-r--r--tests/UnitTest++/src/TestReporterStdout.cpp41
-rw-r--r--tests/UnitTest++/src/TestReporterStdout.h19
-rw-r--r--tests/UnitTest++/src/TestResults.cpp60
-rw-r--r--tests/UnitTest++/src/TestResults.h36
-rw-r--r--tests/UnitTest++/src/TestRunner.cpp76
-rw-r--r--tests/UnitTest++/src/TestRunner.h61
-rw-r--r--tests/UnitTest++/src/TestSuite.h14
-rw-r--r--tests/UnitTest++/src/TimeConstraint.cpp29
-rw-r--r--tests/UnitTest++/src/TimeConstraint.h33
-rw-r--r--tests/UnitTest++/src/TimeHelpers.h7
-rw-r--r--tests/UnitTest++/src/UnitTest++.h18
-rw-r--r--tests/UnitTest++/src/XmlTestReporter.cpp127
-rw-r--r--tests/UnitTest++/src/XmlTestReporter.h34
46 files changed, 2074 insertions, 0 deletions
diff --git a/tests/UnitTest++/COPYING b/tests/UnitTest++/COPYING
new file mode 100644
index 0000000..9f96308
--- /dev/null
+++ b/tests/UnitTest++/COPYING
@@ -0,0 +1,20 @@
+Copyright (c) 2006 Noel Llopis and Charles Nicholson
+
+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.
diff --git a/tests/UnitTest++/Makefile b/tests/UnitTest++/Makefile
new file mode 100644
index 0000000..007bc80
--- /dev/null
+++ b/tests/UnitTest++/Makefile
@@ -0,0 +1,96 @@
+CXX = g++
+CXXFLAGS ?= -g -Wall -W -ansi # -pedantic
+LDFLAGS ?=
+SED = sed
+MV = mv
+RM = rm
+
+.SUFFIXES: .o .cpp
+
+lib = libUnitTest++.a
+test = TestUnitTest++
+
+src = src/AssertException.cpp \
+ src/Test.cpp \
+ src/Checks.cpp \
+ src/TestRunner.cpp \
+ src/TestResults.cpp \
+ src/TestReporter.cpp \
+ src/TestReporterStdout.cpp \
+ src/ReportAssert.cpp \
+ src/TestList.cpp \
+ src/TimeConstraint.cpp \
+ src/TestDetails.cpp \
+ src/MemoryOutStream.cpp \
+ src/DeferredTestReporter.cpp \
+ src/DeferredTestResult.cpp \
+ src/XmlTestReporter.cpp \
+ src/CurrentTest.cpp
+
+ifeq ($(MSYSTEM), MINGW32)
+ src += src/Win32/TimeHelpers.cpp
+else
+ src += src/Posix/SignalTranslator.cpp \
+ src/Posix/TimeHelpers.cpp
+endif
+
+test_src = src/tests/Main.cpp \
+ src/tests/TestAssertHandler.cpp \
+ src/tests/TestChecks.cpp \
+ src/tests/TestUnitTest++.cpp \
+ src/tests/TestTest.cpp \
+ src/tests/TestTestResults.cpp \
+ src/tests/TestTestRunner.cpp \
+ src/tests/TestCheckMacros.cpp \
+ src/tests/TestTestList.cpp \
+ src/tests/TestTestMacros.cpp \
+ src/tests/TestTimeConstraint.cpp \
+ src/tests/TestTimeConstraintMacro.cpp \
+ src/tests/TestMemoryOutStream.cpp \
+ src/tests/TestDeferredTestReporter.cpp \
+ src/tests/TestXmlTestReporter.cpp \
+ src/tests/TestCurrentTest.cpp
+
+objects = $(patsubst %.cpp, %.o, $(src))
+test_objects = $(patsubst %.cpp, %.o, $(test_src))
+dependencies = $(subst .o,.d,$(objects))
+test_dependencies = $(subst .o,.d,$(test_objects))
+
+define make-depend
+ $(CXX) $(CXXFLAGS) -M $1 | \
+ $(SED) -e 's,\($(notdir $2)\) *:,$(dir $2)\1: ,' > $3.tmp
+ $(SED) -e 's/#.*//' \
+ -e 's/^[^:]*: *//' \
+ -e 's/ *\\$$//' \
+ -e '/^$$/ d' \
+ -e 's/$$/ :/' $3.tmp >> $3.tmp
+ $(MV) $3.tmp $3
+endef
+
+
+all: $(lib)
+
+
+$(lib): $(objects)
+ @echo Creating $(lib) library...
+ @ar cr $(lib) $(objects)
+
+$(test): $(lib) $(test_objects)
+ @echo Linking $(test)...
+ @$(CXX) $(LDFLAGS) -o $(test) $(test_objects) $(lib)
+ @echo Running unit tests...
+ @./$(test)
+
+clean:
+ -@$(RM) $(objects) $(test_objects) $(dependencies) $(test_dependencies) $(test) $(lib) 2> /dev/null
+
+%.o : %.cpp
+ @echo $<
+ @$(call make-depend,$<,$@,$(subst .o,.d,$@))
+ @$(CXX) $(CXXFLAGS) -c $< -o $(patsubst %.cpp, %.o, $<)
+
+
+ifneq "$(MAKECMDGOALS)" "clean"
+-include $(dependencies)
+-include $(test_dependencies)
+endif
diff --git a/tests/UnitTest++/README b/tests/UnitTest++/README
new file mode 100644
index 0000000..e0a5192
--- /dev/null
+++ b/tests/UnitTest++/README
@@ -0,0 +1,68 @@
+UnitTest++ README
+Version: v1.4
+Last update: 2008-10-30
+
+UnitTest++ is free software. You may copy, distribute, and modify it under
+the terms of the License contained in the file COPYING distributed
+with this package. This license is the same as the MIT/X Consortium
+license.
+
+See src/tests/TestUnitTest++.cpp for usage.
+
+Authors:
+Noel Llopis ([email protected])
+Charles Nicholson ([email protected])
+
+Contributors:
+Jim Tilander
+Kim Grasman
+Jonathan Jansson
+Dirck Blaskey
+Rory Driscoll
+Dan Lind
+Matt Kimmel -- Submitted with permission from Blue Fang Games
+Anthony Moralez
+Jeff Dixon
+Randy Coulman
+Lieven van der Heide
+
+Release notes:
+--------------
+Version 1.4 (2008-10-30)
+- CHECK macros work at arbitrary stack depth from inside TESTs.
+- Remove obsolete TEST_UTILITY macros
+- Predicated test execution (via TestRunner::RunTestsIf)
+- Better exception handling for fixture ctors/dtors.
+- VC6/7/8/9 support
+
+Version 1.3 (2007-4-22)
+- Removed dynamic memory allocations (other than streams)
+- MinGW support
+- Consistent (native) line endings
+- Minor bug fixing
+
+Version 1.2 (2006-10-29)
+- First pass at documentation.
+- More detailed error crash catching in fixtures.
+- Standard streams used for printing objects under check. This should allow the
+ use of standard class types such as std::string or other custom classes with
+ stream operators to ostream.
+- Standard streams can be optionally compiled off by defining UNITTEST_USE_CUSTOM_STREAMS
+ in Config.h
+- Added named test suites
+- Added CHECK_ARRAY2D_CLOSE
+- Posix library name is libUnitTest++.a now
+- Floating point numbers are postfixed with f in the failure reports
+
+Version 1.1 (2006-04-18)
+- CHECK macros do not have side effects even if one of the parameters changes state
+- Removed CHECK_ARRAY_EQUAL (too similar to CHECK_ARRAY_CLOSE)
+- Added local and global time constraints
+- Removed dependencies on strstream
+- Improved Posix signal to exception translator
+- Failing tests are added to Visual Studio's error list
+- Fixed Visual Studio projects to work with spaces in directories
+
+Version 1.0 (2006-03-15)
+- Initial release
+
diff --git a/tests/UnitTest++/src/AssertException.cpp b/tests/UnitTest++/src/AssertException.cpp
new file mode 100644
index 0000000..63f556e
--- /dev/null
+++ b/tests/UnitTest++/src/AssertException.cpp
@@ -0,0 +1,34 @@
+#include "AssertException.h"
+#include <cstring>
+
+namespace UnitTest {
+
+AssertException::AssertException(char const* description, char const* filename, int lineNumber)
+ : m_lineNumber(lineNumber)
+{
+ using namespace std;
+
+ strcpy(m_description, description);
+ strcpy(m_filename, filename);
+}
+
+AssertException::~AssertException() throw()
+{
+}
+
+char const* AssertException::what() const throw()
+{
+ return m_description;
+}
+
+char const* AssertException::Filename() const
+{
+ return m_filename;
+}
+
+int AssertException::LineNumber() const
+{
+ return m_lineNumber;
+}
+
+}
diff --git a/tests/UnitTest++/src/AssertException.h b/tests/UnitTest++/src/AssertException.h
new file mode 100644
index 0000000..e04d450
--- /dev/null
+++ b/tests/UnitTest++/src/AssertException.h
@@ -0,0 +1,28 @@
+#ifndef UNITTEST_ASSERTEXCEPTION_H
+#define UNITTEST_ASSERTEXCEPTION_H
+
+#include <exception>
+
+
+namespace UnitTest {
+
+class AssertException : public std::exception
+{
+public:
+ AssertException(char const* description, char const* filename, int lineNumber);
+ virtual ~AssertException() throw();
+
+ virtual char const* what() const throw();
+
+ char const* Filename() const;
+ int LineNumber() const;
+
+private:
+ char m_description[512];
+ char m_filename[256];
+ int m_lineNumber;
+};
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/CheckMacros.h b/tests/UnitTest++/src/CheckMacros.h
new file mode 100644
index 0000000..8fbdae7
--- /dev/null
+++ b/tests/UnitTest++/src/CheckMacros.h
@@ -0,0 +1,122 @@
+#ifndef UNITTEST_CHECKMACROS_H
+#define UNITTEST_CHECKMACROS_H
+
+#include "Checks.h"
+#include "AssertException.h"
+#include "MemoryOutStream.h"
+#include "TestDetails.h"
+#include "CurrentTest.h"
+
+#ifdef CHECK
+ #error UnitTest++ redefines CHECK
+#endif
+
+#ifdef CHECK_EQUAL
+ #error UnitTest++ redefines CHECK_EQUAL
+#endif
+
+#ifdef CHECK_CLOSE
+ #error UnitTest++ redefines CHECK_CLOSE
+#endif
+
+#ifdef CHECK_ARRAY_EQUAL
+ #error UnitTest++ redefines CHECK_ARRAY_EQUAL
+#endif
+
+#ifdef CHECK_ARRAY_CLOSE
+ #error UnitTest++ redefines CHECK_ARRAY_CLOSE
+#endif
+
+#ifdef CHECK_ARRAY2D_CLOSE
+ #error UnitTest++ redefines CHECK_ARRAY2D_CLOSE
+#endif
+
+#define CHECK(value) \
+ do \
+ { \
+ try { \
+ if (!UnitTest::Check(value)) \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), #value); \
+ } \
+ catch (...) { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
+ "Unhandled exception in CHECK(" #value ")"); \
+ } \
+ } while (0)
+
+#define CHECK_EQUAL(expected, actual) \
+ do \
+ { \
+ try { \
+ UnitTest::CheckEqual(*UnitTest::CurrentTest::Results(), expected, actual, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
+ } \
+ catch (...) { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
+ "Unhandled exception in CHECK_EQUAL(" #expected ", " #actual ")"); \
+ } \
+ } while (0)
+
+#define CHECK_CLOSE(expected, actual, tolerance) \
+ do \
+ { \
+ try { \
+ UnitTest::CheckClose(*UnitTest::CurrentTest::Results(), expected, actual, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
+ } \
+ catch (...) { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
+ "Unhandled exception in CHECK_CLOSE(" #expected ", " #actual ")"); \
+ } \
+ } while (0)
+
+#define CHECK_ARRAY_EQUAL(expected, actual, count) \
+ do \
+ { \
+ try { \
+ UnitTest::CheckArrayEqual(*UnitTest::CurrentTest::Results(), expected, actual, count, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
+ } \
+ catch (...) { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
+ "Unhandled exception in CHECK_ARRAY_EQUAL(" #expected ", " #actual ")"); \
+ } \
+ } while (0)
+
+#define CHECK_ARRAY_CLOSE(expected, actual, count, tolerance) \
+ do \
+ { \
+ try { \
+ UnitTest::CheckArrayClose(*UnitTest::CurrentTest::Results(), expected, actual, count, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
+ } \
+ catch (...) { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
+ "Unhandled exception in CHECK_ARRAY_CLOSE(" #expected ", " #actual ")"); \
+ } \
+ } while (0)
+
+#define CHECK_ARRAY2D_CLOSE(expected, actual, rows, columns, tolerance) \
+ do \
+ { \
+ try { \
+ UnitTest::CheckArray2DClose(*UnitTest::CurrentTest::Results(), expected, actual, rows, columns, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
+ } \
+ catch (...) { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
+ "Unhandled exception in CHECK_ARRAY_CLOSE(" #expected ", " #actual ")"); \
+ } \
+ } while (0)
+
+
+#define CHECK_THROW(expression, ExpectedExceptionType) \
+ do \
+ { \
+ bool caught_ = false; \
+ try { expression; } \
+ catch (ExpectedExceptionType const&) { caught_ = true; } \
+ catch (...) {} \
+ if (!caught_) \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), "Expected exception: \"" #ExpectedExceptionType "\" not thrown"); \
+ } while(0)
+
+#define CHECK_ASSERT(expression) \
+ CHECK_THROW(expression, UnitTest::AssertException);
+
+#endif
diff --git a/tests/UnitTest++/src/Checks.cpp b/tests/UnitTest++/src/Checks.cpp
new file mode 100644
index 0000000..2d09519
--- /dev/null
+++ b/tests/UnitTest++/src/Checks.cpp
@@ -0,0 +1,50 @@
+#include "Checks.h"
+#include <cstring>
+
+namespace UnitTest {
+
+namespace {
+
+void CheckStringsEqual(TestResults& results, char const* expected, char const* actual,
+ TestDetails const& details)
+{
+ using namespace std;
+
+ if (strcmp(expected, actual))
+ {
+ UnitTest::MemoryOutStream stream;
+ stream << "Expected " << expected << " but was " << actual;
+
+ results.OnTestFailure(details, stream.GetText());
+ }
+}
+
+}
+
+
+void CheckEqual(TestResults& results, char const* expected, char const* actual,
+ TestDetails const& details)
+{
+ CheckStringsEqual(results, expected, actual, details);
+}
+
+void CheckEqual(TestResults& results, char* expected, char* actual,
+ TestDetails const& details)
+{
+ CheckStringsEqual(results, expected, actual, details);
+}
+
+void CheckEqual(TestResults& results, char* expected, char const* actual,
+ TestDetails const& details)
+{
+ CheckStringsEqual(results, expected, actual, details);
+}
+
+void CheckEqual(TestResults& results, char const* expected, char* actual,
+ TestDetails const& details)
+{
+ CheckStringsEqual(results, expected, actual, details);
+}
+
+
+}
diff --git a/tests/UnitTest++/src/Checks.h b/tests/UnitTest++/src/Checks.h
new file mode 100644
index 0000000..0090084
--- /dev/null
+++ b/tests/UnitTest++/src/Checks.h
@@ -0,0 +1,158 @@
+#ifndef UNITTEST_CHECKS_H
+#define UNITTEST_CHECKS_H
+
+#include "Config.h"
+#include "TestResults.h"
+#include "MemoryOutStream.h"
+
+namespace UnitTest {
+
+
+template< typename Value >
+bool Check(Value const value)
+{
+ return !!value; // doing double negative to avoid silly VS warnings
+}
+
+
+template< typename Expected, typename Actual >
+void CheckEqual(TestResults& results, Expected const& expected, Actual const& actual, TestDetails const& details)
+{
+ if (!(expected == actual))
+ {
+ UnitTest::MemoryOutStream stream;
+ stream << "Expected " << expected << " but was " << actual;
+
+ results.OnTestFailure(details, stream.GetText());
+ }
+}
+
+void CheckEqual(TestResults& results, char const* expected, char const* actual, TestDetails const& details);
+
+void CheckEqual(TestResults& results, char* expected, char* actual, TestDetails const& details);
+
+void CheckEqual(TestResults& results, char* expected, char const* actual, TestDetails const& details);
+
+void CheckEqual(TestResults& results, char const* expected, char* actual, TestDetails const& details);
+
+template< typename Expected, typename Actual, typename Tolerance >
+bool AreClose(Expected const& expected, Actual const& actual, Tolerance const& tolerance)
+{
+ return (actual >= (expected - tolerance)) && (actual <= (expected + tolerance));
+}
+
+template< typename Expected, typename Actual, typename Tolerance >
+void CheckClose(TestResults& results, Expected const& expected, Actual const& actual, Tolerance const& tolerance,
+ TestDetails const& details)
+{
+ if (!AreClose(expected, actual, tolerance))
+ {
+ UnitTest::MemoryOutStream stream;
+ stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual;
+
+ results.OnTestFailure(details, stream.GetText());
+ }
+}
+
+
+template< typename Expected, typename Actual >
+void CheckArrayEqual(TestResults& results, Expected const& expected, Actual const& actual,
+ int const count, TestDetails const& details)
+{
+ bool equal = true;
+ for (int i = 0; i < count; ++i)
+ equal &= (expected[i] == actual[i]);
+
+ if (!equal)
+ {
+ UnitTest::MemoryOutStream stream;
+
+ stream << "Expected [ ";
+
+ for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex)
+ stream << expected[expectedIndex] << " ";
+
+ stream << "] but was [ ";
+
+ for (int actualIndex = 0; actualIndex < count; ++actualIndex)
+ stream << actual[actualIndex] << " ";
+
+ stream << "]";
+
+ results.OnTestFailure(details, stream.GetText());
+ }
+}
+
+template< typename Expected, typename Actual, typename Tolerance >
+bool ArrayAreClose(Expected const& expected, Actual const& actual, int const count, Tolerance const& tolerance)
+{
+ bool equal = true;
+ for (int i = 0; i < count; ++i)
+ equal &= AreClose(expected[i], actual[i], tolerance);
+ return equal;
+}
+
+template< typename Expected, typename Actual, typename Tolerance >
+void CheckArrayClose(TestResults& results, Expected const& expected, Actual const& actual,
+ int const count, Tolerance const& tolerance, TestDetails const& details)
+{
+ bool equal = ArrayAreClose(expected, actual, count, tolerance);
+
+ if (!equal)
+ {
+ UnitTest::MemoryOutStream stream;
+
+ stream << "Expected [ ";
+ for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex)
+ stream << expected[expectedIndex] << " ";
+ stream << "] +/- " << tolerance << " but was [ ";
+
+ for (int actualIndex = 0; actualIndex < count; ++actualIndex)
+ stream << actual[actualIndex] << " ";
+ stream << "]";
+
+ results.OnTestFailure(details, stream.GetText());
+ }
+}
+
+template< typename Expected, typename Actual, typename Tolerance >
+void CheckArray2DClose(TestResults& results, Expected const& expected, Actual const& actual,
+ int const rows, int const columns, Tolerance const& tolerance, TestDetails const& details)
+{
+ bool equal = true;
+ for (int i = 0; i < rows; ++i)
+ equal &= ArrayAreClose(expected[i], actual[i], columns, tolerance);
+
+ if (!equal)
+ {
+ UnitTest::MemoryOutStream stream;
+
+ stream << "Expected [ ";
+
+ for (int expectedRow = 0; expectedRow < rows; ++expectedRow)
+ {
+ stream << "[ ";
+ for (int expectedColumn = 0; expectedColumn < columns; ++expectedColumn)
+ stream << expected[expectedRow][expectedColumn] << " ";
+ stream << "] ";
+ }
+
+ stream << "] +/- " << tolerance << " but was [ ";
+
+ for (int actualRow = 0; actualRow < rows; ++actualRow)
+ {
+ stream << "[ ";
+ for (int actualColumn = 0; actualColumn < columns; ++actualColumn)
+ stream << actual[actualRow][actualColumn] << " ";
+ stream << "] ";
+ }
+
+ stream << "]";
+
+ results.OnTestFailure(details, stream.GetText());
+ }
+}
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/Config.h b/tests/UnitTest++/src/Config.h
new file mode 100644
index 0000000..0ba21a0
--- /dev/null
+++ b/tests/UnitTest++/src/Config.h
@@ -0,0 +1,31 @@
+#ifndef UNITTEST_CONFIG_H
+#define UNITTEST_CONFIG_H
+
+// Standard defines documented here: http://predef.sourceforge.net
+
+#if defined(_MSC_VER)
+ #pragma warning(disable:4127) // conditional expression is constant
+ #pragma warning(disable:4702) // unreachable code
+ #pragma warning(disable:4722) // destructor never returns, potential memory leak
+
+ #if (_MSC_VER == 1200) // VC6
+ #pragma warning(disable:4786)
+ #pragma warning(disable:4290)
+ #endif
+#endif
+
+#if defined(unix) || defined(__unix__) || defined(__unix) || defined(linux) || \
+ defined(__APPLE__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
+ #define UNITTEST_POSIX
+#endif
+
+#if defined(__MINGW32__)
+ #define UNITTEST_MINGW
+#endif
+
+// by default, MemoryOutStream is implemented in terms of std::ostringstream, which can be expensive.
+// uncomment this line to use the custom MemoryOutStream (no deps on std::ostringstream).
+
+//#define UNITTEST_USE_CUSTOM_STREAMS
+
+#endif
diff --git a/tests/UnitTest++/src/CurrentTest.cpp b/tests/UnitTest++/src/CurrentTest.cpp
new file mode 100644
index 0000000..ad8d9af
--- /dev/null
+++ b/tests/UnitTest++/src/CurrentTest.cpp
@@ -0,0 +1,18 @@
+#include "CurrentTest.h"
+#include <cstddef>
+
+namespace UnitTest {
+
+TestResults*& CurrentTest::Results()
+{
+ static TestResults* testResults = NULL;
+ return testResults;
+}
+
+const TestDetails*& CurrentTest::Details()
+{
+ static const TestDetails* testDetails = NULL;
+ return testDetails;
+}
+
+}
diff --git a/tests/UnitTest++/src/CurrentTest.h b/tests/UnitTest++/src/CurrentTest.h
new file mode 100644
index 0000000..9e9d07a
--- /dev/null
+++ b/tests/UnitTest++/src/CurrentTest.h
@@ -0,0 +1,17 @@
+#ifndef UNITTEST_CURRENTTESTRESULTS_H
+#define UNITTEST_CURRENTTESTRESULTS_H
+
+namespace UnitTest {
+
+class TestResults;
+class TestDetails;
+
+namespace CurrentTest
+{
+ TestResults*& Results();
+ const TestDetails*& Details();
+}
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/DeferredTestReporter.cpp b/tests/UnitTest++/src/DeferredTestReporter.cpp
new file mode 100644
index 0000000..f90826b
--- /dev/null
+++ b/tests/UnitTest++/src/DeferredTestReporter.cpp
@@ -0,0 +1,29 @@
+#include "DeferredTestReporter.h"
+#include "TestDetails.h"
+#include "Config.h"
+
+using namespace UnitTest;
+
+void DeferredTestReporter::ReportTestStart(TestDetails const& details)
+{
+ m_results.push_back(DeferredTestResult(details.suiteName, details.testName));
+}
+
+void DeferredTestReporter::ReportFailure(TestDetails const& details, char const* failure)
+{
+ DeferredTestResult& r = m_results.back();
+ r.failed = true;
+ r.failures.push_back(DeferredTestResult::Failure(details.lineNumber, failure));
+ r.failureFile = details.filename;
+}
+
+void DeferredTestReporter::ReportTestFinish(TestDetails const&, float secondsElapsed)
+{
+ DeferredTestResult& r = m_results.back();
+ r.timeElapsed = secondsElapsed;
+}
+
+DeferredTestReporter::DeferredTestResultList& DeferredTestReporter::GetResults()
+{
+ return m_results;
+}
diff --git a/tests/UnitTest++/src/DeferredTestReporter.h b/tests/UnitTest++/src/DeferredTestReporter.h
new file mode 100644
index 0000000..026ed05
--- /dev/null
+++ b/tests/UnitTest++/src/DeferredTestReporter.h
@@ -0,0 +1,28 @@
+#ifndef UNITTEST_DEFERREDTESTREPORTER_H
+#define UNITTEST_DEFERREDTESTREPORTER_H
+
+#include "TestReporter.h"
+#include "DeferredTestResult.h"
+
+#include <vector>
+
+namespace UnitTest
+{
+
+class DeferredTestReporter : public TestReporter
+{
+public:
+ virtual void ReportTestStart(TestDetails const& details);
+ virtual void ReportFailure(TestDetails const& details, char const* failure);
+ virtual void ReportTestFinish(TestDetails const& details, float secondsElapsed);
+
+ typedef std::vector< DeferredTestResult > DeferredTestResultList;
+ DeferredTestResultList& GetResults();
+
+private:
+ DeferredTestResultList m_results;
+};
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/DeferredTestResult.cpp b/tests/UnitTest++/src/DeferredTestResult.cpp
new file mode 100644
index 0000000..c2daa25
--- /dev/null
+++ b/tests/UnitTest++/src/DeferredTestResult.cpp
@@ -0,0 +1,25 @@
+#include "DeferredTestResult.h"
+#include "Config.h"
+
+namespace UnitTest
+{
+
+DeferredTestResult::DeferredTestResult()
+ : suiteName("")
+ , testName("")
+ , failureFile("")
+ , timeElapsed(0.0f)
+ , failed(false)
+{
+}
+
+DeferredTestResult::DeferredTestResult(char const* suite, char const* test)
+ : suiteName(suite)
+ , testName(test)
+ , failureFile("")
+ , timeElapsed(0.0f)
+ , failed(false)
+{
+}
+
+}
diff --git a/tests/UnitTest++/src/DeferredTestResult.h b/tests/UnitTest++/src/DeferredTestResult.h
new file mode 100644
index 0000000..6cca77c
--- /dev/null
+++ b/tests/UnitTest++/src/DeferredTestResult.h
@@ -0,0 +1,29 @@
+#ifndef UNITTEST_DEFERREDTESTRESULT_H
+#define UNITTEST_DEFERREDTESTRESULT_H
+
+#include <string>
+#include <vector>
+
+namespace UnitTest
+{
+
+struct DeferredTestResult
+{
+ DeferredTestResult();
+ DeferredTestResult(char const* suite, char const* test);
+
+ std::string suiteName;
+ std::string testName;
+ std::string failureFile;
+
+ typedef std::pair< int, std::string > Failure;
+ typedef std::vector< Failure > FailureVec;
+ FailureVec failures;
+
+ float timeElapsed;
+ bool failed;
+};
+
+}
+
+#endif //UNITTEST_DEFERREDTESTRESULT_H
diff --git a/tests/UnitTest++/src/ExecuteTest.h b/tests/UnitTest++/src/ExecuteTest.h
new file mode 100644
index 0000000..a00d4c5
--- /dev/null
+++ b/tests/UnitTest++/src/ExecuteTest.h
@@ -0,0 +1,46 @@
+#ifndef UNITTEST_EXECUTE_TEST_H
+#define UNITTEST_EXECUTE_TEST_H
+
+#include "TestDetails.h"
+#include "MemoryOutStream.h"
+#include "AssertException.h"
+#include "CurrentTest.h"
+
+#ifdef UNITTEST_POSIX
+ #include "Posix/SignalTranslator.h"
+#endif
+
+namespace UnitTest {
+
+template< typename T >
+void ExecuteTest(T& testObject, TestDetails const& details)
+{
+ CurrentTest::Details() = &details;
+
+ try
+ {
+#ifdef UNITTEST_POSIX
+ UNITTEST_THROW_SIGNALS
+#endif
+ testObject.RunImpl();
+ }
+ catch (AssertException const& e)
+ {
+ CurrentTest::Results()->OnTestFailure(
+ TestDetails(details.testName, details.suiteName, e.Filename(), e.LineNumber()), e.what());
+ }
+ catch (std::exception const& e)
+ {
+ MemoryOutStream stream;
+ stream << "Unhandled exception: " << e.what();
+ CurrentTest::Results()->OnTestFailure(details, stream.GetText());
+ }
+ catch (...)
+ {
+ CurrentTest::Results()->OnTestFailure(details, "Unhandled exception: Crash!");
+ }
+}
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/MemoryOutStream.cpp b/tests/UnitTest++/src/MemoryOutStream.cpp
new file mode 100644
index 0000000..67a3e37
--- /dev/null
+++ b/tests/UnitTest++/src/MemoryOutStream.cpp
@@ -0,0 +1,149 @@
+#include "MemoryOutStream.h"
+
+#ifndef UNITTEST_USE_CUSTOM_STREAMS
+
+
+namespace UnitTest {
+
+char const* MemoryOutStream::GetText() const
+{
+ m_text = this->str();
+ return m_text.c_str();
+}
+
+
+}
+
+
+#else
+
+
+#include <cstring>
+#include <cstdio>
+
+namespace UnitTest {
+
+namespace {
+
+template<typename ValueType>
+void FormatToStream(MemoryOutStream& stream, char const* format, ValueType const& value)
+{
+ using namespace std;
+
+ char txt[32];
+ sprintf(txt, format, value);
+ stream << txt;
+}
+
+int RoundUpToMultipleOfPow2Number (int n, int pow2Number)
+{
+ return (n + (pow2Number - 1)) & ~(pow2Number - 1);
+}
+
+}
+
+
+MemoryOutStream::MemoryOutStream(int const size)
+ : m_capacity (0)
+ , m_buffer (0)
+
+{
+ GrowBuffer(size);
+}
+
+MemoryOutStream::~MemoryOutStream()
+{
+ delete [] m_buffer;
+}
+
+char const* MemoryOutStream::GetText() const
+{
+ return m_buffer;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (char const* txt)
+{
+ using namespace std;
+
+ int const bytesLeft = m_capacity - (int)strlen(m_buffer);
+ int const bytesRequired = (int)strlen(txt) + 1;
+
+ if (bytesRequired > bytesLeft)
+ {
+ int const requiredCapacity = bytesRequired + m_capacity - bytesLeft;
+ GrowBuffer(requiredCapacity);
+ }
+
+ strcat(m_buffer, txt);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (int const n)
+{
+ FormatToStream(*this, "%i", n);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (long const n)
+{
+ FormatToStream(*this, "%li", n);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (unsigned long const n)
+{
+ FormatToStream(*this, "%lu", n);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (float const f)
+{
+ FormatToStream(*this, "%ff", f);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (void const* p)
+{
+ FormatToStream(*this, "%p", p);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator << (unsigned int const s)
+{
+ FormatToStream(*this, "%u", s);
+ return *this;
+}
+
+MemoryOutStream& MemoryOutStream::operator <<(double const d)
+{
+ FormatToStream(*this, "%f", d);
+ return *this;
+}
+
+int MemoryOutStream::GetCapacity() const
+{
+ return m_capacity;
+}
+
+
+void MemoryOutStream::GrowBuffer(int const desiredCapacity)
+{
+ int const newCapacity = RoundUpToMultipleOfPow2Number(desiredCapacity, GROW_CHUNK_SIZE);
+
+ using namespace std;
+
+ char* buffer = new char[newCapacity];
+ if (m_buffer)
+ strcpy(buffer, m_buffer);
+ else
+ strcpy(buffer, "");
+
+ delete [] m_buffer;
+ m_buffer = buffer;
+ m_capacity = newCapacity;
+}
+
+}
+
+
+#endif
diff --git a/tests/UnitTest++/src/MemoryOutStream.h b/tests/UnitTest++/src/MemoryOutStream.h
new file mode 100644
index 0000000..e03227e
--- /dev/null
+++ b/tests/UnitTest++/src/MemoryOutStream.h
@@ -0,0 +1,67 @@
+#ifndef UNITTEST_MEMORYOUTSTREAM_H
+#define UNITTEST_MEMORYOUTSTREAM_H
+
+#include "Config.h"
+
+#ifndef UNITTEST_USE_CUSTOM_STREAMS
+
+#include <sstream>
+
+namespace UnitTest
+{
+
+class MemoryOutStream : public std::ostringstream
+{
+public:
+ MemoryOutStream() {}
+ char const* GetText() const;
+
+private:
+ MemoryOutStream(MemoryOutStream const&);
+ void operator =(MemoryOutStream const&);
+
+ mutable std::string m_text;
+};
+
+}
+
+#else
+
+#include <cstddef>
+
+namespace UnitTest
+{
+
+class MemoryOutStream
+{
+public:
+ explicit MemoryOutStream(int const size = 256);
+ ~MemoryOutStream();
+
+ char const* GetText() const;
+
+ MemoryOutStream& operator << (char const* txt);
+ MemoryOutStream& operator << (int n);
+ MemoryOutStream& operator << (long n);
+ MemoryOutStream& operator << (unsigned long n);
+ MemoryOutStream& operator << (float f);
+ MemoryOutStream& operator << (double d);
+ MemoryOutStream& operator << (void const* p);
+ MemoryOutStream& operator << (unsigned int s);
+
+ enum { GROW_CHUNK_SIZE = 32 };
+ int GetCapacity() const;
+
+private:
+ void operator= (MemoryOutStream const&);
+ void GrowBuffer(int capacity);
+
+ int m_capacity;
+ char* m_buffer;
+};
+
+}
+
+#endif
+
+#endif
diff --git a/tests/UnitTest++/src/Posix/SignalTranslator.cpp b/tests/UnitTest++/src/Posix/SignalTranslator.cpp
new file mode 100644
index 0000000..3689c8c
--- /dev/null
+++ b/tests/UnitTest++/src/Posix/SignalTranslator.cpp
@@ -0,0 +1,46 @@
+#include "SignalTranslator.h"
+
+namespace UnitTest {
+
+sigjmp_buf* SignalTranslator::s_jumpTarget = 0;
+
+namespace {
+
+void SignalHandler(int sig)
+{
+ siglongjmp(*SignalTranslator::s_jumpTarget, sig );
+}
+
+}
+
+
+SignalTranslator::SignalTranslator()
+{
+ m_oldJumpTarget = s_jumpTarget;
+ s_jumpTarget = &m_currentJumpTarget;
+
+ struct sigaction action;
+ action.sa_flags = 0;
+ action.sa_handler = SignalHandler;
+ sigemptyset( &action.sa_mask );
+
+ sigaction( SIGSEGV, &action, &m_old_SIGSEGV_action );
+ sigaction( SIGFPE , &action, &m_old_SIGFPE_action );
+ sigaction( SIGTRAP, &action, &m_old_SIGTRAP_action );
+ sigaction( SIGBUS , &action, &m_old_SIGBUS_action );
+ sigaction( SIGILL , &action, &m_old_SIGBUS_action );
+}
+
+SignalTranslator::~SignalTranslator()
+{
+ sigaction( SIGILL , &m_old_SIGBUS_action , 0 );
+ sigaction( SIGBUS , &m_old_SIGBUS_action , 0 );
+ sigaction( SIGTRAP, &m_old_SIGTRAP_action, 0 );
+ sigaction( SIGFPE , &m_old_SIGFPE_action , 0 );
+ sigaction( SIGSEGV, &m_old_SIGSEGV_action, 0 );
+
+ s_jumpTarget = m_oldJumpTarget;
+}
+
+
+}
diff --git a/tests/UnitTest++/src/Posix/SignalTranslator.h b/tests/UnitTest++/src/Posix/SignalTranslator.h
new file mode 100644
index 0000000..bf98ad3
--- /dev/null
+++ b/tests/UnitTest++/src/Posix/SignalTranslator.h
@@ -0,0 +1,42 @@
+#ifndef UNITTEST_SIGNALTRANSLATOR_H
+#define UNITTEST_SIGNALTRANSLATOR_H
+
+#include <setjmp.h>
+#include <signal.h>
+
+namespace UnitTest {
+
+class SignalTranslator
+{
+public:
+ SignalTranslator();
+ ~SignalTranslator();
+
+ static sigjmp_buf* s_jumpTarget;
+
+private:
+ sigjmp_buf m_currentJumpTarget;
+ sigjmp_buf* m_oldJumpTarget;
+
+ struct sigaction m_old_SIGFPE_action;
+ struct sigaction m_old_SIGTRAP_action;
+ struct sigaction m_old_SIGSEGV_action;
+ struct sigaction m_old_SIGBUS_action;
+ struct sigaction m_old_SIGABRT_action;
+ struct sigaction m_old_SIGALRM_action;
+};
+
+#if !defined (__GNUC__)
+ #define UNITTEST_EXTENSION
+#else
+ #define UNITTEST_EXTENSION __extension__
+#endif
+
+#define UNITTEST_THROW_SIGNALS \
+ UnitTest::SignalTranslator sig; \
+ if (UNITTEST_EXTENSION sigsetjmp(*UnitTest::SignalTranslator::s_jumpTarget, 1) != 0) \
+ throw ("Unhandled system exception");
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/Posix/TimeHelpers.cpp b/tests/UnitTest++/src/Posix/TimeHelpers.cpp
new file mode 100644
index 0000000..bfd23c0
--- /dev/null
+++ b/tests/UnitTest++/src/Posix/TimeHelpers.cpp
@@ -0,0 +1,33 @@
+#include "TimeHelpers.h"
+#include <unistd.h>
+
+namespace UnitTest {
+
+Timer::Timer()
+{
+ m_startTime.tv_sec = 0;
+ m_startTime.tv_usec = 0;
+}
+
+void Timer::Start()
+{
+ gettimeofday(&m_startTime, 0);
+}
+
+
+int Timer::GetTimeInMs() const
+{
+ struct timeval currentTime;
+ gettimeofday(&currentTime, 0);
+ int const dsecs = currentTime.tv_sec - m_startTime.tv_sec;
+ int const dus = currentTime.tv_usec - m_startTime.tv_usec;
+ return dsecs*1000 + dus/1000;
+}
+
+
+void TimeHelpers::SleepMs (int ms)
+{
+ usleep(ms * 1000);
+}
+
+}
diff --git a/tests/UnitTest++/src/Posix/TimeHelpers.h b/tests/UnitTest++/src/Posix/TimeHelpers.h
new file mode 100644
index 0000000..fdc8428
--- /dev/null
+++ b/tests/UnitTest++/src/Posix/TimeHelpers.h
@@ -0,0 +1,28 @@
+#ifndef UNITTEST_TIMEHELPERS_H
+#define UNITTEST_TIMEHELPERS_H
+
+#include <sys/time.h>
+
+namespace UnitTest {
+
+class Timer
+{
+public:
+ Timer();
+ void Start();
+ int GetTimeInMs() const;
+
+private:
+ struct timeval m_startTime;
+};
+
+
+namespace TimeHelpers
+{
+void SleepMs (int ms);
+}
+
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/ReportAssert.cpp b/tests/UnitTest++/src/ReportAssert.cpp
new file mode 100644
index 0000000..a1810d9
--- /dev/null
+++ b/tests/UnitTest++/src/ReportAssert.cpp
@@ -0,0 +1,10 @@
+#include "AssertException.h"
+
+namespace UnitTest {
+
+void ReportAssert(char const* description, char const* filename, int lineNumber)
+{
+ throw AssertException(description, filename, lineNumber);
+}
+
+}
diff --git a/tests/UnitTest++/src/ReportAssert.h b/tests/UnitTest++/src/ReportAssert.h
new file mode 100644
index 0000000..d4dd864
--- /dev/null
+++ b/tests/UnitTest++/src/ReportAssert.h
@@ -0,0 +1,10 @@
+#ifndef UNITTEST_ASSERT_H
+#define UNITTEST_ASSERT_H
+
+namespace UnitTest {
+
+void ReportAssert(char const* description, char const* filename, int lineNumber);
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/Test.cpp b/tests/UnitTest++/src/Test.cpp
new file mode 100644
index 0000000..bc9cb3d
--- /dev/null
+++ b/tests/UnitTest++/src/Test.cpp
@@ -0,0 +1,41 @@
+#include "Config.h"
+#include "Test.h"
+#include "TestList.h"
+#include "TestResults.h"
+#include "AssertException.h"
+#include "MemoryOutStream.h"
+#include "ExecuteTest.h"
+
+#ifdef UNITTEST_POSIX
+ #include "Posix/SignalTranslator.h"
+#endif
+
+namespace UnitTest {
+
+TestList& Test::GetTestList()
+{
+ static TestList s_list;
+ return s_list;
+}
+
+Test::Test(char const* testName, char const* suiteName, char const* filename, int lineNumber)
+ : m_details(testName, suiteName, filename, lineNumber)
+ , next(0)
+ , m_timeConstraintExempt(false)
+{
+}
+
+Test::~Test()
+{
+}
+
+void Test::Run()
+{
+ ExecuteTest(*this, m_details);
+}
+
+void Test::RunImpl() const
+{
+}
+
+}
diff --git a/tests/UnitTest++/src/Test.h b/tests/UnitTest++/src/Test.h
new file mode 100644
index 0000000..91026b6
--- /dev/null
+++ b/tests/UnitTest++/src/Test.h
@@ -0,0 +1,34 @@
+#ifndef UNITTEST_TEST_H
+#define UNITTEST_TEST_H
+
+#include "TestDetails.h"
+
+namespace UnitTest {
+
+class TestResults;
+class TestList;
+
+class Test
+{
+public:
+ explicit Test(char const* testName, char const* suiteName = "DefaultSuite", char const* filename = "", int lineNumber = 0);
+ virtual ~Test();
+ void Run();
+
+ TestDetails const m_details;
+ Test* next;
+ mutable bool m_timeConstraintExempt;
+
+ static TestList& GetTestList();
+
+ virtual void RunImpl() const;
+
+private:
+ Test(Test const&);
+ Test& operator =(Test const&);
+};
+
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/TestDetails.cpp b/tests/UnitTest++/src/TestDetails.cpp
new file mode 100644
index 0000000..a13a168
--- /dev/null
+++ b/tests/UnitTest++/src/TestDetails.cpp
@@ -0,0 +1,22 @@
+#include "TestDetails.h"
+
+namespace UnitTest {
+
+TestDetails::TestDetails(char const* testName_, char const* suiteName_, char const* filename_, int lineNumber_)
+ : suiteName(suiteName_)
+ , testName(testName_)
+ , filename(filename_)
+ , lineNumber(lineNumber_)
+{
+}
+
+TestDetails::TestDetails(const TestDetails& details, int lineNumber_)
+ : suiteName(details.suiteName)
+ , testName(details.testName)
+ , filename(details.filename)
+ , lineNumber(lineNumber_)
+{
+}
+
+
+}
diff --git a/tests/UnitTest++/src/TestDetails.h b/tests/UnitTest++/src/TestDetails.h
new file mode 100644
index 0000000..eae0e71
--- /dev/null
+++ b/tests/UnitTest++/src/TestDetails.h
@@ -0,0 +1,24 @@
+#ifndef UNITTEST_TESTDETAILS_H
+#define UNITTEST_TESTDETAILS_H
+
+namespace UnitTest {
+
+class TestDetails
+{
+public:
+ TestDetails(char const* testName, char const* suiteName, char const* filename, int lineNumber);
+ TestDetails(const TestDetails& details, int lineNumber);
+
+ char const* const suiteName;
+ char const* const testName;
+ char const* const filename;
+ int const lineNumber;
+
+ TestDetails(TestDetails const&); // Why is it public? --> http://gcc.gnu.org/bugs.html#cxx_rvalbind
+private:
+ TestDetails& operator=(TestDetails const&);
+};
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/TestList.cpp b/tests/UnitTest++/src/TestList.cpp
new file mode 100644
index 0000000..e31e613
--- /dev/null
+++ b/tests/UnitTest++/src/TestList.cpp
@@ -0,0 +1,39 @@
+#include "TestList.h"
+#include "Test.h"
+
+#include <cassert>
+
+namespace UnitTest {
+
+TestList::TestList()
+ : m_head(0)
+ , m_tail(0)
+{
+}
+
+void TestList::Add(Test* test)
+{
+ if (m_tail == 0)
+ {
+ assert(m_head == 0);
+ m_head = test;
+ m_tail = test;
+ }
+ else
+ {
+ m_tail->next = test;
+ m_tail = test;
+ }
+}
+
+Test* TestList::GetHead() const
+{
+ return m_head;
+}
+
+ListAdder::ListAdder(TestList& list, Test* test)
+{
+ list.Add(test);
+}
+
+}
diff --git a/tests/UnitTest++/src/TestList.h b/tests/UnitTest++/src/TestList.h
new file mode 100644
index 0000000..b17424b
--- /dev/null
+++ b/tests/UnitTest++/src/TestList.h
@@ -0,0 +1,32 @@
+#ifndef UNITTEST_TESTLIST_H
+#define UNITTEST_TESTLIST_H
+
+
+namespace UnitTest {
+
+class Test;
+
+class TestList
+{
+public:
+ TestList();
+ void Add (Test* test);
+
+ Test* GetHead() const;
+
+private:
+ Test* m_head;
+ Test* m_tail;
+};
+
+
+class ListAdder
+{
+public:
+ ListAdder(TestList& list, Test* test);
+};
+
+}
+
+
+#endif
diff --git a/tests/UnitTest++/src/TestMacros.h b/tests/UnitTest++/src/TestMacros.h
new file mode 100644
index 0000000..a297f15
--- /dev/null
+++ b/tests/UnitTest++/src/TestMacros.h
@@ -0,0 +1,113 @@
+#ifndef UNITTEST_TESTMACROS_H
+#define UNITTEST_TESTMACROS_H
+
+#include "Config.h"
+#include "ExecuteTest.h"
+#include "AssertException.h"
+#include "TestDetails.h"
+#include "MemoryOutStream.h"
+
+#ifndef UNITTEST_POSIX
+ #define UNITTEST_THROW_SIGNALS
+#else
+ #include "Posix/SignalTranslator.h"
+#endif
+
+#ifdef TEST
+ #error UnitTest++ redefines TEST
+#endif
+
+#ifdef TEST_EX
+ #error UnitTest++ redefines TEST_EX
+#endif
+
+#ifdef TEST_FIXTURE_EX
+ #error UnitTest++ redefines TEST_FIXTURE_EX
+#endif
+
+#define SUITE(Name) \
+ namespace Suite##Name { \
+ namespace UnitTestSuite { \
+ inline char const* GetSuiteName () { \
+ return #Name ; \
+ } \
+ } \
+ } \
+ namespace Suite##Name
+
+#define TEST_EX(Name, List) \
+ class Test##Name : public UnitTest::Test \
+ { \
+ public: \
+ Test##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {} \
+ private: \
+ virtual void RunImpl() const; \
+ } test##Name##Instance; \
+ \
+ UnitTest::ListAdder adder##Name (List, &test##Name##Instance); \
+ \
+ void Test##Name::RunImpl() const
+
+
+#define TEST(Name) TEST_EX(Name, UnitTest::Test::GetTestList())
+
+
+#define TEST_FIXTURE_EX(Fixture, Name, List) \
+ class Fixture##Name##Helper : public Fixture \
+ { \
+ public: \
+ explicit Fixture##Name##Helper(UnitTest::TestDetails const& details) : m_details(details) {} \
+ void RunImpl(); \
+ UnitTest::TestDetails const& m_details; \
+ private: \
+ Fixture##Name##Helper(Fixture##Name##Helper const&); \
+ Fixture##Name##Helper& operator =(Fixture##Name##Helper const&); \
+ }; \
+ \
+ class Test##Fixture##Name : public UnitTest::Test \
+ { \
+ public: \
+ Test##Fixture##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {} \
+ private: \
+ virtual void RunImpl() const; \
+ } test##Fixture##Name##Instance; \
+ \
+ UnitTest::ListAdder adder##Fixture##Name (List, &test##Fixture##Name##Instance); \
+ \
+ void Test##Fixture##Name::RunImpl() const \
+ { \
+ bool ctorOk = false; \
+ try { \
+ Fixture##Name##Helper fixtureHelper(m_details); \
+ ctorOk = true; \
+ UnitTest::ExecuteTest(fixtureHelper, m_details); \
+ } \
+ catch (UnitTest::AssertException const& e) \
+ { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(m_details.testName, m_details.suiteName, e.Filename(), e.LineNumber()), e.what()); \
+ } \
+ catch (std::exception const& e) \
+ { \
+ UnitTest::MemoryOutStream stream; \
+ stream << "Unhandled exception: " << e.what(); \
+ UnitTest::CurrentTest::Results()->OnTestFailure(m_details, stream.GetText()); \
+ } \
+ catch (...) { \
+ if (ctorOk) \
+ { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(m_details, __LINE__), \
+ "Unhandled exception while destroying fixture " #Fixture); \
+ } \
+ else \
+ { \
+ UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(m_details, __LINE__), \
+ "Unhandled exception while constructing fixture " #Fixture); \
+ } \
+ } \
+ } \
+ void Fixture##Name##Helper::RunImpl()
+
+#define TEST_FIXTURE(Fixture,Name) TEST_FIXTURE_EX(Fixture, Name, UnitTest::Test::GetTestList())
+
+
+#endif
diff --git a/tests/UnitTest++/src/TestReporter.cpp b/tests/UnitTest++/src/TestReporter.cpp
new file mode 100644
index 0000000..608d3c6
--- /dev/null
+++ b/tests/UnitTest++/src/TestReporter.cpp
@@ -0,0 +1,10 @@
+#include "TestReporter.h"
+
+namespace UnitTest {
+
+
+TestReporter::~TestReporter()
+{
+}
+
+}
diff --git a/tests/UnitTest++/src/TestReporter.h b/tests/UnitTest++/src/TestReporter.h
new file mode 100644
index 0000000..5a2f404
--- /dev/null
+++ b/tests/UnitTest++/src/TestReporter.h
@@ -0,0 +1,20 @@
+#ifndef UNITTEST_TESTREPORTER_H
+#define UNITTEST_TESTREPORTER_H
+
+namespace UnitTest {
+
+class TestDetails;
+
+class TestReporter
+{
+public:
+ virtual ~TestReporter();
+
+ virtual void ReportTestStart(TestDetails const& test) = 0;
+ virtual void ReportFailure(TestDetails const& test, char const* failure) = 0;
+ virtual void ReportTestFinish(TestDetails const& test, float secondsElapsed) = 0;
+ virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed) = 0;
+};
+
+}
+#endif
diff --git a/tests/UnitTest++/src/TestReporterStdout.cpp b/tests/UnitTest++/src/TestReporterStdout.cpp
new file mode 100644
index 0000000..f495c0f
--- /dev/null
+++ b/tests/UnitTest++/src/TestReporterStdout.cpp
@@ -0,0 +1,41 @@
+#include "TestReporterStdout.h"
+#include <cstdio>
+
+#include "TestDetails.h"
+
+namespace UnitTest {
+
+void TestReporterStdout::ReportFailure(TestDetails const& details, char const* failure)
+{
+#if defined(__APPLE__) || defined(__GNUG__)
+ char const* const errorFormat = "%s:%d: error: Failure in %s: %s\n";
+#else
+ char const* const errorFormat = "%s(%d): error: Failure in %s: %s\n";
+#endif
+
+ using namespace std;
+ printf(errorFormat, details.filename, details.lineNumber, details.testName, failure);
+}
+
+void TestReporterStdout::ReportTestStart(TestDetails const& /*test*/)
+{
+}
+
+void TestReporterStdout::ReportTestFinish(TestDetails const& /*test*/, float)
+{
+}
+
+void TestReporterStdout::ReportSummary(int const totalTestCount, int const failedTestCount,
+ int const failureCount, float secondsElapsed)
+{
+ using namespace std;
+
+ if (failureCount > 0)
+ printf("FAILURE: %d out of %d tests failed (%d failures).\n", failedTestCount, totalTestCount, failureCount);
+ else
+ printf("Success: %d tests passed.\n", totalTestCount);
+
+ printf("Test time: %.2f seconds.\n", secondsElapsed);
+}
+
+}
diff --git a/tests/UnitTest++/src/TestReporterStdout.h b/tests/UnitTest++/src/TestReporterStdout.h
new file mode 100644
index 0000000..eacbba3
--- /dev/null
+++ b/tests/UnitTest++/src/TestReporterStdout.h
@@ -0,0 +1,19 @@
+#ifndef UNITTEST_TESTREPORTERSTDOUT_H
+#define UNITTEST_TESTREPORTERSTDOUT_H
+
+#include "TestReporter.h"
+
+namespace UnitTest {
+
+class TestReporterStdout : public TestReporter
+{
+private:
+ virtual void ReportTestStart(TestDetails const& test);
+ virtual void ReportFailure(TestDetails const& test, char const* failure);
+ virtual void ReportTestFinish(TestDetails const& test, float secondsElapsed);
+ virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed);
+};
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/TestResults.cpp b/tests/UnitTest++/src/TestResults.cpp
new file mode 100644
index 0000000..b3b67c0
--- /dev/null
+++ b/tests/UnitTest++/src/TestResults.cpp
@@ -0,0 +1,60 @@
+#include "TestResults.h"
+#include "TestReporter.h"
+
+#include "TestDetails.h"
+
+namespace UnitTest {
+
+TestResults::TestResults(TestReporter* testReporter)
+ : m_testReporter(testReporter)
+ , m_totalTestCount(0)
+ , m_failedTestCount(0)
+ , m_failureCount(0)
+ , m_currentTestFailed(false)
+{
+}
+
+void TestResults::OnTestStart(TestDetails const& test)
+{
+ ++m_totalTestCount;
+ m_currentTestFailed = false;
+ if (m_testReporter)
+ m_testReporter->ReportTestStart(test);
+}
+
+void TestResults::OnTestFailure(TestDetails const& test, char const* failure)
+{
+ ++m_failureCount;
+ if (!m_currentTestFailed)
+ {
+ ++m_failedTestCount;
+ m_currentTestFailed = true;
+ }
+
+ if (m_testReporter)
+ m_testReporter->ReportFailure(test, failure);
+}
+
+void TestResults::OnTestFinish(TestDetails const& test, float secondsElapsed)
+{
+ if (m_testReporter)
+ m_testReporter->ReportTestFinish(test, secondsElapsed);
+}
+
+int TestResults::GetTotalTestCount() const
+{
+ return m_totalTestCount;
+}
+
+int TestResults::GetFailedTestCount() const
+{
+ return m_failedTestCount;
+}
+
+int TestResults::GetFailureCount() const
+{
+ return m_failureCount;
+}
+
+
+}
diff --git a/tests/UnitTest++/src/TestResults.h b/tests/UnitTest++/src/TestResults.h
new file mode 100644
index 0000000..8ef7fda
--- /dev/null
+++ b/tests/UnitTest++/src/TestResults.h
@@ -0,0 +1,36 @@
+#ifndef UNITTEST_TESTRESULTS_H
+#define UNITTEST_TESTRESULTS_H
+
+namespace UnitTest {
+
+class TestReporter;
+class TestDetails;
+
+class TestResults
+{
+public:
+ explicit TestResults(TestReporter* reporter = 0);
+
+ void OnTestStart(TestDetails const& test);
+ void OnTestFailure(TestDetails const& test, char const* failure);
+ void OnTestFinish(TestDetails const& test, float secondsElapsed);
+
+ int GetTotalTestCount() const;
+ int GetFailedTestCount() const;
+ int GetFailureCount() const;
+
+private:
+ TestReporter* m_testReporter;
+ int m_totalTestCount;
+ int m_failedTestCount;
+ int m_failureCount;
+
+ bool m_currentTestFailed;
+
+ TestResults(TestResults const&);
+ TestResults& operator =(TestResults const&);
+};
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/TestRunner.cpp b/tests/UnitTest++/src/TestRunner.cpp
new file mode 100644
index 0000000..466265d
--- /dev/null
+++ b/tests/UnitTest++/src/TestRunner.cpp
@@ -0,0 +1,76 @@
+#include "TestRunner.h"
+#include "TestResults.h"
+#include "TestReporter.h"
+#include "TestReporterStdout.h"
+#include "TimeHelpers.h"
+#include "MemoryOutStream.h"
+
+#include <cstring>
+
+
+namespace UnitTest {
+
+int RunAllTests()
+{
+ TestReporterStdout reporter;
+ TestRunner runner(reporter);
+ return runner.RunTestsIf(Test::GetTestList(), NULL, True(), 0);
+}
+
+
+TestRunner::TestRunner(TestReporter& reporter)
+ : m_reporter(&reporter)
+ , m_result(new TestResults(&reporter))
+ , m_timer(new Timer)
+{
+ m_timer->Start();
+}
+
+TestRunner::~TestRunner()
+{
+ delete m_result;
+ delete m_timer;
+}
+
+int TestRunner::Finish() const
+{
+ float const secondsElapsed = m_timer->GetTimeInMs() / 1000.0f;
+ m_reporter->ReportSummary(m_result->GetTotalTestCount(),
+ m_result->GetFailedTestCount(),
+ m_result->GetFailureCount(),
+ secondsElapsed);
+
+ return m_result->GetFailureCount();
+}
+
+bool TestRunner::IsTestInSuite(const Test* const curTest, char const* suiteName) const
+{
+ using namespace std;
+ return (suiteName == NULL) || !strcmp(curTest->m_details.suiteName, suiteName);
+}
+
+void TestRunner::RunTest(TestResults* const result, Test* const curTest, int const maxTestTimeInMs) const
+{
+ CurrentTest::Results() = result;
+
+ Timer testTimer;
+ testTimer.Start();
+
+ result->OnTestStart(curTest->m_details);
+
+ curTest->Run();
+
+ int const testTimeInMs = testTimer.GetTimeInMs();
+ if (maxTestTimeInMs > 0 && testTimeInMs > maxTestTimeInMs && !curTest->m_timeConstraintExempt)
+ {
+ MemoryOutStream stream;
+ stream << "Global time constraint failed. Expected under " << maxTestTimeInMs <<
+ "ms but took " << testTimeInMs << "ms.";
+
+ result->OnTestFailure(curTest->m_details, stream.GetText());
+ }
+
+ result->OnTestFinish(curTest->m_details, testTimeInMs/1000.0f);
+}
+
+}
diff --git a/tests/UnitTest++/src/TestRunner.h b/tests/UnitTest++/src/TestRunner.h
new file mode 100644
index 0000000..0798af9
--- /dev/null
+++ b/tests/UnitTest++/src/TestRunner.h
@@ -0,0 +1,61 @@
+#ifndef UNITTEST_TESTRUNNER_H
+#define UNITTEST_TESTRUNNER_H
+
+#include "Test.h"
+#include "TestList.h"
+#include "CurrentTest.h"
+
+namespace UnitTest {
+
+class TestReporter;
+class TestResults;
+class Timer;
+
+int RunAllTests();
+
+struct True
+{
+ bool operator()(const Test* const) const
+ {
+ return true;
+ }
+};
+
+class TestRunner
+{
+public:
+ explicit TestRunner(TestReporter& reporter);
+ ~TestRunner();
+
+ template <class Predicate>
+ int RunTestsIf(TestList const& list, char const* suiteName,
+ const Predicate& predicate, int maxTestTimeInMs) const
+ {
+ Test* curTest = list.GetHead();
+
+ while (curTest != 0)
+ {
+ if (IsTestInSuite(curTest,suiteName) && predicate(curTest))
+ {
+ RunTest(m_result, curTest, maxTestTimeInMs);
+ }
+
+ curTest = curTest->next;
+ }
+
+ return Finish();
+ }
+
+private:
+ TestReporter* m_reporter;
+ TestResults* m_result;
+ Timer* m_timer;
+
+ int Finish() const;
+ bool IsTestInSuite(const Test* const curTest, char const* suiteName) const;
+ void RunTest(TestResults* const result, Test* const curTest, int const maxTestTimeInMs) const;
+};
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/TestSuite.h b/tests/UnitTest++/src/TestSuite.h
new file mode 100644
index 0000000..dd3717e
--- /dev/null
+++ b/tests/UnitTest++/src/TestSuite.h
@@ -0,0 +1,14 @@
+#ifndef UNITTEST_TESTSUITE_H
+#define UNITTEST_TESTSUITE_H
+
+namespace UnitTestSuite {
+
+ inline char const* GetSuiteName ()
+ {
+ return "DefaultSuite";
+ }
+
+}
+
+#endif
+
diff --git a/tests/UnitTest++/src/TimeConstraint.cpp b/tests/UnitTest++/src/TimeConstraint.cpp
new file mode 100644
index 0000000..b313be7
--- /dev/null
+++ b/tests/UnitTest++/src/TimeConstraint.cpp
@@ -0,0 +1,29 @@
+#include "TimeConstraint.h"
+#include "TestResults.h"
+#include "MemoryOutStream.h"
+#include "CurrentTest.h"
+
+namespace UnitTest {
+
+
+TimeConstraint::TimeConstraint(int ms, TestDetails const& details)
+ : m_details(details)
+ , m_maxMs(ms)
+{
+ m_timer.Start();
+}
+
+TimeConstraint::~TimeConstraint()
+{
+ int const totalTimeInMs = m_timer.GetTimeInMs();
+ if (totalTimeInMs > m_maxMs)
+ {
+ MemoryOutStream stream;
+ stream << "Time constraint failed. Expected to run test under " << m_maxMs <<
+ "ms but took " << totalTimeInMs << "ms.";
+
+ UnitTest::CurrentTest::Results()->OnTestFailure(m_details, stream.GetText());
+ }
+}
+
+}
diff --git a/tests/UnitTest++/src/TimeConstraint.h b/tests/UnitTest++/src/TimeConstraint.h
new file mode 100644
index 0000000..ac32914
--- /dev/null
+++ b/tests/UnitTest++/src/TimeConstraint.h
@@ -0,0 +1,33 @@
+#ifndef UNITTEST_TIMECONSTRAINT_H
+#define UNITTEST_TIMECONSTRAINT_H
+
+#include "TimeHelpers.h"
+
+namespace UnitTest {
+
+class TestResults;
+class TestDetails;
+
+class TimeConstraint
+{
+public:
+ TimeConstraint(int ms, TestDetails const& details);
+ ~TimeConstraint();
+
+private:
+ void operator=(TimeConstraint const&);
+ TimeConstraint(TimeConstraint const&);
+
+ Timer m_timer;
+ TestDetails const& m_details;
+ int const m_maxMs;
+};
+
+#define UNITTEST_TIME_CONSTRAINT(ms) \
+ UnitTest::TimeConstraint unitTest__timeConstraint__(ms, UnitTest::TestDetails(m_details, __LINE__))
+
+#define UNITTEST_TIME_CONSTRAINT_EXEMPT() do { m_timeConstraintExempt = true; } while (0)
+
+}
+
+#endif
diff --git a/tests/UnitTest++/src/TimeHelpers.h b/tests/UnitTest++/src/TimeHelpers.h
new file mode 100644
index 0000000..f34ed00
--- /dev/null
+++ b/tests/UnitTest++/src/TimeHelpers.h
@@ -0,0 +1,7 @@
+#include "Config.h"
+
+#if defined UNITTEST_POSIX
+ #include "Posix/TimeHelpers.h"
+#else
+ #include "Win32/TimeHelpers.h"
+#endif
diff --git a/tests/UnitTest++/src/UnitTest++.h b/tests/UnitTest++/src/UnitTest++.h
new file mode 100644
index 0000000..3318735
--- /dev/null
+++ b/tests/UnitTest++/src/UnitTest++.h
@@ -0,0 +1,18 @@
+#ifndef UNITTESTCPP_H
+#define UNITTESTCPP_H
+
+//lint -esym(1509,*Fixture)
+
+#include "Config.h"
+#include "Test.h"
+#include "TestList.h"
+#include "TestSuite.h"
+#include "TestResults.h"
+
+#include "TestMacros.h"
+
+#include "CheckMacros.h"
+#include "TestRunner.h"
+#include "TimeConstraint.h"
+
+#endif
diff --git a/tests/UnitTest++/src/XmlTestReporter.cpp b/tests/UnitTest++/src/XmlTestReporter.cpp
new file mode 100644
index 0000000..fe8966f
--- /dev/null
+++ b/tests/UnitTest++/src/XmlTestReporter.cpp
@@ -0,0 +1,127 @@
+#include "XmlTestReporter.h"
+#include "Config.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+using std::string;
+using std::ostringstream;
+using std::ostream;
+
+namespace {
+
+void ReplaceChar(string& str, char c, string const& replacement)
+{
+ for (size_t pos = str.find(c); pos != string::npos; pos = str.find(c, pos + 1))
+ str.replace(pos, 1, replacement);
+}
+
+string XmlEscape(string const& value)
+{
+ string escaped = value;
+
+ ReplaceChar(escaped, '&', "&amp;");
+ ReplaceChar(escaped, '<', "&lt;");
+ ReplaceChar(escaped, '>', "&gt;");
+ ReplaceChar(escaped, '\'', "&apos;");
+ ReplaceChar(escaped, '\"', "&quot;");
+
+ return escaped;
+}
+
+string BuildFailureMessage(string const& file, int line, string const& message)
+{
+ ostringstream failureMessage;
+ failureMessage << file << "(" << line << ") : " << message;
+ return failureMessage.str();
+}
+
+}
+
+namespace UnitTest {
+
+XmlTestReporter::XmlTestReporter(ostream& ostream)
+ : m_ostream(ostream)
+{
+}
+
+void XmlTestReporter::ReportSummary(int totalTestCount, int failedTestCount,
+ int failureCount, float secondsElapsed)
+{
+ AddXmlElement(m_ostream, NULL);
+
+ BeginResults(m_ostream, totalTestCount, failedTestCount, failureCount, secondsElapsed);
+
+ DeferredTestResultList const& results = GetResults();
+ for (DeferredTestResultList::const_iterator i = results.begin(); i != results.end(); ++i)
+ {
+ BeginTest(m_ostream, *i);
+
+ if (i->failed)
+ AddFailure(m_ostream, *i);
+
+ EndTest(m_ostream, *i);
+ }
+
+ EndResults(m_ostream);
+}
+
+void XmlTestReporter::AddXmlElement(ostream& os, char const* encoding)
+{
+ os << "<?xml version=\"1.0\"";
+
+ if (encoding != NULL)
+ os << " encoding=\"" << encoding << "\"";
+
+ os << "?>";
+}
+
+void XmlTestReporter::BeginResults(std::ostream& os, int totalTestCount, int failedTestCount,
+ int failureCount, float secondsElapsed)
+{
+ os << "<unittest-results"
+ << " tests=\"" << totalTestCount << "\""
+ << " failedtests=\"" << failedTestCount << "\""
+ << " failures=\"" << failureCount << "\""
+ << " time=\"" << secondsElapsed << "\""
+ << ">";
+}
+
+void XmlTestReporter::EndResults(std::ostream& os)
+{
+ os << "</unittest-results>";
+}
+
+void XmlTestReporter::BeginTest(std::ostream& os, DeferredTestResult const& result)
+{
+ os << "<test"
+ << " suite=\"" << result.suiteName << "\""
+ << " name=\"" << result.testName << "\""
+ << " time=\"" << result.timeElapsed << "\"";
+}
+
+void XmlTestReporter::EndTest(std::ostream& os, DeferredTestResult const& result)
+{
+ if (result.failed)
+ os << "</test>";
+ else
+ os << "/>";
+}
+
+void XmlTestReporter::AddFailure(std::ostream& os, DeferredTestResult const& result)
+{
+ os << ">"; // close <test> element
+
+ for (DeferredTestResult::FailureVec::const_iterator it = result.failures.begin();
+ it != result.failures.end();
+ ++it)
+ {
+ string const escapedMessage = XmlEscape(it->second);
+ string const message = BuildFailureMessage(result.failureFile, it->first, escapedMessage);
+
+ os << "<failure" << " message=\"" << message << "\"" << "/>";
+ }
+}
+
+}
diff --git a/tests/UnitTest++/src/XmlTestReporter.h b/tests/UnitTest++/src/XmlTestReporter.h
new file mode 100644
index 0000000..884123b
--- /dev/null
+++ b/tests/UnitTest++/src/XmlTestReporter.h
@@ -0,0 +1,34 @@
+#ifndef UNITTEST_XMLTESTREPORTER_H
+#define UNITTEST_XMLTESTREPORTER_H
+
+#include "DeferredTestReporter.h"
+
+#include <iosfwd>
+
+namespace UnitTest
+{
+
+class XmlTestReporter : public DeferredTestReporter
+{
+public:
+ explicit XmlTestReporter(std::ostream& ostream);
+
+ virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed);
+
+private:
+ XmlTestReporter(XmlTestReporter const&);
+ XmlTestReporter& operator=(XmlTestReporter const&);
+
+ void AddXmlElement(std::ostream& os, char const* encoding);
+ void BeginResults(std::ostream& os, int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed);
+ void EndResults(std::ostream& os);
+ void BeginTest(std::ostream& os, DeferredTestResult const& result);
+ void AddFailure(std::ostream& os, DeferredTestResult const& result);
+ void EndTest(std::ostream& os, DeferredTestResult const& result);
+
+ std::ostream& m_ostream;
+};
+
+}
+
+#endif