1. Matt Oswald
  2. xUnit++

Commits

Matt Oswald  committed cdf872e

added nicer output for test events

  • Participants
  • Parent commits b43aa23
  • Branches default

Comments (0)

Files changed (18)

File src/TestEvent.cpp

View file
  • Ignore whitespace
     return evt.message;
 }
 
-TestEvent::TestEvent(EventLevel level, const std::string &message)
+TestEvent::TestEvent(EventLevel level, const std::string &message, const xUnitpp::LineInfo &lineInfo)
     : level(level)
     , message(message)
-    , lineInfo(LineInfo::empty())
+    , lineInfo(lineInfo)
 {
 }
 

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

View file
  • Ignore whitespace
                     {
                         TestCase ^testCase = gcnew TestCase(marshal_as<String ^>(td.ShortName), uri, marshal_as<String ^>(source));
                         testCase->DisplayName = marshal_as<String ^>(td.ShortName);
-                        testCase->CodeFilePath = marshal_as<String ^>(td.Filename);
-                        testCase->LineNumber = td.Line;
+                        testCase->CodeFilePath = marshal_as<String ^>(td.LineInfo.file);
+                        testCase->LineNumber = td.LineInfo.line;
 
                         dict->Add(marshal_as<String ^>(td.ShortName), testCase);
                     }

File xUnit++.console/ConsoleReporter.cpp

View file
  • Ignore whitespace
+#include "ConsoleReporter.h"
+#include <chrono>
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include "xUnit++/LineInfo.h"
+#include "xUnit++/TestDetails.h"
+#include "xUnit++/TestEvent.h"
+
+
+namespace
+{
+    std::string FileAndLine(const xUnitpp::TestDetails &td, const xUnitpp::LineInfo &lineInfo)
+    {
+        auto result = to_string(lineInfo);
+        if (result.empty())
+        {
+            result = to_string(td.LineInfo);
+        }
+
+        return result;
+    }
+
+    class CachedOutput
+    {
+        typedef std::map<int, std::shared_ptr<CachedOutput>> OutputCache;
+
+    public:
+        CachedOutput(const std::string &name)
+            : name(name)
+        {
+        }
+
+        ~CachedOutput()
+        {
+            if (!messages.empty())
+            {
+                std::cout << "\n[" << name << "]\n";
+
+                for (const auto &msg : messages)
+                {
+                    std::cout << msg << std::endl;
+                }
+            }
+        }
+
+        static void Instant(const std::string &output)
+        {
+            std::cout << output;
+        }
+
+        static CachedOutput &Cache(const xUnitpp::TestDetails &td)
+        {
+            auto &cache = Cache();
+
+            auto it = cache.find(td.Id);
+            if (it == cache.end())
+            {
+                cache.insert(std::make_pair(td.Id, std::make_shared<CachedOutput>(td.Name)));
+            }
+
+            return *cache[td.Id];
+        }
+
+        static void Finish(const xUnitpp::TestDetails &td)
+        {
+            auto &cache = Cache();
+
+            auto it = cache.find(td.Id);
+            if (it != cache.end())
+            {
+                cache.erase(it);
+            }
+        }
+
+        CachedOutput &operator <<(const std::string &output)
+        {
+            messages.push_back(output);
+            return *this;
+        }
+
+    private:
+        static OutputCache &Cache()
+        {
+            static OutputCache cache;
+            return cache;
+        }
+
+        std::string name;
+        std::vector<std::string> messages;
+    };
+}
+
+namespace xUnitpp
+{
+
+ConsoleReporter::ConsoleReporter(bool verbose, bool veryVerbose)
+    : mVerbose(verbose)
+    , mVeryVerbose(veryVerbose)
+{
+}
+
+void ConsoleReporter::ReportStart(const TestDetails &testDetails)
+{
+    if (mVeryVerbose)
+    {
+        CachedOutput::Instant("Starting test " + testDetails.Name + ".");
+    }
+}
+
+void ConsoleReporter::ReportEvent(const TestDetails &testDetails, const TestEvent &evt)
+{
+    CachedOutput::Cache(testDetails) << (FileAndLine(testDetails, evt.LineInfo()) +
+        ": " + to_string(evt.Level()) + ": " + to_string(evt));
+}
+
+void ConsoleReporter::ReportSkip(const TestDetails &testDetails, const std::string &reason)
+{
+    CachedOutput::Instant(FileAndLine(testDetails, LineInfo::empty()) +
+        ": skipping " + testDetails.Name + ": " + reason);
+}
+
+void ConsoleReporter::ReportFinish(const TestDetails &testDetails, Time::Duration timeTaken)
+{
+    if (mVerbose)
+    {
+        auto ms = Time::ToMilliseconds(timeTaken);
+        CachedOutput::Cache(testDetails) << (testDetails.Name + ": Completed in " +
+            (ms.count() == 0 ? (std::to_string(timeTaken.count()) + " nanoseconds.\n") : (std::to_string(ms.count()) + " milliseconds.\n")));
+    }
+
+    CachedOutput::Finish(testDetails);
+}
+
+void ConsoleReporter::ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failureCount, Time::Duration totalTime)
+{
+    std::string total = std::to_string(testCount) + " tests, ";
+    std::string failures = std::to_string(failureCount) + " failed, ";
+    std::string skips = std::to_string(skipped) + " skipped.";
+
+    std::string header;
+
+    if (failureCount > 0)
+    {
+        header = "\nFAILURE: ";
+    }
+    else if (skipped > 0)
+    {
+        header = "\nWARNING: ";
+    }
+    else
+    {
+        header = "\nSuccess: ";
+    }
+
+    CachedOutput::Instant(header + total + failures + skips);
+
+    header = "Test time: ";
+
+    auto ms = Time::ToMilliseconds(totalTime);
+
+    if (ms.count() > 500)
+    {
+        CachedOutput::Instant(header + std::to_string(Time::ToSeconds(totalTime).count()) + " seconds.");
+    }
+    else
+    {
+        CachedOutput::Instant(header + std::to_string(ms.count()) + " milliseconds.");
+    }
+}
+
+}

File xUnit++.console/ConsoleReporter.h

View file
  • Ignore whitespace
+#ifndef CONSOLEREPORTER_H_
+#define CONSOLEREPORTER_H_
+
+#include "xUnit++/IOutput.h"
+
+namespace xUnitpp
+{
+
+class ConsoleReporter : public IOutput
+{
+public:
+    ConsoleReporter(bool verbose, bool veryVerbose);
+
+    virtual void ReportStart(const TestDetails &) 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;
+
+private:
+    bool mVerbose;
+    bool mVeryVerbose;
+};
+
+}
+
+#endif

File xUnit++.console/StdOutReporter.cpp

  • Ignore whitespace
-#include "StdOutReporter.h"
-#include <chrono>
-#include <cstdio>
-#include <iostream>
-#include "xUnit++/LineInfo.h"
-#include "xUnit++/TestDetails.h"
-#include "xUnit++/TestEvent.h"
-
-namespace
-{
-    std::string FileAndLine(const xUnitpp::TestDetails &td, const xUnitpp::LineInfo &lineInfo)
-    {
-        auto file = lineInfo.file.empty() ? td.Filename : lineInfo.file;
-        auto line = lineInfo.file.empty() ? td.Line : lineInfo.line;
-
-        return file + "(" + std::to_string(line) + ")";
-    }
-}
-
-namespace xUnitpp
-{
-
-StdOutReporter::StdOutReporter(bool verbose, bool veryVerbose)
-    : mVerbose(verbose)
-    , mVeryVerbose(veryVerbose)
-{
-}
-
-void StdOutReporter::ReportStart(const TestDetails &testDetails)
-{
-    if (mVeryVerbose)
-    {
-        std::cout << ("Starting test " + testDetails.Name + ".\n");
-    }
-}
-
-void StdOutReporter::ReportEvent(const TestDetails &testDetails, const TestEvent &evt)
-{
-    std::cout << (FileAndLine(testDetails, evt.LineInfo()) +
-        ": " + testDetails.Name+ ": " + to_string(evt.Level()) + ": " + to_string(evt) + "\n");
-}
-
-void StdOutReporter::ReportSkip(const TestDetails &testDetails, const std::string &reason)
-{
-    std::cout << (FileAndLine(testDetails, LineInfo::empty()) +
-        ": skipping " + testDetails.Name + ": " + reason + "\n");
-}
-
-void StdOutReporter::ReportFinish(const TestDetails &testDetails, Time::Duration timeTaken)
-{
-    if (mVerbose)
-    {
-        auto ms = Time::ToMilliseconds(timeTaken);
-        std::cout << (testDetails.Name + ": Completed in " + (ms.count() == 0 ? (std::to_string(timeTaken.count()) + " nanoseconds.\n") : (std::to_string(ms.count()) + " milliseconds.\n")));
-    }
-}
-
-void StdOutReporter::ReportAllTestsComplete(size_t testCount, size_t skipped, size_t failureCount, Time::Duration totalTime)
-{
-    std::string total = std::to_string(testCount) + " tests, ";
-    std::string failures = std::to_string(failureCount) + " failed, ";
-    std::string skips = std::to_string(skipped) + " skipped.\n";
-
-    std::string header;
-
-    if (failureCount > 0)
-    {
-        header = "\nFAILURE: ";
-    }
-    else if (skipped > 0)
-    {
-        header = "\nWARNING: ";
-    }
-    else
-    {
-        header = "\nSuccess: ";
-    }
-
-    std::cout << (header + total + failures + skips);
-
-    header = "Test time: ";
-
-    auto ms = Time::ToMilliseconds(totalTime);
-
-    if (ms.count() > 500)
-    {
-        std::cout << (header + std::to_string(Time::ToSeconds(totalTime).count()) + " seconds.\n");
-    }
-    else
-    {
-        std::cout << (header + std::to_string(ms.count()) + " milliseconds.\n");
-    }
-}
-
-}

