/* * Created by Phil on 27/11/2013. * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include "../internal/catch_enforce.h" #include "../internal/catch_interfaces_reporter.h" #include #include #include #include #include #include namespace Catch { void prepareExpandedExpression(AssertionResult& result); // Returns double formatted as %.3f (format expected on output) std::string getFormattedDuration( double duration ); template struct StreamingReporterBase : IStreamingReporter { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); } ReporterPreferences getPreferences() const override { return m_reporterPrefs; } static std::set getSupportedVerbosities() { return { Verbosity::Normal }; } ~StreamingReporterBase() override = default; void noMatchingTestCases(std::string const&) override {} void testRunStarting(TestRunInfo const& _testRunInfo) override { currentTestRunInfo = _testRunInfo; } void testGroupStarting(GroupInfo const& _groupInfo) override { currentGroupInfo = _groupInfo; } void testCaseStarting(TestCaseInfo const& _testInfo) override { currentTestCaseInfo = _testInfo; } void sectionStarting(SectionInfo const& _sectionInfo) override { m_sectionStack.push_back(_sectionInfo); } void sectionEnded(SectionStats const& /* _sectionStats */) override { m_sectionStack.pop_back(); } void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { currentTestCaseInfo.reset(); } void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { currentGroupInfo.reset(); } void testRunEnded(TestRunStats const& /* _testRunStats */) override { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } void skipTest(TestCaseInfo const&) override { // Don't do anything with this by default. // It can optionally be overridden in the derived class. } IConfigPtr m_config; std::ostream& stream; LazyStat currentTestRunInfo; LazyStat currentGroupInfo; LazyStat currentTestCaseInfo; std::vector m_sectionStack; ReporterPreferences m_reporterPrefs; }; template struct CumulativeReporterBase : IStreamingReporter { template struct Node { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} using ChildNodes = std::vector>; T value; ChildNodes children; }; struct SectionNode { explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} virtual ~SectionNode() = default; bool operator == (SectionNode const& other) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } bool operator == (std::shared_ptr const& other) const { return operator==(*other); } SectionStats stats; using ChildSections = std::vector>; using Assertions = std::vector; ChildSections childSections; Assertions assertions; std::string stdOut; std::string stdErr; }; struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() (std::shared_ptr const& node) const { return ((node->stats.sectionInfo.name == m_other.name) && (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } void operator=(BySectionInfo const&) = delete; private: SectionInfo const& m_other; }; using TestCaseNode = Node; using TestGroupNode = Node; using TestRunNode = Node; CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); } ~CumulativeReporterBase() override = default; ReporterPreferences getPreferences() const override { return m_reporterPrefs; } static std::set getSupportedVerbosities() { return { Verbosity::Normal }; } void testRunStarting( TestRunInfo const& ) override {} void testGroupStarting( GroupInfo const& ) override {} void testCaseStarting( TestCaseInfo const& ) override {} void sectionStarting( SectionInfo const& sectionInfo ) override { SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); std::shared_ptr node; if( m_sectionStack.empty() ) { if( !m_rootSection ) m_rootSection = std::make_shared( incompleteStats ); node = m_rootSection; } else { SectionNode& parentNode = *m_sectionStack.back(); typename SectionNode::ChildSections::const_iterator it = std::find_if( parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo( sectionInfo ) ); if( it == parentNode.childSections.end() ) { node = std::make_shared( incompleteStats ); parentNode.childSections.push_back( node ); } else node = *it; } m_sectionStack.push_back( node ); m_deepestSection = std::move(node); } void assertionStarting(AssertionInfo const&) override {} bool assertionEnded(AssertionStats const& assertionStats) override { assert(!m_sectionStack.empty()); // AssertionResult holds a pointer to a temporary DecomposedExpression, // which getExpandedExpression() calls to build the expression string. // Our section stack copy of the assertionResult will likely outlive the // temporary, so it must be expanded or discarded now to avoid calling // a destroyed object later. prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back(assertionStats); return true; } void sectionEnded(SectionStats const& sectionStats) override { assert(!m_sectionStack.empty()); SectionNode& node = *m_sectionStack.back(); node.stats = sectionStats; m_sectionStack.pop_back(); } void testCaseEnded(TestCaseStats const& testCaseStats) override { auto node = std::make_shared(testCaseStats); assert(m_sectionStack.size() == 0); node->children.push_back(m_rootSection); m_testCases.push_back(node); m_rootSection.reset(); assert(m_deepestSection); m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } void testGroupEnded(TestGroupStats const& testGroupStats) override { auto node = std::make_shared(testGroupStats); node->children.swap(m_testCases); m_testGroups.push_back(node); } void testRunEnded(TestRunStats const& testRunStats) override { auto node = std::make_shared(testRunStats); node->children.swap(m_testGroups); m_testRuns.push_back(node); testRunEndedCumulative(); } virtual void testRunEndedCumulative() = 0; void skipTest(TestCaseInfo const&) override {} IConfigPtr m_config; std::ostream& stream; std::vector m_assertions; std::vector>> m_sections; std::vector> m_testCases; std::vector> m_testGroups; std::vector> m_testRuns; std::shared_ptr m_rootSection; std::shared_ptr m_deepestSection; std::vector> m_sectionStack; ReporterPreferences m_reporterPrefs; }; template char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; } inline char const* getBoxCharsAcross() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { std::memset( line, '-', CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; line[0] = '+'; line[CATCH_CONFIG_CONSOLE_WIDTH-2] = '+'; } return line; } struct TestEventListenerBase : StreamingReporterBase { TestEventListenerBase( ReporterConfig const& _config ); void assertionStarting(AssertionInfo const&) override; bool assertionEnded(AssertionStats const&) override; }; } // end namespace Catch #endif // TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED