Commits

Matt Oswald  committed 9361703

refactored xUnit++ so that xUnitTest object contains full test record, allowing more than just error messages

  • Participants
  • Parent commits db1ba60

Comments (0)

Files changed (33)

File Tests/UnitTests/Attributes.cpp

 #include "xUnit++/TestCollection.h"
 #include "xUnit++/xUnitTestRunner.h"
 #include "xUnit++/xUnit++.h"
-
+#include "Helpers/OutputRecord.h"
 
 SUITE("Attributes")
 {
         }
     };
 
-    struct : xUnitpp::IOutput
-    {
-        virtual void ReportStart(const xUnitpp::TestDetails &) override
-        {
-        }
-
-        virtual void ReportFailure(const xUnitpp::TestDetails &, const std::string &, const xUnitpp::LineInfo &) override
-        {
-        }
-
-        virtual void ReportSkip(const xUnitpp::TestDetails &, const std::string &) override
-        {
-        }
-
-        virtual void ReportFinish(const xUnitpp::TestDetails &, xUnitpp::Time::Duration) override
-        {
-        }
-
-        virtual void ReportAllTestsComplete(size_t, size_t, size_t, xUnitpp::Time::Duration) override 
-        {
-        }
-    } emptyReporter;
+    xUnitpp::Tests::OutputRecord record;
 
     xUnitpp::AttributeCollection attributes;
     attributes.insert(std::make_pair("Skip", "Testing skip."));
 
     xUnitpp::TestCollection collection;
-    auto check = std::make_shared<xUnitpp::Check>();
+    std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> localEventSources;
     xUnitpp::TestCollection::Register reg(collection, []() { SkippedTest().RunTest(); },
-        "SkippedTest", "Attributes", attributes, -1, __FILE__, __LINE__, check);
+        "SkippedTest", "Attributes", attributes, -1, __FILE__, __LINE__, localEventSources);
 
-    xUnitpp::RunTests(emptyReporter, [](const xUnitpp::TestDetails &) { return true; },
+    xUnitpp::RunTests(record, [](const xUnitpp::TestDetails &) { return true; },
         collection.Tests(), xUnitpp::Time::Duration::zero(), 0);
 }
 

File Tests/UnitTests/Check.cpp

+#include "xUnit++/xUnit++.h"
+#include "xUnit++/ITestEventSource.h"
+#include "xUnit++/TestCollection.h"
+#include "xUnit++/xUnitTestRunner.h"
+#include "Helpers/OutputRecord.h"
+
+SUITE("Check")
+{
+
+FACT("Check should be usable within Theories")
+{
+    std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> localEventSources;
+    localEventSources.push_back(std::make_shared<xUnitpp::Check>());
+    xUnitpp::Check &localCheck = *static_cast<xUnitpp::Check *>(localEventSources[0].get());
+
+    auto theoryWithChecks = [&](int x) { localCheck.Fail() << "id: " << x; };
+
+    std::vector<std::tuple<int>> theoryData;
+    for (int i = 0; i != 10; ++i)
+    {
+        theoryData.push_back(std::make_tuple(i));
+    }
+
+    xUnitpp::TestCollection collection;
+    xUnitpp::TestCollection::Register reg(collection, theoryWithChecks, [&]() { return theoryData; },
+        "Name", "Theory", xUnitpp::AttributeCollection(), -1, "file", 0, localEventSources);
+
+    xUnitpp::Tests::OutputRecord record;
+
+    xUnitpp::RunTests(record, [](const xUnitpp::TestDetails &) { return true; }, collection.Tests(), xUnitpp::Time::Duration::zero(), 0);
+
+    for (const auto &test : collection.Tests())
+    {
+        Assert.Equal(1U, test->TestEvents().size());
+    }
+}
+
+}

File Tests/UnitTests/Helpers/OutputRecord.cpp

 #include "OutputRecord.h"
 #include "xUnit++/LineInfo.h"
 #include "xUnit++/TestDetails.h"
+#include "xUnit++/TestEvent.h"
 
 namespace xUnitpp { namespace Tests {
 
     orderedTestList.push_back(testDetails);
 }
 
-void OutputRecord::ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo)
+void OutputRecord::ReportEvent(const TestDetails &testDetails, const TestEvent &evt)
 {
     std::lock_guard<std::mutex> guard(lock);
-    failures.push_back(std::make_tuple(testDetails, msg, lineInfo));
+    events.push_back(std::make_tuple(testDetails, evt));
 }
 
 void OutputRecord::ReportSkip(const TestDetails &testDetails, const std::string &reason)

File Tests/UnitTests/Helpers/OutputRecord.h

 {
 public:
     virtual void ReportStart(const TestDetails &testDetails) override;
-    virtual void ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo) override;
+    virtual void ReportEvent(const TestDetails &testDetails, const TestEvent &evt) override;
     virtual void ReportSkip(const TestDetails &testDetails, const std::string &reason) override;
     virtual void ReportFinish(const TestDetails &testDetails, Time::Duration timeTaken) override;
     virtual void ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failed, Time::Duration totalTime) override;
 
     std::vector<TestDetails> orderedTestList;
-    std::vector<std::tuple<TestDetails, std::string, LineInfo>> failures;
+    std::vector<std::tuple<TestDetails, TestEvent>> events;
     std::vector<std::tuple<TestDetails, std::string>> skips;
     std::vector<std::tuple<TestDetails, Time::Duration>> finishedTests;
 

File Tests/UnitTests/LineInfo.cpp

 #include "xUnit++/IOutput.h"
 #include "xUnit++/xUnit++.h"
-
+#include "xUnit++/xUnitTestRunner.h"
+#include "Helpers/OutputRecord.h"
 
 SUITE("LineInfo")
 {
     auto line = 1;
     auto test = [=]() { Assert.Fail(xUnitpp::LineInfo(file, line)); };
 
-    struct EmptyReporter : xUnitpp::IOutput
-    {
-        EmptyReporter(const std::string &file, int line)
-            : file(file)
-            , line(line)
-        {
-        }
-
-        virtual void ReportStart(const xUnitpp::TestDetails &) override
-        {
-        }
-
-        virtual void ReportFailure(const xUnitpp::TestDetails &, const std::string &, const xUnitpp::LineInfo &lineInfo) override
-        {
-            Assert.Equal(file, lineInfo.file);
-            Assert.Equal(line, lineInfo.line);
-        }
-
-        virtual void ReportSkip(const xUnitpp::TestDetails &, const std::string &) override
-        {
-        }
-
-        virtual void ReportFinish(const xUnitpp::TestDetails &, xUnitpp::Time::Duration) override
-        {
-        }
-
-        virtual void ReportAllTestsComplete(size_t, size_t, size_t, xUnitpp::Time::Duration) override 
-        {
-        }
-
-    private:
-        std::string file;
-        int line;
-    } emptyReporter(file, line);
-
-
+    xUnitpp::Tests::OutputRecord record;
     xUnitpp::AttributeCollection attributes;
     xUnitpp::TestCollection collection;
-    auto localCheck = std::make_shared<xUnitpp::Check>();
+    std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> localEventSources;
     xUnitpp::TestCollection::Register reg(collection, test,
         "LineInfoOverridesDefaultTestLineInfo", "LineInfo", attributes,
-        -1, __FILE__, __LINE__, localCheck);
+        -1, __FILE__, __LINE__, localEventSources);
 