File xUnit++.console/StdOutReporter.h

  • Ignore whitespace
-#ifndef STDOUTREPORTER_H_
-#define STDOUTREPORTER_H_
-
-#include "xUnit++/IOutput.h"
-
-namespace xUnitpp
-{
-
-class StdOutReporter : public IOutput
-{
-public:
-    StdOutReporter(bool verbose, bool veryVerbose);
-
-    virtual void ReportStart(const TestDetails &) 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;
-
-private:
-    bool mVerbose;
-    bool mVeryVerbose;
-};
-
-}
-
-#endif

File xUnit++.console/XmlReporter.cpp

View file
  • Ignore whitespace
             " />\n";
     }
 
-    std::string XmlTestFailed(const std::string &file, int line, const std::vector<std::string> &messages)
+    std::string XmlTestFailed(const std::string &fileAndLine, const std::vector<std::string> &messages)
     {
         std::string result;
         for (const auto &message : messages)
         {
             result += std::string("         ") +
                 "<failure" +
-                    XmlAttribute("message", file + "(" + std::to_string(line) + "): " + XmlEscape(message)) +
+                    XmlAttribute("message", fileAndLine + ": " + XmlEscape(message)) +
                 "</failure>\n";
         }
 
 
                     if (test.status == TestResult::Failure)
                     {
-                        stream << XmlTestFailed(test.testDetails.Filename, test.testDetails.Line, test.messages);
+                        stream << XmlTestFailed(to_string(test.testDetails.LineInfo), test.messages);
                     }
                     else if (test.status == TestResult::Skipped)
                     {

File xUnit++.console/main.cpp

View file
  • Ignore whitespace
 #include "xUnit++/ExportApi.h"
 #include "xUnit++/TestDetails.h"
 #include "CommandLine.h"
-#include "StdOutReporter.h"
+#include "ConsoleReporter.h"
 #include "TestAssembly.h"
 #include "XmlReporter.h"
 
             std::sort(activeTestIds.begin(), activeTestIds.end());
 
             std::unique_ptr<xUnitpp::IOutput> reporter(options.xmlOutput.empty() ?
-                (xUnitpp::IOutput *)new xUnitpp::StdOutReporter(options.verbose, options.veryVerbose) :
+                (xUnitpp::IOutput *)new xUnitpp::ConsoleReporter(options.verbose, options.veryVerbose) :
                 (xUnitpp::IOutput *)new xUnitpp::XmlReporter(options.xmlOutput));
             totalFailures += testAssembly.FilteredTestsRunner(options.timeLimit, options.threadLimit, *reporter,
                 [&](const xUnitpp::TestDetails &testDetails)

File xUnit++.console/xUnit++.console.vcxproj

View file
  • Ignore whitespace
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="CommandLine.cpp" />
+    <ClCompile Include="ConsoleReporter.cpp" />
     <ClCompile Include="main.cpp" />
-    <ClCompile Include="StdOutReporter.cpp" />
     <ClCompile Include="TestAssembly.cpp" />
     <ClCompile Include="XmlReporter.cpp" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="CommandLine.h" />
-    <ClInclude Include="StdOutReporter.h" />
+    <ClInclude Include="ConsoleReporter.h" />
     <ClInclude Include="TestAssembly.h" />
     <ClInclude Include="XmlReporter.h" />
   </ItemGroup>

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

View file
  • Ignore whitespace
   <ItemGroup>
     <ClCompile Include="main.cpp" />
     <ClCompile Include="CommandLine.cpp" />
-    <ClCompile Include="StdOutReporter.cpp" />
     <ClCompile Include="XmlReporter.cpp" />
     <ClCompile Include="TestAssembly.cpp" />
+    <ClCompile Include="ConsoleReporter.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="CommandLine.h" />
-    <ClInclude Include="StdOutReporter.h" />
     <ClInclude Include="XmlReporter.h" />
     <ClInclude Include="TestAssembly.h" />
+    <ClInclude Include="ConsoleReporter.h" />
   </ItemGroup>
 </Project>

File xUnit++/src/LineInfo.cpp

View file
  • Ignore whitespace
     return empty;
 }
 
+std::string to_string(const LineInfo &lineInfo)
+{
+    if (lineInfo.file.empty())
+    {
+        return std::string();
+    }
+
+    return lineInfo.file + "(" + std::to_string(lineInfo.line) + ")";
 }
+
+}

File xUnit++/src/TestDetails.cpp

View file
  • Ignore whitespace
 {
 
 TestDetails::TestDetails()
+    : LineInfo(xUnitpp::LineInfo::empty())
 {
 }
 
     , Suite(suite)
     , Attributes(attributes)
     , TimeLimit(timeLimit)
-    , Filename(filename)
-    , Line(line)
+    , LineInfo(filename, line)
 {
 }
 
     , Suite(other.Suite)
     , Attributes(other.Attributes)
     , TimeLimit(other.TimeLimit)
-    , Filename(other.Filename)
-    , Line(other.Line)
+    , LineInfo(other.LineInfo)
 {
 }
 
 TestDetails::TestDetails(TestDetails &&other)
+    : LineInfo(xUnitpp::LineInfo::empty())
 {
     swap(*this, other);
 }
     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);
+    swap(td0.LineInfo, td1.LineInfo);
 }
 
 }

