• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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