+    xUnitpp::RunTests(record, [](const xUnitpp::TestDetails &) { return true; }, collection.Tests(), xUnitpp::Time::Duration::zero(), 0);
 
+    Assert.Equal(1U, record.events.size());
+    Assert.Equal(file, std::get<1>(record.events[0]).LineInfo().file);
+    Assert.Equal(line, std::get<1>(record.events[0]).LineInfo().line);
 }
 
 }

File Tests/UnitTests/TestRunner.cpp

 
     operator std::shared_ptr<xUnitTest>() const
     {
-        return std::make_shared<xUnitTest>(testFn, name, suite, attributes, timeLimit, file, line, check);
+        std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> testEventSources;
+        testEventSources.push_back(check);
+        return std::make_shared<xUnitTest>(testFn, name, suite, attributes, timeLimit, file, line, testEventSources);
     }
 
 private:
     tests.push_back(TestFactory(FailingTest(), testCheck).Name("failing"));
 
     Assert.Equal(2, RunTests(output, &Filter::AllTests, tests, duration, 0));
-    Assert.Equal(2U, output.failures.size());
+    Assert.Equal(2U, output.events.size());
     
 }
 
     tests.push_back(TestFactory([]() { Assert.Fail() << "first"; Assert.Fail() << "second"; }, testCheck));
 
     Assert.Equal(1, RunTests(output, &Filter::AllTests, tests, duration, 0), LI);
-    Assert.Equal(1U, output.failures.size(), LI);
-    Assert.Contains(std::get<1>(output.failures[0]), "first", LI);
-    Assert.DoesNotContain(std::get<1>(output.failures[0]), "second", LI);
+    Assert.Equal(1U, output.events.size(), LI);
+    Assert.Contains(std::get<1>(output.events[0]).ToString(), "first", LI);
+    Assert.DoesNotContain(std::get<1>(output.events[0]).ToString(), "second", LI);
 }
 
 FACT_FIXTURE("FailureIsReportedOncePerCheck", TestRunnerFixture)
     tests.push_back(TestFactory([=]() { testCheck->Fail(); }, testCheck));
 
     Assert.Equal(1, RunTests(output, &Filter::AllTests, tests, duration, 0));
-    Assert.Equal(1U, output.failures.size());
+    Assert.Equal(1U, output.events.size());
 }
 
 FACT_FIXTURE("TestsDoNotAbortOnCheck", TestRunnerFixture)
     tests.push_back(TestFactory([=]() { testCheck->Fail() << "first"; testCheck->Fail() << "second"; }, testCheck));
 
     Assert.Equal(1, RunTests(output, &Filter::AllTests, tests, duration, 0));
-    Assert.Equal(2U, output.failures.size());
-    Assert.Contains(std::get<1>(output.failures[0]), "first");
-    Assert.Contains(std::get<1>(output.failures[1]), "second");
+    Assert.Equal(2U, output.events.size());
+    Assert.Contains(std::get<1>(output.events[0]).ToString(), "first");
+    Assert.Contains(std::get<1>(output.events[1]).ToString(), "second");
+}
+
+FACT_FIXTURE("FailuresAreReported", TestRunnerFixture)
+{
+    tests.push_back(TestFactory([=]() { testCheck->Fail(); testCheck->Fail(); }, testCheck));
+    RunTests(output, &Filter::AllTests, tests, duration, 0);
+
+    Assert.Equal(2U, output.events.size());
 }
 
 FACT_FIXTURE("TestCountIsReported", TestRunnerFixture)
     Assert.Equal(1U, output.summaryFailed);
 }
 
-FACT_FIXTURE("FailuresAreReported", TestRunnerFixture)
-{
-    tests.push_back(TestFactory([=]() { testCheck->Fail(); testCheck->Fail(); }, testCheck));
-    RunTests(output, &Filter::AllTests, tests, duration, 0);
-
-    Assert.Equal(2U, output.failures.size());
-}
-
 FACT_FIXTURE("SkippedTestsAreReported", TestRunnerFixture)
 {
     xUnitpp::AttributeCollection attributes;
     tests.push_back(TestFactory(SleepyTest(), testCheck));
     RunTests(output, &Filter::AllTests, tests, Time::ToDuration(Time::ToMilliseconds(200)), 0);
 
-    Assert.Equal(0U, output.failures.size());
+    Assert.Equal(0U, output.events.size());
     Assert.Equal(0U, output.summaryFailed);
 }
 
     tests.push_back(TestFactory(sleepyTest, testCheck));
     RunTests(output, &Filter::AllTests, tests, Time::ToDuration(Time::ToMilliseconds(1)), 0);
 
-    Assert.Equal(1U, output.failures.size());
+    Assert.Equal(1U, output.events.size());
     Assert.Equal(1U, output.summaryFailed);
 }
 
     tests.push_back(TestFactory(SleepyTest(), testCheck));
     RunTests(output, &Filter::AllTests, tests, Time::ToDuration(Time::ToMilliseconds(1)), 0);
 
-    Assert.Equal(1U, output.failures.size());
-    Assert.Contains(std::get<1>(output.failures[0]), "Test failed to complete within");
-    Assert.Contains(std::get<1>(output.failures[0]), "1 milliseconds.");
+    Assert.Equal(1U, output.events.size());
+    Assert.Contains(std::get<1>(output.events[0]).ToString(), "Test failed to complete within");
+    Assert.Contains(std::get<1>(output.events[0]).ToString(), "1 milliseconds.");
 }
 
 UNTIMED_FACT_FIXTURE("SlowTestWithTimeExemptionPasses", TestRunnerFixture)

File Tests/UnitTests/Theory.cpp

 #include "xUnit++/xUnitTestRunner.h"
 #include "xUnit++/xUnitTime.h"
 #include "xUnit++/xUnit++.h"
