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 reportInvalidArgumentsCatch::StreamingReporterBase54 void reportInvalidArguments(std::string const&) override {} 55 testRunStartingCatch::StreamingReporterBase56 void testRunStarting(TestRunInfo const& _testRunInfo) override { 57 currentTestRunInfo = _testRunInfo; 58 } 59 testGroupStartingCatch::StreamingReporterBase60 void testGroupStarting(GroupInfo const& _groupInfo) override { 61 currentGroupInfo = _groupInfo; 62 } 63 testCaseStartingCatch::StreamingReporterBase64 void testCaseStarting(TestCaseInfo const& _testInfo) override { 65 currentTestCaseInfo = _testInfo; 66 } sectionStartingCatch::StreamingReporterBase67 void sectionStarting(SectionInfo const& _sectionInfo) override { 68 m_sectionStack.push_back(_sectionInfo); 69 } 70 sectionEndedCatch::StreamingReporterBase71 void sectionEnded(SectionStats const& /* _sectionStats */) override { 72 m_sectionStack.pop_back(); 73 } testCaseEndedCatch::StreamingReporterBase74 void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { 75 currentTestCaseInfo.reset(); 76 } testGroupEndedCatch::StreamingReporterBase77 void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { 78 currentGroupInfo.reset(); 79 } testRunEndedCatch::StreamingReporterBase80 void testRunEnded(TestRunStats const& /* _testRunStats */) override { 81 currentTestCaseInfo.reset(); 82 currentGroupInfo.reset(); 83 currentTestRunInfo.reset(); 84 } 85 skipTestCatch::StreamingReporterBase86 void skipTest(TestCaseInfo const&) override { 87 // Don't do anything with this by default. 88 // It can optionally be overridden in the derived class. 89 } 90 91 IConfigPtr m_config; 92 std::ostream& stream; 93 94 LazyStat<TestRunInfo> currentTestRunInfo; 95 LazyStat<GroupInfo> currentGroupInfo; 96 LazyStat<TestCaseInfo> currentTestCaseInfo; 97 98 std::vector<SectionInfo> m_sectionStack; 99 ReporterPreferences m_reporterPrefs; 100 }; 101 102 template<typename DerivedT> 103 struct CumulativeReporterBase : IStreamingReporter { 104 template<typename T, typename ChildNodeT> 105 struct Node { NodeCatch::CumulativeReporterBase::Node106 explicit Node( T const& _value ) : value( _value ) {} ~NodeCatch::CumulativeReporterBase::Node107 virtual ~Node() {} 108 109 using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; 110 T value; 111 ChildNodes children; 112 }; 113 struct SectionNode { SectionNodeCatch::CumulativeReporterBase::SectionNode114 explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} 115 virtual ~SectionNode() = default; 116 operator ==Catch::CumulativeReporterBase::SectionNode117 bool operator == (SectionNode const& other) const { 118 return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; 119 } operator ==Catch::CumulativeReporterBase::SectionNode120 bool operator == (std::shared_ptr<SectionNode> const& other) const { 121 return operator==(*other); 122 } 123 124 SectionStats stats; 125 using ChildSections = std::vector<std::shared_ptr<SectionNode>>; 126 using Assertions = std::vector<AssertionStats>; 127 ChildSections childSections; 128 Assertions assertions; 129 std::string stdOut; 130 std::string stdErr; 131 }; 132 133 struct BySectionInfo { BySectionInfoCatch::CumulativeReporterBase::BySectionInfo134 BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfoCatch::CumulativeReporterBase::BySectionInfo135 BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} operator ()Catch::CumulativeReporterBase::BySectionInfo136 bool operator() (std::shared_ptr<SectionNode> const& node) const { 137 return ((node->stats.sectionInfo.name == m_other.name) && 138 (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); 139 } 140 void operator=(BySectionInfo const&) = delete; 141 142 private: 143 SectionInfo const& m_other; 144 }; 145 146 147 using TestCaseNode = Node<TestCaseStats, SectionNode>; 148 using TestGroupNode = Node<TestGroupStats, TestCaseNode>; 149 using TestRunNode = Node<TestRunStats, TestGroupNode>; 150 CumulativeReporterBaseCatch::CumulativeReporterBase151 CumulativeReporterBase( ReporterConfig const& _config ) 152 : m_config( _config.fullConfig() ), 153 stream( _config.stream() ) 154 { 155 m_reporterPrefs.shouldRedirectStdOut = false; 156 if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) 157 CATCH_ERROR( "Verbosity level not supported by this reporter" ); 158 } 159 ~CumulativeReporterBase() override = default; 160 getPreferencesCatch::CumulativeReporterBase161 ReporterPreferences getPreferences() const override { 162 return m_reporterPrefs; 163 } 164 getSupportedVerbositiesCatch::CumulativeReporterBase165 static std::set<Verbosity> getSupportedVerbosities() { 166 return { Verbosity::Normal }; 167 } 168 testRunStartingCatch::CumulativeReporterBase169 void testRunStarting( TestRunInfo const& ) override {} testGroupStartingCatch::CumulativeReporterBase170 void testGroupStarting( GroupInfo const& ) override {} 171 testCaseStartingCatch::CumulativeReporterBase172 void testCaseStarting( TestCaseInfo const& ) override {} 173 sectionStartingCatch::CumulativeReporterBase174 void sectionStarting( SectionInfo const& sectionInfo ) override { 175 SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); 176 std::shared_ptr<SectionNode> node; 177 if( m_sectionStack.empty() ) { 178 if( !m_rootSection ) 179 m_rootSection = std::make_shared<SectionNode>( incompleteStats ); 180 node = m_rootSection; 181 } 182 else { 183 SectionNode& parentNode = *m_sectionStack.back(); 184 auto it = 185 std::find_if( parentNode.childSections.begin(), 186 parentNode.childSections.end(), 187 BySectionInfo( sectionInfo ) ); 188 if( it == parentNode.childSections.end() ) { 189 node = std::make_shared<SectionNode>( incompleteStats ); 190 parentNode.childSections.push_back( node ); 191 } 192 else 193 node = *it; 194 } 195 m_sectionStack.push_back( node ); 196 m_deepestSection = std::move(node); 197 } 198 assertionStartingCatch::CumulativeReporterBase199 void assertionStarting(AssertionInfo const&) override {} 200 assertionEndedCatch::CumulativeReporterBase201 bool assertionEnded(AssertionStats const& assertionStats) override { 202 assert(!m_sectionStack.empty()); 203 // AssertionResult holds a pointer to a temporary DecomposedExpression, 204 // which getExpandedExpression() calls to build the expression string. 205 // Our section stack copy of the assertionResult will likely outlive the 206 // temporary, so it must be expanded or discarded now to avoid calling 207 // a destroyed object later. 208 prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); 209 SectionNode& sectionNode = *m_sectionStack.back(); 210 sectionNode.assertions.push_back(assertionStats); 211 return true; 212 } sectionEndedCatch::CumulativeReporterBase213 void sectionEnded(SectionStats const& sectionStats) override { 214 assert(!m_sectionStack.empty()); 215 SectionNode& node = *m_sectionStack.back(); 216 node.stats = sectionStats; 217 m_sectionStack.pop_back(); 218 } testCaseEndedCatch::CumulativeReporterBase219 void testCaseEnded(TestCaseStats const& testCaseStats) override { 220 auto node = std::make_shared<TestCaseNode>(testCaseStats); 221 assert(m_sectionStack.size() == 0); 222 node->children.push_back(m_rootSection); 223 m_testCases.push_back(node); 224 m_rootSection.reset(); 225 226 assert(m_deepestSection); 227 m_deepestSection->stdOut = testCaseStats.stdOut; 228 m_deepestSection->stdErr = testCaseStats.stdErr; 229 } testGroupEndedCatch::CumulativeReporterBase230 void testGroupEnded(TestGroupStats const& testGroupStats) override { 231 auto node = std::make_shared<TestGroupNode>(testGroupStats); 232 node->children.swap(m_testCases); 233 m_testGroups.push_back(node); 234 } testRunEndedCatch::CumulativeReporterBase235 void testRunEnded(TestRunStats const& testRunStats) override { 236 auto node = std::make_shared<TestRunNode>(testRunStats); 237 node->children.swap(m_testGroups); 238 m_testRuns.push_back(node); 239 testRunEndedCumulative(); 240 } 241 virtual void testRunEndedCumulative() = 0; 242 skipTestCatch::CumulativeReporterBase243 void skipTest(TestCaseInfo const&) override {} 244 245 IConfigPtr m_config; 246 std::ostream& stream; 247 std::vector<AssertionStats> m_assertions; 248 std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; 249 std::vector<std::shared_ptr<TestCaseNode>> m_testCases; 250 std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; 251 252 std::vector<std::shared_ptr<TestRunNode>> m_testRuns; 253 254 std::shared_ptr<SectionNode> m_rootSection; 255 std::shared_ptr<SectionNode> m_deepestSection; 256 std::vector<std::shared_ptr<SectionNode>> m_sectionStack; 257 ReporterPreferences m_reporterPrefs; 258 }; 259 260 template<char C> getLineOfChars()261 char const* getLineOfChars() { 262 static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; 263 if( !*line ) { 264 std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); 265 line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; 266 } 267 return line; 268 } 269 270 271 struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { 272 TestEventListenerBase( ReporterConfig const& _config ); 273 274 static std::set<Verbosity> getSupportedVerbosities(); 275 276 void assertionStarting(AssertionInfo const&) override; 277 bool assertionEnded(AssertionStats const&) override; 278 }; 279 280 } // end namespace Catch 281 282 #endif // TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED