mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-08 23:29:53 +01:00
289 lines
11 KiB
C++
289 lines
11 KiB
C++
/*
|
|
* Created by Phil on 27/11/2013.
|
|
* Copyright 2013 Two Blue Cubes Ltd. All rights reserved.
|
|
*
|
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
#ifndef TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
|
|
#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
|
|
|
|
#include "../internal/catch_interfaces_reporter.h"
|
|
#include "../internal/catch_errno_guard.hpp"
|
|
|
|
#include <cstring>
|
|
#include <cfloat>
|
|
#include <cstdio>
|
|
#include <assert.h>
|
|
#include <memory>
|
|
|
|
namespace Catch {
|
|
|
|
namespace {
|
|
// Because formatting using c++ streams is stateful, drop down to C is required
|
|
// Alternatively we could use stringstream, but its performance is... not good.
|
|
std::string getFormattedDuration( double duration ) {
|
|
// Max exponent + 1 is required to represent the whole part
|
|
// + 1 for decimal point
|
|
// + 3 for the 3 decimal places
|
|
// + 1 for null terminator
|
|
const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
|
|
char buffer[maxDoubleSize];
|
|
|
|
// Save previous errno, to prevent sprintf from overwriting it
|
|
ErrnoGuard guard;
|
|
#ifdef _MSC_VER
|
|
sprintf_s(buffer, "%.3f", duration);
|
|
#else
|
|
sprintf(buffer, "%.3f", duration);
|
|
#endif
|
|
return std::string(buffer);
|
|
}
|
|
}
|
|
|
|
|
|
struct StreamingReporterBase : IStreamingReporter {
|
|
|
|
StreamingReporterBase( ReporterConfig const& _config )
|
|
: m_config( _config.fullConfig() ),
|
|
stream( _config.stream() )
|
|
{
|
|
m_reporterPrefs.shouldRedirectStdOut = false;
|
|
}
|
|
|
|
virtual ReporterPreferences getPreferences() const override {
|
|
return m_reporterPrefs;
|
|
}
|
|
|
|
virtual ~StreamingReporterBase() override;
|
|
|
|
virtual void noMatchingTestCases( std::string const& ) override {}
|
|
|
|
virtual void testRunStarting( TestRunInfo const& _testRunInfo ) override {
|
|
currentTestRunInfo = _testRunInfo;
|
|
}
|
|
virtual void testGroupStarting( GroupInfo const& _groupInfo ) override {
|
|
currentGroupInfo = _groupInfo;
|
|
}
|
|
|
|
virtual void testCaseStarting( TestCaseInfo const& _testInfo ) override {
|
|
currentTestCaseInfo = _testInfo;
|
|
}
|
|
virtual void sectionStarting( SectionInfo const& _sectionInfo ) override {
|
|
m_sectionStack.push_back( _sectionInfo );
|
|
}
|
|
|
|
virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) override {
|
|
m_sectionStack.pop_back();
|
|
}
|
|
virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) override {
|
|
currentTestCaseInfo.reset();
|
|
}
|
|
virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) override {
|
|
currentGroupInfo.reset();
|
|
}
|
|
virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) override {
|
|
currentTestCaseInfo.reset();
|
|
currentGroupInfo.reset();
|
|
currentTestRunInfo.reset();
|
|
}
|
|
|
|
virtual void skipTest( TestCaseInfo const& ) override {
|
|
// Don't do anything with this by default.
|
|
// It can optionally be overridden in the derived class.
|
|
}
|
|
|
|
IConfigPtr m_config;
|
|
std::ostream& stream;
|
|
|
|
LazyStat<TestRunInfo> currentTestRunInfo;
|
|
LazyStat<GroupInfo> currentGroupInfo;
|
|
LazyStat<TestCaseInfo> currentTestCaseInfo;
|
|
|
|
std::vector<SectionInfo> m_sectionStack;
|
|
ReporterPreferences m_reporterPrefs;
|
|
};
|
|
|
|
struct CumulativeReporterBase : IStreamingReporter {
|
|
template<typename T, typename ChildNodeT>
|
|
struct Node {
|
|
explicit Node( T const& _value ) : value( _value ) {}
|
|
virtual ~Node() {}
|
|
|
|
using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>;
|
|
T value;
|
|
ChildNodes children;
|
|
};
|
|
struct SectionNode {
|
|
explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
|
|
virtual ~SectionNode();
|
|
|
|
bool operator == ( SectionNode const& other ) const {
|
|
return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
|
|
}
|
|
bool operator == ( std::shared_ptr<SectionNode> const& other ) const {
|
|
return operator==( *other );
|
|
}
|
|
|
|
SectionStats stats;
|
|
using ChildSections = std::vector<std::shared_ptr<SectionNode>>;
|
|
using Assertions = std::vector<AssertionStats>;
|
|
ChildSections childSections;
|
|
Assertions assertions;
|
|
std::string stdOut;
|
|
std::string stdErr;
|
|
};
|
|
|
|
struct BySectionInfo {
|
|
BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
|
|
BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
|
|
bool operator() ( std::shared_ptr<SectionNode> const& node ) const {
|
|
return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
|
|
}
|
|
private:
|
|
void operator=( BySectionInfo const& );
|
|
SectionInfo const& m_other;
|
|
};
|
|
|
|
|
|
using TestCaseNode = Node<TestCaseStats, SectionNode>;
|
|
using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
|
|
using TestRunNode = Node<TestRunStats, TestGroupNode>;
|
|
|
|
CumulativeReporterBase( ReporterConfig const& _config )
|
|
: m_config( _config.fullConfig() ),
|
|
stream( _config.stream() )
|
|
{
|
|
m_reporterPrefs.shouldRedirectStdOut = false;
|
|
}
|
|
~CumulativeReporterBase();
|
|
|
|
virtual ReporterPreferences getPreferences() const override {
|
|
return m_reporterPrefs;
|
|
}
|
|
|
|
virtual void testRunStarting( TestRunInfo const& ) override {}
|
|
virtual void testGroupStarting( GroupInfo const& ) override {}
|
|
|
|
virtual void testCaseStarting( TestCaseInfo const& ) override {}
|
|
|
|
virtual void sectionStarting( SectionInfo const& sectionInfo ) override {
|
|
SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
|
|
std::shared_ptr<SectionNode> node;
|
|
if( m_sectionStack.empty() ) {
|
|
if( !m_rootSection )
|
|
m_rootSection = std::make_shared<SectionNode>( incompleteStats );
|
|
node = m_rootSection;
|
|
}
|
|
else {
|
|
SectionNode& parentNode = *m_sectionStack.back();
|
|
SectionNode::ChildSections::const_iterator it =
|
|
std::find_if( parentNode.childSections.begin(),
|
|
parentNode.childSections.end(),
|
|
BySectionInfo( sectionInfo ) );
|
|
if( it == parentNode.childSections.end() ) {
|
|
node = std::make_shared<SectionNode>( incompleteStats );
|
|
parentNode.childSections.push_back( node );
|
|
}
|
|
else
|
|
node = *it;
|
|
}
|
|
m_sectionStack.push_back( node );
|
|
m_deepestSection = node;
|
|
}
|
|
|
|
virtual void assertionStarting( AssertionInfo const& ) override {}
|
|
|
|
virtual bool assertionEnded( AssertionStats const& assertionStats ) override {
|
|
assert( !m_sectionStack.empty() );
|
|
SectionNode& sectionNode = *m_sectionStack.back();
|
|
sectionNode.assertions.push_back( assertionStats );
|
|
// AssertionResult holds a pointer to a temporary DecomposedExpression,
|
|
// which getExpandedExpression() calls to build the expression string.
|
|
// Our section stack copy of the assertionResult will likely outlive the
|
|
// temporary, so it must be expanded or discarded now to avoid calling
|
|
// a destroyed object later.
|
|
prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
|
|
return true;
|
|
}
|
|
virtual void sectionEnded( SectionStats const& sectionStats ) override {
|
|
assert( !m_sectionStack.empty() );
|
|
SectionNode& node = *m_sectionStack.back();
|
|
node.stats = sectionStats;
|
|
m_sectionStack.pop_back();
|
|
}
|
|
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) override {
|
|
auto node = std::make_shared<TestCaseNode>( testCaseStats );
|
|
assert( m_sectionStack.size() == 0 );
|
|
node->children.push_back( m_rootSection );
|
|
m_testCases.push_back( node );
|
|
m_rootSection.reset();
|
|
|
|
assert( m_deepestSection );
|
|
m_deepestSection->stdOut = testCaseStats.stdOut;
|
|
m_deepestSection->stdErr = testCaseStats.stdErr;
|
|
}
|
|
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) override {
|
|
auto node = std::make_shared<TestGroupNode>( testGroupStats );
|
|
node->children.swap( m_testCases );
|
|
m_testGroups.push_back( node );
|
|
}
|
|
virtual void testRunEnded( TestRunStats const& testRunStats ) override {
|
|
auto node = std::make_shared<TestRunNode>( testRunStats );
|
|
node->children.swap( m_testGroups );
|
|
m_testRuns.push_back( node );
|
|
testRunEndedCumulative();
|
|
}
|
|
virtual void testRunEndedCumulative() = 0;
|
|
|
|
virtual void skipTest( TestCaseInfo const& ) override {}
|
|
|
|
virtual void prepareExpandedExpression( AssertionResult& result ) const {
|
|
if( result.isOk() )
|
|
result.discardDecomposedExpression();
|
|
else
|
|
result.expandDecomposedExpression();
|
|
}
|
|
|
|
IConfigPtr m_config;
|
|
std::ostream& stream;
|
|
std::vector<AssertionStats> m_assertions;
|
|
std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections;
|
|
std::vector<std::shared_ptr<TestCaseNode>> m_testCases;
|
|
std::vector<std::shared_ptr<TestGroupNode>> m_testGroups;
|
|
|
|
std::vector<std::shared_ptr<TestRunNode>> m_testRuns;
|
|
|
|
std::shared_ptr<SectionNode> m_rootSection;
|
|
std::shared_ptr<SectionNode> m_deepestSection;
|
|
std::vector<std::shared_ptr<SectionNode>> m_sectionStack;
|
|
ReporterPreferences m_reporterPrefs;
|
|
|
|
};
|
|
|
|
template<char C>
|
|
char const* getLineOfChars() {
|
|
static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
|
|
if( !*line ) {
|
|
std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
|
|
line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
|
|
struct TestEventListenerBase : StreamingReporterBase {
|
|
TestEventListenerBase( ReporterConfig const& _config )
|
|
: StreamingReporterBase( _config )
|
|
{}
|
|
|
|
virtual void assertionStarting( AssertionInfo const& ) override {}
|
|
virtual bool assertionEnded( AssertionStats const& ) override {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
|