+#include "Helpers/OutputRecord.h"
 
 SUITE("Theory")
 {
 
 struct TheoryFixture
 {
-private:
-    struct : xUnitpp::IOutput
-    {
-        virtual void ReportStart(const xUnitpp::TestDetails &) override
-        {
-        }
-
-        virtual void ReportFailure(const xUnitpp::TestDetails &, const std::string &, const xUnitpp::LineInfo &) override
-        {
-        }
-
-        virtual void ReportSkip(const xUnitpp::TestDetails &, const std::string &) override
-        {
-        }
-
-        virtual void ReportFinish(const xUnitpp::TestDetails &, xUnitpp::Time::Duration) override
-        {
-        }
-
-        virtual void ReportAllTestsComplete(size_t, size_t, size_t, xUnitpp::Time::Duration) override
-        {
-        }
-    } emptyReporter;
-
 public:
     TheoryFixture()
-        : localCheck(std::make_shared<xUnitpp::Check>())
     {
     }
 
     void Register(const std::string &name, TTheoryData &&theoryData)
     {
         xUnitpp::TestCollection::Register reg(collection, &TheoryUnderTest, theoryData,
-            name, "Theory", attributes, -1, __FILE__, __LINE__, localCheck);
+            name, "Theory", attributes, -1, __FILE__, __LINE__, localEventSources);
     }
 
     void Run()
     {
-        RunTests(emptyReporter, [](const xUnitpp::TestDetails &) { return true; },
+        RunTests(record, [](const xUnitpp::TestDetails &) { return true; },
             collection.Tests(), xUnitpp::Time::Duration::zero(), 0);
     }
 
         Run();
     }
 
+    xUnitpp::Tests::OutputRecord record;
     xUnitpp::AttributeCollection attributes;
     xUnitpp::TestCollection collection;
-    std::shared_ptr<xUnitpp::Check> localCheck;
+    std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> localEventSources;
 };
 
 std::vector<std::tuple<int>> RawFunctionProvider()
 
     auto doTheory = [&](int x) { std::lock_guard<std::mutex> guard(lock); dataProvided.push_back(x); };
     xUnitpp::TestCollection::Register reg(collection, doTheory, RawFunctionProvider,
-        "TheoriesGetAllDataPassedToThem", "Theory", attributes, -1, __FILE__, __LINE__, localCheck);
+        "TheoriesGetAllDataPassedToThem", "Theory", attributes, -1, __FILE__, __LINE__, localEventSources);
 
     Run();
 
     auto doTheory = [](int) { Assert.Fail() << "Should not be run."; };
 
     xUnitpp::TestCollection::Register reg(collection, doTheory, RawFunctionProvider,
-        "TheoriesGetAllDataPassedToThem", "Theory", attributes, -1, __FILE__, __LINE__, localCheck);
+        "TheoriesGetAllDataPassedToThem", "Theory", attributes, -1, __FILE__, __LINE__, localEventSources);
 
     Run();
 }

File Tests/UnitTests/UnitTests.vcxproj

     <ClCompile Include="Assert.Throws.cpp" />
     <ClCompile Include="Assert.True.cpp" />
     <ClCompile Include="Attributes.cpp" />
+    <ClCompile Include="Check.cpp" />
     <ClCompile Include="Helpers\OutputRecord.cpp" />
     <ClCompile Include="LineInfo.cpp" />
     <ClCompile Include="TestRunner.cpp" />

File Tests/UnitTests/UnitTests.vcxproj.filters

       <Filter>Test Helpers</Filter>
     </ClCompile>
     <ClCompile Include="TestsCanOutputAnythingWithToString.cpp" />
+    <ClCompile Include="Check.cpp" />
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Test Helpers">

File src/TestEvent.cpp

+#include "TestEvent.h"
+#include "xUnitAssert.h"
+
+namespace xUnitpp
+{
+
+TestEvent::TestEvent(EventLevel level, const std::string &message)
+    : level(level)
+    , message(message)
+    , lineInfo(LineInfo::empty())
+{
+}
+
+TestEvent::TestEvent(const xUnitAssert &assert)
+    : level(EventLevel::Assert)
+    , message(assert.what())
+    , lineInfo(assert.LineInfo())
+{
+}
+
+TestEvent::TestEvent(const std::exception &e)
+    : level(EventLevel::Fatal)
+    , message(e.what())
+    , lineInfo(LineInfo::empty())
+{
+}
+
+bool TestEvent::IsFailure() const
+{
+    return level > EventLevel::Warning;
+}
+
+EventLevel TestEvent::Level() const
+{
+    return level;
+}
+
+const xUnitpp::LineInfo &TestEvent::LineInfo() const
+{
+    return lineInfo;
+}
+
+const std::string &TestEvent::LevelString() const
+{
+    static std::string msg[] =
+    {
+        "DEBUG",
+        "info",
+        "warning",
+        "error",
+        "error",
+        "error"
+    };
+
+    return msg[(int)level];
+}
+
+const std::string &TestEvent::ToString() const
+{
+    return message;
+}
+
+}

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

 #include "xUnit++/IOutput.h"
 #include "xUnit++/LineInfo.h"
 #include "xUnit++/TestDetails.h"
+#include "xUnit++/TestEvent.h"
 
 using namespace System;
 using namespace System::Collections::Generic;
             testResults.Add(key, result);
         }
 
-        void ReportFailure(const xUnitpp::TestDetails &td, const std::string &msg, const xUnitpp::LineInfo &)
+        void ReportEvent(const xUnitpp::TestDetails &td, const xUnitpp::TestEvent &evt)
         {
             auto result = testResults[marshal_as<String ^>(td.Name)];
-            result->Outcome = TestOutcome::Failed;
-            result->ErrorMessage = marshal_as<String ^>(msg);
+
+            if (evt.IsFailure())
+            {
+                result->Outcome = TestOutcome::Failed;
+            }
+
+            result->Messages->Add(gcnew TestResultMessage(marshal_as<String ^>(evt.LevelString()), marshal_as<String ^>(evt.ToString())));
         }
 
         void ReportSkip(const xUnitpp::TestDetails &td, const std::string &)
             reporter->ReportStart(td);
         }
 
-        virtual void ReportFailure(const xUnitpp::TestDetails &testDetails, const std::string &msg, const xUnitpp::LineInfo &lineInfo) override
+        virtual void ReportEvent(const xUnitpp::TestDetails &testDetails, const xUnitpp::TestEvent &evt) override
         {
-            reporter->ReportFailure(testDetails, msg, lineInfo);
+            reporter->ReportEvent(testDetails, evt);
         }
 
         virtual void ReportSkip(const xUnitpp::TestDetails &testDetails, const std::string &reason) override

File xUnit++.console/StdOutReporter.cpp

 #include <iostream>
 #include "xUnit++/LineInfo.h"
 #include "xUnit++/TestDetails.h"
+#include "xUnit++/TestEvent.h"
 
 namespace
 {
 
         return file + "(" + std::to_string(line) + ")";
     }
-
-    std::string NameAndDataIndex(const std::string &name, int dataIndex)
-    {
-        if (dataIndex < 0)
-        {
-            return name;
-        }
-        else
-        {
-            return name + "(" + std::to_string(dataIndex) +")";
-        }
-    }
 }
 
 namespace xUnitpp
     }
 }
 
-void StdOutReporter::ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo)
+void StdOutReporter::ReportEvent(const TestDetails &testDetails, const TestEvent &evt)
 {
-    std::cout << (FileAndLine(testDetails, lineInfo) +
-        ": error in " + testDetails.Name+ ": " + msg + "\n");
+    std::cout << (FileAndLine(testDetails, evt.LineInfo()) +
+        ": " + testDetails.Name+ ": " + evt.LevelString() + ": " + evt.ToString() + "\n");
 }
 
 void StdOutReporter::ReportSkip(const TestDetails &testDetails, const std::string &reason)

File xUnit++.console/StdOutReporter.h

     StdOutReporter(bool verbose, bool veryVerbose);
 
     virtual void ReportStart(const TestDetails &) override;
-    virtual void ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo) override;
+    virtual void ReportEvent(const TestDetails &testDetails, const TestEvent &evt) override;
     virtual void ReportSkip(const TestDetails &testDetails, const std::string &reason) override;
     virtual void ReportFinish(const TestDetails &, Time::Duration) override;
     virtual void ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failureCount, Time::Duration totalTime) override;

File xUnit++.console/TestAssembly.h

 
 #if defined(WIN32)
 #include <Windows.h>
+
+// thanks, Microsoft. "ReportEvent" isn't likely to be used anywhere else, ever
+#undef ReportEvent
+
 #endif
 
 #include <string>

File xUnit++.console/XmlReporter.cpp

 #include <fstream>
 #include <iostream>
 #include "xUnit++/TestDetails.h"
+#include "xUnit++/TestEvent.h"
 #include "xUnit++/xUnitTime.h"
 
 namespace
         } status;
 
         xUnitpp::Time::Duration time;
-        std::string message;
+        std::vector<std::string> messages;
     };
 }
 
             " />\n";
     }
 
-    std::string XmlTestFailed(const std::string &file, int line, const std::string &message)
+    std::string XmlTestFailed(const std::string &file, int line, const std::vector<std::string> &messages)
     {
-        return std::string("         ") +
-            "<failure" +
-                XmlAttribute("message", file + "(" + std::to_string(line) + "): " + XmlEscape(message)) +
-            "</failure>\n";
+        std::string result;
+        for (const auto &message : messages)
+        {
+            result += std::string("         ") +
+                "<failure" +
+                    XmlAttribute("message", file + "(" + std::to_string(line) + "): " + XmlEscape(message)) +
+                "</failure>\n";
+        }
+
+        return result;
     }
 
     std::string XmlTestSkipped(const std::string &message)
                         if (att.first != "Skip")
                         {
                             stream << XmlTestAttribute(att.first, att.second);
+                            break;
                         }
                     }
 
                     if (test.status == TestResult::Failure)
                     {
-                        stream << XmlTestFailed(test.testDetails.Filename, test.testDetails.Line, test.message);
+                        stream << XmlTestFailed(test.testDetails.Filename, test.testDetails.Line, test.messages);
                     }
                     else if (test.status == TestResult::Skipped)
                     {
-                        stream << XmlTestSkipped(test.message);
+                        stream << XmlTestSkipped(test.messages[0]);
                     }
 
                     stream << XmlEndTest(test.status == TestResult::Success && test.testDetails.Attributes.empty());
     suiteResults[testDetails.Suite].testResults.insert(std::make_pair(testDetails.Name, TestResult(testDetails)));
 }
 
-void XmlReporter::ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &)
+void XmlReporter::ReportEvent(const TestDetails &testDetails, const TestEvent &evt)
 {
-    suiteResults[testDetails.Suite].failures++;
-    suiteResults[testDetails.Suite].testResults[testDetails.Name].message = msg;
-    suiteResults[testDetails.Suite].testResults[testDetails.Name].status = TestResult::Failure;
+    if (evt.IsFailure())
+    {
+        suiteResults[testDetails.Suite].failures++;
+        suiteResults[testDetails.Suite].testResults[testDetails.Name].messages.push_back(evt.ToString());
+        suiteResults[testDetails.Suite].testResults[testDetails.Name].status = TestResult::Failure;
+    }
 }
 
 void XmlReporter::ReportSkip(const TestDetails &testDetails, const std::string &reason)
 {
     ReportStart(testDetails);
     suiteResults[testDetails.Suite].skipped++;
-    suiteResults[testDetails.Suite].testResults[testDetails.Name].message = reason;
+    suiteResults[testDetails.Suite].testResults[testDetails.Name].messages.push_back(reason);
     suiteResults[testDetails.Suite].testResults[testDetails.Name].status = TestResult::Skipped;
 }
 

File xUnit++.console/XmlReporter.h

     XmlReporter(const std::string &filename);
 
     virtual void ReportStart(const TestDetails &td) override;
-    virtual void ReportFailure(const TestDetails &testDetails, const std::string &msg, const LineInfo &lineInfo) override;
+    virtual void ReportEvent(const TestDetails &testDetails, const TestEvent &evt) override;
     virtual void ReportSkip(const TestDetails &testDetails, const std::string &reason) override;
     virtual void ReportFinish(const TestDetails &testDetails, Time::Duration timeTaken) override;
     virtual void ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failureCount, Time::Duration totalTime) override;

File xUnit++.console/main.cpp

 #include "TestAssembly.h"
 #include "XmlReporter.h"
 
-
 int main(int argc, char **argv)
 {
     xUnitpp::Utilities::CommandLine::Options options;

File xUnit++/src/ITestEventSource.cpp

+#include "ITestEventSource.h"
+
+namespace xUnitpp
+{
+
+ITestEventSource::~ITestEventSource()
+{
+}
+
+}

File xUnit++/src/TestCollection.cpp

+#include "TestCollection.h"
 #include <chrono>
 #include <memory>
 #include <string>
 #include <vector>
 #include "ExportApi.h"
 #include "IOutput.h"
-#include "TestCollection.h"
+#include "ITestEventSource.h"
 #include "xUnitTestRunner.h"
 #include "xUnitTime.h"
 
 }
 
 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, std::shared_ptr<Check> check)