File xUnit++/src/xUnitAssert.cpp

View file
  • Ignore whitespace
             msg += "\n       Actual: " + actual;
         }
 
-        msg += "\n";
-
         return msg;
     }
 }

File xUnit++/src/xUnitLog.cpp

View file
  • Ignore whitespace
 namespace xUnitpp
 {
 
-Log::Logger::Message::Message(std::function<void(const std::string &)> recordMessage)
+Log::Logger::Message::Message(std::function<void(const std::string &, const LineInfo &)> recordMessage, const LineInfo &lineInfo)
     : refCount(*(new size_t(1)))
     , recordMessage(recordMessage)
+    , lineInfo(lineInfo)
 {
 }
 
     : refCount(other.refCount)
     , recordMessage(std::move(other.recordMessage))
     , message(other.message.str())
+    , lineInfo(other.lineInfo)
 {
     ++refCount;
 }
     if (--refCount == 0)
     {
         delete &refCount;
-        recordMessage(message.str());
+        recordMessage(message.str(), lineInfo);
     }
 }
 
-Log::Logger::Logger(std::function<void(const std::string &)> recordMessage)
+Log::Logger::Logger(std::function<void(const std::string &, const LineInfo &)> recordMessage)
     : recordMessage(recordMessage)
 {
 }
 
