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 template<typename DerivedT> 29 struct StreamingReporterBase : IStreamingReporter { 30 StreamingReporterBaseCatch::StreamingReporterBase31 StreamingReporterBase( ReporterConfig const& _config ) 32 : m_config( _config.fullConfig() ), 33 stream( _config.stream() ) 34 { 35 m_reporterPrefs.shouldRedirectStdOut = false; 36 if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) 37 CATCH_ERROR( "Verbosity level not supported by this reporter" ); 38 } 39 getPreferencesCatch::StreamingReporterBase40 ReporterPreferences getPreferences() const override { 41 return m_reporterPrefs; 42 } 43 getSupportedVerbositiesCatch::StreamingReporterBase44 static std::set<Verbosity> getSupportedVerbosities() { 45 return { Verbosity::Normal }; 46 } 47 48 ~StreamingReporterBase() override = default; 49 noMatchingTestCasesCatch::StreamingReporterBase50 void noMatchingTestCases(std::string const&) override {} 51 testRunStartingCatch::StreamingReporterBase52 void testRunStarting(TestRunInfo const& _testRunInfo) override { 53 currentTestRunInfo = _testRunInfo; 54 } testGroupStartingCatch::StreamingReporterBase55 void testGroupStarting(GroupInfo const& _groupInfo) override { 56 currentGroupInfo = _groupInfo; 57 } 58 testCaseStartingCatch::StreamingReporterBase59 void testCaseStarting(TestCaseInfo const& _testInfo) override { 60 currentTestCaseInfo = _testInfo; 61 } sectionStartingCatch::StreamingReporterBase62 void sectionStarting(SectionInfo const& _sectionInfo) override { 63 m_sectionStack.push_back(_sectionInfo); 64 } 65 sectionEndedCatch::StreamingReporterBase66 void sectionEnded(SectionStats const& /* _sectionStats */) override { 67 m_sectionStack.pop_back(); 68 } testCaseEndedCatch::StreamingReporterBase69 void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { 70 currentTestCaseInfo.reset(); 71 } testGroupEndedCatch::StreamingReporterBase72 void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { 73 currentGroupInfo.reset(); 74 } testRunEndedCatch::StreamingReporterBase75 void testRunEnded(TestRunStats const& /* _testRunStats */) override { 76 currentTestCaseInfo.reset(); 77 currentGroupInfo.reset(); 78 currentTestRunInfo.reset(); 79 } 80 skipTestCatch::StreamingReporterBase81 void skipTest(TestCaseInfo const&) override { 82 // Don't do anything with this by default. 83 // It can optionally be overridden in the derived class. 84 } 85 86 IConfigPtr m_config; 87 std::ostream& stream; 88 89 LazyStat<TestRunInfo> currentTestRunInfo; 90 LazyStat<GroupInfo> currentGroupInfo; 91 LazyStat<TestCaseInfo> currentTestCaseInfo; 92 93 std::vector<SectionInfo> m_sectionStack; 94 ReporterPreferences m_reporterPrefs; 95 }; 96 97 template<typename DerivedT> 98 struct CumulativeReporterBase : IStreamingReporter { 99 template<typename T, typename ChildNodeT> 100 struct Node { NodeCatch::CumulativeReporterBase::Node101 explicit Node( T const& _value ) : value( _value ) {} ~NodeCatch::CumulativeReporterBase::Node102 virtual ~Node() {} 103 104 using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; 105 T value; 106 ChildNodes children; 107 }; 108 struct SectionNode { SectionNodeCatch::CumulativeReporterBase::SectionNode109 explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} 110 virtual ~SectionNode() = default; 111 operator ==Catch::CumulativeReporterBase::SectionNode112 bool operator == (SectionNode const& other) const { 113 return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; 114 } operator ==Catch::CumulativeReporterBase::SectionNode115 bool operator == (std::shared_ptr<SectionNode> const& other) const { 116 return operator==(*other); 117 } 118 119 SectionStats stats; 120 using ChildSections = std::vector<std::shared_ptr<SectionNode>>; 121 using Assertions = std::vector<AssertionStats>; 122 ChildSections childSections; 123 Assertions assertions; 124 std::string stdOut; 125 std::string stdErr; 126 }; 127 128 struct BySectionInfo { BySectionInfoCatch::CumulativeReporterBase::BySectionInfo129 BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfoCatch::CumulativeReporterBase::BySectionInfo130 BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} operator ()Catch::CumulativeReporterBase::BySectionInfo131 bool operator() (std::shared_ptr<SectionNode> const& node) const { 132 return ((node->stats.sectionInfo.name == m_other.name) && 133 (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); 134 } 135 void operator=(BySectionInfo const&) = delete; 136 137 private: 138 SectionInfo const& m_other; 139 }; 140 141 142 using TestCaseNode = Node<TestCaseStats, SectionNode>; 143 using TestGroupNode = Node<TestGroupStats, TestCaseNode>; 144 using TestRunNode = Node<TestRunStats, TestGroupNode>; 145 CumulativeReporterBaseCatch::CumulativeReporterBase146 CumulativeReporterBase( ReporterConfig const& _config ) 147 : m_config( _config.fullConfig() ), 148 stream( _config.stream() ) 149 { 150 m_reporterPrefs.shouldRedirectStdOut = false; 151 if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) 152 CATCH_ERROR( "Verbosity level not supported by this reporter" ); 153 } 154 ~CumulativeReporterBase() override = default; 155 getPreferencesCatch::CumulativeReporterBase156 ReporterPreferences getPreferences() const override { 157 return m_reporterPrefs; 158 } 159 getSupportedVerbositiesCatch::CumulativeReporterBase160 static std::set<Verbosity> getSupportedVerbosities() { 161 return { Verbosity::Normal }; 162 } 163 testRunStartingCatch::CumulativeReporterBase164 void testRunStarting( TestRunInfo const& ) override {} testGroupStartingCatch::CumulativeReporterBase165 void testGroupStarting( GroupInfo const& ) override {} 166 testCaseStartingCatch::CumulativeReporterBase167 void testCaseStarting( TestCaseInfo const& ) override {} 168 sectionStartingCatch::CumulativeReporterBase169 void sectionStarting( SectionInfo const& sectionInfo ) override { 170 SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); 171 std::shared_ptr<SectionNode> node; 172 if( m_sectionStack.empty() ) { 173 if( !m_rootSection ) 174 m_rootSection = std::make_shared<SectionNode>( incompleteStats ); 175 node = m_rootSection; 176 } 177 else { 178 SectionNode& parentNode = *m_sectionStack.back(); 179 auto it = 180 std::find_if( parentNode.childSections.begin(), 181 parentNode.childSections.end(), 182 BySectionInfo( sectionInfo ) ); 183 if( it == parentNode.childSections.end() ) { 184 node = std::make_shared<SectionNode>( incompleteStats ); 185 parentNode.childSections.push_back( node ); 186 } 187 else 188 node = *it; 189 } 190 m_sectionStack.push_back( node ); 191 m_deepestSection = std::move(node); 192 } 193 assertionStartingCatch::CumulativeReporterBase194 void assertionStarting(AssertionInfo const&) override {} 195 assertionEndedCatch::CumulativeReporterBase196 bool assertionEnded(AssertionStats const& assertionStats) override { 197 assert(!m_sectionStack.empty()); 198 // AssertionResult holds a pointer to a temporary DecomposedExpression, 199 // which getExpandedExpression() calls to build the expression string. 200 // Our section stack copy of the assertionResult will likely outlive the 201 // temporary, so it must be expanded or discarded now to avoid calling 202 // a destroyed object later. 203 prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); 204 SectionNode& sectionNode = *m_sectionStack.back(); 205 sectionNode.assertions.push_back(assertionStats); 206 return true; 207 } sectionEndedCatch::CumulativeReporterBase208 void sectionEnded(SectionStats const& sectionStats) override { 209 assert(!m_sectionStack.empty()); 210 SectionNode& node = *m_sectionStack.back(); 211 node.stats = sectionStats; 212 m_sectionStack.pop_back(); 213 } testCaseEndedCatch::CumulativeReporterBase214 void testCaseEnded(TestCaseStats const& testCaseStats) override { 215 auto node = std::make_shared<TestCaseNode>(testCaseStats); 216 assert(m_sectionStack.size() == 0); 217 node->children.push_back(m_rootSection); 218 m_testCases.push_back(node); 219 m_rootSection.reset(); 220 221 assert(m_deepestSection); 222 m_deepestSection->stdOut = testCaseStats.stdOut; 223 m_deepestSection->stdErr = testCaseStats.stdErr; 224 } testGroupEndedCatch::CumulativeReporterBase225 void testGroupEnded(TestGroupStats const& testGroupStats) override { 226 auto node = std::make_shared<TestGroupNode>(testGroupStats); 227 node->children.swap(m_testCases); 228 m_testGroups.push_back(node); 229 } testRunEndedCatch::CumulativeReporterBase230 void testRunEnded(TestRunStats const& testRunStats) override { 231 auto node = std::make_shared<TestRunNode>(testRunStats); 232 node->children.swap(m_testGroups); 233 m_testRuns.push_back(node); 234 testRunEndedCumulative(); 235 } 236 virtual void testRunEndedCumulative() = 0; 237 skipTestCatch::CumulativeReporterBase238 void skipTest(TestCaseInfo const&) override {} 239 240 IConfigPtr m_config; 241 std::ostream& stream; 242 std::vector<AssertionStats> m_assertions; 243 std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; 244 std::vector<std::shared_ptr<TestCaseNode>> m_testCases; 245 std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; 246 247 std::vector<std::shared_ptr<TestRunNode>> m_testRuns; 248 249 std::shared_ptr<SectionNode> m_rootSection; 250 std::shared_ptr<SectionNode> m_deepestSection; 251 std::vector<std::shared_ptr<SectionNode>> m_sectionStack; 252 ReporterPreferences m_reporterPrefs; 253 }; 254 255 template<char C> getLineOfChars()256 char const* getLineOfChars() { 257 static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; 258 if( !*line ) { 259 std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); 260 line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; 261 } 262 return line; 263 } 264 265 266 struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { 267 TestEventListenerBase( ReporterConfig const& _config ); 268 269 static std::set<Verbosity> getSupportedVerbosities(); 270 271 void assertionStarting(AssertionInfo const&) override; 272 bool assertionEnded(AssertionStats const&) override; 273 }; 274 275 } // end namespace Catch 276 277 #endif // TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED 278