+                                   const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line, const std::vector<std::shared_ptr<ITestEventSource>> &testEventSources)
 {
-    collection.mTests.emplace_back(std::make_shared<xUnitTest>(fn, name, suite, attributes, Time::ToDuration(Time::ToMilliseconds(milliseconds)), filename, line, check));
+    collection.mTests.emplace_back(std::make_shared<xUnitTest>(fn, name, suite, attributes, Time::ToDuration(Time::ToMilliseconds(milliseconds)), filename, line, testEventSources));
 }
 
 const std::vector<std::shared_ptr<xUnitTest>> &TestCollection::Tests()

File xUnit++/src/xUnitAssert.cpp

 {
 }
 
-xUnitFailure::xUnitFailure(xUnitAssert assert, std::function<void(const xUnitAssert &)> onFailureComplete)
+xUnitFailure::xUnitFailure(const xUnitAssert &assert, std::function<void(const xUnitAssert &)> onFailureComplete)
     : OnFailureComplete(onFailureComplete)
     , assert(assert)
     , refCount(*(new int(1)))
     return xUnitFailure();
 }
 
+xUnitFailure Assert::OnFailure(xUnitAssert assert) const
+{
+    return xUnitFailure(std::move(assert), handleFailure);
+}
+
 xUnitFailure Assert::OnSuccess() const
 {
     return xUnitFailure::None();
     return OnSuccess();
 }
 
-Assert::Assert(const std::string &callPrefix, std::function<xUnitFailure(xUnitAssert)> onFailure)
+Assert::Assert(const std::string &callPrefix, std::function<void (const xUnitAssert &)> onFailure)
     : callPrefix(callPrefix)
-    , OnFailure(onFailure)
+    , handleFailure(onFailure)
 {
 }
 

File xUnit++/src/xUnitCheck.cpp

 #include "xUnitCheck.h"
+#include "TestEvent.h"
 
 namespace xUnitpp
 {
 
 Check::Check()
-    : Assert("Check.",
-        [&](xUnitAssert assert)
+    : Assert("Check.", [&](const xUnitAssert &assert)
         {
-            return xUnitFailure(assert,
-                [&](const xUnitAssert &assert)
-                {
-                    failedChecks.emplace_back(assert);
-                });
+            sinks[std::this_thread::get_id()](TestEvent(assert));
         })
 {
 }
 
-const std::vector<xUnitAssert> &Check::Failures() const
+void Check::SetSink(std::function<void(TestEvent &&)> sink)
 {
-    return failedChecks;
+    sinks[std::this_thread::get_id()] = sink;
 }
 
 }

File xUnit++/src/xUnitTest.cpp

 #include "xUnitTest.h"
-#include "xUnitCheck.h"
+#include "ITestEventSource.h"
+#include "xUnitAssert.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, std::shared_ptr<Check> check)
-    : mTest(test)
-    , mTestDetails(name, suite, attributes, timeLimit, filename, line)
-    , mCheck(check)
+                     const std::string &filename, int line, const std::vector<std::shared_ptr<ITestEventSource>> &testEventSources)
+    : test(test)
+    , testDetails(name, suite, attributes, timeLimit, filename, line)
+    , failureEventLogged(false)
+    , testEventSources(testEventSources)
 {
 }
 
 const TestDetails &xUnitTest::TestDetails() const
 {
-    return mTestDetails;
+    return testDetails;
 }
 
-void xUnitTest::Run()
+TestResult xUnitTest::Run()
 {
-    return mTest();
+    for (auto &source : testEventSources)
+    {
+        source->SetSink([&](TestEvent &&evt) { AddEvent(std::move(evt)); });
+    }
+
+    testStart = Time::Clock::now();
+
+    try
+    {
+        test();
+    }
+    catch (const xUnitAssert &assert)
+    {
+        AddEvent(TestEvent(assert));
+    }
+    catch (const std::exception &e)
+    {
+        AddEvent(TestEvent(e));
+    }
+    catch (...)
+    {
+        AddEvent(TestEvent(EventLevel::Fatal, "Unknown exception caught: test has crashed."));
+    }
+
+    testStop = Time::Clock::now();
+
+    return failureEventLogged ? TestResult::Failure : TestResult::Success;
 }
 
-const std::vector<xUnitAssert> &xUnitTest::NonFatalFailures() const
+Time::Duration xUnitTest::Duration() const
 {
-    return mCheck->Failures();
+    return Time::ToDuration(testStop - testStart);
+}
+
+void xUnitTest::AddEvent(TestEvent &&evt)
+{
+    std::lock_guard<std::mutex> lock(eventLock);
+
+    testEvents.emplace_back(evt);
+
+    if (evt.IsFailure())
+    {
+        failureEventLogged = true;
+    }
+}
+
+const std::vector<TestEvent> &xUnitTest::TestEvents() const
+{
+    return testEvents;
 }
 
 }

File xUnit++/src/xUnitTestRunner.cpp

         mOutput.get().ReportStart(details);
     }
 
-    virtual void ReportFailure(const xUnitpp::TestDetails &details, const std::string &message, const xUnitpp::LineInfo &lineInfo) override
+    virtual void ReportEvent(const xUnitpp::TestDetails &details, const xUnitpp::TestEvent &evt) override
     {
         std::lock_guard<std::mutex> guard(mLock);
-        mOutput.get().ReportFailure(details, message, lineInfo);
+        mOutput.get().ReportEvent(details, evt);
     }
 
     virtual void ReportSkip(const xUnitpp::TestDetails &details, const std::string &reason) override
     std::reference_wrapper<xUnitpp::IOutput> mOutput;
 };
 
-class AttachedOutput : public xUnitpp::IOutput
+class AttachedOutput
 {
 public:
     AttachedOutput(SharedOutput &output)
         mAttached = false;
     }
 
-    virtual void ReportStart(const xUnitpp::TestDetails &details) override
+    void ReportStart(const xUnitpp::TestDetails &details)
     {
         std::lock_guard<std::mutex> guard(mLock);
 
         }
     }
 
-    virtual void ReportFailure(const xUnitpp::TestDetails &details, const std::string &message, const xUnitpp::LineInfo &lineInfo) override
+    void ReportEvent(const xUnitpp::TestDetails &details, const xUnitpp::TestEvent &evt)
     {
         std::lock_guard<std::mutex> guard(mLock);
 
         if (mAttached)
         {
-            mOutput.get().ReportFailure(details, message, lineInfo);
+            mOutput.get().ReportEvent(details, evt);
         }
     }
 
-    virtual void ReportSkip(const xUnitpp::TestDetails &details, const std::string &reason) override
+    void ReportSkip(const xUnitpp::TestDetails &details, const std::string &reason)
     {
         std::lock_guard<std::mutex> guard(mLock);
 
         }
     }
 
-    virtual void ReportFinish(const xUnitpp::TestDetails &details, xUnitpp::Time::Duration time) override
+    void ReportFinish(const xUnitpp::TestDetails &details, xUnitpp::Time::Duration time)
     {
         std::lock_guard<std::mutex> guard(mLock);
 
         }
     }
 
-    virtual void ReportAllTestsComplete(size_t, size_t, size_t, xUnitpp::Time::Duration) override
+    void ReportAllTestsComplete(size_t, size_t, size_t, xUnitpp::Time::Duration)
     {
         throw std::logic_error("No one holding an AttachedOutput object should be calling ReportAllTestsComplete.");
     }
                 // and abandoned by a timed test. If that were to happen, variables on the stack would get destroyed out from underneath us.
                 // Instead, we're going to make copies that are guaranteed to outlive our method, and return the test status.
                 // If the running thread is still valid, it can manage updating the count of failed threads if necessary.
-                auto actualTest = [](bool reportEnd, std::shared_ptr<xUnitTest> runningTest, std::shared_ptr<AttachedOutput> output)-> std::tuple<Time::TimeStamp, bool>
+                auto actualTest = [](std::shared_ptr<xUnitTest> runningTest, std::shared_ptr<AttachedOutput> output) -> TestResult
                     {
-                        bool failed = false;
-                        Time::TimeStamp testStart;
+                        output->ReportStart(runningTest->TestDetails());
+                        
+                        auto result = runningTest->Run();
 
-                        auto CheckNonFatalErrors = [&]()
+                        for (auto &event : runningTest->TestEvents())
                         {
-                            if (!failed && !runningTest->NonFatalFailures().empty())
-                            {
-                                failed = true;
-                                for (auto &assert : runningTest->NonFatalFailures())
-                                {
-                                    output->ReportFailure(runningTest->TestDetails(), assert.what(), assert.LineInfo());
-                                }
-                            }
-                        };
-
-                        try
-                        {
-                            output->ReportStart(runningTest->TestDetails());
-
-                            testStart = Time::Clock::now();
-                            runningTest->Run();
-                        }
-                        catch (const xUnitAssert &e)
-                        {
-                            CheckNonFatalErrors();
-                            output->ReportFailure(runningTest->TestDetails(), e.what(), e.LineInfo());
-                            failed = true;
-                        }
-                        catch (const std::exception &e)
-                        {
-                            CheckNonFatalErrors();
-                            output->ReportFailure(runningTest->TestDetails(), e.what(), LineInfo::empty());
-                            failed = true;
-                        }
-                        catch (...)
-                        {
-                            CheckNonFatalErrors();
-                            output->ReportFailure(runningTest->TestDetails(), "Unknown exception caught: test has crashed", LineInfo::empty());
-                            failed = true;
+                            output->ReportEvent(runningTest->TestDetails(), event);
                         }
 
-                        CheckNonFatalErrors();
-
-                        if (reportEnd)
-                        {
-                            output->ReportFinish(runningTest->TestDetails(), Time::ToDuration(Time::Clock::now() - testStart));
-                        }
-
-                        return std::make_tuple(testStart, failed);
+                        return result;
                     };
 
                 auto testTimeLimit = test->TestDetails().TimeLimit;
 
                     auto attachedOutput = std::make_shared<AttachedOutput>(sharedOutput);
                     auto threadStarted = std::make_shared<std::condition_variable>();
-                    auto testStart = std::make_shared<Time::TimeStamp>();
-                    auto failed = std::make_shared<bool>();
+                    auto testResult = std::make_shared<TestResult>();
                     std::thread timedRunner([=]()
                         {
                             m->lock();
                             m->unlock();
 
-                            auto result = actualTest(false, test, attachedOutput);
-                            *testStart = std::get<0>(result);
-                            *failed = std::get<1>(result);
+                            *testResult = actualTest(test, attachedOutput);
 
                             threadStarted->notify_all();
                         });
                     if (threadStarted->wait_for(gate, std::chrono::duration_cast<std::chrono::nanoseconds>(testTimeLimit)) == std::cv_status::timeout)
                     {
                         attachedOutput->Detach();
-                        sharedOutput.ReportFailure(test->TestDetails(), "Test failed to complete within " + std::to_string(Time::ToMilliseconds(testTimeLimit).count()) + " milliseconds.", LineInfo::empty());
+                        sharedOutput.ReportEvent(test->TestDetails(), TestEvent(EventLevel::Fatal, "Test failed to complete within " + std::to_string(Time::ToMilliseconds(testTimeLimit).count()) + " milliseconds."));
                         sharedOutput.ReportFinish(test->TestDetails(), testTimeLimit);
                         ++failedTests;
                     }
                     else
                     {
-                        if (*failed)
+                        sharedOutput.ReportFinish(test->TestDetails(), test->Duration());
+
+                        if (*testResult == TestResult::Failure)
                         {
                             ++failedTests;
                         }
-
-                        sharedOutput.ReportFinish(test->TestDetails(), Time::ToDuration(Time::Clock::now() - *testStart));
                     }
                 }
                 else
                 {
-                    auto result = actualTest(true, test, std::make_shared<AttachedOutput>(sharedOutput));
+                    auto result = actualTest(test, std::make_shared<AttachedOutput>(sharedOutput));
 
-                    if (std::get<1>(result))
+                    sharedOutput.ReportFinish(test->TestDetails(), test->Duration());
+
+                    if (result == TestResult::Failure)
                     {
                         ++failedTests;
                     }

File xUnit++/xUnit++.vcxproj

     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\src\TestEvent.cpp" />
     <ClCompile Include="src/IOutput.cpp" />
     <ClCompile Include="src/LineInfo.cpp" />
     <ClCompile Include="src/TestCollection.cpp" />
     <ClCompile Include="src/xUnitAssert.cpp" />
     <ClCompile Include="src/xUnitTest.cpp" />
     <ClCompile Include="src/xUnitTestRunner.cpp" />
+    <ClCompile Include="src\ITestEventSource.cpp" />
     <ClCompile Include="src\xUnitCheck.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="xUnit++/xUnitTest.h" />
     <ClInclude Include="xUnit++/xUnitTestRunner.h" />
     <ClInclude Include="xUnit++/xUnitTime.h" />
+    <ClInclude Include="xUnit++\ITestEventSource.h" />
+    <ClInclude Include="xUnit++\TestEvent.h" />
     <ClInclude Include="xUnit++\xUnitCheck.h" />
   </ItemGroup>
   <PropertyGroup Label="Globals">

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

     <ClCompile Include="src/xUnitTest.cpp" />
     <ClCompile Include="src/IOutput.cpp" />
     <ClCompile Include="src\xUnitCheck.cpp" />
+    <ClCompile Include="..\src\TestEvent.cpp" />
+    <ClCompile Include="src\ITestEventSource.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="xUnit++/xUnitAssert.h" />
     <ClInclude Include="xUnit++/xUnitTest.h" />
     <ClInclude Include="xUnit++/xUnitMacros.h" />
     <ClInclude Include="xUnit++\xUnitCheck.h" />
+    <ClInclude Include="xUnit++\TestEvent.h" />
+    <ClInclude Include="xUnit++\ITestEventSource.h" />
   </ItemGroup>
 </Project>

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

 
 struct LineInfo;
 struct TestDetails;
+class TestEvent;
 
 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 ReportEvent(const TestDetails &testDetails, const TestEvent &evt) = 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; 

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

+#ifndef ITESTEVENTSOURCE_H_
+#define ITESTEVENTSOURCE_H_
+
+#include <functional>
+
+namespace xUnitpp
+{
+
+class TestEvent;
+
+struct ITestEventSource
+{
+    virtual ~ITestEventSource();
+
+    virtual void SetSink(std::function<void(TestEvent &&)> sink) = 0;
+};
+
+}
+
+#endif

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

 #include <chrono>
 #include <functional>
 #include <map>
+#include <memory>
 #include <vector>
 #include "xUnitTest.h"
 
 namespace xUnitpp
 {
 
+struct ITestEventSource;
+
 // !!!VS convert this to an initializer list when VS implements them
 template<typename TTuple>
 static std::function<std::vector<TTuple>()> TheoryData(int count, TTuple tuples[])
 
     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, std::shared_ptr<Check> check);
+            const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line, const std::vector<std::shared_ptr<ITestEventSource>> &testEventSources);
 
         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, std::shared_ptr<Check> check)
+            const AttributeCollection &attributes, int milliseconds, const std::string &filename, int line, const std::vector<std::shared_ptr<ITestEventSource>> &testEventSources)
         {
-            int id = 1;
+            int id = 0;
             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++) + ")";
+                auto theoryName = name + "(" + std::to_string(++id) + ")";
 
                 collection.mTests.emplace_back(std::make_shared<xUnitTest>(TheoryHelper(theory, std::move(t)), theoryName, suite,
-                    attributes, Time::ToDuration(Time::ToMilliseconds(milliseconds)), filename, line, check));
+                    attributes, Time::ToDuration(Time::ToMilliseconds(milliseconds)), filename, line, testEventSources));
             }
         }
     };

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

+#ifndef TESTEVENT_H_
+#define TESTEVENT_H_
+
+#include <exception>
+#include <string>
+#include "LineInfo.h"
+
+namespace xUnitpp
+{
+
+class xUnitAssert;
+
+enum class EventLevel
+{
+    Debug,
+    Info,
+    Warning,
+    Check,
+    Assert,
+    Fatal
+};
+
+class TestEvent
+{
+public:
+    TestEvent(EventLevel level, const std::string &message);
+    TestEvent(const xUnitAssert &assert);
+    TestEvent(const std::exception &e);
+
+    bool IsFailure() const;
+
+    EventLevel Level() const;
+    const xUnitpp::LineInfo &LineInfo() const;
+
+    const std::string &LevelString() const;
+    const std::string &ToString() const;
+
+private:
+    EventLevel level;
+    std::string message;
+    xUnitpp::LineInfo lineInfo;
+};
+
+}
+
+#endif

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

     xUnitFailure();
 
 public:
-    xUnitFailure(xUnitAssert assert, std::function<void(const xUnitAssert &)> onFailureComplete);
+    xUnitFailure(const xUnitAssert &assert, std::function<void(const xUnitAssert &)> onFailureComplete);
     xUnitFailure(const xUnitFailure &other);
     ~xUnitFailure() noexcept(false);
 
         static const bool value = (sizeof(f<T>(nullptr)) == sizeof(char));
     };
 
+    xUnitFailure OnFailure(xUnitAssert assert) const;
+    xUnitFailure OnSuccess() const;
+
     std::string callPrefix;
-    std::function<xUnitFailure(xUnitAssert)> OnFailure;
-
-    xUnitFailure OnSuccess() const;
+    std::function<void (const xUnitAssert &)> handleFailure;
 
 public:
     template<typename TExpected, typename TActual, typename TComparer>
     }
 
     Assert(const std::string &callPrefix = "Assert.",
-           std::function<xUnitFailure(xUnitAssert)> onFailure = [](xUnitAssert assert) { return xUnitFailure(assert, [](const xUnitAssert &assert) { throw assert; }); });
+           std::function<void (const xUnitAssert &)> onFailure = [](const xUnitAssert &assert) { throw assert; });
 };
 
 const class : public Assert

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

 #ifndef XUNITCHECK_H_
 #define XUNITCHECK_H_
 