+Log::Logger::Message Log::Logger::operator()(const LineInfo &lineInfo) const
+{
+    return Message(recordMessage, lineInfo);
+}
+
 Log::Log(const TestEventRecorder &recorder)
-    : Debug([&](const std::string &msg) { recorder(TestEvent(EventLevel::Debug, msg)); })
-    , Info([&](const std::string &msg) { recorder(TestEvent(EventLevel::Info, msg)); })
-    , Warn([&](const std::string &msg) { recorder(TestEvent(EventLevel::Warning, msg)); })
+    : Debug([&](const std::string &msg, const LineInfo &lineInfo) { recorder(TestEvent(EventLevel::Debug, msg, lineInfo)); })
+    , Info([&](const std::string &msg, const LineInfo &lineInfo) { recorder(TestEvent(EventLevel::Info, msg, lineInfo)); })
+    , Warn([&](const std::string &msg, const LineInfo &lineInfo) { recorder(TestEvent(EventLevel::Warning, msg, lineInfo)); })
 {
 }
 

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

View file
  • Ignore whitespace
     std::string file;
     int line;
 
+    friend std::string to_string(const LineInfo &lineInfo);
+
 private:
     LineInfo();
 };

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

View file
  • Ignore whitespace
 #include <string>
 #include <tuple>
 #include <vector>
+#include "LineInfo.h"
 #include "xUnitTime.h"
 
 namespace xUnitpp
     std::string Suite;
     AttributeCollection Attributes;
     Time::Duration TimeLimit;
-    std::string Filename;
-    int Line;
+    xUnitpp::LineInfo LineInfo;
 };
 
 }

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

View file
  • Ignore whitespace
 class TestEvent
 {
 public:
-    TestEvent(EventLevel level, const std::string &message);
+    TestEvent(EventLevel level, const std::string &message, const LineInfo &lineInfo = LineInfo::empty());
     TestEvent(EventLevel level, const xUnitAssert &assert);
     TestEvent(const std::exception &e);
 

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

View file
  • Ignore whitespace
         class Message
         {
         public:
-            Message(std::function<void(const std::string &)> recordMessage);
+            Message(std::function<void(const std::string &, const LineInfo &)> recordMessage, const LineInfo &lineInfo = LineInfo::empty());
             Message(const Message &other);
             ~Message();
 
 
         private:
             size_t &refCount;
-            std::function<void(const std::string &)> recordMessage;
+            std::function<void(const std::string &, const LineInfo &)> recordMessage;
             std::stringstream message;
+            LineInfo lineInfo;
         };
     public:
-        Logger(std::function<void(const std::string &)> recordMessage);
+        Logger(std::function<void(const std::string &, const LineInfo &)> recordMessage);
 
         template<typename T>
         Message operator <<(const T &value) const
             return (Message(recordMessage) << value);
         }
 
+        Message operator()(const LineInfo &lineInfo) const;
+
     private:
-        std::function<void(const std::string &)> recordMessage;
+        std::function<void(const std::string &, const LineInfo &)> recordMessage;
     };
 
 public: