blob: 1d3ce06e6fbb767dda1b47c888125c2c98aa01f9 [file] [log] [blame]
Martin Hořeňovskýe8717422017-08-31 10:31:52 +02001/*
2 * Created by Martin on 31/08/2017.
3 *
4 * Distributed under the Boost Software License, Version 1.0. (See accompanying
5 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 */
7
8#include "catch_session.h"
Phil Nashc5608f02017-09-07 11:24:33 +01009#include "catch_commandline.h"
10#include "catch_console_colour.h"
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020011#include "catch_enforce.h"
12#include "catch_list.h"
Phil Nashc5608f02017-09-07 11:24:33 +010013#include "catch_run_context.h"
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020014#include "catch_stream.h"
Phil Nashc5608f02017-09-07 11:24:33 +010015#include "catch_test_spec.h"
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020016#include "catch_version.h"
17#include "catch_interfaces_reporter.h"
18#include "catch_random_number_generator.h"
19#include "catch_startup_exception_registry.h"
20#include "catch_text.h"
21
22#include <cstdlib>
23#include <iomanip>
24
Phil Nash5e063612017-12-05 23:26:21 +000025namespace Catch {
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020026
Phil Nash5e063612017-12-05 23:26:21 +000027 namespace {
28 const int MaxExitCode = 255;
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020029
Phil Nash5e063612017-12-05 23:26:21 +000030 IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
31 auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
32 CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020033
Phil Nash5e063612017-12-05 23:26:21 +000034 return reporter;
35 }
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020036
37#ifndef CATCH_CONFIG_DEFAULT_REPORTER
38#define CATCH_CONFIG_DEFAULT_REPORTER "console"
39#endif
40
Phil Nash5e063612017-12-05 23:26:21 +000041 IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
42 auto const& reporterNames = config->getReporterNames();
43 if (reporterNames.empty())
44 return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config);
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020045
Phil Nash5e063612017-12-05 23:26:21 +000046 IStreamingReporterPtr reporter;
47 for (auto const& name : reporterNames)
48 addReporter(reporter, createReporter(name, config));
49 return reporter;
50 }
Martin Hořeňovskýe8ec6bd2017-08-31 11:46:37 +020051
52#undef CATCH_CONFIG_DEFAULT_REPORTER
53
Phil Nash5e063612017-12-05 23:26:21 +000054 void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
55 auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
56 for (auto const& listener : listeners)
57 addReporter(reporters, listener->create(Catch::ReporterConfig(config)));
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020058 }
59
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020060
Phil Nash5e063612017-12-05 23:26:21 +000061 Catch::Totals runTests(std::shared_ptr<Config> const& config) {
62 IStreamingReporterPtr reporter = makeReporter(config);
63 addListeners(reporter, config);
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020064
Phil Nash5e063612017-12-05 23:26:21 +000065 RunContext context(config, std::move(reporter));
66
67 Totals totals;
68
69 context.testGroupStarting(config->name(), 1, 1);
70
71 TestSpec testSpec = config->testSpec();
72 if (!testSpec.hasFilters())
73 testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests
74
75 auto const& allTestCases = getAllTestCasesSorted(*config);
76 for (auto const& testCase : allTestCases) {
77 if (!context.aborting() && matchTest(testCase, testSpec, *config))
78 totals += context.runTest(testCase);
79 else
80 context.reporter().skipTest(testCase);
Martin Hořeňovskýe8ec6bd2017-08-31 11:46:37 +020081 }
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020082
Phil Nash5e063612017-12-05 23:26:21 +000083 context.testGroupEnded(config->name(), totals, 1, 1);
84 return totals;
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020085 }
Martin Hořeňovskýe8717422017-08-31 10:31:52 +020086
Phil Nash5e063612017-12-05 23:26:21 +000087 void applyFilenamesAsTags(Catch::IConfig const& config) {
88 auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
89 for (auto& testCase : tests) {
90 auto tags = testCase.tags;
Martin Hořeňovskýe8ec6bd2017-08-31 11:46:37 +020091
Phil Nash5e063612017-12-05 23:26:21 +000092 std::string filename = testCase.lineInfo.file;
93 auto lastSlash = filename.find_last_of("\\/");
94 if (lastSlash != std::string::npos) {
95 filename.erase(0, lastSlash);
96 filename[0] = '#';
97 }
98
99 auto lastDot = filename.find_last_of('.');
100 if (lastDot != std::string::npos) {
101 filename.erase(lastDot);
102 }
103
104 tags.push_back(std::move(filename));
105 setTags(testCase, tags);
106 }
107 }
108
109 } // anon namespace
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200110
111 Session::Session() {
112 static bool alreadyInstantiated = false;
Phil Nashfe050622017-11-02 17:57:52 +0000113 if( alreadyInstantiated ) {
114 try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
115 catch(...) { getMutableRegistryHub().registerStartupException(); }
116 }
117
118 const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
119 if ( !exceptions.empty() ) {
120 m_startupExceptions = true;
121 Colour colourGuard( Colour::Red );
122 Catch::cerr() << "Errors occured during startup!" << '\n';
123 // iterate over all exceptions and notify user
124 for ( const auto& ex_ptr : exceptions ) {
125 try {
126 std::rethrow_exception(ex_ptr);
127 } catch ( std::exception const& ex ) {
128 Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
129 }
130 }
131 }
132
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200133 alreadyInstantiated = true;
134 m_cli = makeCommandLineParser( m_configData );
135 }
136 Session::~Session() {
137 Catch::cleanUp();
138 }
139
140 void Session::showHelp() const {
141 Catch::cout()
142 << "\nCatch v" << libraryVersion() << "\n"
143 << m_cli << std::endl
144 << "For more detailed usage please see the project docs\n" << std::endl;
145 }
146 void Session::libIdentify() {
147 Catch::cout()
148 << std::left << std::setw(16) << "description: " << "A Catch test executable\n"
149 << std::left << std::setw(16) << "category: " << "testframework\n"
150 << std::left << std::setw(16) << "framework: " << "Catch Test\n"
151 << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
152 }
153
154 int Session::applyCommandLine( int argc, char* argv[] ) {
Phil Nashfe050622017-11-02 17:57:52 +0000155 if( m_startupExceptions )
156 return 1;
157
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200158 auto result = m_cli.parse( clara::Args( argc, argv ) );
159 if( !result ) {
160 Catch::cerr()
161 << Colour( Colour::Red )
162 << "\nError(s) in input:\n"
163 << Column( result.errorMessage() ).indent( 2 )
164 << "\n\n";
165 Catch::cerr() << "Run with -? for usage\n" << std::endl;
166 return MaxExitCode;
167 }
168
169 if( m_configData.showHelp )
170 showHelp();
171 if( m_configData.libIdentify )
172 libIdentify();
173 m_config.reset();
174 return 0;
175 }
176
177 void Session::useConfigData( ConfigData const& configData ) {
178 m_configData = configData;
179 m_config.reset();
180 }
181
182 int Session::run( int argc, char* argv[] ) {
Phil Nashfe050622017-11-02 17:57:52 +0000183 if( m_startupExceptions )
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200184 return 1;
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200185 int returnCode = applyCommandLine( argc, argv );
186 if( returnCode == 0 )
187 returnCode = run();
188 return returnCode;
189 }
190
191#if defined(WIN32) && defined(UNICODE)
192 int Session::run( int argc, wchar_t* const argv[] ) {
193
194 char **utf8Argv = new char *[ argc ];
195
196 for ( int i = 0; i < argc; ++i ) {
197 int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
198
199 utf8Argv[ i ] = new char[ bufSize ];
200
201 WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
202 }
203
204 int returnCode = run( argc, utf8Argv );
205
206 for ( int i = 0; i < argc; ++i )
207 delete [] utf8Argv[ i ];
208
209 delete [] utf8Argv;
210
211 return returnCode;
212 }
213#endif
214 int Session::run() {
215 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
216 Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
217 static_cast<void>(std::getchar());
218 }
219 int exitCode = runInternal();
220 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
221 Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
222 static_cast<void>(std::getchar());
223 }
224 return exitCode;
225 }
226
227 clara::Parser const& Session::cli() const {
228 return m_cli;
229 }
230 void Session::cli( clara::Parser const& newParser ) {
231 m_cli = newParser;
232 }
233 ConfigData& Session::configData() {
234 return m_configData;
235 }
236 Config& Session::config() {
237 if( !m_config )
238 m_config = std::make_shared<Config>( m_configData );
239 return *m_config;
240 }
241
242 int Session::runInternal() {
Phil Nashfe050622017-11-02 17:57:52 +0000243 if( m_startupExceptions )
244 return 1;
245
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200246 if( m_configData.showHelp || m_configData.libIdentify )
247 return 0;
248
249 try
250 {
251 config(); // Force config to be constructed
252
253 seedRng( *m_config );
254
255 if( m_configData.filenamesAsTags )
256 applyFilenamesAsTags( *m_config );
257
258 // Handle list request
259 if( Option<std::size_t> listed = list( config() ) )
260 return static_cast<int>( *listed );
261
Martin Hořeňovský45755942018-01-12 11:49:48 +0100262 // Note that on unices only the lower 8 bits are usually used, clamping
263 // the return value to 255 prevents false negative when some multiple
264 // of 256 tests has failed
Martin Hořeňovskýe8717422017-08-31 10:31:52 +0200265 return (std::min)( MaxExitCode, static_cast<int>( runTests( m_config ).assertions.failed ) );
266 }
267 catch( std::exception& ex ) {
268 Catch::cerr() << ex.what() << std::endl;
269 return MaxExitCode;
270 }
271 }
272
273} // end namespace Catch