1 /* 2 * Created by Phil on 27/11/2013. 3 * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 #ifndef TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED 10 11 #include "../internal/catch_enforce.h" 12 #include "../internal/catch_interfaces_reporter.h" 13 14 #include <algorithm> 15 #include <cstring> 16 #include <cfloat> 17 #include <cstdio> 18 #include <cassert> 19 #include <memory> 20 #include <ostream> 21 22 namespace Catch { 23 void prepareExpandedExpression(AssertionResult& result); 24 25 // Returns double formatted as %.3f (format expected on output) 26 std::string getFormattedDuration( double duration ); 27 28 std::string serializeFilters( std::vector<std::string> const& container ); 29 30 template<typename DerivedT> 31 struct StreamingReporterBase : IStreamingReporter { 32 StreamingReporterBaseCatch::StreamingReporterBase33 StreamingReporterBase( ReporterConfig const& _config ) 34 : m_config( _config.fullConfig() ), 35 stream( _config.stream() ) 36 { 37 m_reporterPrefs.shouldRedirectStdOut = false; 38 if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) 39 CATCH_ERROR( "Verbosity level not supported by this reporter" ); 40 } 41 getPreferencesCatch::StreamingReporterBase42 ReporterPreferences getPreferences() const override { 43 return m_reporterPrefs; 44 } 45 getSupportedVerbositiesCatch::StreamingReporterBase46 static std::set<Verbosity> getSupportedVerbosities() { 47 return { Verbosity::Normal }; 48 } 49 50 ~StreamingReporterBase() override = default; 51 noMatchingTestCasesCatch::StreamingReporterBase52 void noMatchingTestCases(std::string const&) override {} 53 testRunStartingCatch::StreamingReporterBase54 void testRunStarting(TestRunInfo const& _testRunInfo) override { 55 currentTestRunInfo = _testRunInfo; 56 } 57 testGroupStartingCatch::StreamingReporterBase58 void testGroupStarting(GroupInfo const& _groupInfo) override { 59 currentGroupInfo = _groupInfo; 60 } 61 testCaseStartingCatch::StreamingReporterBase62 void testCaseStarting(TestCaseInfo const& _testInfo) override { 63 currentTestCaseInfo = _testInfo; 64 } sectionStartingCatch::StreamingReporterBase65 void sectionStarting(SectionInfo const& _sectionInfo) override { 66 m_sectionStack.push_back(_sectionInfo); 67 } 68 sectionEndedCatch::StreamingReporterBase69 void sectionEnded(SectionStats const& /* _sectionStats */) override { 70 m_sectionStack.pop_back(); 71 } testCaseEndedCatch::StreamingReporterBase72 void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { 73 currentTestCaseInfo.reset(); 74 } testGroupEndedCatch::StreamingReporterBase75 void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { 76 currentGroupInfo.reset(); 77 } testRunEndedCatch::StreamingReporterBase78 void testRunEnded(TestRunStats const& /* _testRunStats */) override { 79 currentTestCaseInfo.reset(); 80 currentGroupInfo.reset(); 81 currentTestRunInfo.reset(); 82 } 83 skipTestCatch::StreamingReporterBase84 void skipTest(TestCaseInfo const&) override { 85 // Don't do anything with this by default. 86 // It can optionally be overridden in the derived class. 87 } 88 89 IConfigPtr m_config; 90 std::ostream& stream; 91 92 LazyStat<TestRunInfo> currentTestRunInfo; 93 LazyStat<GroupInfo> currentGroupInfo; 94 LazyStat<TestCaseInfo> currentTestCaseInfo; 95 96 std::vector<SectionInfo> m_sectionStack; 97 ReporterPreferences m_reporterPrefs; 98 }; 99 100 template<typename DerivedT> 101 struct CumulativeReporterBase : IStreamingReporter { 102 template<typename T, typename ChildNodeT> 103 struct Node { NodeCatch::CumulativeReporterBase::Node104 explicit Node( T const& _value ) : value( _value ) {} ~NodeCatch::CumulativeReporterBase::Node105 virtual ~Node() {} 106 107 using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; 108 T value; 109 ChildNodes children; 110 }; 111 struct SectionNode { SectionNodeCatch::CumulativeReporterBase::SectionNode112 explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} 113 virtual ~SectionNode() = default; 114 operator ==Catch::CumulativeReporterBase::SectionNode115 bool operator == (SectionNode const& other) const { 116 return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; 117 } operator ==Catch::CumulativeReporterBase::SectionNode118 bool operator == (std::shared_ptr<SectionNode> const& other) const { 119 return operator==(*other); 120 } 121 122 SectionStats stats; 123 using ChildSections = std::vector<std::shared_ptr<SectionNode>>; 124 using Assertions = std::vector<AssertionStats>; 125 ChildSections childSections; 126 Assertions assertions; 127 std::string stdOut; 128 std::string stdErr; 129 }; 130 131 struct BySectionInfo { BySectionInfoCatch::CumulativeReporterBase::BySectionInfo132 BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfoCatch::CumulativeReporterBase::BySectionInfo133 BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} operator ()Catch::CumulativeReporterBase::BySectionInfo134 bool operator() (std::shared_ptr<SectionNode> const& node) const { 135 return ((node->stats.sectionInfo.name == m_other.name) && 136 (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); 137 } 138 void operator=(BySectionInfo const&) = delete; 139 140 private: 141 SectionInfo const& m_other; 142 }; 143 144 145 using TestCaseNode = Node<TestCaseStats, SectionNode>; 146 using TestGroupNode = Node<TestGroupStats, TestCaseNode>; 147 using TestRunNode = Node<TestRunStats, TestGroupNode>; 148 CumulativeReporterBaseCatch::CumulativeReporterBase149 CumulativeReporterBase( ReporterConfig const& _config ) 150 : m_config( _config.fullConfig() ), 151 stream( _config.stream() ) 152 { 153 m_reporterPrefs.shouldRedirectStdOut = false; 154 if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) 155 CATCH_ERROR( "Verbosity level not supported by this reporter" ); 156 } 157 ~CumulativeReporterBase() override = default; 158 getPreferencesCatch::CumulativeReporterBase159 ReporterPreferences getPreferences() const override { 160 return m_reporterPrefs; 161 } 162 getSupportedVerbositiesCatch::CumulativeReporterBase163 static std::set<Verbosity> getSupportedVerbosities() { 164 return { Verbosity::Normal }; 165 } 166 testRunStartingCatch::CumulativeReporterBase167 void testRunStarting( TestRunInfo const& ) override {} testGroupStartingCatch::CumulativeReporterBase168 void testGroupStarting( GroupInfo const& ) override {} 169 testCaseStartingCatch::CumulativeReporterBase170 void testCaseStarting( TestCaseInfo const& ) override {} 171 sectionStartingCatch::CumulativeReporterBase172 void sectionStarting( SectionInfo const& sectionInfo ) override { 173 SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); 174 std::shared_ptr<SectionNode> node; 175 if( m_sectionStack.empty() ) { 176 if( !m_rootSection ) 177 m_rootSection = std::make_shared<SectionNode>( incompleteStats ); 178 node = m_rootSection; 179 } 180 else { 181 SectionNode& parentNode = *m_sectionStack.back(); 182 auto it = 183 std::find_if( parentNode.childSections.begin(), 184 parentNode.childSections.end(), 185 BySectionInfo( sectionInfo ) ); 186 if( it == parentNode.childSections.end() ) { 187 node = std::make_shared<SectionNode>( incompleteStats ); 188 parentNode.childSections.push_back( node ); 189 } 190 else 191 node = *it; 192 } 193 m_sectionStack.push_back( node ); 194 m_deepestSection = std::move(node); 195 } 196 assertionStartingCatch::CumulativeReporterBase197 void assertionStarting(AssertionInfo const&) override {} 198 assertionEndedCatch::CumulativeReporterBase199 bool assertionEnded(AssertionStats const& assertionStats) override { 200 assert(!m_sectionStack.empty()); 201 // AssertionResult holds a pointer to a temporary DecomposedExpression, 202 // which getExpandedExpression() calls to build the expression string. 203 // Our section stack copy of the assertionResult will likely outlive the 204 // temporary, so it must be expanded or discarded now to avoid calling 205 // a destroyed object later. 206 prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); 207 SectionNode& sectionNode = *m_sectionStack.back(); 208 sectionNode.assertions.push_back(assertionStats); 209 return true; 210 } sectionEndedCatch::CumulativeReporterBase211 void sectionEnded(SectionStats const& sectionStats) override { 212 assert(!m_sectionStack.empty()); 213 SectionNode& node = *m_sectionStack.back(); 214 node.stats = sectionStats; 215 m_sectionStack.pop_back(); 216 } testCaseEndedCatch::CumulativeReporterBase217 void testCaseEnded(TestCaseStats const& testCaseStats) override { 218 auto node = std::make_shared<TestCaseNode>(testCaseStats); 219 assert(m_sectionStack.size() == 0); 220 node->children.push_back(m_rootSection); 221 m_testCases.push_back(node); 222 m_rootSection.reset(); 223 224 assert(m_deepestSection); 225 m_deepestSection->stdOut = testCaseStats.stdOut; 226 m_deepestSection->stdErr = testCaseStats.stdErr; 227 } testGroupEndedCatch::CumulativeReporterBase228 void testGroupEnded(TestGroupStats const& testGroupStats) override { 229 auto node = std::make_shared<TestGroupNode>(testGroupStats); 230 node->children.swap(m_testCases); 231 m_testGroups.push_back(node); 232 } testRunEndedCatch::CumulativeReporterBase233 void testRunEnded(TestRunStats const& testRunStats) override { 234 auto node = std::make_shared<TestRunNode>(testRunStats); 235 node->children.swap(m_testGroups); 236 m_testRuns.push_back(node); 237 testRunEndedCumulative(); 238 } 239 virtual void testRunEndedCumulative() = 0; 240 skipTestCatch::CumulativeReporterBase241 void skipTest(TestCaseInfo const&) override {} 242 243 IConfigPtr m_config; 244 std::ostream& stream; 245 std::vector<AssertionStats> m_assertions; 246 std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; 247 std::vector<std::shared_ptr<TestCaseNode>> m_testCases; 248 std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; 249 250 std::vector<std::shared_ptr<TestRunNode>> m_testRuns; 251 252 std::shared_ptr<SectionNode> m_rootSection; 253 std::shared_ptr<SectionNode> m_deepestSection; 254 std::vector<std::shared_ptr<SectionNode>> m_sectionStack; 255 ReporterPreferences m_reporterPrefs; 256 }; 257 258 template<char C> getLineOfChars()259 char const* getLineOfChars() { 260 static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; 261 if( !*line ) { 262 std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); 263 line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; 264 } 265 return line; 266 } 267 268 269 struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { 270 TestEventListenerBase( ReporterConfig const& _config ); 271 272 static std::set<Verbosity> getSupportedVerbosities(); 273 274 void assertionStarting(AssertionInfo const&) override; 275 bool assertionEnded(AssertionStats const&) override; 276 }; 277 278 } // end namespace Catch 279 280 #endif // TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED 281