1. Matt Oswald
  2. xUnit++

Commits

Matt Oswald  committed acc811e

moved the includes into a xUnit++ dir

  • Participants
  • Parent commits 5fed1bb
  • Branches default

Comments (0)

Files changed (71)

File Tests/BareTests/BareTest.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 //
 // FilteredTestsRunner has a tendency to not get linked into the test dlls.

File Tests/UnitTests/Assert.Contains.cpp

View file
 #include <string>
 #include <vector>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.DoesNotContain.cpp

View file
 #include <string>
 #include <vector>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.DoesNotThrow.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.Empty.cpp

View file
 #include <vector>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.Equal.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.Fail.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.False.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.InRange.cpp

View file
 #include <tuple>
 #include <vector>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.NotEqual.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.NotInRange.cpp

View file
 #include <tuple>
 #include <vector>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.NotNull.cpp

View file
 #include <memory>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.NotSame.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.Null.cpp

View file
 #include <memory>
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.Same.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.Throws.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Assert.True.cpp

View file
-#include "xUnit++.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::xUnitAssert;
 using xUnitpp::Assert;

File Tests/UnitTests/Attributes.cpp

View file
-#include "IOutput.h"
-#include "TestCollection.h"
-#include "xUnitTestRunner.h"
-#include "xUnit++.h"
+#include "xUnit++/IOutput.h"
+#include "xUnit++/TestCollection.h"
+#include "xUnit++/xUnitTestRunner.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::Assert;
 

File Tests/UnitTests/LineInfo.cpp

View file
-#include "xUnit++.h"
-#include "IOutput.h"
+#include "xUnit++/IOutput.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::Assert;
 

File Tests/UnitTests/Theory.cpp

View file
 #include <string>
 #include <tuple>
 #include <vector>
-#include "IOutput.h"
-#include "xUnitTestRunner.h"
-#include "xUnitTime.h"
-#include "xUnit++.h"
+#include "xUnit++/IOutput.h"
+#include "xUnit++/xUnitTestRunner.h"
+#include "xUnit++/xUnitTime.h"
+#include "xUnit++/xUnit++.h"
 
 using xUnitpp::Assert;
 

File notes.txt

View file
 finish unit testing the lib
 	- ensure tests are actually ran
 	- ensure output formats
-create lib/include structure
 check Release builds for warnings (and failing tests)
 can runner be made configuration agnostic? (ie, can debug runner load release tests, and vice versa?)
 inline theories

File xUnit++.VsRunner/xUnit++.VsRunner.cpp

View file
 #include <memory>
 #include <vector>
 #include <msclr/marshal_cppstd.h>
-#include "ExportApi.h"
-#include "IOutput.h"
-#include "LineInfo.h"
-#include "TestDetails.h"
+#include "xUnit++/ExportApi.h"
+#include "xUnit++/IOutput.h"
+#include "xUnit++/LineInfo.h"
+#include "xUnit++/TestDetails.h"
 
 using namespace System;
 using namespace System::Collections::Generic;

File xUnit++.console/StdOutReporter.cpp

View file
 #include <chrono>
 #include <cstdio>
 #include <iostream>
-#include "LineInfo.h"
-#include "TestDetails.h"
+#include "xUnit++/LineInfo.h"
+#include "xUnit++/TestDetails.h"
 
 namespace
 {

File xUnit++.console/StdOutReporter.h

View file
 #ifndef STDOUTREPORTER_H_
 #define STDOUTREPORTER_H_
 
-#include "IOutput.h"
+#include "xUnit++/IOutput.h"
 
 namespace xUnitpp
 {

File xUnit++.console/XmlReporter.cpp

View file
 #include <chrono>
 #include <fstream>
 #include <iostream>
-#include "TestDetails.h"
-#include "xUnitTime.h"
+#include "xUnit++/TestDetails.h"
+#include "xUnit++/xUnitTime.h"
 
 namespace
 {

File xUnit++.console/XmlReporter.h

View file
 #define XMLREPORTER_H_
 
 #include <map>
-#include "IOutput.h"
+#include "xUnit++/IOutput.h"
 
 namespace xUnitpp
 {

File xUnit++.console/main.cpp

View file
 #include <tuple>
 #include <vector>
 #include <Windows.h>
+#include "xUnit++/ExportApi.h"
+#include "xUnit++/TestDetails.h"
 #include "CommandLine.h"
-#include "ExportApi.h"
 #include "StdOutReporter.h"
-#include "TestDetails.h"
 #include "XmlReporter.h"
 
 int main(int argc, char **argv)

File xUnit++/Attributes.h

-#ifndef ATTRIBUTES_H_
-#define ATTRIBUTES_H_
-
-#include <map>
-#include <string>
-#include "TestDetails.h"
-
-namespace xUnitAttributes
-{
-    inline xUnitpp::AttributeCollection Attributes()
-    {
-        return xUnitpp::AttributeCollection();
-    }
-
-}
-
-#endif

File xUnit++/ExportApi.h

-#ifndef EXPORTSAPI_H_
-#define EXPORTSAPI_H_
-
-#include <functional>
-#include <memory>
-#include <vector>
-
-namespace xUnitpp
-{
-    struct IOutput;
-    struct TestDetails;
-
-    typedef std::function<void(const TestDetails &)> EnumerateTestDetailsCallback;
-    typedef void(*EnumerateTestDetails)(EnumerateTestDetailsCallback callback);
-
-    typedef std::function<bool(const TestDetails &)> TestFilterCallback;
-    typedef int(*FilteredTestsRunner)(int, int, IOutput &, TestFilterCallback);
-}
-
-#endif

File xUnit++/IOutput.cpp

-#include "IOutput.h"
-
-namespace xUnitpp
-{
-
-IOutput::~IOutput()
-{
-}
-
-}

File xUnit++/IOutput.h

-#ifndef IOUTPUT_H_
-#define IOUTPUT_H_
-
-#include <string>
-#include "xUnitTime.h"
-
-namespace xUnitpp
-{
-
-struct LineInfo;
-struct TestDetails;
-
-struct IOutput
-{
-    virtual ~IOutput();
-
-    virtual void ReportStart(const TestDetails &testDetails) = 0;
-    virtual void ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo) = 0;
-    virtual void ReportSkip(const TestDetails &testDetails, const std::string &reason) = 0;
-    virtual void ReportFinish(const TestDetails &testDetails, Time::Duration timeTaken) = 0;
-    virtual void ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failed, Time::Duration totalTime) = 0; 
-};
-
-}
-
-#endif

File xUnit++/LineInfo.cpp

-#include "LineInfo.h"
-
-namespace xUnitpp
-{
-
-LineInfo::LineInfo()
-{
-}
-
-LineInfo::LineInfo(const std::string &file, int line)
-    : file(file)
-    , line(line)
-{
-}
-
-const LineInfo &LineInfo::empty()
-{
-    static LineInfo empty;
-    return empty;
-}
-
-}

File xUnit++/LineInfo.h

-#ifndef LINEINFO_H_
-#define LINEINFO_H_
-
-#include <string>
-
-namespace xUnitpp
-{
-
-struct LineInfo
-{
-    LineInfo(const std::string &file, int line);
-
-    static const LineInfo &empty();
-
-    std::string file;
-    int line;
-
-private:
-    LineInfo();
-};
-
-}
-
-#endif

File xUnit++/Suite.h

-#ifndef SUITE_H_
-#define SUITE_H_
-
-#include <string>
-
-namespace xUnitSuite
-{
-    inline const std::string &Name()
-    {
-        static std::string name = "xUnit++ Default Suite";
-        return name;
-    }
-
-}
-
-#endif

File xUnit++/TestCollection.cpp

-#include <chrono>
-#include <string>
-#include <tuple>
-#include <vector>
-#include "ExportApi.h"
-#include "IOutput.h"
-#include "TestCollection.h"
-#include "xUnitTestRunner.h"
-#include "xUnitTime.h"
-
-namespace
-{
-    extern "C" __declspec(dllexport) void EnumerateTestDetails(xUnitpp::EnumerateTestDetailsCallback callback)
-    {
-        for (const auto &test : xUnitpp::TestCollection::Instance().Tests())
-        {
-            callback(test.TestDetails());
-        }
-    }
-
-    extern "C" __declspec(dllexport) int FilteredTestsRunner(int timeLimit, int threadLimit, xUnitpp::IOutput &testReporter, std::function<bool(const xUnitpp::TestDetails &)> filter)
-    {
-        return xUnitpp::TestRunner(testReporter).RunTests(filter,
-            xUnitpp::TestCollection::Instance().Tests(), xUnitpp::Time::ToDuration(std::chrono::milliseconds(timeLimit)), threadLimit);
-    }
-}
-
-namespace xUnitpp
-{
-
-TestCollection &TestCollection::Instance()
-{
-    static TestCollection collection;
-    return collection;
-}
-
-TestCollection::Register::Register(TestCollection &collection, const std::function<void()> &fn, const std::string &name, const std::string &suite,
-                                   const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line)
-{
-    collection.mTests.emplace_back(xUnitTest(fn, name, suite, attributes, Time::ToDuration(std::chrono::milliseconds(milliseconds)), filename, line));
-}
-
-const std::vector<xUnitTest> &TestCollection::Tests()
-{
-    return mTests;
-}
-
-}

File xUnit++/TestCollection.h

-#ifndef TESTCOLLECTION_H_
-#define TESTCOLLECTION_H_
-
-#include <chrono>
-#include <functional>
-#include <map>
-#include <vector>
-#include "xUnitTest.h"
-
-namespace xUnitpp
-{
-
-class TestCollection
-{
-    friend class Register;
-
-public:
-    class Register
-    {
-        // !!!VS someday, Visual Studio will understand variadic macros
-        // when it does, fix this collection
-
-        // !!! should use a macro system to automate this
-        template<typename TFn, typename TArg0>
-        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0> &&t)
-        {
-            return [=]() { return theory(std::get<0>(t)); };
-        }
-
-        template<typename TFn, typename TArg0, typename TArg1>
-        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1> &&t)
-        {
-            return [=]() { return theory(std::get<0>(t),
-                                         std::get<1>(t)); };
-        }
-
-        template<typename TFn, typename TArg0, typename TArg1, typename TArg2>
-        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1, TArg2> &&t)
-        {
-            return [=]() { return theory(std::get<0>(t),
-                                         std::get<1>(t),
-                                         std::get<2>(t)); };
-        }
-
-        template<typename TFn, typename TArg0, typename TArg1, typename TArg2, typename TArg3>
-        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1, TArg2, TArg3> &&t)
-        {
-            return [=]() { return theory(std::get<0>(t),
-                                         std::get<1>(t),
-                                         std::get<2>(t),
-                                         std::get<3>(t)); };
-        }
-
-        template<typename TFn, typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4>
-        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1, TArg2, TArg3, TArg4> &&t)
-        {
-            return [=]() { return theory(std::get<0>(t),
-                                         std::get<1>(t),
-                                         std::get<2>(t),
-                                         std::get<3>(t),
-                                         std::get<4>(t)); };
-        }
-
-    public:
-        Register(TestCollection &collection, const std::function<void()> &fn, const std::string &name, const std::string &suite,
-            const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line);
-
-        template<typename TTheory, typename TTheoryData>
-        Register(TestCollection &collection, TTheory theory, TTheoryData theoryData, const std::string &name, const std::string &suite,
-            const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line)
-        {
-            int id = 1;
-            for (auto t : theoryData())
-            {
-                // !!! someday, I'd like to embed the actual parameter values, rather than the theory data index
-                // not sure how feasible that is in C++, since it's lacking the type reflection of C# :(
-                auto theoryName = name + "(" + std::to_string(id++) + ")";
-
-                collection.mTests.emplace_back(xUnitTest(TheoryHelper(theory, std::move(t)), theoryName, suite,
-                        attributes, Time::ToDuration(std::chrono::milliseconds(milliseconds)), filename, line));
-            }
-        }
-    };
-
-    static TestCollection &Instance();
-
-    const std::vector<xUnitTest> &Tests();
-
-private:
-    std::vector<xUnitTest> mTests;
-};
-
-}
-
-#endif

File xUnit++/TestDetails.cpp

-#include "TestDetails.h"
-#include <utility>
-#include "xUnitTime.h"
-
-namespace
-{
-    inline int NextId()
-    {
-        static int id = 0;
-        return id++;
-    }
-
-    std::string shorten(const std::string &name)
-    {
-        auto idx = name.rfind('(');
-        if (idx == std::string::npos)
-        {
-            return name;
-        }
-
-        return name.substr(0, idx);
-    }
-}
-
-namespace xUnitpp
-{
-
-TestDetails::TestDetails()
-{
-}
-
-TestDetails::TestDetails(const std::string &name, const std::string &suite, const AttributeCollection &attributes,
-                         Time::Duration timeLimit, const std::string &filename, int line)
-    : Id(NextId())
-    , Name(name)
-    , ShortName(shorten(name))
-    , Suite(suite)
-    , Attributes(attributes)
-    , TimeLimit(timeLimit)
-    , Filename(filename)
-    , Line(line)
-{
-}
-
-TestDetails::TestDetails(const TestDetails &other)
-    : Id(other.Id)
-    , Name(other.Name)
-    , ShortName(other.ShortName)
-    , Suite(other.Suite)
-    , Attributes(other.Attributes)
-    , TimeLimit(other.TimeLimit)
-    , Filename(other.Filename)
-    , Line(other.Line)
-{
-}
-
-TestDetails::TestDetails(TestDetails &&other)
-{
-    swap(*this, other);
-}
-
-TestDetails &TestDetails::operator =(TestDetails other)
-{
-    swap(*this, other);
-    return *this;
-}
-
-void swap(TestDetails &td0, TestDetails &td1)
-{
-    using std::swap;
-
-    swap(td0.Id, td1.Id);
-    swap(td0.Name, td1.Name);
-    swap(td0.ShortName, td1.ShortName);
-    swap(td0.Suite, td1.Suite);
-    swap(td0.Attributes, td1.Attributes);
-    swap(td0.TimeLimit, td1.TimeLimit);
-    swap(td0.Filename, td1.Filename);
-    swap(td0.Line, td1.Line);
-}
-
-}

File xUnit++/TestDetails.h

-#ifndef TESTDETAILS_H_
-#define TESTDETAILS_H_
-
-#include <chrono>
-#include <map>
-#include <string>
-#include <tuple>
-#include "xUnitTime.h"
-
-namespace xUnitpp
-{
-
-typedef std::multimap<std::string, std::string> AttributeCollection;
-
-struct TestDetails
-{
-    TestDetails();
-    TestDetails(const std::string &name, const std::string &suite,
-        const AttributeCollection &attributes, Time::Duration timeLimit,
-        const std::string &filename, int line);
-    TestDetails(const TestDetails &other);
-    TestDetails(TestDetails &&other);
-    TestDetails &operator =(TestDetails other);
-    friend void swap(TestDetails &td0, TestDetails &td1);
-
-    int Id;
-    std::string Name;
-    std::string ShortName;
-    std::string Suite;
-    AttributeCollection Attributes;
-    Time::Duration TimeLimit;
-    std::string Filename;
-    int Line;
-};
-
-}
-
-#endif

File xUnit++/src/IOutput.cpp

View file
+#include "IOutput.h"
+
+namespace xUnitpp
+{
+
+IOutput::~IOutput()
+{
+}
+
+}

File xUnit++/src/LineInfo.cpp

View file
+#include "LineInfo.h"
+
+namespace xUnitpp
+{
+
+LineInfo::LineInfo()
+{
+}
+
+LineInfo::LineInfo(const std::string &file, int line)
+    : file(file)
+    , line(line)
+{
+}
+
+const LineInfo &LineInfo::empty()
+{
+    static LineInfo empty;
+    return empty;
+}
+
+}

File xUnit++/src/TestCollection.cpp

View file
+#include <chrono>
+#include <string>
+#include <tuple>
+#include <vector>
+#include "ExportApi.h"
+#include "IOutput.h"
+#include "TestCollection.h"
+#include "xUnitTestRunner.h"
+#include "xUnitTime.h"
+
+namespace
+{
+    extern "C" __declspec(dllexport) void EnumerateTestDetails(xUnitpp::EnumerateTestDetailsCallback callback)
+    {
+        for (const auto &test : xUnitpp::TestCollection::Instance().Tests())
+        {
+            callback(test.TestDetails());
+        }
+    }
+
+    extern "C" __declspec(dllexport) int FilteredTestsRunner(int timeLimit, int threadLimit, xUnitpp::IOutput &testReporter, std::function<bool(const xUnitpp::TestDetails &)> filter)
+    {
+        return xUnitpp::TestRunner(testReporter).RunTests(filter,
+            xUnitpp::TestCollection::Instance().Tests(), xUnitpp::Time::ToDuration(std::chrono::milliseconds(timeLimit)), threadLimit);
+    }
+}
+
+namespace xUnitpp
+{
+
+TestCollection &TestCollection::Instance()
+{
+    static TestCollection collection;
+    return collection;
+}
+
+TestCollection::Register::Register(TestCollection &collection, const std::function<void()> &fn, const std::string &name, const std::string &suite,
+                                   const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line)
+{
+    collection.mTests.emplace_back(xUnitTest(fn, name, suite, attributes, Time::ToDuration(std::chrono::milliseconds(milliseconds)), filename, line));
+}
+
+const std::vector<xUnitTest> &TestCollection::Tests()
+{
+    return mTests;
+}
+
+}

File xUnit++/src/TestDetails.cpp

View file
+#include "TestDetails.h"
+#include <utility>
+#include "xUnitTime.h"
+
+namespace
+{
+    inline int NextId()
+    {
+        static int id = 0;
+        return id++;
+    }
+
+    std::string shorten(const std::string &name)
+    {
+        auto idx = name.rfind('(');
+        if (idx == std::string::npos)
+        {
+            return name;
+        }
+
+        return name.substr(0, idx);
+    }
+}
+
+namespace xUnitpp
+{
+
+TestDetails::TestDetails()
+{
+}
+
+TestDetails::TestDetails(const std::string &name, const std::string &suite, const AttributeCollection &attributes,
+                         Time::Duration timeLimit, const std::string &filename, int line)
+    : Id(NextId())
+    , Name(name)
+    , ShortName(shorten(name))
+    , Suite(suite)
+    , Attributes(attributes)
+    , TimeLimit(timeLimit)
+    , Filename(filename)
+    , Line(line)
+{
+}
+
+TestDetails::TestDetails(const TestDetails &other)
+    : Id(other.Id)
+    , Name(other.Name)
+    , ShortName(other.ShortName)
+    , Suite(other.Suite)
+    , Attributes(other.Attributes)
+    , TimeLimit(other.TimeLimit)
+    , Filename(other.Filename)
+    , Line(other.Line)
+{
+}
+
+TestDetails::TestDetails(TestDetails &&other)
+{
+    swap(*this, other);
+}
+
+TestDetails &TestDetails::operator =(TestDetails other)
+{
+    swap(*this, other);
+    return *this;
+}
+
+void swap(TestDetails &td0, TestDetails &td1)
+{
+    using std::swap;
+
+    swap(td0.Id, td1.Id);
+    swap(td0.Name, td1.Name);
+    swap(td0.ShortName, td1.ShortName);
+    swap(td0.Suite, td1.Suite);
+    swap(td0.Attributes, td1.Attributes);
+    swap(td0.TimeLimit, td1.TimeLimit);
+    swap(td0.Filename, td1.Filename);
+    swap(td0.Line, td1.Line);
+}
+
+}

File xUnit++/src/xUnitAssert.cpp

View file
+#include "xUnitAssert.h"
+
+namespace
+{
+    std::string AssembleWhat(const std::string &call, const std::string &userMsg, const std::string &customMsg,
+                             const std::string &expected, const std::string &actual)
+    {
+        std::string msg = "Assert." + call + "() failure";
+        if (!userMsg.empty())
+        {
+            msg += ": " + userMsg;
+
+            if (!customMsg.empty())
+            {
+                msg += "\n     " + customMsg;
+            }
+        }
+        else
+        {
+            if (!customMsg.empty())
+            {
+                msg += ": " + customMsg;
+            }
+        }
+
+        if (!expected.empty())
+        {
+            msg += "\n     Expected: " + expected;
+            msg += "\n       Actual: " + actual;
+        }
+
+        msg += "\n";
+
+        return msg;
+    }
+}
+
+namespace xUnitpp
+{
+
+xUnitAssert::xUnitAssert(const std::string &call, const std::string &userMsg, const std::string &customMsg,
+                         const std::string &expected, const std::string &actual, const xUnitpp::LineInfo &lineInfo)
+    : base(AssembleWhat(call, userMsg, customMsg, expected, actual).c_str())
+    , lineInfo(lineInfo)
+{
+}
+
+xUnitAssert::xUnitAssert(const xUnitAssert &other)
+    : base(other.what())
+    , lineInfo(other.lineInfo)
+{
+}
+
+const LineInfo &xUnitAssert::LineInfo() const
+{
+    return lineInfo;
+}
+
+double Assert::round(double value, size_t precision)
+{
+    if (value < 0)
+    {
+        return std::ceil((value - 0.5) * std::pow(10, precision)) / std::pow(10, precision);
+    }
+    else
+    {
+        return std::floor((value + 0.5) * std::pow(10, precision)) / std::pow(10, precision);
+    }
+}
+
+void Assert::Equal(const std::string &expected, const std::string &actual, const std::string &msg, const LineInfo &lineInfo) const
+{
+    if (expected != actual)
+    {
+        throw xUnitAssert("Equal", msg, "", expected, actual, lineInfo);
+    }
+}
+
+void Assert::Equal(const std::string &expected, const std::string &actual, const LineInfo &lineInfo) const
+{
+    Equal(expected, actual, std::string(""), lineInfo);
+}
+
+void Assert::Equal(const char *expected, const char *actual, const std::string &msg, const LineInfo &lineInfo) const
+{
+    Equal(std::string(expected), std::string(actual), msg, lineInfo);
+}
+
+void Assert::Equal(const char *expected, const char *actual, const LineInfo &lineInfo) const
+{
+    Equal(std::string(expected), std::string(actual), std::string(""), lineInfo);
+}
+
+void Assert::Equal(const std::string &expected, const char *actual, const std::string &msg, const LineInfo &lineInfo) const
+{
+    Equal(expected, std::string(actual), msg, lineInfo);
+}
+
+void Assert::Equal(const std::string &expected, const char *actual, const LineInfo &lineInfo) const
+{
+    Equal(expected, std::string(actual), std::string(""), lineInfo);
+}
+
+void Assert::Equal(float expected, float actual, int precision, const std::string &msg, const LineInfo &lineInfo) const
+{
+    Equal((double)expected, (double)actual, precision, msg, lineInfo);
+}
+
+void Assert::Equal(float expected, float actual, int precision, const LineInfo &lineInfo) const
+{
+    Equal(expected, actual, precision, "", lineInfo);
+}
+
+void Assert::Equal(double expected, double actual, int precision, const std::string &msg, const LineInfo &lineInfo) const
+{
+    auto er = round(expected, precision);
+    auto ar = round(actual, precision);
+
+    Equal(er, ar, [](double er, double ar) { return er == ar; }, msg, lineInfo);
+}
+
+void Assert::Equal(double expected, double actual, int precision, const LineInfo &lineInfo) const
+{
+    Equal(expected, actual, precision, "", lineInfo);
+}
+
+void Assert::Fail(const std::string &msg, const LineInfo &lineInfo) const
+{
+    throw xUnitAssert("Fail", msg, "", "", "", lineInfo);
+}
+
+void Assert::Fail(const LineInfo &lineInfo) const
+{
+    Fail("", lineInfo);
+}
+
+void Assert::False(bool b, const std::string &msg, const LineInfo &lineInfo) const
+{
+    if (b)
+    {
+        throw xUnitAssert("False", msg, "", "false", "true", lineInfo);
+    }
+}
+
+void Assert::False(bool b, const LineInfo &lineInfo) const
+{
+    False(b, "", lineInfo);
+}
+
+void Assert::True(bool b, const std::string &msg, const LineInfo &lineInfo) const
+{
+    if (!b)
+    {
+        throw xUnitAssert("True", msg, "", "true", "false", lineInfo);
+    }
+}
+
+void Assert::True(bool b, const LineInfo &lineInfo) const
+{
+    True(b, "", lineInfo);
+}
+
+void Assert::DoesNotContain(const char *actualString, const char *value, const std::string &msg, const LineInfo &lineInfo) const
+{
+    DoesNotContain(std::string(actualString), std::string(value), msg, lineInfo);
+}
+
+void Assert::DoesNotContain(const char *actualString, const char *value, const LineInfo &lineInfo) const
+{
+    DoesNotContain(actualString, value, "", lineInfo);
+}
+
+void Assert::DoesNotContain(const std::string &actualString, const char *value, const std::string &msg, const LineInfo &lineInfo) const
+{
+    DoesNotContain(actualString, std::string(value), msg, lineInfo);
+}
+
+void Assert::DoesNotContain(const std::string &actualString, const char *value, const LineInfo &lineInfo) const
+{
+    DoesNotContain(actualString, value, "", lineInfo);
+}
+
+void Assert::DoesNotContain(const std::string &actualString, const std::string &value, const std::string &msg, const LineInfo &lineInfo) const
+{
+    auto found = actualString.find(value);
+    if (found != std::string::npos)
+    {
+        throw xUnitAssert("DoesNotContain", msg, "Found: \"" + value + "\" at position " + std::to_string(found) + ".", "", "", lineInfo);
+    }
+}
+
+void Assert::DoesNotContain(const std::string &actualString, const std::string &value, const LineInfo &lineInfo) const
+{
+    DoesNotContain(actualString, value, "", lineInfo);
+}
+
+void Assert::Contains(const char *actualString, const char *value, const std::string &msg, const LineInfo &lineInfo) const
+{
+    Contains(std::string(actualString), std::string(value), msg, lineInfo);
+}
+
+void Assert::Contains(const char *actualString, const char *value, const LineInfo &lineInfo) const
+{
+    Contains(actualString, value, "", lineInfo);
+}
+
+void Assert::Contains(const std::string &actualString, const char *value, const std::string &msg, const LineInfo &lineInfo) const
+{
+    Contains(actualString, std::string(value), msg, lineInfo);
+}
+
+void Assert::Contains(const std::string &actualString, const char *value, const LineInfo &lineInfo) const
+{
+    Contains(actualString, value, "", lineInfo);
+}
+
+void Assert::Contains(const std::string &actualString, const std::string &value, const std::string &msg, const LineInfo &lineInfo) const
+{
+    if (actualString.find(value) == std::string::npos)
+    {
+        throw xUnitAssert("Contains", msg, "", actualString, value, lineInfo);
+    }
+}
+
+void Assert::Contains(const std::string &actualString, const std::string &value, const LineInfo &lineInfo) const
+{
+    Contains(actualString, value, "", lineInfo);
+}
+
+}

File xUnit++/src/xUnitTest.cpp

View file
+#include "xUnitTest.h"
+
+namespace xUnitpp
+{
+
+xUnitTest::xUnitTest(std::function<void()> test, const std::string &name, const std::string &suite,
+                     const AttributeCollection &attributes, Time::Duration timeLimit,
+                     const std::string &filename, int line)
+    : mTest(test)
+    , mTestDetails(name, suite, attributes, timeLimit, filename, line)
+{
+}
+
+xUnitTest::xUnitTest(const xUnitTest &other)
+    : mTest(other.mTest)
+    , mTestDetails(other.mTestDetails)
+{
+}
+
+xUnitTest::xUnitTest(xUnitTest &&other)
+{
+    swap(*this, other);
+}
+
+xUnitTest &xUnitTest::operator =(xUnitTest other)
+{
+    swap(*this, other);
+    return *this;
+}
+
+void swap(xUnitTest &a, xUnitTest &b)
+{
+    using std::swap;
+
+    swap(a.mTest, b.mTest);
+    swap(a.mTestDetails, b.mTestDetails);
+}
+
+const TestDetails &xUnitTest::TestDetails() const
+{
+    return mTestDetails;
+}
+
+void xUnitTest::Run()
+{
+    return mTest();
+}
+
+}

File xUnit++/src/xUnitTestRunner.cpp

View file
+#include "xUnitTestRunner.h"
+#include <atomic>
+#include <chrono>
+#include <future>
+#include <limits>
+#include <mutex>
+#include <random>
+#include <stdexcept>
+#include <vector>
+#include "IOutput.h"
+#include "TestCollection.h"
+#include "TestDetails.h"
+#include "xUnitAssert.h"
+#include "xUnitTime.h"
+
+namespace
+{
+
+class ActiveTests
+{
+public:
+
+    ActiveTests(std::function<bool(const xUnitpp::TestDetails &)> filter, const std::vector<xUnitpp::xUnitTest> &tests)
+    {
+        for (auto &test : tests)
+        {
+            if (filter(test.TestDetails()))
+            {
+                mTests.push_back(test);
+            }
+        }
+
+        std::shuffle(mTests.begin(), mTests.end(), std::default_random_engine(std::random_device()()));
+    }
+
+    std::vector<xUnitpp::xUnitTest>::iterator begin()
+    {
+        return mTests.begin();
+    }
+
+    std::vector<xUnitpp::xUnitTest>::iterator end()
+    {
+        return mTests.end();
+    }
+
+private:
+    std::vector<xUnitpp::xUnitTest> mTests;
+};
+
+}
+
+namespace xUnitpp
+{
+
+class TestRunner::Impl
+{
+public:
+    Impl(IOutput &testReporter)
+        : mTestReporter(testReporter)
+    {
+    }
+
+    void OnTestStart(const TestDetails &details)
+    {
+        std::lock_guard<std::mutex> guard(mStartMtx);
+        mTestReporter.ReportStart(details);
+    }
+
+    void OnTestFailure(const TestDetails &details, const std::string &message, const LineInfo &lineInfo)
+    {
+        std::lock_guard<std::mutex> guard(mFailureMtx);
+        mTestReporter.ReportFailure(details, message, lineInfo);
+    }
+
+    void OnTestSkip(const TestDetails &details, const std::string &reason)
+    {
+        mTestReporter.ReportSkip(details, reason);
+    }
+
+    void OnTestFinish(const TestDetails &details, Time::Duration time)
+    {
+        std::lock_guard<std::mutex> guard(mFinishMtx);
+        mTestReporter.ReportFinish(details, time);
+    }
+
+
+    void OnAllTestsComplete(int total, int skipped, int failed, Time::Duration totalTime)
+    {
+        mTestReporter.ReportAllTestsComplete(total, skipped, failed, totalTime);
+    }
+
+private:
+    Impl(const Impl &);
+    Impl &operator=(Impl);
+
+private:
+    IOutput &mTestReporter;
+
+    std::mutex mStartMtx;
+    std::mutex mFailureMtx;
+    std::mutex mFinishMtx;
+};
+
+TestRunner::TestRunner(IOutput &testReporter)
+    : mImpl(new Impl(testReporter))
+{
+}
+
+int TestRunner::RunTests(std::function<bool(const TestDetails &)> filter, const std::vector<xUnitTest> &tests, Time::Duration maxTestRunTime, size_t maxConcurrent)
+{
+    auto timeStart = std::chrono::system_clock::now();
+
+    if (maxConcurrent == 0)
+    {
+        maxConcurrent = std::numeric_limits<decltype(maxConcurrent)>::max();
+    }
+
+    class ThreadCounter
+    {
+    public:
+        ThreadCounter(size_t maxThreads)
+            : maxThreads(maxThreads)
+            , activeThreads(0)
+        {
+        }
+
+        void operator++()
+        {
+            std::unique_lock<std::mutex> lock(mtx);
+            condition.wait(lock, [&]() { return activeThreads < maxThreads; });
+
+            ++activeThreads;
+        }
+
+        void operator--()
+        {
+            --activeThreads;
+            condition.notify_one();
+        }
+
+    private:
+        size_t maxThreads;
+        std::atomic<size_t> activeThreads;
+        std::mutex mtx;
+        std::condition_variable condition;
+    } threadCounter(maxConcurrent);
+
+    std::atomic<int> failedTests = 0;
+    int skippedTests = 0;
+
+    std::vector<xUnitTest> activeTests;
+    std::copy_if(tests.begin(), tests.end(), std::back_inserter(activeTests), [&filter](const xUnitTest &test) { return filter(test.TestDetails()); });
+
+    std::vector<std::future<void>> futures;
+    for (auto &test : activeTests)
+    {
+        {
+            auto skip = test.TestDetails().Attributes.find("Skip");
+            if (skip != test.TestDetails().Attributes.end())
+            {
+                skippedTests++;
+                mImpl->OnTestSkip(test.TestDetails(), skip->second);
+                continue;
+            }
+        }
+
+        futures.push_back(std::async([&]()
+            {
+                struct CounterGuard
+                {
+                    CounterGuard(ThreadCounter &tc)
+                        : tc(tc)
+                    {
+                        ++tc;
+                    }
+
+                    ~CounterGuard()
+                    {
+                        --tc;
+                    }
+
+                private:
+                    CounterGuard &operator =(const CounterGuard &) { throw std::logic_error("not supported"); }
+                    ThreadCounter &tc;
+                } counterGuard(threadCounter);
+
+                auto actualTest = [&](bool reportEnd) -> Time::TimeStamp
+                    {
+                        Time::TimeStamp testStart;
+                        try
+                        {
+                            mImpl->OnTestStart(test.TestDetails());
+
+                            testStart = Time::Clock::now();
+                            test.Run();
+                        }
+                        catch (const xUnitAssert &e)
+                        {
+                            mImpl->OnTestFailure(test.TestDetails(), e.what(), e.LineInfo());
+                        }
+                        catch (const std::exception &e)
+                        {
+                            mImpl->OnTestFailure(test.TestDetails(), e.what(), LineInfo::empty());
+                            ++failedTests;
+                        }
+                        catch (...)
+                        {
+                            mImpl->OnTestFailure(test.TestDetails(), "Unknown exception caught: test has crashed", LineInfo::empty());
+                            ++failedTests;
+                        }
+
+                        if (reportEnd)
+                        {
+                            mImpl->OnTestFinish(test.TestDetails(), Time::ToDuration(Time::Clock::now() - testStart));
+                        }
+
+                        return testStart;
+                    };
+
+                auto testTimeLimit = test.TestDetails().TimeLimit;
+                if (testTimeLimit < Time::Duration::zero())
+                {
+                    testTimeLimit = maxTestRunTime;
+                }
+
+                if (testTimeLimit > Time::Duration::zero())
+                {
+                    //
+                    // note that forcing a test to run in under a certain amount of time is inherently fragile
+                    // there's no guarantee that a thread, once started, actually gets `maxTestRunTime` nanoseconds of CPU
+
+                    Time::TimeStamp testStart;
+
+                    std::mutex m;
+                    std::unique_lock<std::mutex> gate(m);
+
+                    auto threadStarted = std::make_shared<std::condition_variable>();
+                    std::thread timedRunner([&, threadStarted]()
+                        {
+                            m.lock();
+                            m.unlock();
+
+                            testStart = actualTest(false);
+                            threadStarted->notify_all();
+                        });
+                    timedRunner.detach();
+
+                    if (threadStarted->wait_for(gate, std::chrono::duration_cast<std::chrono::nanoseconds>(testTimeLimit)) == std::cv_status::timeout)
+                    {
+                        mImpl->OnTestFailure(test.TestDetails(), "Test failed to complete within " + std::to_string(Time::ToMilliseconds(testTimeLimit).count()) + " milliseconds.", LineInfo::empty());
+                        mImpl->OnTestFinish(test.TestDetails(), testTimeLimit);
+                        ++failedTests;
+                    }
+                    else
+                    {
+                        mImpl->OnTestFinish(test.TestDetails(), Time::ToDuration(Time::Clock::now() - testStart));
+                    }
+                }
+                else
+                {
+                    actualTest(true);
+                }
+            }));
+    }
+
+    for (auto &test : futures)
+    {
+        test.get();
+    }
+    
+    mImpl->OnAllTestsComplete(futures.size(), skippedTests, failedTests, Time::ToDuration(Time::Clock::now() - timeStart));
+
+    return -failedTests;
+}
+
+}

File xUnit++/xUnit++.h

-#ifndef XUNITPP_H_
-#define XUNITPP_H_
-
-#include "xUnitAssert.h"
-#include "xUnitMacros.h"
-
-#endif

File xUnit++/xUnit++.vcxproj

View file
     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="IOutput.cpp" />
-    <ClCompile Include="LineInfo.cpp" />
-    <ClCompile Include="TestCollection.cpp" />
-    <ClCompile Include="TestDetails.cpp" />
-    <ClCompile Include="xUnitAssert.cpp" />
-    <ClCompile Include="xUnitTest.cpp" />
-    <ClCompile Include="xUnitTestRunner.cpp" />
+    <ClCompile Include="src/IOutput.cpp" />
+    <ClCompile Include="src/LineInfo.cpp" />
+    <ClCompile Include="src/TestCollection.cpp" />
+    <ClCompile Include="src/TestDetails.cpp" />
+    <ClCompile Include="src/xUnitAssert.cpp" />
+    <ClCompile Include="src/xUnitTest.cpp" />
+    <ClCompile Include="src/xUnitTestRunner.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="Attributes.h" />
-    <ClInclude Include="ExportApi.h" />
-    <ClInclude Include="ForceLinkModuleMacros.h" />
-    <ClInclude Include="IOutput.h" />
-    <ClInclude Include="LineInfo.h" />
-    <ClInclude Include="Suite.h" />
-    <ClInclude Include="TestCollection.h" />
-    <ClInclude Include=".\xUnitMacros.h" />
-    <ClInclude Include="TestDetails.h" />
-    <ClInclude Include="xUnit++.h" />
-    <ClInclude Include="xUnitAssert.h" />
-    <ClInclude Include="xUnitMacroHelpers.h" />
-    <ClInclude Include="xUnitTest.h" />
-    <ClInclude Include="xUnitTestRunner.h" />
-    <ClInclude Include="xUnitTime.h" />
+    <ClInclude Include="xUnit++/Attributes.h" />
+    <ClInclude Include="xUnit++/ExportApi.h" />
+    <ClInclude Include="xUnit++/ForceLinkModuleMacros.h" />
+    <ClInclude Include="xUnit++/IOutput.h" />
+    <ClInclude Include="xUnit++/LineInfo.h" />
+    <ClInclude Include="xUnit++/Suite.h" />
+    <ClInclude Include="xUnit++/TestCollection.h" />
+    <ClInclude Include="xUnit++/xUnitMacros.h" />
+    <ClInclude Include="xUnit++/TestDetails.h" />
+    <ClInclude Include="xUnit++/xUnit++.h" />
+    <ClInclude Include="xUnit++/xUnitAssert.h" />
+    <ClInclude Include="xUnit++/xUnitMacroHelpers.h" />
+    <ClInclude Include="xUnit++/xUnitTest.h" />
+    <ClInclude Include="xUnit++/xUnitTestRunner.h" />
+    <ClInclude Include="xUnit++/xUnitTime.h" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
     <VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VSVersion)' == '' and '$(VisualStudioVersion)' == ''">$(VCTargetsPath11)</VCTargetsPath>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <Lib />
+    <ClCompile>
+      <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
+    </ClCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
+    </ClCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
+    </ClCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
+    </ClCompile>
   </ItemDefinitionGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

File xUnit++/xUnit++.vcxproj.filters

View file
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="xUnitAssert.cpp" />
-    <ClCompile Include="TestCollection.cpp" />
-    <ClCompile Include="TestDetails.cpp" />
-    <ClCompile Include="xUnitTestRunner.cpp" />
-    <ClCompile Include="LineInfo.cpp" />
-    <ClCompile Include="xUnitTest.cpp" />
-    <ClCompile Include="IOutput.cpp" />
+    <ClCompile Include="src/xUnitAssert.cpp" />
+    <ClCompile Include="src/TestCollection.cpp" />
+    <ClCompile Include="src/TestDetails.cpp" />
+    <ClCompile Include="src/xUnitTestRunner.cpp" />
+    <ClCompile Include="src/LineInfo.cpp" />
+    <ClCompile Include="src/xUnitTest.cpp" />
+    <ClCompile Include="src/IOutput.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="xUnitAssert.h" />
-    <ClInclude Include="TestCollection.h" />
-    <ClInclude Include="Suite.h" />
-    <ClInclude Include=".\xUnitMacros.h" />
-    <ClInclude Include="TestDetails.h" />
-    <ClInclude Include="xUnitTime.h" />
-    <ClInclude Include="xUnit++.h" />
-    <ClInclude Include="xUnitTestRunner.h" />
-    <ClInclude Include="Attributes.h" />
-    <ClInclude Include="xUnitMacroHelpers.h" />
-    <ClInclude Include="IOutput.h" />
-    <ClInclude Include="ForceLinkModuleMacros.h" />
-    <ClInclude Include="ExportApi.h" />
-    <ClInclude Include="LineInfo.h" />
-    <ClInclude Include="xUnitTest.h" />
+    <ClInclude Include="xUnit++/xUnitAssert.h" />
+    <ClInclude Include="xUnit++/TestCollection.h" />
+    <ClInclude Include="xUnit++/Suite.h" />
+    <ClInclude Include="xUnit++/TestDetails.h" />
+    <ClInclude Include="xUnit++/xUnitTime.h" />
+    <ClInclude Include="xUnit++/xUnit++.h" />
+    <ClInclude Include="xUnit++/xUnitTestRunner.h" />
+    <ClInclude Include="xUnit++/Attributes.h" />
+    <ClInclude Include="xUnit++/xUnitMacroHelpers.h" />
+    <ClInclude Include="xUnit++/IOutput.h" />
+    <ClInclude Include="xUnit++/ForceLinkModuleMacros.h" />
+    <ClInclude Include="xUnit++/ExportApi.h" />
+    <ClInclude Include="xUnit++/LineInfo.h" />
+    <ClInclude Include="xUnit++/xUnitTest.h" />
+    <ClInclude Include="xUnit++/xUnitMacros.h" />
   </ItemGroup>
 </Project>

File xUnit++/xUnit++/Attributes.h

View file
+#ifndef ATTRIBUTES_H_
+#define ATTRIBUTES_H_
+
+#include <map>
+#include <string>
+#include "TestDetails.h"
+
+namespace xUnitAttributes
+{
+    inline xUnitpp::AttributeCollection Attributes()
+    {
+        return xUnitpp::AttributeCollection();
+    }
+
+}
+
+#endif

File xUnit++/xUnit++/ExportApi.h

View file
+#ifndef EXPORTSAPI_H_
+#define EXPORTSAPI_H_
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace xUnitpp
+{
+    struct IOutput;
+    struct TestDetails;
+
+    typedef std::function<void(const TestDetails &)> EnumerateTestDetailsCallback;
+    typedef void(*EnumerateTestDetails)(EnumerateTestDetailsCallback callback);
+
+    typedef std::function<bool(const TestDetails &)> TestFilterCallback;
+    typedef int(*FilteredTestsRunner)(int, int, IOutput &, TestFilterCallback);
+}
+
+#endif

File xUnit++/xUnit++/IOutput.h

View file
+#ifndef IOUTPUT_H_
+#define IOUTPUT_H_
+
+#include <string>
+#include "xUnitTime.h"
+
+namespace xUnitpp
+{
+
+struct LineInfo;
+struct TestDetails;
+
+struct IOutput
+{
+    virtual ~IOutput();
+
+    virtual void ReportStart(const TestDetails &testDetails) = 0;
+    virtual void ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo) = 0;
+    virtual void ReportSkip(const TestDetails &testDetails, const std::string &reason) = 0;
+    virtual void ReportFinish(const TestDetails &testDetails, Time::Duration timeTaken) = 0;
+    virtual void ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failed, Time::Duration totalTime) = 0; 
+};
+
+}
+
+#endif

File xUnit++/xUnit++/LineInfo.h

View file
+#ifndef LINEINFO_H_
+#define LINEINFO_H_
+
+#include <string>
+
+namespace xUnitpp
+{
+
+struct LineInfo
+{
+    LineInfo(const std::string &file, int line);
+
+    static const LineInfo &empty();
+
+    std::string file;
+    int line;
+
+private:
+    LineInfo();
+};
+
+}
+
+#endif

File xUnit++/xUnit++/Suite.h

View file
+#ifndef SUITE_H_
+#define SUITE_H_
+
+#include <string>
+
+namespace xUnitSuite
+{
+    inline const std::string &Name()
+    {
+        static std::string name = "xUnit++ Default Suite";
+        return name;
+    }
+
+}
+
+#endif

File xUnit++/xUnit++/TestCollection.h

View file
+#ifndef TESTCOLLECTION_H_
+#define TESTCOLLECTION_H_
+
+#include <chrono>
+#include <functional>
+#include <map>
+#include <vector>
+#include "xUnitTest.h"
+
+namespace xUnitpp
+{
+
+class TestCollection
+{
+    friend class Register;
+
+public:
+    class Register
+    {
+        // !!!VS someday, Visual Studio will understand variadic macros
+        // when it does, fix this collection
+
+        // !!! should use a macro system to automate this
+        template<typename TFn, typename TArg0>
+        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0> &&t)
+        {
+            return [=]() { return theory(std::get<0>(t)); };
+        }
+
+        template<typename TFn, typename TArg0, typename TArg1>
+        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1> &&t)
+        {
+            return [=]() { return theory(std::get<0>(t),
+                                         std::get<1>(t)); };
+        }
+
+        template<typename TFn, typename TArg0, typename TArg1, typename TArg2>
+        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1, TArg2> &&t)
+        {
+            return [=]() { return theory(std::get<0>(t),
+                                         std::get<1>(t),
+                                         std::get<2>(t)); };
+        }
+
+        template<typename TFn, typename TArg0, typename TArg1, typename TArg2, typename TArg3>
+        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1, TArg2, TArg3> &&t)
+        {
+            return [=]() { return theory(std::get<0>(t),
+                                         std::get<1>(t),
+                                         std::get<2>(t),
+                                         std::get<3>(t)); };
+        }
+
+        template<typename TFn, typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4>
+        static std::function<void()> TheoryHelper(TFn &&theory, std::tuple<TArg0, TArg1, TArg2, TArg3, TArg4> &&t)
+        {
+            return [=]() { return theory(std::get<0>(t),
+                                         std::get<1>(t),
+                                         std::get<2>(t),
+                                         std::get<3>(t),
+                                         std::get<4>(t)); };
+        }
+
+    public:
+        Register(TestCollection &collection, const std::function<void()> &fn, const std::string &name, const std::string &suite,
+            const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line);
+
+        template<typename TTheory, typename TTheoryData>
+        Register(TestCollection &collection, TTheory theory, TTheoryData theoryData, const std::string &name, const std::string &suite,
+            const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line)
+        {
+            int id = 1;
+            for (auto t : theoryData())
+            {
+                // !!! someday, I'd like to embed the actual parameter values, rather than the theory data index
+                // not sure how feasible that is in C++, since it's lacking the type reflection of C# :(
+                auto theoryName = name + "(" + std::to_string(id++) + ")";
+
+                collection.mTests.emplace_back(xUnitTest(TheoryHelper(theory, std::move(t)), theoryName, suite,
+                        attributes, Time::ToDuration(std::chrono::milliseconds(milliseconds)), filename, line));
+            }
+        }
+    };
+
+    static TestCollection &Instance();
+
+    const std::vector<xUnitTest> &Tests();
+
+private:
+    std::vector<xUnitTest> mTests;
+};
+
+}
+
+#endif

File xUnit++/xUnit++/TestDetails.h

View file
+#ifndef TESTDETAILS_H_
+#define TESTDETAILS_H_
+
+#include <chrono>
+#include <map>
+#include <string>
+#include <tuple>
+#include "xUnitTime.h"
+
+namespace xUnitpp
+{
+
+typedef std::multimap<std::string, std::string> AttributeCollection;
+
+struct TestDetails
+{
+    TestDetails();
+    TestDetails(const std::string &name, const std::string &suite,
+        const AttributeCollection &attributes, Time::Duration timeLimit,
+        const std::string &filename, int line);
+    TestDetails(const TestDetails &other);
+    TestDetails(TestDetails &&other);
+    TestDetails &operator =(TestDetails other);
+    friend void swap(TestDetails &td0, TestDetails &td1);
+
+    int Id;
+    std::string Name;
+    std::string ShortName;
+    std::string Suite;
+    AttributeCollection Attributes;
+    Time::Duration TimeLimit;
+    std::string Filename;
+    int Line;
+};
+
+}
+
+#endif

File xUnit++/xUnit++/xUnit++.h

View file
+#ifndef XUNITPP_H_
+#define XUNITPP_H_
+
+#include "xUnitAssert.h"
+#include "xUnitMacros.h"
+
+#endif

File xUnit++/xUnit++/xUnitAssert.h

View file
+#ifndef XUNITASSERT_H_
+#define XUNITASSERT_H_
+
+#include <algorithm>
+#include <exception>
+#include <string>
+#include <type_traits>
+#include "LineInfo.h"
+
+namespace xUnitpp
+{
+
+class xUnitAssert : public std::exception
+{
+    typedef std::exception base;
+
+public:
+    xUnitAssert(const std::string &call, const std::string &userMsg, const std::string &customMsg,
+                const std::string &expected, const std::string &actual, const LineInfo &lineInfo);
+    xUnitAssert(const xUnitAssert &other);
+
+    const LineInfo &LineInfo() const;
+
+private:
+    xUnitpp::LineInfo lineInfo;
+};
+
+const class Assert
+{
+private:
+    static double round(double value, size_t precision);
+
+    template<typename T>
+    static std::string RangeToString(const T &begin, const T &end)
+    {
+        std::string result = "[ ";
+
+        std::for_each(begin, end, [&result](decltype(*begin) val) { result += std::to_string(val) + ", "; });
+
+        result[result.size() - 2] = ' ';
+        result[result.size() - 1] = ']';
+
+        return result;
+    }
+
+    template<typename T>
+    struct has_empty
+    {
+    private:
+        template<typename U, U>
+        class check {};
+
+        template<typename C>
+        static char f(check<bool (C::*)() const, &C::empty> *);
+
+        template<typename C>
+        static long f(...);
+
+    public:
+        static const bool value = (sizeof(f<T>(nullptr)) == sizeof(char));
+    };
+
+public:
+    template<typename TExpected, typename TActual, typename TComparer>
+    void Equal(TExpected expected, TActual actual, TComparer comparer, const std::string &msg, const LineInfo &lineInfo = LineInfo::empty()) const
+    {
+        using namespace std;
+
+        if (!comparer(expected, actual))
+        {
+            throw xUnitAssert("Equal", msg, "", to_string(expected), to_string(actual), lineInfo);
+        }
+    }
+
+    template<typename TExpected, typename TActual, typename TComparer>
+    void Equal(TExpected expected, TActual actual, TComparer comparer, const LineInfo &lineInfo = LineInfo::empty()) const
+    {