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