This commit is contained in:
Martin Hořeňovský 2018-06-06 23:16:06 +02:00
parent 7be8a41adf
commit d2a130f243
8 changed files with 452 additions and 177 deletions

View File

@ -6,7 +6,7 @@ if(NOT DEFINED PROJECT_NAME)
set(NOT_SUBPROJECT ON) set(NOT_SUBPROJECT ON)
endif() endif()
project(Catch2 LANGUAGES CXX VERSION 2.2.2) project(Catch2 LANGUAGES CXX VERSION 2.2.3)
include(GNUInstallDirs) include(GNUInstallDirs)

View File

@ -5,9 +5,9 @@
[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2) [![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2)
[![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2) [![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2)
[![codecov](https://codecov.io/gh/catchorg/Catch2/branch/master/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2) [![codecov](https://codecov.io/gh/catchorg/Catch2/branch/master/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/fRDMfYjUnrbOFwLn) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/u7qF77qgv9YqOr55)
<a href="https://github.com/catchorg/Catch2/releases/download/v2.2.2/catch.hpp">The latest version of the single header can be downloaded directly using this link</a> <a href="https://github.com/catchorg/Catch2/releases/download/v2.2.3/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
## Catch2 is released! ## Catch2 is released!

View File

@ -4,7 +4,7 @@ from conans import ConanFile
class CatchConan(ConanFile): class CatchConan(ConanFile):
name = "Catch" name = "Catch"
version = "2.2.2" version = "2.2.3"
description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD" description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
author = "philsquared" author = "philsquared"
generators = "cmake" generators = "cmake"

View File

@ -1,5 +1,43 @@
<a id="top"></a> <a id="top"></a>
# 2.2.3
**To fix some of the bugs, some behavior had to change in potentially breaking manner.**
**This means that even though this is a patch release, it might not be a drop-in replacement.**
## Fixes
* Listeners are now called before reporter
* This was always documented to be the case, now it actually works that way
* Catch's commandline will no longer accept multiple reporters
* This was done because multiple reporters never worked properly and broke things in non-obvious ways
* **This has potential to be a breaking change**
* MinGW is now detected as Windows platform w/o SEH support (#1257)
* This means that Catch2 no longer tries to use POSIX signal handling when compiled with MinGW
* Fixed potential UB in parsing tags using non-ASCII characters (#1266)
* Note that Catch2 still supports only ASCII test names/tags/etc
* `TEST_CASE_METHOD` can now be used on classnames containing commas (#1245)
* You have to enclose the classname in extra set of parentheses
* Fixed insufficient alt stack size for POSIX signal handling (#1225)
* Fixed compilation error on Android due to missing `std::to_string` in C++11 mode (#1280)
* Fixed the order of user-provided `FALLBACK_STRINGIFIER` in stringification machinery (#1024)
* It was intended to be replacement for built-in fallbacks, but it was used _after_ them.
* **This has potential to be a breaking change**
* Fixed compilation error when a type has an `operator<<` with templated lhs (#1285, #1306)
## Improvements
* Added a new, experimental, output capture (#1243)
* This capture can also redirect output written via C apis, e.g. `printf`
* To opt-in, define `CATCH_CONFIG_EXPERIMENTAL_REDIRECT` in the implementation file
* Added a new fallback stringifier for classes derived from `std::exception`
* Both `StringMaker` specialization and `operator<<` overload are given priority
## Miscellaneous
* `contrib/` now contains dbg scripts that skip over Catch's internals (#904, #1283)
* `gdbinit` for gdb `lldbinit` for lldb
* `CatchAddTests.cmake` no longer strips whitespace from tests (#1265, #1281)
* Online documentation now describes `--use-colour` option (#1263)
# 2.2.2 # 2.2.2
## Fixes ## Fixes

View File

@ -11,7 +11,7 @@
#define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 2 #define CATCH_VERSION_MINOR 2
#define CATCH_VERSION_PATCH 2 #define CATCH_VERSION_PATCH 3
#ifdef __clang__ #ifdef __clang__
# pragma clang system_header # pragma clang system_header

View File

@ -37,7 +37,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 2, 2, 2, "", 0 ); static Version version( 2, 2, 3, "", 0 );
return version; return version;
} }

View File

@ -1,6 +1,6 @@
/* /*
* Catch v2.2.2 * Catch v2.2.3
* Generated: 2018-04-06 12:05:03.186665 * Generated: 2018-06-06 23:11:57.601416
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
@ -15,7 +15,7 @@
#define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 2 #define CATCH_VERSION_MINOR 2
#define CATCH_VERSION_PATCH 2 #define CATCH_VERSION_PATCH 3
#ifdef __clang__ #ifdef __clang__
# pragma clang system_header # pragma clang system_header
@ -72,7 +72,7 @@
#elif defined(linux) || defined(__linux) || defined(__linux__) #elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX # define CATCH_PLATFORM_LINUX
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
# define CATCH_PLATFORM_WINDOWS # define CATCH_PLATFORM_WINDOWS
#endif #endif
@ -164,6 +164,18 @@ namespace Catch {
# define CATCH_CONFIG_COLOUR_NONE # define CATCH_CONFIG_COLOUR_NONE
#endif #endif
////////////////////////////////////////////////////////////////////////////////
// Android somehow still does not support std::to_string
#if defined(__ANDROID__)
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
#endif
////////////////////////////////////////////////////////////////////////////////
// Not all Windows environments support SEH properly
#if defined(__MINGW32__)
# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Cygwin // Cygwin
#ifdef __CYGWIN__ #ifdef __CYGWIN__
@ -213,7 +225,7 @@ namespace Catch {
#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
# define CATCH_CONFIG_COUNTER # define CATCH_CONFIG_COUNTER
#endif #endif
#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
# define CATCH_CONFIG_WINDOWS_SEH # define CATCH_CONFIG_WINDOWS_SEH
#endif #endif
// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. // This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
@ -225,6 +237,10 @@ namespace Catch {
# define CATCH_CONFIG_WCHAR # define CATCH_CONFIG_WCHAR
#endif #endif
#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
# define CATCH_CONFIG_CPP11_TO_STRING
#endif
#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) #if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS # define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
#endif #endif
@ -513,12 +529,17 @@ struct AutoReg : NonCopyable {
} // end namespace Catch } // end namespace Catch
#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
#if defined(CATCH_CONFIG_DISABLE) #if defined(CATCH_CONFIG_DISABLE)
#define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
static void TestName() static void TestName()
#define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
namespace{ \ namespace{ \
struct TestName : ClassName { \ struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \
void test(); \ void test(); \
}; \ }; \
} \ } \
@ -546,7 +567,7 @@ struct AutoReg : NonCopyable {
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ \ namespace{ \
struct TestName : ClassName{ \ struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \
void test(); \ void test(); \
}; \ }; \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
@ -770,16 +791,22 @@ namespace Catch {
std::string convertUnknownEnumToString( E e ); std::string convertUnknownEnumToString( E e );
template<typename T> template<typename T>
typename std::enable_if<!std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) { typename std::enable_if<
#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
(void)value; std::string>::type convertUnstreamable( T const& ) {
return Detail::unprintableString; return Detail::unprintableString;
#else
return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
#endif
} }
template<typename T> template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) { typename std::enable_if<
!std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
std::string>::type convertUnstreamable(T const& ex) {
return ex.what();
}
template<typename T>
typename std::enable_if<
std::is_enum<T>::value
, std::string>::type convertUnstreamable( T const& value ) {
return convertUnknownEnumToString( value ); return convertUnknownEnumToString( value );
} }
@ -805,7 +832,9 @@ namespace Catch {
typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert(const Fake& value) { convert(const Fake& value) {
ReusableStringStream rss; ReusableStringStream rss;
rss << value; // NB: call using the function-like syntax to avoid ambiguity with
// user-defined templated operator<< under clang.
rss.operator<<(value);
return rss.str(); return rss.str();
} }
@ -813,7 +842,11 @@ namespace Catch {
static static
typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert( const Fake& value ) { convert( const Fake& value ) {
return Detail::convertUnstreamable( value ); #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
return Detail::convertUnstreamable(value);
#else
return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
#endif
} }
}; };
@ -3354,8 +3387,12 @@ namespace Catch {
std::string outputFilename; std::string outputFilename;
std::string name; std::string name;
std::string processName; std::string processName;
#ifndef CATCH_CONFIG_DEFAULT_REPORTER
#define CATCH_CONFIG_DEFAULT_REPORTER "console"
#endif
std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER;
#undef CATCH_CONFIG_DEFAULT_REPORTER
std::vector<std::string> reporterNames;
std::vector<std::string> testsOrTags; std::vector<std::string> testsOrTags;
std::vector<std::string> sectionsToRun; std::vector<std::string> sectionsToRun;
}; };
@ -3375,8 +3412,8 @@ namespace Catch {
bool listReporters() const; bool listReporters() const;
std::string getProcessName() const; std::string getProcessName() const;
std::string const& getReporterName() const;
std::vector<std::string> const& getReporterNames() const;
std::vector<std::string> const& getTestsOrTags() const; std::vector<std::string> const& getTestsOrTags() const;
std::vector<std::string> const& getSectionsToRun() const override; std::vector<std::string> const& getSectionsToRun() const override;
@ -3733,8 +3770,6 @@ namespace Catch {
virtual Listeners const& getListeners() const = 0; virtual Listeners const& getListeners() const = 0;
}; };
void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter );
} // end namespace Catch } // end namespace Catch
// end catch_interfaces_reporter.h // end catch_interfaces_reporter.h
@ -3742,7 +3777,7 @@ namespace Catch {
#include <cstring> #include <cstring>
#include <cfloat> #include <cfloat>
#include <cstdio> #include <cstdio>
#include <assert.h> #include <cassert>
#include <memory> #include <memory>
#include <ostream> #include <ostream>
@ -6592,7 +6627,7 @@ namespace Catch {
| Opt( config.outputFilename, "filename" ) | Opt( config.outputFilename, "filename" )
["-o"]["--out"] ["-o"]["--out"]
( "output filename" ) ( "output filename" )
| Opt( config.reporterNames, "name" ) | Opt( config.reporterName, "name" )
["-r"]["--reporter"] ["-r"]["--reporter"]
( "reporter to use (defaults to console)" ) ( "reporter to use (defaults to console)" )
| Opt( config.name, "name" ) | Opt( config.name, "name" )
@ -6734,8 +6769,8 @@ namespace Catch {
bool Config::listReporters() const { return m_data.listReporters; } bool Config::listReporters() const { return m_data.listReporters; }
std::string Config::getProcessName() const { return m_data.processName; } std::string Config::getProcessName() const { return m_data.processName; }
std::string const& Config::getReporterName() const { return m_data.reporterName; }
std::vector<std::string> const& Config::getReporterNames() const { return m_data.reporterNames; }
std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; } std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
@ -7381,6 +7416,11 @@ namespace Catch {
int id; int id;
const char* name; const char* name;
}; };
// 32kb for the alternate stack seems to be sufficient. However, this value
// is experimentally determined, so that's not guaranteed.
constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
static SignalDefs signalDefs[] = { static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGINT, "SIGINT - Terminal interrupt signal" },
{ SIGILL, "SIGILL - Illegal instruction signal" }, { SIGILL, "SIGILL - Illegal instruction signal" },
@ -7407,7 +7447,7 @@ namespace Catch {
isSet = true; isSet = true;
stack_t sigStack; stack_t sigStack;
sigStack.ss_sp = altStackMem; sigStack.ss_sp = altStackMem;
sigStack.ss_size = SIGSTKSZ; sigStack.ss_size = sigStackSize;
sigStack.ss_flags = 0; sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack); sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { }; struct sigaction sa = { };
@ -7438,7 +7478,7 @@ namespace Catch {
bool FatalConditionHandler::isSet = false; bool FatalConditionHandler::isSet = false;
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
stack_t FatalConditionHandler::oldSigStack = {}; stack_t FatalConditionHandler::oldSigStack = {};
char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; char FatalConditionHandler::altStackMem[sigStackSize] = {};
} // namespace Catch } // namespace Catch
@ -7482,16 +7522,18 @@ namespace Catch {
// end catch_interfaces_registry_hub.cpp // end catch_interfaces_registry_hub.cpp
// start catch_interfaces_reporter.cpp // start catch_interfaces_reporter.cpp
// start catch_reporter_multi.h // start catch_reporter_listening.h
namespace Catch { namespace Catch {
class MultipleReporters : public IStreamingReporter { class ListeningReporter : public IStreamingReporter {
using Reporters = std::vector<IStreamingReporterPtr>; using Reporters = std::vector<IStreamingReporterPtr>;
Reporters m_reporters; Reporters m_listeners;
IStreamingReporterPtr m_reporter = nullptr;
public: public:
void add( IStreamingReporterPtr&& reporter ); void addListener( IStreamingReporterPtr&& listener );
void addReporter( IStreamingReporterPtr&& reporter );
public: // IStreamingReporter public: // IStreamingReporter
@ -7524,7 +7566,7 @@ namespace Catch {
} // end namespace Catch } // end namespace Catch
// end catch_reporter_multi.h // end catch_reporter_listening.h
namespace Catch { namespace Catch {
ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig )
@ -7625,27 +7667,6 @@ namespace Catch {
IReporterFactory::~IReporterFactory() = default; IReporterFactory::~IReporterFactory() = default;
IReporterRegistry::~IReporterRegistry() = default; IReporterRegistry::~IReporterRegistry() = default;
void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) {
if( !existingReporter ) {
existingReporter = std::move( additionalReporter );
return;
}
MultipleReporters* multi = nullptr;
if( existingReporter->isMulti() ) {
multi = static_cast<MultipleReporters*>( existingReporter.get() );
}
else {
auto newMulti = std::unique_ptr<MultipleReporters>( new MultipleReporters );
newMulti->add( std::move( existingReporter ) );
multi = newMulti.get();
existingReporter = std::move( newMulti );
}
multi->add( std::move( additionalReporter ) );
}
} // end namespace Catch } // end namespace Catch
// end catch_interfaces_reporter.cpp // end catch_interfaces_reporter.cpp
// start catch_interfaces_runner.cpp // start catch_interfaces_runner.cpp
@ -7887,6 +7908,24 @@ using Matchers::Impl::MatcherBase;
// end catch_matchers.cpp // end catch_matchers.cpp
// start catch_matchers_floating.cpp // start catch_matchers_floating.cpp
// start catch_to_string.hpp
#include <string>
namespace Catch {
template <typename T>
std::string to_string(T const& t) {
#if defined(CATCH_CONFIG_CPP11_TO_STRING)
return std::to_string(t);
#else
ReusableStringStream rss;
rss << t;
return rss.str();
#endif
}
} // end namespace Catch
// end catch_to_string.hpp
#include <cstdlib> #include <cstdlib>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
@ -7992,7 +8031,7 @@ namespace Floating {
} }
std::string WithinUlpsMatcher::describe() const { std::string WithinUlpsMatcher::describe() const {
return "is within " + std::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
} }
}// namespace Floating }// namespace Floating
@ -8179,6 +8218,213 @@ namespace Catch {
} }
} // end namespace Catch } // end namespace Catch
// end catch_message.cpp // end catch_message.cpp
// start catch_output_redirect.cpp
// start catch_output_redirect.h
#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
#include <cstdio>
#include <iosfwd>
#include <string>
namespace Catch {
class RedirectedStream {
std::ostream& m_originalStream;
std::ostream& m_redirectionStream;
std::streambuf* m_prevBuf;
public:
RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
~RedirectedStream();
};
class RedirectedStdOut {
ReusableStringStream m_rss;
RedirectedStream m_cout;
public:
RedirectedStdOut();
auto str() const -> std::string;
};
// StdErr has two constituent streams in C++, std::cerr and std::clog
// This means that we need to redirect 2 streams into 1 to keep proper
// order of writes
class RedirectedStdErr {
ReusableStringStream m_rss;
RedirectedStream m_cerr;
RedirectedStream m_clog;
public:
RedirectedStdErr();
auto str() const -> std::string;
};
// Windows's implementation of std::tmpfile is terrible (it tries
// to create a file inside system folder, thus requiring elevated
// privileges for the binary), so we have to use tmpnam(_s) and
// create the file ourselves there.
class TempFile {
public:
TempFile(TempFile const&) = delete;
TempFile& operator=(TempFile const&) = delete;
TempFile(TempFile&&) = delete;
TempFile& operator=(TempFile&&) = delete;
TempFile();
~TempFile();
std::FILE* getFile();
std::string getContents();
private:
std::FILE* m_file = nullptr;
#if defined(_MSC_VER)
char m_buffer[L_tmpnam] = { 0 };
#endif
};
class OutputRedirect {
public:
OutputRedirect(OutputRedirect const&) = delete;
OutputRedirect& operator=(OutputRedirect const&) = delete;
OutputRedirect(OutputRedirect&&) = delete;
OutputRedirect& operator=(OutputRedirect&&) = delete;
OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
~OutputRedirect();
private:
int m_originalStdout = -1;
int m_originalStderr = -1;
TempFile m_stdoutFile;
TempFile m_stderrFile;
std::string& m_stdoutDest;
std::string& m_stderrDest;
};
} // end namespace Catch
#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
// end catch_output_redirect.h
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <stdexcept>
#if defined(_MSC_VER)
#include <io.h> //_dup and _dup2
#define dup _dup
#define dup2 _dup2
#define fileno _fileno
#else
#include <unistd.h> // dup and dup2
#endif
namespace Catch {
RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
: m_originalStream( originalStream ),
m_redirectionStream( redirectionStream ),
m_prevBuf( m_originalStream.rdbuf() )
{
m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
}
RedirectedStream::~RedirectedStream() {
m_originalStream.rdbuf( m_prevBuf );
}
RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
RedirectedStdErr::RedirectedStdErr()
: m_cerr( Catch::cerr(), m_rss.get() ),
m_clog( Catch::clog(), m_rss.get() )
{}
auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
#if defined(_MSC_VER)
TempFile::TempFile() {
if (tmpnam_s(m_buffer)) {
throw std::runtime_error("Could not get a temp filename");
}
if (fopen_s(&m_file, m_buffer, "w")) {
char buffer[100];
if (strerror_s(buffer, errno)) {
throw std::runtime_error("Could not translate errno to string");
}
throw std::runtime_error("Could not open the temp file: " + std::string(m_buffer) + buffer);
}
}
#else
TempFile::TempFile() {
m_file = std::tmpfile();
if (!m_file) {
throw std::runtime_error("Could not create a temp file.");
}
}
#endif
TempFile::~TempFile() {
// TBD: What to do about errors here?
std::fclose(m_file);
// We manually create the file on Windows only, on Linux
// it will be autodeleted
#if defined(_MSC_VER)
std::remove(m_buffer);
#endif
}
FILE* TempFile::getFile() {
return m_file;
}
std::string TempFile::getContents() {
std::stringstream sstr;
char buffer[100] = {};
std::rewind(m_file);
while (std::fgets(buffer, sizeof(buffer), m_file)) {
sstr << buffer;
}
return sstr.str();
}
OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
m_originalStdout(dup(1)),
m_originalStderr(dup(2)),
m_stdoutDest(stdout_dest),
m_stderrDest(stderr_dest) {
dup2(fileno(m_stdoutFile.getFile()), 1);
dup2(fileno(m_stderrFile.getFile()), 2);
}
OutputRedirect::~OutputRedirect() {
Catch::cout() << std::flush;
fflush(stdout);
// Since we support overriding these streams, we flush cerr
// even though std::cerr is unbuffered
Catch::cerr() << std::flush;
Catch::clog() << std::flush;
fflush(stderr);
dup2(m_originalStdout, 1);
dup2(m_originalStderr, 2);
m_stdoutDest += m_stdoutFile.getContents();
m_stderrDest += m_stderrFile.getContents();
}
} // namespace Catch
#if defined(_MSC_VER)
#undef dup
#undef dup2
#undef fileno
#endif
// end catch_output_redirect.cpp
// start catch_random_number_generator.cpp // start catch_random_number_generator.cpp
// start catch_random_number_generator.h // start catch_random_number_generator.h
@ -8507,47 +8753,6 @@ namespace Catch {
namespace Catch { namespace Catch {
class RedirectedStream {
std::ostream& m_originalStream;
std::ostream& m_redirectionStream;
std::streambuf* m_prevBuf;
public:
RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
: m_originalStream( originalStream ),
m_redirectionStream( redirectionStream ),
m_prevBuf( m_originalStream.rdbuf() )
{
m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
}
~RedirectedStream() {
m_originalStream.rdbuf( m_prevBuf );
}
};
class RedirectedStdOut {
ReusableStringStream m_rss;
RedirectedStream m_cout;
public:
RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
auto str() const -> std::string { return m_rss.str(); }
};
// StdErr has two constituent streams in C++, std::cerr and std::clog
// This means that we need to redirect 2 streams into 1 to keep proper
// order of writes
class RedirectedStdErr {
ReusableStringStream m_rss;
RedirectedStream m_cerr;
RedirectedStream m_clog;
public:
RedirectedStdErr()
: m_cerr( Catch::cerr(), m_rss.get() ),
m_clog( Catch::clog(), m_rss.get() )
{}
auto str() const -> std::string { return m_rss.str(); }
};
RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
: m_runInfo(_config->name()), : m_runInfo(_config->name()),
m_context(getCurrentMutableContext()), m_context(getCurrentMutableContext()),
@ -8794,13 +8999,19 @@ namespace Catch {
Timer timer; Timer timer;
try { try {
if (m_reporter->getPreferences().shouldRedirectStdOut) { if (m_reporter->getPreferences().shouldRedirectStdOut) {
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
RedirectedStdOut redirectedStdOut; RedirectedStdOut redirectedStdOut;
RedirectedStdErr redirectedStdErr; RedirectedStdErr redirectedStdErr;
timer.start(); timer.start();
invokeActiveTestCase(); invokeActiveTestCase();
redirectedCout += redirectedStdOut.str(); redirectedCout += redirectedStdOut.str();
redirectedCerr += redirectedStdErr.str(); redirectedCerr += redirectedStdErr.str();
#else
OutputRedirect r(redirectedCout, redirectedCerr);
timer.start();
invokeActiveTestCase();
#endif
} else { } else {
timer.start(); timer.start();
invokeActiveTestCase(); invokeActiveTestCase();
@ -9094,32 +9305,25 @@ namespace Catch {
return reporter; return reporter;
} }
#ifndef CATCH_CONFIG_DEFAULT_REPORTER
#define CATCH_CONFIG_DEFAULT_REPORTER "console"
#endif
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
auto const& reporterNames = config->getReporterNames(); if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
if (reporterNames.empty()) return createReporter(config->getReporterName(), config);
return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); }
IStreamingReporterPtr reporter; auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter);
for (auto const& name : reporterNames)
addReporter(reporter, createReporter(name, config));
return reporter;
}
#undef CATCH_CONFIG_DEFAULT_REPORTER
void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners) for (auto const& listener : listeners) {
addReporter(reporters, listener->create(Catch::ReporterConfig(config))); multi->addListener(listener->create(Catch::ReporterConfig(config)));
}
multi->addReporter(createReporter(config->getReporterName(), config));
return std::move(multi);
} }
Catch::Totals runTests(std::shared_ptr<Config> const& config) { Catch::Totals runTests(std::shared_ptr<Config> const& config) {
IStreamingReporterPtr reporter = makeReporter(config); // FixMe: Add listeners in order first, then add reporters.
addListeners(reporter, config);
auto reporter = makeReporter(config);
RunContext context(config, std::move(reporter)); RunContext context(config, std::move(reporter));
@ -9847,7 +10051,7 @@ namespace Catch {
return TestCaseInfo::None; return TestCaseInfo::None;
} }
bool isReservedTag( std::string const& tag ) { bool isReservedTag( std::string const& tag ) {
return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) );
} }
void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
CATCH_ENFORCE( !isReservedTag(tag), CATCH_ENFORCE( !isReservedTag(tag),
@ -10085,7 +10289,7 @@ namespace Catch {
// start catch_test_case_tracker.cpp // start catch_test_case_tracker.cpp
#include <algorithm> #include <algorithm>
#include <assert.h> #include <cassert>
#include <stdexcept> #include <stdexcept>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
@ -10913,7 +11117,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 2, 2, 2, "", 0 ); static Version version( 2, 2, 3, "", 0 );
return version; return version;
} }
@ -11240,7 +11444,7 @@ namespace {
#include <cstring> #include <cstring>
#include <cfloat> #include <cfloat>
#include <cstdio> #include <cstdio>
#include <assert.h> #include <cassert>
#include <memory> #include <memory>
namespace Catch { namespace Catch {
@ -12183,7 +12387,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
// end catch_reporter_console.cpp // end catch_reporter_console.cpp
// start catch_reporter_junit.cpp // start catch_reporter_junit.cpp
#include <assert.h> #include <cassert>
#include <sstream> #include <sstream>
#include <ctime> #include <ctime>
#include <algorithm> #include <algorithm>
@ -12416,100 +12620,133 @@ namespace Catch {
} // end namespace Catch } // end namespace Catch
// end catch_reporter_junit.cpp // end catch_reporter_junit.cpp
// start catch_reporter_multi.cpp // start catch_reporter_listening.cpp
#include <cassert>
namespace Catch { namespace Catch {
void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
m_reporters.push_back( std::move( reporter ) ); m_listeners.push_back( std::move( listener ) );
} }
ReporterPreferences MultipleReporters::getPreferences() const { void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
return m_reporters[0]->getPreferences(); assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
m_reporter = std::move( reporter );
} }
std::set<Verbosity> MultipleReporters::getSupportedVerbosities() { ReporterPreferences ListeningReporter::getPreferences() const {
return m_reporter->getPreferences();
}
std::set<Verbosity> ListeningReporter::getSupportedVerbosities() {
return std::set<Verbosity>{ }; return std::set<Verbosity>{ };
} }
void MultipleReporters::noMatchingTestCases( std::string const& spec ) { void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->noMatchingTestCases( spec ); listener->noMatchingTestCases( spec );
}
m_reporter->noMatchingTestCases( spec );
} }
void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->benchmarkStarting( benchmarkInfo ); listener->benchmarkStarting( benchmarkInfo );
}
m_reporter->benchmarkStarting( benchmarkInfo );
} }
void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->benchmarkEnded( benchmarkStats ); listener->benchmarkEnded( benchmarkStats );
}
m_reporter->benchmarkEnded( benchmarkStats );
} }
void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->testRunStarting( testRunInfo ); listener->testRunStarting( testRunInfo );
}
m_reporter->testRunStarting( testRunInfo );
} }
void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->testGroupStarting( groupInfo ); listener->testGroupStarting( groupInfo );
}
m_reporter->testGroupStarting( groupInfo );
} }
void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->testCaseStarting( testInfo ); listener->testCaseStarting( testInfo );
}
m_reporter->testCaseStarting( testInfo );
} }
void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->sectionStarting( sectionInfo ); listener->sectionStarting( sectionInfo );
}
m_reporter->sectionStarting( sectionInfo );
} }
void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->assertionStarting( assertionInfo ); listener->assertionStarting( assertionInfo );
}
m_reporter->assertionStarting( assertionInfo );
} }
// The return value indicates if the messages buffer should be cleared: // The return value indicates if the messages buffer should be cleared:
bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
bool clearBuffer = false; for( auto const& listener : m_listeners ) {
for( auto const& reporter : m_reporters ) static_cast<void>( listener->assertionEnded( assertionStats ) );
clearBuffer |= reporter->assertionEnded( assertionStats ); }
return clearBuffer; return m_reporter->assertionEnded( assertionStats );
} }
void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->sectionEnded( sectionStats ); listener->sectionEnded( sectionStats );
}
m_reporter->sectionEnded( sectionStats );
} }
void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->testCaseEnded( testCaseStats ); listener->testCaseEnded( testCaseStats );
}
m_reporter->testCaseEnded( testCaseStats );
} }
void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->testGroupEnded( testGroupStats ); listener->testGroupEnded( testGroupStats );
}
m_reporter->testGroupEnded( testGroupStats );
} }
void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->testRunEnded( testRunStats ); listener->testRunEnded( testRunStats );
}
m_reporter->testRunEnded( testRunStats );
} }
void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
for( auto const& reporter : m_reporters ) for ( auto const& listener : m_listeners ) {
reporter->skipTest( testInfo ); listener->skipTest( testInfo );
}
m_reporter->skipTest( testInfo );
} }
bool MultipleReporters::isMulti() const { bool ListeningReporter::isMulti() const {
return true; return true;
} }
} // end namespace Catch } // end namespace Catch
// end catch_reporter_multi.cpp // end catch_reporter_listening.cpp
// start catch_reporter_xml.cpp // start catch_reporter_xml.cpp
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -10,7 +10,7 @@ class CatchConanTest(ConanFile):
settings = "os", "compiler", "arch", "build_type" settings = "os", "compiler", "arch", "build_type"
username = getenv("CONAN_USERNAME", "philsquared") username = getenv("CONAN_USERNAME", "philsquared")
channel = getenv("CONAN_CHANNEL", "testing") channel = getenv("CONAN_CHANNEL", "testing")
requires = "Catch/2.2.2@%s/%s" % (username, channel) requires = "Catch/2.2.3@%s/%s" % (username, channel)
def build(self): def build(self):
cmake = CMake(self) cmake = CMake(self)