+#include <functional>
+#include <map>
+#include <memory>
+#include <thread>
+#include "ITestEventSource.h"
 #include "xUnitAssert.h"
 
 namespace xUnitpp
 {
 
-class Check : public Assert
+class Check : public Assert, public ITestEventSource
 {
-    friend class xUnitTest;
-
 public:
     Check();
 
     Check(Check &&) /* = delete */;
     Check &operator =(Check) /* = delete */;
 
-    const std::vector<xUnitAssert> &Failures() const;
+    virtual void SetSink(std::function<void(TestEvent &&)> sink) override;
 
 private:
-    std::vector<xUnitAssert> failedChecks;
+    std::map<std::thread::id, std::function<void(TestEvent &&)>> sinks;
 };
 
 }

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

 #define TIMED_FACT_FIXTURE(FactDetails, FixtureType, timeout) \
     namespace XU_UNIQUE_NS { \
         using xUnitpp::Assert; \
-        std::shared_ptr<xUnitpp::Check> pCheck = std::make_shared<xUnitpp::Check>(); \
+        namespace detail { \
+            /* !!!VS fix when initializer lists are supported */ \
+            std::shared_ptr<xUnitpp::ITestEventSource> eventSources[] = { \
+                std::make_shared<xUnitpp::Check>(), \
+            }; \
+        } \
+        std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> eventSources(std::begin(detail::eventSources), std::end(detail::eventSources)); \
         class XU_UNIQUE_FIXTURE : public FixtureType \
         { \
             /* !!!VS fix when '= delete' is supported */ \
             XU_UNIQUE_FIXTURE &operator =(XU_UNIQUE_FIXTURE) /* = delete */; \
         public: \
-            XU_UNIQUE_FIXTURE() : Check(*pCheck) { } \
+            XU_UNIQUE_FIXTURE() : Check(*static_cast<xUnitpp::Check *>(eventSources[0].get())) { } \
             void XU_UNIQUE_TEST(); \
             const xUnitpp::Check &Check; \
         }; \
         void XU_UNIQUE_RUNNER() { XU_UNIQUE_FIXTURE().XU_UNIQUE_TEST(); } \
         xUnitpp::TestCollection::Register reg(xUnitpp::TestCollection::Instance(), \
             &XU_UNIQUE_RUNNER, FactDetails, xUnitSuite::Name(), \
-            xUnitAttributes::Attributes(), timeout, __FILE__, __LINE__, pCheck); \
+            xUnitAttributes::Attributes(), timeout, __FILE__, __LINE__, eventSources); \
     } \
     void XU_UNIQUE_NS :: XU_UNIQUE_FIXTURE :: XU_UNIQUE_TEST()
 
 #define TIMED_DATA_THEORY(TheoryDetails, params, DataProvider, timeout) \
     namespace XU_UNIQUE_NS { \
         using xUnitpp::Assert; \
-        std::shared_ptr<xUnitpp::Check> pCheck = std::make_shared<xUnitpp::Check>(); \
-        xUnitpp::Check &Check = *pCheck; \
+        namespace detail { \
+            /* !!!VS fix when initializer lists are supported */ \
+            std::shared_ptr<xUnitpp::ITestEventSource> eventSources[] = { \
+                std::make_shared<xUnitpp::Check>(), \
+            }; \
+        } \
+        std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> eventSources(std::begin(detail::eventSources), std::end(detail::eventSources)); \
+        xUnitpp::Check &Check = *static_cast<xUnitpp::Check *>(eventSources[0].get()); \
         void XU_UNIQUE_TEST params; \
         xUnitpp::TestCollection::Register reg(xUnitpp::TestCollection::Instance(), \
             XU_UNIQUE_TEST, DataProvider, TheoryDetails, xUnitSuite::Name(), \
-            xUnitAttributes::Attributes(), timeout, __FILE__, __LINE__, pCheck); \
+            xUnitAttributes::Attributes(), timeout, __FILE__, __LINE__, eventSources); \
     } \
     void XU_UNIQUE_NS :: XU_UNIQUE_TEST params
 
 #define TIMED_THEORY(TheoryDetails, params, timeout, ...) \
     namespace XU_UNIQUE_NS { \
         using xUnitpp::Assert; \
-        std::shared_ptr<xUnitpp::Check> pCheck = std::make_shared<xUnitpp::Check>(); \
-        xUnitpp::Check &Check = *pCheck; \
+        namespace detail { \
+            /* !!!VS fix when initializer lists are supported */ \
+            std::shared_ptr<xUnitpp::ITestEventSource> eventSources[] = { \
+                std::make_shared<xUnitpp::Check>(), \
+            }; \
+        } \
+        std::vector<std::shared_ptr<xUnitpp::ITestEventSource>> eventSources(std::begin(detail::eventSources), std::end(detail::eventSources)); \
+        xUnitpp::Check &Check = *static_cast<xUnitpp::Check *>(eventSources[0].get()); \
         void XU_UNIQUE_TEST params; \
         decltype(FIRST_ARG(__VA_ARGS__)) args[] = { __VA_ARGS__ }; \
         xUnitpp::TestCollection::Register reg(xUnitpp::TestCollection::Instance(), \
             XU_UNIQUE_TEST, xUnitpp::TheoryData(PP_NARGS(__VA_ARGS__), args), TheoryDetails, \
-            xUnitSuite::Name(), xUnitAttributes::Attributes(), timeout, __FILE__, __LINE__, pCheck); \
+            xUnitSuite::Name(), xUnitAttributes::Attributes(), timeout, __FILE__, __LINE__, eventSources); \
     } \
     void XU_UNIQUE_NS :: XU_UNIQUE_TEST params
 
 
 #define LI xUnitpp::LineInfo(__FILE__, __LINE__)
 
-#endif
+#endif

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

 
 #include <functional>
 #include <memory>
+#include <mutex>
 #include <string>
+#include <vector>
 #include "TestDetails.h"
-#include "xUnitCheck.h"
+#include "TestEvent.h"
 
 namespace xUnitpp
 {
 
+class TestEvent;
+struct ITestEventSource;
+
+enum class TestResult
+{
+    Success,
+    Failure
+};
+
 class xUnitTest
 {
 public:
     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, std::shared_ptr<Check> check);
+        const std::string &filename, int line, const std::vector<std::shared_ptr<ITestEventSource>> &testEventSources);
 
     const xUnitpp::TestDetails &TestDetails() const;
 
-    void Run();
+    TestResult Run();
+    Time::Duration Duration() const;
 
-    const std::vector<xUnitAssert> &NonFatalFailures() const;
+    void AddEvent(TestEvent &&evt);
+    const std::vector<TestEvent> &TestEvents() const;
 
 private:
-    xUnitTest(const xUnitTest &other);
-    xUnitTest(xUnitTest &&other);
-    xUnitTest &operator =(xUnitTest other);
+    xUnitTest(const xUnitTest &other) /* = delete */;
+    xUnitTest(xUnitTest &&other) /* = delete */;
+    xUnitTest &operator =(xUnitTest other) /* = delete */;
 
 private:
-    std::function<void()> mTest;
-    xUnitpp::TestDetails mTestDetails;
-    std::shared_ptr<Check> mCheck;
+    std::function<void()> test;
+    xUnitpp::TestDetails testDetails;
+
+    Time::TimeStamp testStart;
+    Time::TimeStamp testStop;
+
+    std::vector<std::shared_ptr<ITestEventSource>> testEventSources;
+
+    std::mutex eventLock;
+    std::vector<TestEvent> testEvents;
+    bool failureEventLogged;
 };
 
 }