]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Catch v2.0.1 | |
3 | * Generated: 2017-11-03 11:53:39.642003 | |
4 | * ---------------------------------------------------------- | |
5 | * This file has been merged from multiple headers. Please don't edit it directly | |
6 | * Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved. | |
7 | * | |
8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying | |
9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
10 | */ | |
11 | #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED | |
12 | #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED | |
13 | // start catch.hpp | |
14 | ||
15 | ||
16 | #ifdef __clang__ | |
17 | # pragma clang system_header | |
18 | #elif defined __GNUC__ | |
19 | # pragma GCC system_header | |
20 | #endif | |
21 | ||
22 | // start catch_suppress_warnings.h | |
23 | ||
24 | #ifdef __clang__ | |
25 | # ifdef __ICC // icpc defines the __clang__ macro | |
26 | # pragma warning(push) | |
27 | # pragma warning(disable: 161 1682) | |
28 | # else // __ICC | |
29 | # pragma clang diagnostic ignored "-Wglobal-constructors" | |
30 | # pragma clang diagnostic ignored "-Wvariadic-macros" | |
31 | # pragma clang diagnostic ignored "-Wc99-extensions" | |
32 | # pragma clang diagnostic ignored "-Wunused-variable" | |
33 | # pragma clang diagnostic push | |
34 | # pragma clang diagnostic ignored "-Wpadded" | |
35 | # pragma clang diagnostic ignored "-Wswitch-enum" | |
36 | # pragma clang diagnostic ignored "-Wcovered-switch-default" | |
37 | # endif | |
38 | #elif defined __GNUC__ | |
39 | # pragma GCC diagnostic ignored "-Wvariadic-macros" | |
40 | # pragma GCC diagnostic ignored "-Wunused-variable" | |
41 | # pragma GCC diagnostic ignored "-Wparentheses" | |
42 | ||
43 | # pragma GCC diagnostic push | |
44 | # pragma GCC diagnostic ignored "-Wpadded" | |
45 | #endif | |
46 | // end catch_suppress_warnings.h | |
47 | #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) | |
48 | # define CATCH_IMPL | |
49 | # define CATCH_CONFIG_EXTERNAL_INTERFACES | |
50 | # if defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
51 | # undef CATCH_CONFIG_DISABLE_MATCHERS | |
52 | # endif | |
53 | #endif | |
54 | ||
55 | // start catch_platform.h | |
56 | ||
57 | #ifdef __APPLE__ | |
58 | # include <TargetConditionals.h> | |
59 | # if TARGET_OS_MAC == 1 | |
60 | # define CATCH_PLATFORM_MAC | |
61 | # elif TARGET_OS_IPHONE == 1 | |
62 | # define CATCH_PLATFORM_IPHONE | |
63 | # endif | |
64 | ||
65 | #elif defined(linux) || defined(__linux) || defined(__linux__) | |
66 | # define CATCH_PLATFORM_LINUX | |
67 | ||
68 | #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) | |
69 | # define CATCH_PLATFORM_WINDOWS | |
70 | #endif | |
71 | ||
72 | // end catch_platform.h | |
73 | #ifdef CATCH_IMPL | |
74 | # ifndef CLARA_CONFIG_MAIN | |
75 | # define CLARA_CONFIG_MAIN_NOT_DEFINED | |
76 | # define CLARA_CONFIG_MAIN | |
77 | # endif | |
78 | #endif | |
79 | ||
80 | // start catch_tag_alias_autoregistrar.h | |
81 | ||
82 | // start catch_common.h | |
83 | ||
84 | // start catch_compiler_capabilities.h | |
85 | ||
86 | // Detect a number of compiler features - by compiler | |
87 | // The following features are defined: | |
88 | // | |
89 | // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? | |
90 | // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? | |
91 | // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? | |
92 | // **************** | |
93 | // Note to maintainers: if new toggles are added please document them | |
94 | // in configuration.md, too | |
95 | // **************** | |
96 | ||
97 | // In general each macro has a _NO_<feature name> form | |
98 | // (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. | |
99 | // Many features, at point of detection, define an _INTERNAL_ macro, so they | |
100 | // can be combined, en-mass, with the _NO_ forms later. | |
101 | ||
102 | #ifdef __cplusplus | |
103 | ||
104 | # if __cplusplus >= 201402L | |
105 | # define CATCH_CPP14_OR_GREATER | |
106 | # endif | |
107 | ||
108 | #endif | |
109 | ||
110 | #ifdef __clang__ | |
111 | ||
112 | # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
113 | _Pragma( "clang diagnostic push" ) \ | |
114 | _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ | |
115 | _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") | |
116 | # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ | |
117 | _Pragma( "clang diagnostic pop" ) | |
118 | ||
119 | # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ | |
120 | _Pragma( "clang diagnostic push" ) \ | |
121 | _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) | |
122 | # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ | |
123 | _Pragma( "clang diagnostic pop" ) | |
124 | ||
125 | #endif // __clang__ | |
126 | ||
127 | //////////////////////////////////////////////////////////////////////////////// | |
128 | // We know some environments not to support full POSIX signals | |
129 | #if defined(__CYGWIN__) || defined(__QNX__) | |
130 | ||
131 | # if !defined(CATCH_CONFIG_POSIX_SIGNALS) | |
132 | # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS | |
133 | # endif | |
134 | ||
135 | #endif | |
136 | ||
137 | #ifdef __OS400__ | |
138 | # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS | |
139 | # define CATCH_CONFIG_COLOUR_NONE | |
140 | #endif | |
141 | ||
142 | //////////////////////////////////////////////////////////////////////////////// | |
143 | // Cygwin | |
144 | #ifdef __CYGWIN__ | |
145 | ||
146 | // Required for some versions of Cygwin to declare gettimeofday | |
147 | // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin | |
148 | # define _BSD_SOURCE | |
149 | ||
150 | #endif // __CYGWIN__ | |
151 | ||
152 | //////////////////////////////////////////////////////////////////////////////// | |
153 | // Visual C++ | |
154 | #ifdef _MSC_VER | |
155 | ||
156 | // Universal Windows platform does not support SEH | |
157 | // Or console colours (or console at all...) | |
158 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) | |
159 | # define CATCH_CONFIG_COLOUR_NONE | |
160 | # else | |
161 | # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH | |
162 | # endif | |
163 | ||
164 | #endif // _MSC_VER | |
165 | ||
166 | //////////////////////////////////////////////////////////////////////////////// | |
167 | ||
168 | // Use of __COUNTER__ is suppressed during code analysis in | |
169 | // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly | |
170 | // handled by it. | |
171 | // Otherwise all supported compilers support COUNTER macro, | |
172 | // but user still might want to turn it off | |
173 | #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) | |
174 | #define CATCH_INTERNAL_CONFIG_COUNTER | |
175 | #endif | |
176 | ||
177 | #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) | |
178 | # define CATCH_CONFIG_COUNTER | |
179 | #endif | |
180 | #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) | |
181 | # define CATCH_CONFIG_WINDOWS_SEH | |
182 | #endif | |
183 | // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. | |
184 | #if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) | |
185 | # define CATCH_CONFIG_POSIX_SIGNALS | |
186 | #endif | |
187 | ||
188 | #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) | |
189 | # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS | |
190 | # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS | |
191 | #endif | |
192 | #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) | |
193 | # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS | |
194 | # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS | |
195 | #endif | |
196 | ||
197 | // end catch_compiler_capabilities.h | |
198 | #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line | |
199 | #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) | |
200 | #ifdef CATCH_CONFIG_COUNTER | |
201 | # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) | |
202 | #else | |
203 | # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) | |
204 | #endif | |
205 | ||
206 | #include <iosfwd> | |
207 | #include <string> | |
208 | #include <cstdint> | |
209 | ||
210 | namespace Catch { | |
211 | ||
212 | struct CaseSensitive { enum Choice { | |
213 | Yes, | |
214 | No | |
215 | }; }; | |
216 | ||
217 | class NonCopyable { | |
218 | NonCopyable( NonCopyable const& ) = delete; | |
219 | NonCopyable( NonCopyable && ) = delete; | |
220 | NonCopyable& operator = ( NonCopyable const& ) = delete; | |
221 | NonCopyable& operator = ( NonCopyable && ) = delete; | |
222 | ||
223 | protected: | |
224 | NonCopyable(); | |
225 | virtual ~NonCopyable(); | |
226 | }; | |
227 | ||
228 | struct SourceLineInfo { | |
229 | ||
230 | SourceLineInfo() = delete; | |
231 | SourceLineInfo( char const* _file, std::size_t _line ) noexcept; | |
232 | ||
233 | SourceLineInfo( SourceLineInfo const& other ) = default; | |
234 | SourceLineInfo( SourceLineInfo && ) = default; | |
235 | SourceLineInfo& operator = ( SourceLineInfo const& ) = default; | |
236 | SourceLineInfo& operator = ( SourceLineInfo && ) = default; | |
237 | ||
238 | bool empty() const noexcept; | |
239 | bool operator == ( SourceLineInfo const& other ) const noexcept; | |
240 | bool operator < ( SourceLineInfo const& other ) const noexcept; | |
241 | ||
242 | char const* file; | |
243 | std::size_t line; | |
244 | }; | |
245 | ||
246 | std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); | |
247 | ||
248 | // This is just here to avoid compiler warnings with macro constants and boolean literals | |
249 | bool isTrue( bool value ); | |
250 | bool alwaysTrue(); | |
251 | bool alwaysFalse(); | |
252 | ||
253 | // Use this in variadic streaming macros to allow | |
254 | // >> +StreamEndStop | |
255 | // as well as | |
256 | // >> stuff +StreamEndStop | |
257 | struct StreamEndStop { | |
258 | std::string operator+() const; | |
259 | }; | |
260 | template<typename T> | |
261 | T const& operator + ( T const& value, StreamEndStop ) { | |
262 | return value; | |
263 | } | |
264 | } | |
265 | ||
266 | #define CATCH_INTERNAL_LINEINFO \ | |
267 | ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) | |
268 | ||
269 | // end catch_common.h | |
270 | namespace Catch { | |
271 | ||
272 | struct RegistrarForTagAliases { | |
273 | RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); | |
274 | }; | |
275 | ||
276 | } // end namespace Catch | |
277 | ||
278 | #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } | |
279 | ||
280 | // end catch_tag_alias_autoregistrar.h | |
281 | // start catch_test_registry.h | |
282 | ||
283 | // start catch_interfaces_testcase.h | |
284 | ||
285 | #include <vector> | |
286 | #include <memory> | |
287 | ||
288 | namespace Catch { | |
289 | ||
290 | class TestSpec; | |
291 | ||
292 | struct ITestInvoker { | |
293 | virtual void invoke () const = 0; | |
294 | virtual ~ITestInvoker(); | |
295 | }; | |
296 | ||
297 | using ITestCasePtr = std::shared_ptr<ITestInvoker>; | |
298 | ||
299 | class TestCase; | |
300 | struct IConfig; | |
301 | ||
302 | struct ITestCaseRegistry { | |
303 | virtual ~ITestCaseRegistry(); | |
304 | virtual std::vector<TestCase> const& getAllTests() const = 0; | |
305 | virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0; | |
306 | }; | |
307 | ||
308 | bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); | |
309 | std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); | |
310 | std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); | |
311 | ||
312 | } | |
313 | ||
314 | // end catch_interfaces_testcase.h | |
315 | // start catch_stringref.h | |
316 | ||
317 | #include <cstddef> | |
318 | #include <string> | |
319 | #include <iosfwd> | |
320 | ||
321 | namespace Catch { | |
322 | ||
323 | class StringData; | |
324 | ||
325 | /// A non-owning string class (similar to the forthcoming std::string_view) | |
326 | /// Note that, because a StringRef may be a substring of another string, | |
327 | /// it may not be null terminated. c_str() must return a null terminated | |
328 | /// string, however, and so the StringRef will internally take ownership | |
329 | /// (taking a copy), if necessary. In theory this ownership is not externally | |
330 | /// visible - but it does mean (substring) StringRefs should not be shared between | |
331 | /// threads. | |
332 | class StringRef { | |
333 | friend struct StringRefTestAccess; | |
334 | ||
335 | using size_type = std::size_t; | |
336 | ||
337 | char const* m_start; | |
338 | size_type m_size; | |
339 | ||
340 | char* m_data = nullptr; | |
341 | ||
342 | void takeOwnership(); | |
343 | ||
344 | public: // construction/ assignment | |
345 | StringRef() noexcept; | |
346 | StringRef( StringRef const& other ) noexcept; | |
347 | StringRef( StringRef&& other ) noexcept; | |
348 | StringRef( char const* rawChars ) noexcept; | |
349 | StringRef( char const* rawChars, size_type size ) noexcept; | |
350 | StringRef( std::string const& stdString ) noexcept; | |
351 | ~StringRef() noexcept; | |
352 | ||
353 | auto operator = ( StringRef other ) noexcept -> StringRef&; | |
354 | operator std::string() const; | |
355 | ||
356 | void swap( StringRef& other ) noexcept; | |
357 | ||
358 | public: // operators | |
359 | auto operator == ( StringRef const& other ) const noexcept -> bool; | |
360 | auto operator != ( StringRef const& other ) const noexcept -> bool; | |
361 | ||
362 | auto operator[] ( size_type index ) const noexcept -> char; | |
363 | ||
364 | public: // named queries | |
365 | auto empty() const noexcept -> bool; | |
366 | auto size() const noexcept -> size_type; | |
367 | auto numberOfCharacters() const noexcept -> size_type; | |
368 | auto c_str() const -> char const*; | |
369 | ||
370 | public: // substrings and searches | |
371 | auto substr( size_type start, size_type size ) const noexcept -> StringRef; | |
372 | ||
373 | private: // ownership queries - may not be consistent between calls | |
374 | auto isOwned() const noexcept -> bool; | |
375 | auto isSubstring() const noexcept -> bool; | |
376 | auto data() const noexcept -> char const*; | |
377 | }; | |
378 | ||
379 | auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; | |
380 | auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; | |
381 | auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; | |
382 | ||
383 | auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; | |
384 | ||
385 | } // namespace Catch | |
386 | ||
387 | // end catch_stringref.h | |
388 | namespace Catch { | |
389 | ||
390 | template<typename C> | |
391 | class TestInvokerAsMethod : public ITestInvoker { | |
392 | void (C::*m_testAsMethod)(); | |
393 | public: | |
394 | TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} | |
395 | ||
396 | void invoke() const override { | |
397 | C obj; | |
398 | (obj.*m_testAsMethod)(); | |
399 | } | |
400 | }; | |
401 | ||
402 | auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; | |
403 | ||
404 | template<typename C> | |
405 | auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { | |
406 | return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod ); | |
407 | } | |
408 | ||
409 | struct NameAndTags { | |
410 | NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; | |
411 | StringRef name; | |
412 | StringRef tags; | |
413 | }; | |
414 | ||
415 | struct AutoReg : NonCopyable { | |
416 | AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; | |
417 | ~AutoReg(); | |
418 | }; | |
419 | ||
420 | } // end namespace Catch | |
421 | ||
422 | #if defined(CATCH_CONFIG_DISABLE) | |
423 | #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ | |
424 | static void TestName() | |
425 | #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ | |
426 | namespace{ \ | |
427 | struct TestName : ClassName { \ | |
428 | void test(); \ | |
429 | }; \ | |
430 | } \ | |
431 | void TestName::test() | |
432 | ||
433 | #endif | |
434 | ||
435 | /////////////////////////////////////////////////////////////////////////////// | |
436 | #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ | |
437 | static void TestName(); \ | |
438 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
439 | namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ | |
440 | CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ | |
441 | static void TestName() | |
442 | #define INTERNAL_CATCH_TESTCASE( ... ) \ | |
443 | INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) | |
444 | ||
445 | /////////////////////////////////////////////////////////////////////////////// | |
446 | #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ | |
447 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
448 | namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ | |
449 | CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS | |
450 | ||
451 | /////////////////////////////////////////////////////////////////////////////// | |
452 | #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ | |
453 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
454 | namespace{ \ | |
455 | struct TestName : ClassName{ \ | |
456 | void test(); \ | |
457 | }; \ | |
458 | Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ | |
459 | } \ | |
460 | CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ | |
461 | void TestName::test() | |
462 | #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ | |
463 | INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) | |
464 | ||
465 | /////////////////////////////////////////////////////////////////////////////// | |
466 | #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ | |
467 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
468 | Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ | |
469 | CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS | |
470 | ||
471 | // end catch_test_registry.h | |
472 | // start catch_capture.hpp | |
473 | ||
474 | // start catch_assertionhandler.h | |
475 | ||
476 | // start catch_decomposer.h | |
477 | ||
478 | // start catch_tostring.h | |
479 | ||
480 | #include <sstream> | |
481 | #include <vector> | |
482 | #include <cstddef> | |
483 | #include <type_traits> | |
484 | #include <string> | |
485 | ||
486 | #ifdef __OBJC__ | |
487 | // start catch_objc_arc.hpp | |
488 | ||
489 | #import <Foundation/Foundation.h> | |
490 | ||
491 | #ifdef __has_feature | |
492 | #define CATCH_ARC_ENABLED __has_feature(objc_arc) | |
493 | #else | |
494 | #define CATCH_ARC_ENABLED 0 | |
495 | #endif | |
496 | ||
497 | void arcSafeRelease( NSObject* obj ); | |
498 | id performOptionalSelector( id obj, SEL sel ); | |
499 | ||
500 | #if !CATCH_ARC_ENABLED | |
501 | inline void arcSafeRelease( NSObject* obj ) { | |
502 | [obj release]; | |
503 | } | |
504 | inline id performOptionalSelector( id obj, SEL sel ) { | |
505 | if( [obj respondsToSelector: sel] ) | |
506 | return [obj performSelector: sel]; | |
507 | return nil; | |
508 | } | |
509 | #define CATCH_UNSAFE_UNRETAINED | |
510 | #define CATCH_ARC_STRONG | |
511 | #else | |
512 | inline void arcSafeRelease( NSObject* ){} | |
513 | inline id performOptionalSelector( id obj, SEL sel ) { | |
514 | #ifdef __clang__ | |
515 | #pragma clang diagnostic push | |
516 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" | |
517 | #endif | |
518 | if( [obj respondsToSelector: sel] ) | |
519 | return [obj performSelector: sel]; | |
520 | #ifdef __clang__ | |
521 | #pragma clang diagnostic pop | |
522 | #endif | |
523 | return nil; | |
524 | } | |
525 | #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained | |
526 | #define CATCH_ARC_STRONG __strong | |
527 | #endif | |
528 | ||
529 | // end catch_objc_arc.hpp | |
530 | #endif | |
531 | ||
532 | #ifdef _MSC_VER | |
533 | #pragma warning(push) | |
534 | #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless | |
535 | #endif | |
536 | ||
537 | // We need a dummy global operator<< so we can bring it into Catch namespace later | |
538 | struct Catch_global_namespace_dummy; | |
539 | std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); | |
540 | ||
541 | namespace Catch { | |
542 | // Bring in operator<< from global namespace into Catch namespace | |
543 | using ::operator<<; | |
544 | ||
545 | namespace Detail { | |
546 | ||
547 | extern const std::string unprintableString; | |
548 | ||
549 | std::string rawMemoryToString( const void *object, std::size_t size ); | |
550 | ||
551 | template<typename T> | |
552 | std::string rawMemoryToString( const T& object ) { | |
553 | return rawMemoryToString( &object, sizeof(object) ); | |
554 | } | |
555 | ||
556 | template<typename T> | |
557 | class IsStreamInsertable { | |
558 | template<typename SS, typename TT> | |
559 | static auto test(int) | |
560 | -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type()); | |
561 | ||
562 | template<typename, typename> | |
563 | static auto test(...)->std::false_type; | |
564 | ||
565 | public: | |
566 | static const bool value = decltype(test<std::ostream, const T&>(0))::value; | |
567 | }; | |
568 | ||
569 | } // namespace Detail | |
570 | ||
571 | // If we decide for C++14, change these to enable_if_ts | |
572 | template <typename T> | |
573 | struct StringMaker { | |
574 | template <typename Fake = T> | |
575 | static | |
576 | typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type | |
577 | convert(const Fake& t) { | |
578 | std::ostringstream sstr; | |
579 | sstr << t; | |
580 | return sstr.str(); | |
581 | } | |
582 | ||
583 | template <typename Fake = T> | |
584 | static | |
585 | typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type | |
586 | convert(const Fake&) { | |
587 | return Detail::unprintableString; | |
588 | } | |
589 | }; | |
590 | ||
591 | namespace Detail { | |
592 | ||
593 | // This function dispatches all stringification requests inside of Catch. | |
594 | // Should be preferably called fully qualified, like ::Catch::Detail::stringify | |
595 | template <typename T> | |
596 | std::string stringify(const T& e) { | |
597 | return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e); | |
598 | } | |
599 | ||
600 | } // namespace Detail | |
601 | ||
602 | // Some predefined specializations | |
603 | ||
604 | template<> | |
605 | struct StringMaker<std::string> { | |
606 | static std::string convert(const std::string& str); | |
607 | }; | |
608 | template<> | |
609 | struct StringMaker<std::wstring> { | |
610 | static std::string convert(const std::wstring& wstr); | |
611 | }; | |
612 | ||
613 | template<> | |
614 | struct StringMaker<char const *> { | |
615 | static std::string convert(char const * str); | |
616 | }; | |
617 | template<> | |
618 | struct StringMaker<char *> { | |
619 | static std::string convert(char * str); | |
620 | }; | |
621 | template<> | |
622 | struct StringMaker<wchar_t const *> { | |
623 | static std::string convert(wchar_t const * str); | |
624 | }; | |
625 | template<> | |
626 | struct StringMaker<wchar_t *> { | |
627 | static std::string convert(wchar_t * str); | |
628 | }; | |
629 | ||
630 | template<int SZ> | |
631 | struct StringMaker<char[SZ]> { | |
632 | static std::string convert(const char* str) { | |
633 | return ::Catch::Detail::stringify(std::string{ str }); | |
634 | } | |
635 | }; | |
636 | template<int SZ> | |
637 | struct StringMaker<signed char[SZ]> { | |
638 | static std::string convert(const char* str) { | |
639 | return ::Catch::Detail::stringify(std::string{ str }); | |
640 | } | |
641 | }; | |
642 | template<int SZ> | |
643 | struct StringMaker<unsigned char[SZ]> { | |
644 | static std::string convert(const char* str) { | |
645 | return ::Catch::Detail::stringify(std::string{ str }); | |
646 | } | |
647 | }; | |
648 | ||
649 | template<> | |
650 | struct StringMaker<int> { | |
651 | static std::string convert(int value); | |
652 | }; | |
653 | template<> | |
654 | struct StringMaker<long> { | |
655 | static std::string convert(long value); | |
656 | }; | |
657 | template<> | |
658 | struct StringMaker<long long> { | |
659 | static std::string convert(long long value); | |
660 | }; | |
661 | template<> | |
662 | struct StringMaker<unsigned int> { | |
663 | static std::string convert(unsigned int value); | |
664 | }; | |
665 | template<> | |
666 | struct StringMaker<unsigned long> { | |
667 | static std::string convert(unsigned long value); | |
668 | }; | |
669 | template<> | |
670 | struct StringMaker<unsigned long long> { | |
671 | static std::string convert(unsigned long long value); | |
672 | }; | |
673 | ||
674 | template<> | |
675 | struct StringMaker<bool> { | |
676 | static std::string convert(bool b); | |
677 | }; | |
678 | ||
679 | template<> | |
680 | struct StringMaker<char> { | |
681 | static std::string convert(char c); | |
682 | }; | |
683 | template<> | |
684 | struct StringMaker<signed char> { | |
685 | static std::string convert(signed char c); | |
686 | }; | |
687 | template<> | |
688 | struct StringMaker<unsigned char> { | |
689 | static std::string convert(unsigned char c); | |
690 | }; | |
691 | ||
692 | template<> | |
693 | struct StringMaker<std::nullptr_t> { | |
694 | static std::string convert(std::nullptr_t); | |
695 | }; | |
696 | ||
697 | template<> | |
698 | struct StringMaker<float> { | |
699 | static std::string convert(float value); | |
700 | }; | |
701 | template<> | |
702 | struct StringMaker<double> { | |
703 | static std::string convert(double value); | |
704 | }; | |
705 | ||
706 | template <typename T> | |
707 | struct StringMaker<T*> { | |
708 | template <typename U> | |
709 | static std::string convert(U* p) { | |
710 | if (p) { | |
711 | return ::Catch::Detail::rawMemoryToString(p); | |
712 | } else { | |
713 | return "nullptr"; | |
714 | } | |
715 | } | |
716 | }; | |
717 | ||
718 | template <typename R, typename C> | |
719 | struct StringMaker<R C::*> { | |
720 | static std::string convert(R C::* p) { | |
721 | if (p) { | |
722 | return ::Catch::Detail::rawMemoryToString(p); | |
723 | } else { | |
724 | return "nullptr"; | |
725 | } | |
726 | } | |
727 | }; | |
728 | ||
729 | namespace Detail { | |
730 | template<typename InputIterator> | |
731 | std::string rangeToString(InputIterator first, InputIterator last) { | |
732 | std::ostringstream oss; | |
733 | oss << "{ "; | |
734 | if (first != last) { | |
735 | oss << ::Catch::Detail::stringify(*first); | |
736 | for (++first; first != last; ++first) | |
737 | oss << ", " << ::Catch::Detail::stringify(*first); | |
738 | } | |
739 | oss << " }"; | |
740 | return oss.str(); | |
741 | } | |
742 | } | |
743 | ||
744 | template<typename T, typename Allocator> | |
745 | struct StringMaker<std::vector<T, Allocator> > { | |
746 | static std::string convert( std::vector<T,Allocator> const& v ) { | |
747 | return ::Catch::Detail::rangeToString( v.begin(), v.end() ); | |
748 | } | |
749 | }; | |
750 | ||
751 | template<typename T> | |
752 | struct EnumStringMaker { | |
753 | static std::string convert(const T& t) { | |
754 | return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t)); | |
755 | } | |
756 | }; | |
757 | ||
758 | #ifdef __OBJC__ | |
759 | template<> | |
760 | struct StringMaker<NSString*> { | |
761 | static std::string convert(NSString * nsstring) { | |
762 | if (!nsstring) | |
763 | return "nil"; | |
764 | return std::string("@") + [nsstring UTF8String]; | |
765 | } | |
766 | }; | |
767 | template<> | |
768 | struct StringMaker<NSObject*> { | |
769 | static std::string convert(NSObject* nsObject) { | |
770 | return ::Catch::Detail::stringify([nsObject description]); | |
771 | } | |
772 | ||
773 | }; | |
774 | namespace Detail { | |
775 | inline std::string stringify( NSString* nsstring ) { | |
776 | return StringMaker<NSString*>::convert( nsstring ); | |
777 | } | |
778 | ||
779 | } // namespace Detail | |
780 | #endif // __OBJC__ | |
781 | ||
782 | } // namespace Catch | |
783 | ||
784 | ////////////////////////////////////////////////////// | |
785 | // Separate std-lib types stringification, so it can be selectively enabled | |
786 | // This means that we do not bring in | |
787 | ||
788 | #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) | |
789 | # define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER | |
790 | # define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER | |
791 | # define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER | |
792 | #endif | |
793 | ||
794 | // Separate std::pair specialization | |
795 | #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) | |
796 | #include <utility> | |
797 | namespace Catch { | |
798 | template<typename T1, typename T2> | |
799 | struct StringMaker<std::pair<T1, T2> > { | |
800 | static std::string convert(const std::pair<T1, T2>& pair) { | |
801 | std::ostringstream oss; | |
802 | oss << "{ " | |
803 | << ::Catch::Detail::stringify(pair.first) | |
804 | << ", " | |
805 | << ::Catch::Detail::stringify(pair.second) | |
806 | << " }"; | |
807 | return oss.str(); | |
808 | } | |
809 | }; | |
810 | } | |
811 | #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER | |
812 | ||
813 | // Separate std::tuple specialization | |
814 | #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) | |
815 | #include <tuple> | |
816 | namespace Catch { | |
817 | namespace Detail { | |
818 | template< | |
819 | typename Tuple, | |
820 | std::size_t N = 0, | |
821 | bool = (N < std::tuple_size<Tuple>::value) | |
822 | > | |
823 | struct TupleElementPrinter { | |
824 | static void print(const Tuple& tuple, std::ostream& os) { | |
825 | os << (N ? ", " : " ") | |
826 | << ::Catch::Detail::stringify(std::get<N>(tuple)); | |
827 | TupleElementPrinter<Tuple, N + 1>::print(tuple, os); | |
828 | } | |
829 | }; | |
830 | ||
831 | template< | |
832 | typename Tuple, | |
833 | std::size_t N | |
834 | > | |
835 | struct TupleElementPrinter<Tuple, N, false> { | |
836 | static void print(const Tuple&, std::ostream&) {} | |
837 | }; | |
838 | ||
839 | } | |
840 | ||
841 | template<typename ...Types> | |
842 | struct StringMaker<std::tuple<Types...>> { | |
843 | static std::string convert(const std::tuple<Types...>& tuple) { | |
844 | std::ostringstream os; | |
845 | os << '{'; | |
846 | Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, os); | |
847 | os << " }"; | |
848 | return os.str(); | |
849 | } | |
850 | }; | |
851 | } | |
852 | #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER | |
853 | ||
854 | // Separate std::chrono::duration specialization | |
855 | #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) | |
856 | #include <ctime> | |
857 | #include <ratio> | |
858 | #include <chrono> | |
859 | ||
860 | template <class Ratio> | |
861 | struct ratio_string { | |
862 | static std::string symbol(); | |
863 | }; | |
864 | ||
865 | template <class Ratio> | |
866 | std::string ratio_string<Ratio>::symbol() { | |
867 | std::ostringstream oss; | |
868 | oss << '[' << Ratio::num << '/' | |
869 | << Ratio::den << ']'; | |
870 | return oss.str(); | |
871 | } | |
872 | template <> | |
873 | struct ratio_string<std::atto> { | |
874 | static std::string symbol() { return "a"; } | |
875 | }; | |
876 | template <> | |
877 | struct ratio_string<std::femto> { | |
878 | static std::string symbol() { return "f"; } | |
879 | }; | |
880 | template <> | |
881 | struct ratio_string<std::pico> { | |
882 | static std::string symbol() { return "p"; } | |
883 | }; | |
884 | template <> | |
885 | struct ratio_string<std::nano> { | |
886 | static std::string symbol() { return "n"; } | |
887 | }; | |
888 | template <> | |
889 | struct ratio_string<std::micro> { | |
890 | static std::string symbol() { return "u"; } | |
891 | }; | |
892 | template <> | |
893 | struct ratio_string<std::milli> { | |
894 | static std::string symbol() { return "m"; } | |
895 | }; | |
896 | ||
897 | namespace Catch { | |
898 | //////////// | |
899 | // std::chrono::duration specializations | |
900 | template<typename Value, typename Ratio> | |
901 | struct StringMaker<std::chrono::duration<Value, Ratio>> { | |
902 | static std::string convert(std::chrono::duration<Value, Ratio> const& duration) { | |
903 | std::ostringstream oss; | |
904 | oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; | |
905 | return oss.str(); | |
906 | } | |
907 | }; | |
908 | template<typename Value> | |
909 | struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> { | |
910 | static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) { | |
911 | std::ostringstream oss; | |
912 | oss << duration.count() << " s"; | |
913 | return oss.str(); | |
914 | } | |
915 | }; | |
916 | template<typename Value> | |
917 | struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> { | |
918 | static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) { | |
919 | std::ostringstream oss; | |
920 | oss << duration.count() << " m"; | |
921 | return oss.str(); | |
922 | } | |
923 | }; | |
924 | template<typename Value> | |
925 | struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> { | |
926 | static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) { | |
927 | std::ostringstream oss; | |
928 | oss << duration.count() << " h"; | |
929 | return oss.str(); | |
930 | } | |
931 | }; | |
932 | ||
933 | //////////// | |
934 | // std::chrono::time_point specialization | |
935 | // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock> | |
936 | template<typename Clock, typename Duration> | |
937 | struct StringMaker<std::chrono::time_point<Clock, Duration>> { | |
938 | static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) { | |
939 | return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; | |
940 | } | |
941 | }; | |
942 | // std::chrono::time_point<system_clock> specialization | |
943 | template<typename Duration> | |
944 | struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> { | |
945 | static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) { | |
946 | auto converted = std::chrono::system_clock::to_time_t(time_point); | |
947 | ||
948 | #ifdef _MSC_VER | |
949 | std::tm timeInfo = {}; | |
950 | gmtime_s(&timeInfo, &converted); | |
951 | #else | |
952 | std::tm* timeInfo = std::gmtime(&converted); | |
953 | #endif | |
954 | ||
955 | auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); | |
956 | char timeStamp[timeStampSize]; | |
957 | const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; | |
958 | ||
959 | #ifdef _MSC_VER | |
960 | std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); | |
961 | #else | |
962 | std::strftime(timeStamp, timeStampSize, fmt, timeInfo); | |
963 | #endif | |
964 | return std::string(timeStamp); | |
965 | } | |
966 | }; | |
967 | } | |
968 | #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER | |
969 | ||
970 | #ifdef _MSC_VER | |
971 | #pragma warning(pop) | |
972 | #endif | |
973 | ||
974 | // end catch_tostring.h | |
975 | #include <ostream> | |
976 | ||
977 | #ifdef _MSC_VER | |
978 | #pragma warning(push) | |
979 | #pragma warning(disable:4389) // '==' : signed/unsigned mismatch | |
980 | #pragma warning(disable:4018) // more "signed/unsigned mismatch" | |
981 | #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) | |
982 | #pragma warning(disable:4180) // qualifier applied to function type has no meaning | |
983 | #endif | |
984 | ||
985 | namespace Catch { | |
986 | ||
987 | struct ITransientExpression { | |
988 | virtual auto isBinaryExpression() const -> bool = 0; | |
989 | virtual auto getResult() const -> bool = 0; | |
990 | virtual void streamReconstructedExpression( std::ostream &os ) const = 0; | |
991 | ||
992 | // We don't actually need a virtual destructore, but many static analysers | |
993 | // complain if it's not here :-( | |
994 | virtual ~ITransientExpression(); | |
995 | }; | |
996 | ||
997 | void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); | |
998 | ||
999 | template<typename LhsT, typename RhsT> | |
1000 | class BinaryExpr : public ITransientExpression { | |
1001 | bool m_result; | |
1002 | LhsT m_lhs; | |
1003 | StringRef m_op; | |
1004 | RhsT m_rhs; | |
1005 | ||
1006 | auto isBinaryExpression() const -> bool override { return true; } | |
1007 | auto getResult() const -> bool override { return m_result; } | |
1008 | ||
1009 | void streamReconstructedExpression( std::ostream &os ) const override { | |
1010 | formatReconstructedExpression | |
1011 | ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); | |
1012 | } | |
1013 | ||
1014 | public: | |
1015 | BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) | |
1016 | : m_result( comparisonResult ), | |
1017 | m_lhs( lhs ), | |
1018 | m_op( op ), | |
1019 | m_rhs( rhs ) | |
1020 | {} | |
1021 | }; | |
1022 | ||
1023 | template<typename LhsT> | |
1024 | class UnaryExpr : public ITransientExpression { | |
1025 | LhsT m_lhs; | |
1026 | ||
1027 | auto isBinaryExpression() const -> bool override { return false; } | |
1028 | auto getResult() const -> bool override { return m_lhs ? true : false; } | |
1029 | ||
1030 | void streamReconstructedExpression( std::ostream &os ) const override { | |
1031 | os << Catch::Detail::stringify( m_lhs ); | |
1032 | } | |
1033 | ||
1034 | public: | |
1035 | UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} | |
1036 | }; | |
1037 | ||
1038 | // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) | |
1039 | template<typename LhsT, typename RhsT> | |
1040 | auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; | |
1041 | template<typename T> | |
1042 | auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } | |
1043 | template<typename T> | |
1044 | auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } | |
1045 | template<typename T> | |
1046 | auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } | |
1047 | template<typename T> | |
1048 | auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } | |
1049 | ||
1050 | template<typename LhsT, typename RhsT> | |
1051 | auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; | |
1052 | template<typename T> | |
1053 | auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } | |
1054 | template<typename T> | |
1055 | auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } | |
1056 | template<typename T> | |
1057 | auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } | |
1058 | template<typename T> | |
1059 | auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } | |
1060 | ||
1061 | template<typename LhsT> | |
1062 | class ExprLhs { | |
1063 | LhsT m_lhs; | |
1064 | public: | |
1065 | ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} | |
1066 | ||
1067 | template<typename RhsT> | |
1068 | auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { | |
1069 | return BinaryExpr<LhsT, RhsT const&>( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); | |
1070 | } | |
1071 | auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { | |
1072 | return BinaryExpr<LhsT, bool>( m_lhs == rhs, m_lhs, "==", rhs ); | |
1073 | } | |
1074 | ||
1075 | template<typename RhsT> | |
1076 | auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { | |
1077 | return BinaryExpr<LhsT, RhsT const&>( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); | |
1078 | } | |
1079 | auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { | |
1080 | return BinaryExpr<LhsT, bool>( m_lhs != rhs, m_lhs, "!=", rhs ); | |
1081 | } | |
1082 | ||
1083 | template<typename RhsT> | |
1084 | auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { | |
1085 | return BinaryExpr<LhsT, RhsT const&>( m_lhs > rhs, m_lhs, ">", rhs ); | |
1086 | } | |
1087 | template<typename RhsT> | |
1088 | auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { | |
1089 | return BinaryExpr<LhsT, RhsT const&>( m_lhs < rhs, m_lhs, "<", rhs ); | |
1090 | } | |
1091 | template<typename RhsT> | |
1092 | auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { | |
1093 | return BinaryExpr<LhsT, RhsT const&>( m_lhs >= rhs, m_lhs, ">=", rhs ); | |
1094 | } | |
1095 | template<typename RhsT> | |
1096 | auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { | |
1097 | return BinaryExpr<LhsT, RhsT const&>( m_lhs <= rhs, m_lhs, "<=", rhs ); | |
1098 | } | |
1099 | ||
1100 | auto makeUnaryExpr() const -> UnaryExpr<LhsT> { | |
1101 | return UnaryExpr<LhsT>( m_lhs ); | |
1102 | } | |
1103 | }; | |
1104 | ||
1105 | void handleExpression( ITransientExpression const& expr ); | |
1106 | ||
1107 | template<typename T> | |
1108 | void handleExpression( ExprLhs<T> const& expr ) { | |
1109 | handleExpression( expr.makeUnaryExpr() ); | |
1110 | } | |
1111 | ||
1112 | struct Decomposer { | |
1113 | template<typename T> | |
1114 | auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { | |
1115 | return ExprLhs<T const&>( lhs ); | |
1116 | } | |
1117 | auto operator <=( bool value ) -> ExprLhs<bool> { | |
1118 | return ExprLhs<bool>( value ); | |
1119 | } | |
1120 | }; | |
1121 | ||
1122 | } // end namespace Catch | |
1123 | ||
1124 | #ifdef _MSC_VER | |
1125 | #pragma warning(pop) | |
1126 | #endif | |
1127 | ||
1128 | // end catch_decomposer.h | |
1129 | // start catch_assertioninfo.h | |
1130 | ||
1131 | // start catch_result_type.h | |
1132 | ||
1133 | namespace Catch { | |
1134 | ||
1135 | // ResultWas::OfType enum | |
1136 | struct ResultWas { enum OfType { | |
1137 | Unknown = -1, | |
1138 | Ok = 0, | |
1139 | Info = 1, | |
1140 | Warning = 2, | |
1141 | ||
1142 | FailureBit = 0x10, | |
1143 | ||
1144 | ExpressionFailed = FailureBit | 1, | |
1145 | ExplicitFailure = FailureBit | 2, | |
1146 | ||
1147 | Exception = 0x100 | FailureBit, | |
1148 | ||
1149 | ThrewException = Exception | 1, | |
1150 | DidntThrowException = Exception | 2, | |
1151 | ||
1152 | FatalErrorCondition = 0x200 | FailureBit | |
1153 | ||
1154 | }; }; | |
1155 | ||
1156 | bool isOk( ResultWas::OfType resultType ); | |
1157 | bool isJustInfo( int flags ); | |
1158 | ||
1159 | // ResultDisposition::Flags enum | |
1160 | struct ResultDisposition { enum Flags { | |
1161 | Normal = 0x01, | |
1162 | ||
1163 | ContinueOnFailure = 0x02, // Failures fail test, but execution continues | |
1164 | FalseTest = 0x04, // Prefix expression with ! | |
1165 | SuppressFail = 0x08 // Failures are reported but do not fail the test | |
1166 | }; }; | |
1167 | ||
1168 | ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); | |
1169 | ||
1170 | bool shouldContinueOnFailure( int flags ); | |
1171 | bool isFalseTest( int flags ); | |
1172 | bool shouldSuppressFailure( int flags ); | |
1173 | ||
1174 | } // end namespace Catch | |
1175 | ||
1176 | // end catch_result_type.h | |
1177 | namespace Catch { | |
1178 | ||
1179 | struct AssertionInfo | |
1180 | { | |
1181 | StringRef macroName; | |
1182 | SourceLineInfo lineInfo; | |
1183 | StringRef capturedExpression; | |
1184 | ResultDisposition::Flags resultDisposition; | |
1185 | ||
1186 | // We want to delete this constructor but a compiler bug in 4.8 means | |
1187 | // the struct is then treated as non-aggregate | |
1188 | //AssertionInfo() = delete; | |
1189 | }; | |
1190 | ||
1191 | } // end namespace Catch | |
1192 | ||
1193 | // end catch_assertioninfo.h | |
1194 | namespace Catch { | |
1195 | ||
1196 | struct TestFailureException{}; | |
1197 | struct AssertionResultData; | |
1198 | ||
1199 | class LazyExpression { | |
1200 | friend class AssertionHandler; | |
1201 | friend struct AssertionStats; | |
1202 | ||
1203 | ITransientExpression const* m_transientExpression = nullptr; | |
1204 | bool m_isNegated; | |
1205 | public: | |
1206 | LazyExpression( bool isNegated ); | |
1207 | LazyExpression( LazyExpression const& other ); | |
1208 | LazyExpression& operator = ( LazyExpression const& ) = delete; | |
1209 | ||
1210 | explicit operator bool() const; | |
1211 | ||
1212 | friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; | |
1213 | }; | |
1214 | ||
1215 | class AssertionHandler { | |
1216 | AssertionInfo m_assertionInfo; | |
1217 | bool m_shouldDebugBreak = false; | |
1218 | bool m_shouldThrow = false; | |
1219 | bool m_inExceptionGuard = false; | |
1220 | ||
1221 | public: | |
1222 | AssertionHandler | |
1223 | ( StringRef macroName, | |
1224 | SourceLineInfo const& lineInfo, | |
1225 | StringRef capturedExpression, | |
1226 | ResultDisposition::Flags resultDisposition ); | |
1227 | ~AssertionHandler(); | |
1228 | ||
1229 | void handle( ITransientExpression const& expr ); | |
1230 | ||
1231 | template<typename T> | |
1232 | void handle( ExprLhs<T> const& expr ) { | |
1233 | handle( expr.makeUnaryExpr() ); | |
1234 | } | |
1235 | void handle( ResultWas::OfType resultType ); | |
1236 | void handle( ResultWas::OfType resultType, StringRef const& message ); | |
1237 | void handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ); | |
1238 | void handle( AssertionResultData const& resultData, ITransientExpression const* expr ); | |
1239 | ||
1240 | auto shouldDebugBreak() const -> bool; | |
1241 | auto allowThrows() const -> bool; | |
1242 | void reactWithDebugBreak() const; | |
1243 | void reactWithoutDebugBreak() const; | |
1244 | void useActiveException(); | |
1245 | void setExceptionGuard(); | |
1246 | void unsetExceptionGuard(); | |
1247 | }; | |
1248 | ||
1249 | void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); | |
1250 | ||
1251 | } // namespace Catch | |
1252 | ||
1253 | // end catch_assertionhandler.h | |
1254 | // start catch_message.h | |
1255 | ||
1256 | #include <string> | |
1257 | #include <sstream> | |
1258 | ||
1259 | namespace Catch { | |
1260 | ||
1261 | struct MessageInfo { | |
1262 | MessageInfo( std::string const& _macroName, | |
1263 | SourceLineInfo const& _lineInfo, | |
1264 | ResultWas::OfType _type ); | |
1265 | ||
1266 | std::string macroName; | |
1267 | std::string message; | |
1268 | SourceLineInfo lineInfo; | |
1269 | ResultWas::OfType type; | |
1270 | unsigned int sequence; | |
1271 | ||
1272 | bool operator == ( MessageInfo const& other ) const; | |
1273 | bool operator < ( MessageInfo const& other ) const; | |
1274 | private: | |
1275 | static unsigned int globalCount; | |
1276 | }; | |
1277 | ||
1278 | struct MessageStream { | |
1279 | ||
1280 | template<typename T> | |
1281 | MessageStream& operator << ( T const& value ) { | |
1282 | m_stream << value; | |
1283 | return *this; | |
1284 | } | |
1285 | ||
1286 | // !TBD reuse a global/ thread-local stream | |
1287 | std::ostringstream m_stream; | |
1288 | }; | |
1289 | ||
1290 | struct MessageBuilder : MessageStream { | |
1291 | MessageBuilder( std::string const& macroName, | |
1292 | SourceLineInfo const& lineInfo, | |
1293 | ResultWas::OfType type ); | |
1294 | ||
1295 | template<typename T> | |
1296 | MessageBuilder& operator << ( T const& value ) { | |
1297 | m_stream << value; | |
1298 | return *this; | |
1299 | } | |
1300 | ||
1301 | MessageInfo m_info; | |
1302 | }; | |
1303 | ||
1304 | class ScopedMessage { | |
1305 | public: | |
1306 | ScopedMessage( MessageBuilder const& builder ); | |
1307 | ~ScopedMessage(); | |
1308 | ||
1309 | MessageInfo m_info; | |
1310 | }; | |
1311 | ||
1312 | } // end namespace Catch | |
1313 | ||
1314 | // end catch_message.h | |
1315 | // start catch_interfaces_capture.h | |
1316 | ||
1317 | #include <string> | |
1318 | ||
1319 | namespace Catch { | |
1320 | ||
1321 | class AssertionResult; | |
1322 | struct AssertionInfo; | |
1323 | struct SectionInfo; | |
1324 | struct SectionEndInfo; | |
1325 | struct MessageInfo; | |
1326 | struct Counts; | |
1327 | struct BenchmarkInfo; | |
1328 | struct BenchmarkStats; | |
1329 | ||
1330 | struct IResultCapture { | |
1331 | ||
1332 | virtual ~IResultCapture(); | |
1333 | ||
1334 | virtual void assertionStarting( AssertionInfo const& info ) = 0; | |
1335 | virtual void assertionEnded( AssertionResult const& result ) = 0; | |
1336 | virtual bool sectionStarted( SectionInfo const& sectionInfo, | |
1337 | Counts& assertions ) = 0; | |
1338 | virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; | |
1339 | virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; | |
1340 | ||
1341 | virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; | |
1342 | virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; | |
1343 | ||
1344 | virtual void pushScopedMessage( MessageInfo const& message ) = 0; | |
1345 | virtual void popScopedMessage( MessageInfo const& message ) = 0; | |
1346 | ||
1347 | virtual std::string getCurrentTestName() const = 0; | |
1348 | virtual const AssertionResult* getLastResult() const = 0; | |
1349 | ||
1350 | virtual void exceptionEarlyReported() = 0; | |
1351 | ||
1352 | virtual void handleFatalErrorCondition( StringRef message ) = 0; | |
1353 | ||
1354 | virtual bool lastAssertionPassed() = 0; | |
1355 | virtual void assertionPassed() = 0; | |
1356 | virtual void assertionRun() = 0; | |
1357 | }; | |
1358 | ||
1359 | IResultCapture& getResultCapture(); | |
1360 | } | |
1361 | ||
1362 | // end catch_interfaces_capture.h | |
1363 | // start catch_debugger.h | |
1364 | ||
1365 | namespace Catch { | |
1366 | bool isDebuggerActive(); | |
1367 | } | |
1368 | ||
1369 | #ifdef CATCH_PLATFORM_MAC | |
1370 | ||
1371 | #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ | |
1372 | ||
1373 | #elif defined(CATCH_PLATFORM_LINUX) | |
1374 | // If we can use inline assembler, do it because this allows us to break | |
1375 | // directly at the location of the failing check instead of breaking inside | |
1376 | // raise() called from it, i.e. one stack frame below. | |
1377 | #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) | |
1378 | #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ | |
1379 | #else // Fall back to the generic way. | |
1380 | #include <signal.h> | |
1381 | ||
1382 | #define CATCH_TRAP() raise(SIGTRAP) | |
1383 | #endif | |
1384 | #elif defined(_MSC_VER) | |
1385 | #define CATCH_TRAP() __debugbreak() | |
1386 | #elif defined(__MINGW32__) | |
1387 | extern "C" __declspec(dllimport) void __stdcall DebugBreak(); | |
1388 | #define CATCH_TRAP() DebugBreak() | |
1389 | #endif | |
1390 | ||
1391 | #ifdef CATCH_TRAP | |
1392 | #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } | |
1393 | #else | |
1394 | #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); | |
1395 | #endif | |
1396 | ||
1397 | // end catch_debugger.h | |
1398 | #if !defined(CATCH_CONFIG_DISABLE) | |
1399 | ||
1400 | #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) | |
1401 | #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ | |
1402 | #else | |
1403 | #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" | |
1404 | #endif | |
1405 | ||
1406 | #if defined(CATCH_CONFIG_FAST_COMPILE) | |
1407 | /////////////////////////////////////////////////////////////////////////////// | |
1408 | // We can speedup compilation significantly by breaking into debugger lower in | |
1409 | // the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER | |
1410 | // macro in each assertion | |
1411 | #define INTERNAL_CATCH_REACT( handler ) \ | |
1412 | handler.reactWithDebugBreak(); | |
1413 | ||
1414 | /////////////////////////////////////////////////////////////////////////////// | |
1415 | // Another way to speed-up compilation is to omit local try-catch for REQUIRE* | |
1416 | // macros. | |
1417 | // This can potentially cause false negative, if the test code catches | |
1418 | // the exception before it propagates back up to the runner. | |
1419 | #define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard(); | |
1420 | #define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard(); | |
1421 | ||
1422 | #else // CATCH_CONFIG_FAST_COMPILE | |
1423 | ||
1424 | /////////////////////////////////////////////////////////////////////////////// | |
1425 | // In the event of a failure works out if the debugger needs to be invoked | |
1426 | // and/or an exception thrown and takes appropriate action. | |
1427 | // This needs to be done as a macro so the debugger will stop in the user | |
1428 | // source code rather than in Catch library code | |
1429 | #define INTERNAL_CATCH_REACT( handler ) \ | |
1430 | if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ | |
1431 | handler.reactWithoutDebugBreak(); | |
1432 | ||
1433 | #define INTERNAL_CATCH_TRY( capturer ) try | |
1434 | #define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); } | |
1435 | ||
1436 | #endif | |
1437 | ||
1438 | /////////////////////////////////////////////////////////////////////////////// | |
1439 | #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ | |
1440 | do { \ | |
1441 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ | |
1442 | INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ | |
1443 | CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ | |
1444 | catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ | |
1445 | CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ | |
1446 | } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ | |
1447 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
1448 | } while( Catch::isTrue( false && static_cast<bool>( !!(__VA_ARGS__) ) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look | |
1449 | // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. | |
1450 | ||
1451 | /////////////////////////////////////////////////////////////////////////////// | |
1452 | #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ | |
1453 | INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ | |
1454 | if( Catch::getResultCapture().lastAssertionPassed() ) | |
1455 | ||
1456 | /////////////////////////////////////////////////////////////////////////////// | |
1457 | #define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ | |
1458 | INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ | |
1459 | if( !Catch::getResultCapture().lastAssertionPassed() ) | |
1460 | ||
1461 | /////////////////////////////////////////////////////////////////////////////// | |
1462 | #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ | |
1463 | do { \ | |
1464 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ | |
1465 | try { \ | |
1466 | static_cast<void>(__VA_ARGS__); \ | |
1467 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
1468 | } \ | |
1469 | catch( ... ) { \ | |
1470 | catchAssertionHandler.useActiveException(); \ | |
1471 | } \ | |
1472 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
1473 | } while( Catch::alwaysFalse() ) | |
1474 | ||
1475 | /////////////////////////////////////////////////////////////////////////////// | |
1476 | #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ | |
1477 | do { \ | |
1478 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ | |
1479 | if( catchAssertionHandler.allowThrows() ) \ | |
1480 | try { \ | |
1481 | static_cast<void>(__VA_ARGS__); \ | |
1482 | catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ | |
1483 | } \ | |
1484 | catch( ... ) { \ | |
1485 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
1486 | } \ | |
1487 | else \ | |
1488 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
1489 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
1490 | } while( Catch::alwaysFalse() ) | |
1491 | ||
1492 | /////////////////////////////////////////////////////////////////////////////// | |
1493 | #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ | |
1494 | do { \ | |
1495 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ | |
1496 | if( catchAssertionHandler.allowThrows() ) \ | |
1497 | try { \ | |
1498 | static_cast<void>(expr); \ | |
1499 | catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ | |
1500 | } \ | |
1501 | catch( exceptionType const& ) { \ | |
1502 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
1503 | } \ | |
1504 | catch( ... ) { \ | |
1505 | catchAssertionHandler.useActiveException(); \ | |
1506 | } \ | |
1507 | else \ | |
1508 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
1509 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
1510 | } while( Catch::alwaysFalse() ) | |
1511 | ||
1512 | /////////////////////////////////////////////////////////////////////////////// | |
1513 | #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ | |
1514 | do { \ | |
1515 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ | |
1516 | catchAssertionHandler.handle( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ | |
1517 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
1518 | } while( Catch::alwaysFalse() ) | |
1519 | ||
1520 | /////////////////////////////////////////////////////////////////////////////// | |
1521 | #define INTERNAL_CATCH_INFO( macroName, log ) \ | |
1522 | Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; | |
1523 | ||
1524 | /////////////////////////////////////////////////////////////////////////////// | |
1525 | // Although this is matcher-based, it can be used with just a string | |
1526 | #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ | |
1527 | do { \ | |
1528 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ | |
1529 | if( catchAssertionHandler.allowThrows() ) \ | |
1530 | try { \ | |
1531 | static_cast<void>(__VA_ARGS__); \ | |
1532 | catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ | |
1533 | } \ | |
1534 | catch( ... ) { \ | |
1535 | handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ | |
1536 | } \ | |
1537 | else \ | |
1538 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
1539 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
1540 | } while( Catch::alwaysFalse() ) | |
1541 | ||
1542 | #endif // CATCH_CONFIG_DISABLE | |
1543 | ||
1544 | // end catch_capture.hpp | |
1545 | // start catch_section.h | |
1546 | ||
1547 | // start catch_section_info.h | |
1548 | ||
1549 | // start catch_totals.h | |
1550 | ||
1551 | #include <cstddef> | |
1552 | ||
1553 | namespace Catch { | |
1554 | ||
1555 | struct Counts { | |
1556 | Counts operator - ( Counts const& other ) const; | |
1557 | Counts& operator += ( Counts const& other ); | |
1558 | ||
1559 | std::size_t total() const; | |
1560 | bool allPassed() const; | |
1561 | bool allOk() const; | |
1562 | ||
1563 | std::size_t passed = 0; | |
1564 | std::size_t failed = 0; | |
1565 | std::size_t failedButOk = 0; | |
1566 | }; | |
1567 | ||
1568 | struct Totals { | |
1569 | ||
1570 | Totals operator - ( Totals const& other ) const; | |
1571 | Totals& operator += ( Totals const& other ); | |
1572 | ||
1573 | Totals delta( Totals const& prevTotals ) const; | |
1574 | ||
1575 | Counts assertions; | |
1576 | Counts testCases; | |
1577 | }; | |
1578 | } | |
1579 | ||
1580 | // end catch_totals.h | |
1581 | #include <string> | |
1582 | ||
1583 | namespace Catch { | |
1584 | ||
1585 | struct SectionInfo { | |
1586 | SectionInfo | |
1587 | ( SourceLineInfo const& _lineInfo, | |
1588 | std::string const& _name, | |
1589 | std::string const& _description = std::string() ); | |
1590 | ||
1591 | std::string name; | |
1592 | std::string description; | |
1593 | SourceLineInfo lineInfo; | |
1594 | }; | |
1595 | ||
1596 | struct SectionEndInfo { | |
1597 | SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); | |
1598 | ||
1599 | SectionInfo sectionInfo; | |
1600 | Counts prevAssertions; | |
1601 | double durationInSeconds; | |
1602 | }; | |
1603 | ||
1604 | } // end namespace Catch | |
1605 | ||
1606 | // end catch_section_info.h | |
1607 | // start catch_timer.h | |
1608 | ||
1609 | #include <cstdint> | |
1610 | ||
1611 | namespace Catch { | |
1612 | ||
1613 | auto getCurrentNanosecondsSinceEpoch() -> uint64_t; | |
1614 | auto getEstimatedClockResolution() -> uint64_t; | |
1615 | ||
1616 | class Timer { | |
1617 | uint64_t m_nanoseconds = 0; | |
1618 | public: | |
1619 | void start(); | |
1620 | auto getElapsedNanoseconds() const -> unsigned int; | |
1621 | auto getElapsedMicroseconds() const -> unsigned int; | |
1622 | auto getElapsedMilliseconds() const -> unsigned int; | |
1623 | auto getElapsedSeconds() const -> double; | |
1624 | }; | |
1625 | ||
1626 | } // namespace Catch | |
1627 | ||
1628 | // end catch_timer.h | |
1629 | #include <string> | |
1630 | ||
1631 | namespace Catch { | |
1632 | ||
1633 | class Section : NonCopyable { | |
1634 | public: | |
1635 | Section( SectionInfo const& info ); | |
1636 | ~Section(); | |
1637 | ||
1638 | // This indicates whether the section should be executed or not | |
1639 | explicit operator bool() const; | |
1640 | ||
1641 | private: | |
1642 | SectionInfo m_info; | |
1643 | ||
1644 | std::string m_name; | |
1645 | Counts m_assertions; | |
1646 | bool m_sectionIncluded; | |
1647 | Timer m_timer; | |
1648 | }; | |
1649 | ||
1650 | } // end namespace Catch | |
1651 | ||
1652 | #define INTERNAL_CATCH_SECTION( ... ) \ | |
1653 | if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) | |
1654 | ||
1655 | // end catch_section.h | |
1656 | // start catch_benchmark.h | |
1657 | ||
1658 | #include <cstdint> | |
1659 | #include <string> | |
1660 | ||
1661 | namespace Catch { | |
1662 | ||
1663 | class BenchmarkLooper { | |
1664 | ||
1665 | std::string m_name; | |
1666 | std::size_t m_count = 0; | |
1667 | std::size_t m_iterationsToRun = 1; | |
1668 | uint64_t m_resolution; | |
1669 | Timer m_timer; | |
1670 | ||
1671 | static auto getResolution() -> uint64_t; | |
1672 | public: | |
1673 | // Keep most of this inline as it's on the code path that is being timed | |
1674 | BenchmarkLooper( StringRef name ) | |
1675 | : m_name( name ), | |
1676 | m_resolution( getResolution() ) | |
1677 | { | |
1678 | reportStart(); | |
1679 | m_timer.start(); | |
1680 | } | |
1681 | ||
1682 | explicit operator bool() { | |
1683 | if( m_count < m_iterationsToRun ) | |
1684 | return true; | |
1685 | return needsMoreIterations(); | |
1686 | } | |
1687 | ||
1688 | void increment() { | |
1689 | ++m_count; | |
1690 | } | |
1691 | ||
1692 | void reportStart(); | |
1693 | auto needsMoreIterations() -> bool; | |
1694 | }; | |
1695 | ||
1696 | } // end namespace Catch | |
1697 | ||
1698 | #define BENCHMARK( name ) \ | |
1699 | for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) | |
1700 | ||
1701 | // end catch_benchmark.h | |
1702 | // start catch_interfaces_exception.h | |
1703 | ||
1704 | // start catch_interfaces_registry_hub.h | |
1705 | ||
1706 | #include <string> | |
1707 | #include <memory> | |
1708 | ||
1709 | namespace Catch { | |
1710 | ||
1711 | class TestCase; | |
1712 | struct ITestCaseRegistry; | |
1713 | struct IExceptionTranslatorRegistry; | |
1714 | struct IExceptionTranslator; | |
1715 | struct IReporterRegistry; | |
1716 | struct IReporterFactory; | |
1717 | struct ITagAliasRegistry; | |
1718 | class StartupExceptionRegistry; | |
1719 | ||
1720 | using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; | |
1721 | ||
1722 | struct IRegistryHub { | |
1723 | virtual ~IRegistryHub(); | |
1724 | ||
1725 | virtual IReporterRegistry const& getReporterRegistry() const = 0; | |
1726 | virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; | |
1727 | virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; | |
1728 | ||
1729 | virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; | |
1730 | ||
1731 | virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; | |
1732 | }; | |
1733 | ||
1734 | struct IMutableRegistryHub { | |
1735 | virtual ~IMutableRegistryHub(); | |
1736 | virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; | |
1737 | virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; | |
1738 | virtual void registerTest( TestCase const& testInfo ) = 0; | |
1739 | virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; | |
1740 | virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; | |
1741 | virtual void registerStartupException() noexcept = 0; | |
1742 | }; | |
1743 | ||
1744 | IRegistryHub& getRegistryHub(); | |
1745 | IMutableRegistryHub& getMutableRegistryHub(); | |
1746 | void cleanUp(); | |
1747 | std::string translateActiveException(); | |
1748 | ||
1749 | } | |
1750 | ||
1751 | // end catch_interfaces_registry_hub.h | |
1752 | #if defined(CATCH_CONFIG_DISABLE) | |
1753 | #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ | |
1754 | static std::string translatorName( signature ) | |
1755 | #endif | |
1756 | ||
1757 | #include <exception> | |
1758 | #include <string> | |
1759 | #include <vector> | |
1760 | ||
1761 | namespace Catch { | |
1762 | using exceptionTranslateFunction = std::string(*)(); | |
1763 | ||
1764 | struct IExceptionTranslator; | |
1765 | using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>; | |
1766 | ||
1767 | struct IExceptionTranslator { | |
1768 | virtual ~IExceptionTranslator(); | |
1769 | virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; | |
1770 | }; | |
1771 | ||
1772 | struct IExceptionTranslatorRegistry { | |
1773 | virtual ~IExceptionTranslatorRegistry(); | |
1774 | ||
1775 | virtual std::string translateActiveException() const = 0; | |
1776 | }; | |
1777 | ||
1778 | class ExceptionTranslatorRegistrar { | |
1779 | template<typename T> | |
1780 | class ExceptionTranslator : public IExceptionTranslator { | |
1781 | public: | |
1782 | ||
1783 | ExceptionTranslator( std::string(*translateFunction)( T& ) ) | |
1784 | : m_translateFunction( translateFunction ) | |
1785 | {} | |
1786 | ||
1787 | std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { | |
1788 | try { | |
1789 | if( it == itEnd ) | |
1790 | std::rethrow_exception(std::current_exception()); | |
1791 | else | |
1792 | return (*it)->translate( it+1, itEnd ); | |
1793 | } | |
1794 | catch( T& ex ) { | |
1795 | return m_translateFunction( ex ); | |
1796 | } | |
1797 | } | |
1798 | ||
1799 | protected: | |
1800 | std::string(*m_translateFunction)( T& ); | |
1801 | }; | |
1802 | ||
1803 | public: | |
1804 | template<typename T> | |
1805 | ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { | |
1806 | getMutableRegistryHub().registerTranslator | |
1807 | ( new ExceptionTranslator<T>( translateFunction ) ); | |
1808 | } | |
1809 | }; | |
1810 | } | |
1811 | ||
1812 | /////////////////////////////////////////////////////////////////////////////// | |
1813 | #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ | |
1814 | static std::string translatorName( signature ); \ | |
1815 | namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ | |
1816 | static std::string translatorName( signature ) | |
1817 | ||
1818 | #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) | |
1819 | ||
1820 | // end catch_interfaces_exception.h | |
1821 | // start catch_approx.h | |
1822 | ||
1823 | // start catch_enforce.h | |
1824 | ||
1825 | #include <sstream> | |
1826 | #include <stdexcept> | |
1827 | ||
1828 | #define CATCH_PREPARE_EXCEPTION( type, msg ) \ | |
1829 | type( static_cast<std::ostringstream&&>( std::ostringstream() << msg ).str() ) | |
1830 | #define CATCH_INTERNAL_ERROR( msg ) \ | |
1831 | throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); | |
1832 | #define CATCH_ERROR( msg ) \ | |
1833 | throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) | |
1834 | #define CATCH_ENFORCE( condition, msg ) \ | |
1835 | do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) | |
1836 | ||
1837 | // end catch_enforce.h | |
1838 | #include <type_traits> | |
1839 | ||
1840 | namespace Catch { | |
1841 | namespace Detail { | |
1842 | ||
1843 | class Approx { | |
1844 | private: | |
1845 | bool equalityComparisonImpl(double other) const; | |
1846 | ||
1847 | public: | |
1848 | explicit Approx ( double value ); | |
1849 | ||
1850 | static Approx custom(); | |
1851 | ||
1852 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1853 | Approx operator()( T const& value ) { | |
1854 | Approx approx( static_cast<double>(value) ); | |
1855 | approx.epsilon( m_epsilon ); | |
1856 | approx.margin( m_margin ); | |
1857 | approx.scale( m_scale ); | |
1858 | return approx; | |
1859 | } | |
1860 | ||
1861 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1862 | explicit Approx( T const& value ): Approx(static_cast<double>(value)) | |
1863 | {} | |
1864 | ||
1865 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1866 | friend bool operator == ( const T& lhs, Approx const& rhs ) { | |
1867 | auto lhs_v = static_cast<double>(lhs); | |
1868 | return rhs.equalityComparisonImpl(lhs_v); | |
1869 | } | |
1870 | ||
1871 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1872 | friend bool operator == ( Approx const& lhs, const T& rhs ) { | |
1873 | return operator==( rhs, lhs ); | |
1874 | } | |
1875 | ||
1876 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1877 | friend bool operator != ( T const& lhs, Approx const& rhs ) { | |
1878 | return !operator==( lhs, rhs ); | |
1879 | } | |
1880 | ||
1881 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1882 | friend bool operator != ( Approx const& lhs, T const& rhs ) { | |
1883 | return !operator==( rhs, lhs ); | |
1884 | } | |
1885 | ||
1886 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1887 | friend bool operator <= ( T const& lhs, Approx const& rhs ) { | |
1888 | return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; | |
1889 | } | |
1890 | ||
1891 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1892 | friend bool operator <= ( Approx const& lhs, T const& rhs ) { | |
1893 | return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; | |
1894 | } | |
1895 | ||
1896 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1897 | friend bool operator >= ( T const& lhs, Approx const& rhs ) { | |
1898 | return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; | |
1899 | } | |
1900 | ||
1901 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1902 | friend bool operator >= ( Approx const& lhs, T const& rhs ) { | |
1903 | return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; | |
1904 | } | |
1905 | ||
1906 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1907 | Approx& epsilon( T const& newEpsilon ) { | |
1908 | double epsilonAsDouble = static_cast<double>(newEpsilon); | |
1909 | CATCH_ENFORCE(epsilonAsDouble >= 0 && epsilonAsDouble <= 1.0, | |
1910 | "Invalid Approx::epsilon: " << epsilonAsDouble | |
1911 | << ", Approx::epsilon has to be between 0 and 1"); | |
1912 | m_epsilon = epsilonAsDouble; | |
1913 | return *this; | |
1914 | } | |
1915 | ||
1916 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1917 | Approx& margin( T const& newMargin ) { | |
1918 | double marginAsDouble = static_cast<double>(newMargin); | |
1919 | CATCH_ENFORCE(marginAsDouble >= 0, | |
1920 | "Invalid Approx::margin: " << marginAsDouble | |
1921 | << ", Approx::Margin has to be non-negative."); | |
1922 | m_margin = marginAsDouble; | |
1923 | return *this; | |
1924 | } | |
1925 | ||
1926 | template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> | |
1927 | Approx& scale( T const& newScale ) { | |
1928 | m_scale = static_cast<double>(newScale); | |
1929 | return *this; | |
1930 | } | |
1931 | ||
1932 | std::string toString() const; | |
1933 | ||
1934 | private: | |
1935 | double m_epsilon; | |
1936 | double m_margin; | |
1937 | double m_scale; | |
1938 | double m_value; | |
1939 | }; | |
1940 | } | |
1941 | ||
1942 | template<> | |
1943 | struct StringMaker<Catch::Detail::Approx> { | |
1944 | static std::string convert(Catch::Detail::Approx const& value); | |
1945 | }; | |
1946 | ||
1947 | } // end namespace Catch | |
1948 | ||
1949 | // end catch_approx.h | |
1950 | // start catch_string_manip.h | |
1951 | ||
1952 | #include <string> | |
1953 | #include <iosfwd> | |
1954 | ||
1955 | namespace Catch { | |
1956 | ||
1957 | bool startsWith( std::string const& s, std::string const& prefix ); | |
1958 | bool startsWith( std::string const& s, char prefix ); | |
1959 | bool endsWith( std::string const& s, std::string const& suffix ); | |
1960 | bool endsWith( std::string const& s, char suffix ); | |
1961 | bool contains( std::string const& s, std::string const& infix ); | |
1962 | void toLowerInPlace( std::string& s ); | |
1963 | std::string toLower( std::string const& s ); | |
1964 | std::string trim( std::string const& str ); | |
1965 | bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); | |
1966 | ||
1967 | struct pluralise { | |
1968 | pluralise( std::size_t count, std::string const& label ); | |
1969 | ||
1970 | friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); | |
1971 | ||
1972 | std::size_t m_count; | |
1973 | std::string m_label; | |
1974 | }; | |
1975 | } | |
1976 | ||
1977 | // end catch_string_manip.h | |
1978 | #ifndef CATCH_CONFIG_DISABLE_MATCHERS | |
1979 | // start catch_capture_matchers.h | |
1980 | ||
1981 | // start catch_matchers.h | |
1982 | ||
1983 | #include <string> | |
1984 | #include <vector> | |
1985 | ||
1986 | namespace Catch { | |
1987 | namespace Matchers { | |
1988 | namespace Impl { | |
1989 | ||
1990 | template<typename ArgT> struct MatchAllOf; | |
1991 | template<typename ArgT> struct MatchAnyOf; | |
1992 | template<typename ArgT> struct MatchNotOf; | |
1993 | ||
1994 | class MatcherUntypedBase { | |
1995 | public: | |
1996 | MatcherUntypedBase() = default; | |
1997 | MatcherUntypedBase ( MatcherUntypedBase const& ) = default; | |
1998 | MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; | |
1999 | std::string toString() const; | |
2000 | ||
2001 | protected: | |
2002 | virtual ~MatcherUntypedBase(); | |
2003 | virtual std::string describe() const = 0; | |
2004 | mutable std::string m_cachedToString; | |
2005 | }; | |
2006 | ||
2007 | template<typename ObjectT> | |
2008 | struct MatcherMethod { | |
2009 | virtual bool match( ObjectT const& arg ) const = 0; | |
2010 | }; | |
2011 | template<typename PtrT> | |
2012 | struct MatcherMethod<PtrT*> { | |
2013 | virtual bool match( PtrT* arg ) const = 0; | |
2014 | }; | |
2015 | ||
2016 | template<typename ObjectT, typename ComparatorT = ObjectT> | |
2017 | struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> { | |
2018 | ||
2019 | MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const; | |
2020 | MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const; | |
2021 | MatchNotOf<ComparatorT> operator ! () const; | |
2022 | }; | |
2023 | ||
2024 | template<typename ArgT> | |
2025 | struct MatchAllOf : MatcherBase<ArgT> { | |
2026 | bool match( ArgT const& arg ) const override { | |
2027 | for( auto matcher : m_matchers ) { | |
2028 | if (!matcher->match(arg)) | |
2029 | return false; | |
2030 | } | |
2031 | return true; | |
2032 | } | |
2033 | std::string describe() const override { | |
2034 | std::string description; | |
2035 | description.reserve( 4 + m_matchers.size()*32 ); | |
2036 | description += "( "; | |
2037 | bool first = true; | |
2038 | for( auto matcher : m_matchers ) { | |
2039 | if( first ) | |
2040 | first = false; | |
2041 | else | |
2042 | description += " and "; | |
2043 | description += matcher->toString(); | |
2044 | } | |
2045 | description += " )"; | |
2046 | return description; | |
2047 | } | |
2048 | ||
2049 | MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) { | |
2050 | m_matchers.push_back( &other ); | |
2051 | return *this; | |
2052 | } | |
2053 | ||
2054 | std::vector<MatcherBase<ArgT> const*> m_matchers; | |
2055 | }; | |
2056 | template<typename ArgT> | |
2057 | struct MatchAnyOf : MatcherBase<ArgT> { | |
2058 | ||
2059 | bool match( ArgT const& arg ) const override { | |
2060 | for( auto matcher : m_matchers ) { | |
2061 | if (matcher->match(arg)) | |
2062 | return true; | |
2063 | } | |
2064 | return false; | |
2065 | } | |
2066 | std::string describe() const override { | |
2067 | std::string description; | |
2068 | description.reserve( 4 + m_matchers.size()*32 ); | |
2069 | description += "( "; | |
2070 | bool first = true; | |
2071 | for( auto matcher : m_matchers ) { | |
2072 | if( first ) | |
2073 | first = false; | |
2074 | else | |
2075 | description += " or "; | |
2076 | description += matcher->toString(); | |
2077 | } | |
2078 | description += " )"; | |
2079 | return description; | |
2080 | } | |
2081 | ||
2082 | MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) { | |
2083 | m_matchers.push_back( &other ); | |
2084 | return *this; | |
2085 | } | |
2086 | ||
2087 | std::vector<MatcherBase<ArgT> const*> m_matchers; | |
2088 | }; | |
2089 | ||
2090 | template<typename ArgT> | |
2091 | struct MatchNotOf : MatcherBase<ArgT> { | |
2092 | ||
2093 | MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} | |
2094 | ||
2095 | bool match( ArgT const& arg ) const override { | |
2096 | return !m_underlyingMatcher.match( arg ); | |
2097 | } | |
2098 | ||
2099 | std::string describe() const override { | |
2100 | return "not " + m_underlyingMatcher.toString(); | |
2101 | } | |
2102 | MatcherBase<ArgT> const& m_underlyingMatcher; | |
2103 | }; | |
2104 | ||
2105 | template<typename ObjectT, typename ComparatorT> | |
2106 | MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const { | |
2107 | return MatchAllOf<ComparatorT>() && *this && other; | |
2108 | } | |
2109 | template<typename ObjectT, typename ComparatorT> | |
2110 | MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const { | |
2111 | return MatchAnyOf<ComparatorT>() || *this || other; | |
2112 | } | |
2113 | template<typename ObjectT, typename ComparatorT> | |
2114 | MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const { | |
2115 | return MatchNotOf<ComparatorT>( *this ); | |
2116 | } | |
2117 | ||
2118 | } // namespace Impl | |
2119 | ||
2120 | } // namespace Matchers | |
2121 | ||
2122 | using namespace Matchers; | |
2123 | using Matchers::Impl::MatcherBase; | |
2124 | ||
2125 | } // namespace Catch | |
2126 | ||
2127 | // end catch_matchers.h | |
2128 | // start catch_matchers_string.h | |
2129 | ||
2130 | #include <string> | |
2131 | ||
2132 | namespace Catch { | |
2133 | namespace Matchers { | |
2134 | ||
2135 | namespace StdString { | |
2136 | ||
2137 | struct CasedString | |
2138 | { | |
2139 | CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); | |
2140 | std::string adjustString( std::string const& str ) const; | |
2141 | std::string caseSensitivitySuffix() const; | |
2142 | ||
2143 | CaseSensitive::Choice m_caseSensitivity; | |
2144 | std::string m_str; | |
2145 | }; | |
2146 | ||
2147 | struct StringMatcherBase : MatcherBase<std::string> { | |
2148 | StringMatcherBase( std::string const& operation, CasedString const& comparator ); | |
2149 | std::string describe() const override; | |
2150 | ||
2151 | CasedString m_comparator; | |
2152 | std::string m_operation; | |
2153 | }; | |
2154 | ||
2155 | struct EqualsMatcher : StringMatcherBase { | |
2156 | EqualsMatcher( CasedString const& comparator ); | |
2157 | bool match( std::string const& source ) const override; | |
2158 | }; | |
2159 | struct ContainsMatcher : StringMatcherBase { | |
2160 | ContainsMatcher( CasedString const& comparator ); | |
2161 | bool match( std::string const& source ) const override; | |
2162 | }; | |
2163 | struct StartsWithMatcher : StringMatcherBase { | |
2164 | StartsWithMatcher( CasedString const& comparator ); | |
2165 | bool match( std::string const& source ) const override; | |
2166 | }; | |
2167 | struct EndsWithMatcher : StringMatcherBase { | |
2168 | EndsWithMatcher( CasedString const& comparator ); | |
2169 | bool match( std::string const& source ) const override; | |
2170 | }; | |
2171 | ||
2172 | } // namespace StdString | |
2173 | ||
2174 | // The following functions create the actual matcher objects. | |
2175 | // This allows the types to be inferred | |
2176 | ||
2177 | StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); | |
2178 | StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); | |
2179 | StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); | |
2180 | StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); | |
2181 | ||
2182 | } // namespace Matchers | |
2183 | } // namespace Catch | |
2184 | ||
2185 | // end catch_matchers_string.h | |
2186 | // start catch_matchers_vector.h | |
2187 | ||
2188 | namespace Catch { | |
2189 | namespace Matchers { | |
2190 | ||
2191 | namespace Vector { | |
2192 | ||
2193 | template<typename T> | |
2194 | struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> { | |
2195 | ||
2196 | ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} | |
2197 | ||
2198 | bool match(std::vector<T> const &v) const override { | |
2199 | for (auto const& el : v) { | |
2200 | if (el == m_comparator) { | |
2201 | return true; | |
2202 | } | |
2203 | } | |
2204 | return false; | |
2205 | } | |
2206 | ||
2207 | std::string describe() const override { | |
2208 | return "Contains: " + ::Catch::Detail::stringify( m_comparator ); | |
2209 | } | |
2210 | ||
2211 | T const& m_comparator; | |
2212 | }; | |
2213 | ||
2214 | template<typename T> | |
2215 | struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > { | |
2216 | ||
2217 | ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} | |
2218 | ||
2219 | bool match(std::vector<T> const &v) const override { | |
2220 | // !TBD: see note in EqualsMatcher | |
2221 | if (m_comparator.size() > v.size()) | |
2222 | return false; | |
2223 | for (auto const& comparator : m_comparator) { | |
2224 | auto present = false; | |
2225 | for (const auto& el : v) { | |
2226 | if (el == comparator) { | |
2227 | present = true; | |
2228 | break; | |
2229 | } | |
2230 | } | |
2231 | if (!present) { | |
2232 | return false; | |
2233 | } | |
2234 | } | |
2235 | return true; | |
2236 | } | |
2237 | std::string describe() const override { | |
2238 | return "Contains: " + ::Catch::Detail::stringify( m_comparator ); | |
2239 | } | |
2240 | ||
2241 | std::vector<T> const& m_comparator; | |
2242 | }; | |
2243 | ||
2244 | template<typename T> | |
2245 | struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > { | |
2246 | ||
2247 | EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} | |
2248 | ||
2249 | bool match(std::vector<T> const &v) const override { | |
2250 | // !TBD: This currently works if all elements can be compared using != | |
2251 | // - a more general approach would be via a compare template that defaults | |
2252 | // to using !=. but could be specialised for, e.g. std::vector<T> etc | |
2253 | // - then just call that directly | |
2254 | if (m_comparator.size() != v.size()) | |
2255 | return false; | |
2256 | for (std::size_t i = 0; i < v.size(); ++i) | |
2257 | if (m_comparator[i] != v[i]) | |
2258 | return false; | |
2259 | return true; | |
2260 | } | |
2261 | std::string describe() const override { | |
2262 | return "Equals: " + ::Catch::Detail::stringify( m_comparator ); | |
2263 | } | |
2264 | std::vector<T> const& m_comparator; | |
2265 | }; | |
2266 | ||
2267 | } // namespace Vector | |
2268 | ||
2269 | // The following functions create the actual matcher objects. | |
2270 | // This allows the types to be inferred | |
2271 | ||
2272 | template<typename T> | |
2273 | Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) { | |
2274 | return Vector::ContainsMatcher<T>( comparator ); | |
2275 | } | |
2276 | ||
2277 | template<typename T> | |
2278 | Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) { | |
2279 | return Vector::ContainsElementMatcher<T>( comparator ); | |
2280 | } | |
2281 | ||
2282 | template<typename T> | |
2283 | Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) { | |
2284 | return Vector::EqualsMatcher<T>( comparator ); | |
2285 | } | |
2286 | ||
2287 | } // namespace Matchers | |
2288 | } // namespace Catch | |
2289 | ||
2290 | // end catch_matchers_vector.h | |
2291 | namespace Catch { | |
2292 | ||
2293 | template<typename ArgT, typename MatcherT> | |
2294 | class MatchExpr : public ITransientExpression { | |
2295 | ArgT const& m_arg; | |
2296 | MatcherT m_matcher; | |
2297 | StringRef m_matcherString; | |
2298 | bool m_result; | |
2299 | public: | |
2300 | MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) | |
2301 | : m_arg( arg ), | |
2302 | m_matcher( matcher ), | |
2303 | m_matcherString( matcherString ), | |
2304 | m_result( matcher.match( arg ) ) | |
2305 | {} | |
2306 | ||
2307 | auto isBinaryExpression() const -> bool override { return true; } | |
2308 | auto getResult() const -> bool override { return m_result; } | |
2309 | ||
2310 | void streamReconstructedExpression( std::ostream &os ) const override { | |
2311 | auto matcherAsString = m_matcher.toString(); | |
2312 | os << Catch::Detail::stringify( m_arg ) << ' '; | |
2313 | if( matcherAsString == Detail::unprintableString ) | |
2314 | os << m_matcherString; | |
2315 | else | |
2316 | os << matcherAsString; | |
2317 | } | |
2318 | }; | |
2319 | ||
2320 | using StringMatcher = Matchers::Impl::MatcherBase<std::string>; | |
2321 | ||
2322 | void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); | |
2323 | ||
2324 | template<typename ArgT, typename MatcherT> | |
2325 | auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr<ArgT, MatcherT> { | |
2326 | return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString ); | |
2327 | } | |
2328 | ||
2329 | } // namespace Catch | |
2330 | ||
2331 | /////////////////////////////////////////////////////////////////////////////// | |
2332 | #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ | |
2333 | do { \ | |
2334 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ | |
2335 | INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ | |
2336 | catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ | |
2337 | } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ | |
2338 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
2339 | } while( Catch::alwaysFalse() ) | |
2340 | ||
2341 | /////////////////////////////////////////////////////////////////////////////// | |
2342 | #define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ | |
2343 | do { \ | |
2344 | Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ | |
2345 | if( catchAssertionHandler.allowThrows() ) \ | |
2346 | try { \ | |
2347 | static_cast<void>(__VA_ARGS__ ); \ | |
2348 | catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ | |
2349 | } \ | |
2350 | catch( exceptionType const& ex ) { \ | |
2351 | catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ | |
2352 | } \ | |
2353 | catch( ... ) { \ | |
2354 | catchAssertionHandler.useActiveException(); \ | |
2355 | } \ | |
2356 | else \ | |
2357 | catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ | |
2358 | INTERNAL_CATCH_REACT( catchAssertionHandler ) \ | |
2359 | } while( Catch::alwaysFalse() ) | |
2360 | ||
2361 | // end catch_capture_matchers.h | |
2362 | #endif | |
2363 | ||
2364 | // These files are included here so the single_include script doesn't put them | |
2365 | // in the conditionally compiled sections | |
2366 | // start catch_test_case_info.h | |
2367 | ||
2368 | #include <string> | |
2369 | #include <vector> | |
2370 | #include <memory> | |
2371 | ||
2372 | #ifdef __clang__ | |
2373 | #pragma clang diagnostic push | |
2374 | #pragma clang diagnostic ignored "-Wpadded" | |
2375 | #endif | |
2376 | ||
2377 | namespace Catch { | |
2378 | ||
2379 | struct ITestInvoker; | |
2380 | ||
2381 | struct TestCaseInfo { | |
2382 | enum SpecialProperties{ | |
2383 | None = 0, | |
2384 | IsHidden = 1 << 1, | |
2385 | ShouldFail = 1 << 2, | |
2386 | MayFail = 1 << 3, | |
2387 | Throws = 1 << 4, | |
2388 | NonPortable = 1 << 5, | |
2389 | Benchmark = 1 << 6 | |
2390 | }; | |
2391 | ||
2392 | TestCaseInfo( std::string const& _name, | |
2393 | std::string const& _className, | |
2394 | std::string const& _description, | |
2395 | std::vector<std::string> const& _tags, | |
2396 | SourceLineInfo const& _lineInfo ); | |
2397 | ||
2398 | friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ); | |
2399 | ||
2400 | bool isHidden() const; | |
2401 | bool throws() const; | |
2402 | bool okToFail() const; | |
2403 | bool expectedToFail() const; | |
2404 | ||
2405 | std::string tagsAsString() const; | |
2406 | ||
2407 | std::string name; | |
2408 | std::string className; | |
2409 | std::string description; | |
2410 | std::vector<std::string> tags; | |
2411 | std::vector<std::string> lcaseTags; | |
2412 | SourceLineInfo lineInfo; | |
2413 | SpecialProperties properties; | |
2414 | }; | |
2415 | ||
2416 | class TestCase : public TestCaseInfo { | |
2417 | public: | |
2418 | ||
2419 | TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); | |
2420 | ||
2421 | TestCase withName( std::string const& _newName ) const; | |
2422 | ||
2423 | void invoke() const; | |
2424 | ||
2425 | TestCaseInfo const& getTestCaseInfo() const; | |
2426 | ||
2427 | bool operator == ( TestCase const& other ) const; | |
2428 | bool operator < ( TestCase const& other ) const; | |
2429 | ||
2430 | private: | |
2431 | std::shared_ptr<ITestInvoker> test; | |
2432 | }; | |
2433 | ||
2434 | TestCase makeTestCase( ITestInvoker* testCase, | |
2435 | std::string const& className, | |
2436 | std::string const& name, | |
2437 | std::string const& description, | |
2438 | SourceLineInfo const& lineInfo ); | |
2439 | } | |
2440 | ||
2441 | #ifdef __clang__ | |
2442 | #pragma clang diagnostic pop | |
2443 | #endif | |
2444 | ||
2445 | // end catch_test_case_info.h | |
2446 | // start catch_interfaces_runner.h | |
2447 | ||
2448 | namespace Catch { | |
2449 | ||
2450 | struct IRunner { | |
2451 | virtual ~IRunner(); | |
2452 | virtual bool aborting() const = 0; | |
2453 | }; | |
2454 | } | |
2455 | ||
2456 | // end catch_interfaces_runner.h | |
2457 | ||
2458 | #ifdef __OBJC__ | |
2459 | // start catch_objc.hpp | |
2460 | ||
2461 | #import <objc/runtime.h> | |
2462 | ||
2463 | #include <string> | |
2464 | ||
2465 | // NB. Any general catch headers included here must be included | |
2466 | // in catch.hpp first to make sure they are included by the single | |
2467 | // header for non obj-usage | |
2468 | ||
2469 | /////////////////////////////////////////////////////////////////////////////// | |
2470 | // This protocol is really only here for (self) documenting purposes, since | |
2471 | // all its methods are optional. | |
2472 | @protocol OcFixture | |
2473 | ||
2474 | @optional | |
2475 | ||
2476 | -(void) setUp; | |
2477 | -(void) tearDown; | |
2478 | ||
2479 | @end | |
2480 | ||
2481 | namespace Catch { | |
2482 | ||
2483 | class OcMethod : public ITestInvoker { | |
2484 | ||
2485 | public: | |
2486 | OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} | |
2487 | ||
2488 | virtual void invoke() const { | |
2489 | id obj = [[m_cls alloc] init]; | |
2490 | ||
2491 | performOptionalSelector( obj, @selector(setUp) ); | |
2492 | performOptionalSelector( obj, m_sel ); | |
2493 | performOptionalSelector( obj, @selector(tearDown) ); | |
2494 | ||
2495 | arcSafeRelease( obj ); | |
2496 | } | |
2497 | private: | |
2498 | virtual ~OcMethod() {} | |
2499 | ||
2500 | Class m_cls; | |
2501 | SEL m_sel; | |
2502 | }; | |
2503 | ||
2504 | namespace Detail{ | |
2505 | ||
2506 | inline std::string getAnnotation( Class cls, | |
2507 | std::string const& annotationName, | |
2508 | std::string const& testCaseName ) { | |
2509 | NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; | |
2510 | SEL sel = NSSelectorFromString( selStr ); | |
2511 | arcSafeRelease( selStr ); | |
2512 | id value = performOptionalSelector( cls, sel ); | |
2513 | if( value ) | |
2514 | return [(NSString*)value UTF8String]; | |
2515 | return ""; | |
2516 | } | |
2517 | } | |
2518 | ||
2519 | inline std::size_t registerTestMethods() { | |
2520 | std::size_t noTestMethods = 0; | |
2521 | int noClasses = objc_getClassList( nullptr, 0 ); | |
2522 | ||
2523 | Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); | |
2524 | objc_getClassList( classes, noClasses ); | |
2525 | ||
2526 | for( int c = 0; c < noClasses; c++ ) { | |
2527 | Class cls = classes[c]; | |
2528 | { | |
2529 | u_int count; | |
2530 | Method* methods = class_copyMethodList( cls, &count ); | |
2531 | for( u_int m = 0; m < count ; m++ ) { | |
2532 | SEL selector = method_getName(methods[m]); | |
2533 | std::string methodName = sel_getName(selector); | |
2534 | if( startsWith( methodName, "Catch_TestCase_" ) ) { | |
2535 | std::string testCaseName = methodName.substr( 15 ); | |
2536 | std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); | |
2537 | std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); | |
2538 | const char* className = class_getName( cls ); | |
2539 | ||
2540 | getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); | |
2541 | noTestMethods++; | |
2542 | } | |
2543 | } | |
2544 | free(methods); | |
2545 | } | |
2546 | } | |
2547 | return noTestMethods; | |
2548 | } | |
2549 | ||
2550 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
2551 | ||
2552 | namespace Matchers { | |
2553 | namespace Impl { | |
2554 | namespace NSStringMatchers { | |
2555 | ||
2556 | struct StringHolder : MatcherBase<NSString*>{ | |
2557 | StringHolder( NSString* substr ) : m_substr( [substr copy] ){} | |
2558 | StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} | |
2559 | StringHolder() { | |
2560 | arcSafeRelease( m_substr ); | |
2561 | } | |
2562 | ||
2563 | bool match( NSString* arg ) const override { | |
2564 | return false; | |
2565 | } | |
2566 | ||
2567 | NSString* CATCH_ARC_STRONG m_substr; | |
2568 | }; | |
2569 | ||
2570 | struct Equals : StringHolder { | |
2571 | Equals( NSString* substr ) : StringHolder( substr ){} | |
2572 | ||
2573 | bool match( NSString* str ) const override { | |
2574 | return (str != nil || m_substr == nil ) && | |
2575 | [str isEqualToString:m_substr]; | |
2576 | } | |
2577 | ||
2578 | std::string describe() const override { | |
2579 | return "equals string: " + Catch::Detail::stringify( m_substr ); | |
2580 | } | |
2581 | }; | |
2582 | ||
2583 | struct Contains : StringHolder { | |
2584 | Contains( NSString* substr ) : StringHolder( substr ){} | |
2585 | ||
2586 | bool match( NSString* str ) const { | |
2587 | return (str != nil || m_substr == nil ) && | |
2588 | [str rangeOfString:m_substr].location != NSNotFound; | |
2589 | } | |
2590 | ||
2591 | std::string describe() const override { | |
2592 | return "contains string: " + Catch::Detail::stringify( m_substr ); | |
2593 | } | |
2594 | }; | |
2595 | ||
2596 | struct StartsWith : StringHolder { | |
2597 | StartsWith( NSString* substr ) : StringHolder( substr ){} | |
2598 | ||
2599 | bool match( NSString* str ) const override { | |
2600 | return (str != nil || m_substr == nil ) && | |
2601 | [str rangeOfString:m_substr].location == 0; | |
2602 | } | |
2603 | ||
2604 | std::string describe() const override { | |
2605 | return "starts with: " + Catch::Detail::stringify( m_substr ); | |
2606 | } | |
2607 | }; | |
2608 | struct EndsWith : StringHolder { | |
2609 | EndsWith( NSString* substr ) : StringHolder( substr ){} | |
2610 | ||
2611 | bool match( NSString* str ) const override { | |
2612 | return (str != nil || m_substr == nil ) && | |
2613 | [str rangeOfString:m_substr].location == [str length] - [m_substr length]; | |
2614 | } | |
2615 | ||
2616 | std::string describe() const override { | |
2617 | return "ends with: " + Catch::Detail::stringify( m_substr ); | |
2618 | } | |
2619 | }; | |
2620 | ||
2621 | } // namespace NSStringMatchers | |
2622 | } // namespace Impl | |
2623 | ||
2624 | inline Impl::NSStringMatchers::Equals | |
2625 | Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } | |
2626 | ||
2627 | inline Impl::NSStringMatchers::Contains | |
2628 | Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } | |
2629 | ||
2630 | inline Impl::NSStringMatchers::StartsWith | |
2631 | StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } | |
2632 | ||
2633 | inline Impl::NSStringMatchers::EndsWith | |
2634 | EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } | |
2635 | ||
2636 | } // namespace Matchers | |
2637 | ||
2638 | using namespace Matchers; | |
2639 | ||
2640 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
2641 | ||
2642 | } // namespace Catch | |
2643 | ||
2644 | /////////////////////////////////////////////////////////////////////////////// | |
2645 | #define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix | |
2646 | #define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ | |
2647 | +(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ | |
2648 | { \ | |
2649 | return @ name; \ | |
2650 | } \ | |
2651 | +(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ | |
2652 | { \ | |
2653 | return @ desc; \ | |
2654 | } \ | |
2655 | -(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) | |
2656 | ||
2657 | #define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) | |
2658 | ||
2659 | // end catch_objc.hpp | |
2660 | #endif | |
2661 | ||
2662 | #ifdef CATCH_CONFIG_EXTERNAL_INTERFACES | |
2663 | // start catch_external_interfaces.h | |
2664 | ||
2665 | // start catch_reporter_bases.hpp | |
2666 | ||
2667 | // start catch_interfaces_reporter.h | |
2668 | ||
2669 | // start catch_config.hpp | |
2670 | ||
2671 | // start catch_test_spec_parser.h | |
2672 | ||
2673 | #ifdef __clang__ | |
2674 | #pragma clang diagnostic push | |
2675 | #pragma clang diagnostic ignored "-Wpadded" | |
2676 | #endif | |
2677 | ||
2678 | // start catch_test_spec.h | |
2679 | ||
2680 | #ifdef __clang__ | |
2681 | #pragma clang diagnostic push | |
2682 | #pragma clang diagnostic ignored "-Wpadded" | |
2683 | #endif | |
2684 | ||
2685 | // start catch_wildcard_pattern.h | |
2686 | ||
2687 | namespace Catch | |
2688 | { | |
2689 | class WildcardPattern { | |
2690 | enum WildcardPosition { | |
2691 | NoWildcard = 0, | |
2692 | WildcardAtStart = 1, | |
2693 | WildcardAtEnd = 2, | |
2694 | WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd | |
2695 | }; | |
2696 | ||
2697 | public: | |
2698 | ||
2699 | WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); | |
2700 | virtual ~WildcardPattern() = default; | |
2701 | virtual bool matches( std::string const& str ) const; | |
2702 | ||
2703 | private: | |
2704 | std::string adjustCase( std::string const& str ) const; | |
2705 | CaseSensitive::Choice m_caseSensitivity; | |
2706 | WildcardPosition m_wildcard = NoWildcard; | |
2707 | std::string m_pattern; | |
2708 | }; | |
2709 | } | |
2710 | ||
2711 | // end catch_wildcard_pattern.h | |
2712 | #include <string> | |
2713 | #include <vector> | |
2714 | #include <memory> | |
2715 | ||
2716 | namespace Catch { | |
2717 | ||
2718 | class TestSpec { | |
2719 | struct Pattern { | |
2720 | virtual ~Pattern(); | |
2721 | virtual bool matches( TestCaseInfo const& testCase ) const = 0; | |
2722 | }; | |
2723 | using PatternPtr = std::shared_ptr<Pattern>; | |
2724 | ||
2725 | class NamePattern : public Pattern { | |
2726 | public: | |
2727 | NamePattern( std::string const& name ); | |
2728 | virtual ~NamePattern(); | |
2729 | virtual bool matches( TestCaseInfo const& testCase ) const override; | |
2730 | private: | |
2731 | WildcardPattern m_wildcardPattern; | |
2732 | }; | |
2733 | ||
2734 | class TagPattern : public Pattern { | |
2735 | public: | |
2736 | TagPattern( std::string const& tag ); | |
2737 | virtual ~TagPattern(); | |
2738 | virtual bool matches( TestCaseInfo const& testCase ) const override; | |
2739 | private: | |
2740 | std::string m_tag; | |
2741 | }; | |
2742 | ||
2743 | class ExcludedPattern : public Pattern { | |
2744 | public: | |
2745 | ExcludedPattern( PatternPtr const& underlyingPattern ); | |
2746 | virtual ~ExcludedPattern(); | |
2747 | virtual bool matches( TestCaseInfo const& testCase ) const override; | |
2748 | private: | |
2749 | PatternPtr m_underlyingPattern; | |
2750 | }; | |
2751 | ||
2752 | struct Filter { | |
2753 | std::vector<PatternPtr> m_patterns; | |
2754 | ||
2755 | bool matches( TestCaseInfo const& testCase ) const; | |
2756 | }; | |
2757 | ||
2758 | public: | |
2759 | bool hasFilters() const; | |
2760 | bool matches( TestCaseInfo const& testCase ) const; | |
2761 | ||
2762 | private: | |
2763 | std::vector<Filter> m_filters; | |
2764 | ||
2765 | friend class TestSpecParser; | |
2766 | }; | |
2767 | } | |
2768 | ||
2769 | #ifdef __clang__ | |
2770 | #pragma clang diagnostic pop | |
2771 | #endif | |
2772 | ||
2773 | // end catch_test_spec.h | |
2774 | // start catch_interfaces_tag_alias_registry.h | |
2775 | ||
2776 | #include <string> | |
2777 | ||
2778 | namespace Catch { | |
2779 | ||
2780 | struct TagAlias; | |
2781 | ||
2782 | struct ITagAliasRegistry { | |
2783 | virtual ~ITagAliasRegistry(); | |
2784 | // Nullptr if not present | |
2785 | virtual TagAlias const* find( std::string const& alias ) const = 0; | |
2786 | virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; | |
2787 | ||
2788 | static ITagAliasRegistry const& get(); | |
2789 | }; | |
2790 | ||
2791 | } // end namespace Catch | |
2792 | ||
2793 | // end catch_interfaces_tag_alias_registry.h | |
2794 | namespace Catch { | |
2795 | ||
2796 | class TestSpecParser { | |
2797 | enum Mode{ None, Name, QuotedName, Tag, EscapedName }; | |
2798 | Mode m_mode = None; | |
2799 | bool m_exclusion = false; | |
2800 | std::size_t m_start = std::string::npos, m_pos = 0; | |
2801 | std::string m_arg; | |
2802 | std::vector<std::size_t> m_escapeChars; | |
2803 | TestSpec::Filter m_currentFilter; | |
2804 | TestSpec m_testSpec; | |
2805 | ITagAliasRegistry const* m_tagAliases = nullptr; | |
2806 | ||
2807 | public: | |
2808 | TestSpecParser( ITagAliasRegistry const& tagAliases ); | |
2809 | ||
2810 | TestSpecParser& parse( std::string const& arg ); | |
2811 | TestSpec testSpec(); | |
2812 | ||
2813 | private: | |
2814 | void visitChar( char c ); | |
2815 | void startNewMode( Mode mode, std::size_t start ); | |
2816 | void escape(); | |
2817 | std::string subString() const; | |
2818 | ||
2819 | template<typename T> | |
2820 | void addPattern() { | |
2821 | std::string token = subString(); | |
2822 | for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) | |
2823 | token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); | |
2824 | m_escapeChars.clear(); | |
2825 | if( startsWith( token, "exclude:" ) ) { | |
2826 | m_exclusion = true; | |
2827 | token = token.substr( 8 ); | |
2828 | } | |
2829 | if( !token.empty() ) { | |
2830 | TestSpec::PatternPtr pattern = std::make_shared<T>( token ); | |
2831 | if( m_exclusion ) | |
2832 | pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern ); | |
2833 | m_currentFilter.m_patterns.push_back( pattern ); | |
2834 | } | |
2835 | m_exclusion = false; | |
2836 | m_mode = None; | |
2837 | } | |
2838 | ||
2839 | void addFilter(); | |
2840 | }; | |
2841 | TestSpec parseTestSpec( std::string const& arg ); | |
2842 | ||
2843 | } // namespace Catch | |
2844 | ||
2845 | #ifdef __clang__ | |
2846 | #pragma clang diagnostic pop | |
2847 | #endif | |
2848 | ||
2849 | // end catch_test_spec_parser.h | |
2850 | // start catch_interfaces_config.h | |
2851 | ||
2852 | #include <iosfwd> | |
2853 | #include <string> | |
2854 | #include <vector> | |
2855 | #include <memory> | |
2856 | ||
2857 | namespace Catch { | |
2858 | ||
2859 | enum class Verbosity { | |
2860 | Quiet = 0, | |
2861 | Normal, | |
2862 | High | |
2863 | }; | |
2864 | ||
2865 | struct WarnAbout { enum What { | |
2866 | Nothing = 0x00, | |
2867 | NoAssertions = 0x01 | |
2868 | }; }; | |
2869 | ||
2870 | struct ShowDurations { enum OrNot { | |
2871 | DefaultForReporter, | |
2872 | Always, | |
2873 | Never | |
2874 | }; }; | |
2875 | struct RunTests { enum InWhatOrder { | |
2876 | InDeclarationOrder, | |
2877 | InLexicographicalOrder, | |
2878 | InRandomOrder | |
2879 | }; }; | |
2880 | struct UseColour { enum YesOrNo { | |
2881 | Auto, | |
2882 | Yes, | |
2883 | No | |
2884 | }; }; | |
2885 | struct WaitForKeypress { enum When { | |
2886 | Never, | |
2887 | BeforeStart = 1, | |
2888 | BeforeExit = 2, | |
2889 | BeforeStartAndExit = BeforeStart | BeforeExit | |
2890 | }; }; | |
2891 | ||
2892 | class TestSpec; | |
2893 | ||
2894 | struct IConfig : NonCopyable { | |
2895 | ||
2896 | virtual ~IConfig(); | |
2897 | ||
2898 | virtual bool allowThrows() const = 0; | |
2899 | virtual std::ostream& stream() const = 0; | |
2900 | virtual std::string name() const = 0; | |
2901 | virtual bool includeSuccessfulResults() const = 0; | |
2902 | virtual bool shouldDebugBreak() const = 0; | |
2903 | virtual bool warnAboutMissingAssertions() const = 0; | |
2904 | virtual int abortAfter() const = 0; | |
2905 | virtual bool showInvisibles() const = 0; | |
2906 | virtual ShowDurations::OrNot showDurations() const = 0; | |
2907 | virtual TestSpec const& testSpec() const = 0; | |
2908 | virtual RunTests::InWhatOrder runOrder() const = 0; | |
2909 | virtual unsigned int rngSeed() const = 0; | |
2910 | virtual int benchmarkResolutionMultiple() const = 0; | |
2911 | virtual UseColour::YesOrNo useColour() const = 0; | |
2912 | virtual std::vector<std::string> const& getSectionsToRun() const = 0; | |
2913 | virtual Verbosity verbosity() const = 0; | |
2914 | }; | |
2915 | ||
2916 | using IConfigPtr = std::shared_ptr<IConfig const>; | |
2917 | } | |
2918 | ||
2919 | // end catch_interfaces_config.h | |
2920 | // Libstdc++ doesn't like incomplete classes for unique_ptr | |
2921 | // start catch_stream.h | |
2922 | ||
2923 | // start catch_streambuf.h | |
2924 | ||
2925 | #include <streambuf> | |
2926 | ||
2927 | namespace Catch { | |
2928 | ||
2929 | class StreamBufBase : public std::streambuf { | |
2930 | public: | |
2931 | virtual ~StreamBufBase(); | |
2932 | }; | |
2933 | } | |
2934 | ||
2935 | // end catch_streambuf.h | |
2936 | #include <streambuf> | |
2937 | #include <ostream> | |
2938 | #include <fstream> | |
2939 | #include <memory> | |
2940 | ||
2941 | namespace Catch { | |
2942 | ||
2943 | std::ostream& cout(); | |
2944 | std::ostream& cerr(); | |
2945 | std::ostream& clog(); | |
2946 | ||
2947 | struct IStream { | |
2948 | virtual ~IStream(); | |
2949 | virtual std::ostream& stream() const = 0; | |
2950 | }; | |
2951 | ||
2952 | class FileStream : public IStream { | |
2953 | mutable std::ofstream m_ofs; | |
2954 | public: | |
2955 | FileStream( std::string const& filename ); | |
2956 | ~FileStream() override = default; | |
2957 | public: // IStream | |
2958 | std::ostream& stream() const override; | |
2959 | }; | |
2960 | ||
2961 | class CoutStream : public IStream { | |
2962 | mutable std::ostream m_os; | |
2963 | public: | |
2964 | CoutStream(); | |
2965 | ~CoutStream() override = default; | |
2966 | ||
2967 | public: // IStream | |
2968 | std::ostream& stream() const override; | |
2969 | }; | |
2970 | ||
2971 | class DebugOutStream : public IStream { | |
2972 | std::unique_ptr<StreamBufBase> m_streamBuf; | |
2973 | mutable std::ostream m_os; | |
2974 | public: | |
2975 | DebugOutStream(); | |
2976 | ~DebugOutStream() override = default; | |
2977 | ||
2978 | public: // IStream | |
2979 | std::ostream& stream() const override; | |
2980 | }; | |
2981 | } | |
2982 | ||
2983 | // end catch_stream.h | |
2984 | ||
2985 | #include <memory> | |
2986 | #include <vector> | |
2987 | #include <string> | |
2988 | ||
2989 | #ifndef CATCH_CONFIG_CONSOLE_WIDTH | |
2990 | #define CATCH_CONFIG_CONSOLE_WIDTH 80 | |
2991 | #endif | |
2992 | ||
2993 | namespace Catch { | |
2994 | ||
2995 | struct IStream; | |
2996 | ||
2997 | struct ConfigData { | |
2998 | bool listTests = false; | |
2999 | bool listTags = false; | |
3000 | bool listReporters = false; | |
3001 | bool listTestNamesOnly = false; | |
3002 | ||
3003 | bool showSuccessfulTests = false; | |
3004 | bool shouldDebugBreak = false; | |
3005 | bool noThrow = false; | |
3006 | bool showHelp = false; | |
3007 | bool showInvisibles = false; | |
3008 | bool filenamesAsTags = false; | |
3009 | bool libIdentify = false; | |
3010 | ||
3011 | int abortAfter = -1; | |
3012 | unsigned int rngSeed = 0; | |
3013 | int benchmarkResolutionMultiple = 100; | |
3014 | ||
3015 | Verbosity verbosity = Verbosity::Normal; | |
3016 | WarnAbout::What warnings = WarnAbout::Nothing; | |
3017 | ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; | |
3018 | RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; | |
3019 | UseColour::YesOrNo useColour = UseColour::Auto; | |
3020 | WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; | |
3021 | ||
3022 | std::string outputFilename; | |
3023 | std::string name; | |
3024 | std::string processName; | |
3025 | ||
3026 | std::vector<std::string> reporterNames; | |
3027 | std::vector<std::string> testsOrTags; | |
3028 | std::vector<std::string> sectionsToRun; | |
3029 | }; | |
3030 | ||
3031 | class Config : public IConfig { | |
3032 | public: | |
3033 | ||
3034 | Config() = default; | |
3035 | Config( ConfigData const& data ); | |
3036 | virtual ~Config() = default; | |
3037 | ||
3038 | std::string const& getFilename() const; | |
3039 | ||
3040 | bool listTests() const; | |
3041 | bool listTestNamesOnly() const; | |
3042 | bool listTags() const; | |
3043 | bool listReporters() const; | |
3044 | ||
3045 | std::string getProcessName() const; | |
3046 | ||
3047 | std::vector<std::string> const& getReporterNames() const; | |
3048 | std::vector<std::string> const& getSectionsToRun() const override; | |
3049 | ||
3050 | virtual TestSpec const& testSpec() const override; | |
3051 | ||
3052 | bool showHelp() const; | |
3053 | ||
3054 | // IConfig interface | |
3055 | bool allowThrows() const override; | |
3056 | std::ostream& stream() const override; | |
3057 | std::string name() const override; | |
3058 | bool includeSuccessfulResults() const override; | |
3059 | bool warnAboutMissingAssertions() const override; | |
3060 | ShowDurations::OrNot showDurations() const override; | |
3061 | RunTests::InWhatOrder runOrder() const override; | |
3062 | unsigned int rngSeed() const override; | |
3063 | int benchmarkResolutionMultiple() const override; | |
3064 | UseColour::YesOrNo useColour() const override; | |
3065 | bool shouldDebugBreak() const override; | |
3066 | int abortAfter() const override; | |
3067 | bool showInvisibles() const override; | |
3068 | Verbosity verbosity() const override; | |
3069 | ||
3070 | private: | |
3071 | ||
3072 | IStream const* openStream(); | |
3073 | ConfigData m_data; | |
3074 | ||
3075 | std::unique_ptr<IStream const> m_stream; | |
3076 | TestSpec m_testSpec; | |
3077 | }; | |
3078 | ||
3079 | } // end namespace Catch | |
3080 | ||
3081 | // end catch_config.hpp | |
3082 | // start catch_assertionresult.h | |
3083 | ||
3084 | #include <string> | |
3085 | ||
3086 | namespace Catch { | |
3087 | ||
3088 | struct AssertionResultData | |
3089 | { | |
3090 | AssertionResultData() = delete; | |
3091 | ||
3092 | AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); | |
3093 | ||
3094 | std::string message; | |
3095 | mutable std::string reconstructedExpression; | |
3096 | LazyExpression lazyExpression; | |
3097 | ResultWas::OfType resultType; | |
3098 | ||
3099 | std::string reconstructExpression() const; | |
3100 | }; | |
3101 | ||
3102 | class AssertionResult { | |
3103 | public: | |
3104 | AssertionResult() = delete; | |
3105 | AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); | |
3106 | ||
3107 | bool isOk() const; | |
3108 | bool succeeded() const; | |
3109 | ResultWas::OfType getResultType() const; | |
3110 | bool hasExpression() const; | |
3111 | bool hasMessage() const; | |
3112 | std::string getExpression() const; | |
3113 | std::string getExpressionInMacro() const; | |
3114 | bool hasExpandedExpression() const; | |
3115 | std::string getExpandedExpression() const; | |
3116 | std::string getMessage() const; | |
3117 | SourceLineInfo getSourceInfo() const; | |
3118 | std::string getTestMacroName() const; | |
3119 | ||
3120 | //protected: | |
3121 | AssertionInfo m_info; | |
3122 | AssertionResultData m_resultData; | |
3123 | }; | |
3124 | ||
3125 | } // end namespace Catch | |
3126 | ||
3127 | // end catch_assertionresult.h | |
3128 | // start catch_option.hpp | |
3129 | ||
3130 | namespace Catch { | |
3131 | ||
3132 | // An optional type | |
3133 | template<typename T> | |
3134 | class Option { | |
3135 | public: | |
3136 | Option() : nullableValue( nullptr ) {} | |
3137 | Option( T const& _value ) | |
3138 | : nullableValue( new( storage ) T( _value ) ) | |
3139 | {} | |
3140 | Option( Option const& _other ) | |
3141 | : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) | |
3142 | {} | |
3143 | ||
3144 | ~Option() { | |
3145 | reset(); | |
3146 | } | |
3147 | ||
3148 | Option& operator= ( Option const& _other ) { | |
3149 | if( &_other != this ) { | |
3150 | reset(); | |
3151 | if( _other ) | |
3152 | nullableValue = new( storage ) T( *_other ); | |
3153 | } | |
3154 | return *this; | |
3155 | } | |
3156 | Option& operator = ( T const& _value ) { | |
3157 | reset(); | |
3158 | nullableValue = new( storage ) T( _value ); | |
3159 | return *this; | |
3160 | } | |
3161 | ||
3162 | void reset() { | |
3163 | if( nullableValue ) | |
3164 | nullableValue->~T(); | |
3165 | nullableValue = nullptr; | |
3166 | } | |
3167 | ||
3168 | T& operator*() { return *nullableValue; } | |
3169 | T const& operator*() const { return *nullableValue; } | |
3170 | T* operator->() { return nullableValue; } | |
3171 | const T* operator->() const { return nullableValue; } | |
3172 | ||
3173 | T valueOr( T const& defaultValue ) const { | |
3174 | return nullableValue ? *nullableValue : defaultValue; | |
3175 | } | |
3176 | ||
3177 | bool some() const { return nullableValue != nullptr; } | |
3178 | bool none() const { return nullableValue == nullptr; } | |
3179 | ||
3180 | bool operator !() const { return nullableValue == nullptr; } | |
3181 | explicit operator bool() const { | |
3182 | return some(); | |
3183 | } | |
3184 | ||
3185 | private: | |
3186 | T *nullableValue; | |
3187 | alignas(alignof(T)) char storage[sizeof(T)]; | |
3188 | }; | |
3189 | ||
3190 | } // end namespace Catch | |
3191 | ||
3192 | // end catch_option.hpp | |
3193 | #include <string> | |
3194 | #include <iosfwd> | |
3195 | #include <map> | |
3196 | #include <set> | |
3197 | #include <memory> | |
3198 | ||
3199 | namespace Catch { | |
3200 | ||
3201 | struct ReporterConfig { | |
3202 | explicit ReporterConfig( IConfigPtr const& _fullConfig ); | |
3203 | ||
3204 | ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); | |
3205 | ||
3206 | std::ostream& stream() const; | |
3207 | IConfigPtr fullConfig() const; | |
3208 | ||
3209 | private: | |
3210 | std::ostream* m_stream; | |
3211 | IConfigPtr m_fullConfig; | |
3212 | }; | |
3213 | ||
3214 | struct ReporterPreferences { | |
3215 | bool shouldRedirectStdOut = false; | |
3216 | }; | |
3217 | ||
3218 | template<typename T> | |
3219 | struct LazyStat : Option<T> { | |
3220 | LazyStat& operator=( T const& _value ) { | |
3221 | Option<T>::operator=( _value ); | |
3222 | used = false; | |
3223 | return *this; | |
3224 | } | |
3225 | void reset() { | |
3226 | Option<T>::reset(); | |
3227 | used = false; | |
3228 | } | |
3229 | bool used = false; | |
3230 | }; | |
3231 | ||
3232 | struct TestRunInfo { | |
3233 | TestRunInfo( std::string const& _name ); | |
3234 | std::string name; | |
3235 | }; | |
3236 | struct GroupInfo { | |
3237 | GroupInfo( std::string const& _name, | |
3238 | std::size_t _groupIndex, | |
3239 | std::size_t _groupsCount ); | |
3240 | ||
3241 | std::string name; | |
3242 | std::size_t groupIndex; | |
3243 | std::size_t groupsCounts; | |
3244 | }; | |
3245 | ||
3246 | struct AssertionStats { | |
3247 | AssertionStats( AssertionResult const& _assertionResult, | |
3248 | std::vector<MessageInfo> const& _infoMessages, | |
3249 | Totals const& _totals ); | |
3250 | ||
3251 | AssertionStats( AssertionStats const& ) = default; | |
3252 | AssertionStats( AssertionStats && ) = default; | |
3253 | AssertionStats& operator = ( AssertionStats const& ) = default; | |
3254 | AssertionStats& operator = ( AssertionStats && ) = default; | |
3255 | virtual ~AssertionStats(); | |
3256 | ||
3257 | AssertionResult assertionResult; | |
3258 | std::vector<MessageInfo> infoMessages; | |
3259 | Totals totals; | |
3260 | }; | |
3261 | ||
3262 | struct SectionStats { | |
3263 | SectionStats( SectionInfo const& _sectionInfo, | |
3264 | Counts const& _assertions, | |
3265 | double _durationInSeconds, | |
3266 | bool _missingAssertions ); | |
3267 | SectionStats( SectionStats const& ) = default; | |
3268 | SectionStats( SectionStats && ) = default; | |
3269 | SectionStats& operator = ( SectionStats const& ) = default; | |
3270 | SectionStats& operator = ( SectionStats && ) = default; | |
3271 | virtual ~SectionStats(); | |
3272 | ||
3273 | SectionInfo sectionInfo; | |
3274 | Counts assertions; | |
3275 | double durationInSeconds; | |
3276 | bool missingAssertions; | |
3277 | }; | |
3278 | ||
3279 | struct TestCaseStats { | |
3280 | TestCaseStats( TestCaseInfo const& _testInfo, | |
3281 | Totals const& _totals, | |
3282 | std::string const& _stdOut, | |
3283 | std::string const& _stdErr, | |
3284 | bool _aborting ); | |
3285 | ||
3286 | TestCaseStats( TestCaseStats const& ) = default; | |
3287 | TestCaseStats( TestCaseStats && ) = default; | |
3288 | TestCaseStats& operator = ( TestCaseStats const& ) = default; | |
3289 | TestCaseStats& operator = ( TestCaseStats && ) = default; | |
3290 | virtual ~TestCaseStats(); | |
3291 | ||
3292 | TestCaseInfo testInfo; | |
3293 | Totals totals; | |
3294 | std::string stdOut; | |
3295 | std::string stdErr; | |
3296 | bool aborting; | |
3297 | }; | |
3298 | ||
3299 | struct TestGroupStats { | |
3300 | TestGroupStats( GroupInfo const& _groupInfo, | |
3301 | Totals const& _totals, | |
3302 | bool _aborting ); | |
3303 | TestGroupStats( GroupInfo const& _groupInfo ); | |
3304 | ||
3305 | TestGroupStats( TestGroupStats const& ) = default; | |
3306 | TestGroupStats( TestGroupStats && ) = default; | |
3307 | TestGroupStats& operator = ( TestGroupStats const& ) = default; | |
3308 | TestGroupStats& operator = ( TestGroupStats && ) = default; | |
3309 | virtual ~TestGroupStats(); | |
3310 | ||
3311 | GroupInfo groupInfo; | |
3312 | Totals totals; | |
3313 | bool aborting; | |
3314 | }; | |
3315 | ||
3316 | struct TestRunStats { | |
3317 | TestRunStats( TestRunInfo const& _runInfo, | |
3318 | Totals const& _totals, | |
3319 | bool _aborting ); | |
3320 | ||
3321 | TestRunStats( TestRunStats const& ) = default; | |
3322 | TestRunStats( TestRunStats && ) = default; | |
3323 | TestRunStats& operator = ( TestRunStats const& ) = default; | |
3324 | TestRunStats& operator = ( TestRunStats && ) = default; | |
3325 | virtual ~TestRunStats(); | |
3326 | ||
3327 | TestRunInfo runInfo; | |
3328 | Totals totals; | |
3329 | bool aborting; | |
3330 | }; | |
3331 | ||
3332 | struct BenchmarkInfo { | |
3333 | std::string name; | |
3334 | }; | |
3335 | struct BenchmarkStats { | |
3336 | BenchmarkInfo info; | |
3337 | std::size_t iterations; | |
3338 | uint64_t elapsedTimeInNanoseconds; | |
3339 | }; | |
3340 | ||
3341 | struct IStreamingReporter { | |
3342 | virtual ~IStreamingReporter() = default; | |
3343 | ||
3344 | // Implementing class must also provide the following static methods: | |
3345 | // static std::string getDescription(); | |
3346 | // static std::set<Verbosity> getSupportedVerbosities() | |
3347 | ||
3348 | virtual ReporterPreferences getPreferences() const = 0; | |
3349 | ||
3350 | virtual void noMatchingTestCases( std::string const& spec ) = 0; | |
3351 | ||
3352 | virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; | |
3353 | virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; | |
3354 | ||
3355 | virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; | |
3356 | virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; | |
3357 | ||
3358 | // *** experimental *** | |
3359 | virtual void benchmarkStarting( BenchmarkInfo const& ) {} | |
3360 | ||
3361 | virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; | |
3362 | ||
3363 | // The return value indicates if the messages buffer should be cleared: | |
3364 | virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; | |
3365 | ||
3366 | // *** experimental *** | |
3367 | virtual void benchmarkEnded( BenchmarkStats const& ) {} | |
3368 | ||
3369 | virtual void sectionEnded( SectionStats const& sectionStats ) = 0; | |
3370 | virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; | |
3371 | virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; | |
3372 | virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; | |
3373 | ||
3374 | virtual void skipTest( TestCaseInfo const& testInfo ) = 0; | |
3375 | ||
3376 | // Default empty implementation provided | |
3377 | virtual void fatalErrorEncountered( StringRef name ); | |
3378 | ||
3379 | virtual bool isMulti() const; | |
3380 | }; | |
3381 | using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>; | |
3382 | ||
3383 | struct IReporterFactory { | |
3384 | virtual ~IReporterFactory(); | |
3385 | virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; | |
3386 | virtual std::string getDescription() const = 0; | |
3387 | }; | |
3388 | using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; | |
3389 | ||
3390 | struct IReporterRegistry { | |
3391 | using FactoryMap = std::map<std::string, IReporterFactoryPtr>; | |
3392 | using Listeners = std::vector<IReporterFactoryPtr>; | |
3393 | ||
3394 | virtual ~IReporterRegistry(); | |
3395 | virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; | |
3396 | virtual FactoryMap const& getFactories() const = 0; | |
3397 | virtual Listeners const& getListeners() const = 0; | |
3398 | }; | |
3399 | ||
3400 | void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); | |
3401 | ||
3402 | } // end namespace Catch | |
3403 | ||
3404 | // end catch_interfaces_reporter.h | |
3405 | #include <algorithm> | |
3406 | #include <cstring> | |
3407 | #include <cfloat> | |
3408 | #include <cstdio> | |
3409 | #include <assert.h> | |
3410 | #include <memory> | |
3411 | ||
3412 | namespace Catch { | |
3413 | void prepareExpandedExpression(AssertionResult& result); | |
3414 | ||
3415 | // Returns double formatted as %.3f (format expected on output) | |
3416 | std::string getFormattedDuration( double duration ); | |
3417 | ||
3418 | template<typename DerivedT> | |
3419 | struct StreamingReporterBase : IStreamingReporter { | |
3420 | ||
3421 | StreamingReporterBase( ReporterConfig const& _config ) | |
3422 | : m_config( _config.fullConfig() ), | |
3423 | stream( _config.stream() ) | |
3424 | { | |
3425 | m_reporterPrefs.shouldRedirectStdOut = false; | |
3426 | CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); | |
3427 | } | |
3428 | ||
3429 | ReporterPreferences getPreferences() const override { | |
3430 | return m_reporterPrefs; | |
3431 | } | |
3432 | ||
3433 | static std::set<Verbosity> getSupportedVerbosities() { | |
3434 | return { Verbosity::Normal }; | |
3435 | } | |
3436 | ||
3437 | ~StreamingReporterBase() override = default; | |
3438 | ||
3439 | void noMatchingTestCases(std::string const&) override {} | |
3440 | ||
3441 | void testRunStarting(TestRunInfo const& _testRunInfo) override { | |
3442 | currentTestRunInfo = _testRunInfo; | |
3443 | } | |
3444 | void testGroupStarting(GroupInfo const& _groupInfo) override { | |
3445 | currentGroupInfo = _groupInfo; | |
3446 | } | |
3447 | ||
3448 | void testCaseStarting(TestCaseInfo const& _testInfo) override { | |
3449 | currentTestCaseInfo = _testInfo; | |
3450 | } | |
3451 | void sectionStarting(SectionInfo const& _sectionInfo) override { | |
3452 | m_sectionStack.push_back(_sectionInfo); | |
3453 | } | |
3454 | ||
3455 | void sectionEnded(SectionStats const& /* _sectionStats */) override { | |
3456 | m_sectionStack.pop_back(); | |
3457 | } | |
3458 | void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { | |
3459 | currentTestCaseInfo.reset(); | |
3460 | } | |
3461 | void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { | |
3462 | currentGroupInfo.reset(); | |
3463 | } | |
3464 | void testRunEnded(TestRunStats const& /* _testRunStats */) override { | |
3465 | currentTestCaseInfo.reset(); | |
3466 | currentGroupInfo.reset(); | |
3467 | currentTestRunInfo.reset(); | |
3468 | } | |
3469 | ||
3470 | void skipTest(TestCaseInfo const&) override { | |
3471 | // Don't do anything with this by default. | |
3472 | // It can optionally be overridden in the derived class. | |
3473 | } | |
3474 | ||
3475 | IConfigPtr m_config; | |
3476 | std::ostream& stream; | |
3477 | ||
3478 | LazyStat<TestRunInfo> currentTestRunInfo; | |
3479 | LazyStat<GroupInfo> currentGroupInfo; | |
3480 | LazyStat<TestCaseInfo> currentTestCaseInfo; | |
3481 | ||
3482 | std::vector<SectionInfo> m_sectionStack; | |
3483 | ReporterPreferences m_reporterPrefs; | |
3484 | }; | |
3485 | ||
3486 | template<typename DerivedT> | |
3487 | struct CumulativeReporterBase : IStreamingReporter { | |
3488 | template<typename T, typename ChildNodeT> | |
3489 | struct Node { | |
3490 | explicit Node( T const& _value ) : value( _value ) {} | |
3491 | virtual ~Node() {} | |
3492 | ||
3493 | using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; | |
3494 | T value; | |
3495 | ChildNodes children; | |
3496 | }; | |
3497 | struct SectionNode { | |
3498 | explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} | |
3499 | virtual ~SectionNode() = default; | |
3500 | ||
3501 | bool operator == (SectionNode const& other) const { | |
3502 | return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; | |
3503 | } | |
3504 | bool operator == (std::shared_ptr<SectionNode> const& other) const { | |
3505 | return operator==(*other); | |
3506 | } | |
3507 | ||
3508 | SectionStats stats; | |
3509 | using ChildSections = std::vector<std::shared_ptr<SectionNode>>; | |
3510 | using Assertions = std::vector<AssertionStats>; | |
3511 | ChildSections childSections; | |
3512 | Assertions assertions; | |
3513 | std::string stdOut; | |
3514 | std::string stdErr; | |
3515 | }; | |
3516 | ||
3517 | struct BySectionInfo { | |
3518 | BySectionInfo( SectionInfo const& other ) : m_other( other ) {} | |
3519 | BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} | |
3520 | bool operator() (std::shared_ptr<SectionNode> const& node) const { | |
3521 | return ((node->stats.sectionInfo.name == m_other.name) && | |
3522 | (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); | |
3523 | } | |
3524 | void operator=(BySectionInfo const&) = delete; | |
3525 | ||
3526 | private: | |
3527 | SectionInfo const& m_other; | |
3528 | }; | |
3529 | ||
3530 | using TestCaseNode = Node<TestCaseStats, SectionNode>; | |
3531 | using TestGroupNode = Node<TestGroupStats, TestCaseNode>; | |
3532 | using TestRunNode = Node<TestRunStats, TestGroupNode>; | |
3533 | ||
3534 | CumulativeReporterBase( ReporterConfig const& _config ) | |
3535 | : m_config( _config.fullConfig() ), | |
3536 | stream( _config.stream() ) | |
3537 | { | |
3538 | m_reporterPrefs.shouldRedirectStdOut = false; | |
3539 | CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); | |
3540 | } | |
3541 | ~CumulativeReporterBase() override = default; | |
3542 | ||
3543 | ReporterPreferences getPreferences() const override { | |
3544 | return m_reporterPrefs; | |
3545 | } | |
3546 | ||
3547 | static std::set<Verbosity> getSupportedVerbosities() { | |
3548 | return { Verbosity::Normal }; | |
3549 | } | |
3550 | ||
3551 | void testRunStarting( TestRunInfo const& ) override {} | |
3552 | void testGroupStarting( GroupInfo const& ) override {} | |
3553 | ||
3554 | void testCaseStarting( TestCaseInfo const& ) override {} | |
3555 | ||
3556 | void sectionStarting( SectionInfo const& sectionInfo ) override { | |
3557 | SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); | |
3558 | std::shared_ptr<SectionNode> node; | |
3559 | if( m_sectionStack.empty() ) { | |
3560 | if( !m_rootSection ) | |
3561 | m_rootSection = std::make_shared<SectionNode>( incompleteStats ); | |
3562 | node = m_rootSection; | |
3563 | } | |
3564 | else { | |
3565 | SectionNode& parentNode = *m_sectionStack.back(); | |
3566 | auto it = | |
3567 | std::find_if( parentNode.childSections.begin(), | |
3568 | parentNode.childSections.end(), | |
3569 | BySectionInfo( sectionInfo ) ); | |
3570 | if( it == parentNode.childSections.end() ) { | |
3571 | node = std::make_shared<SectionNode>( incompleteStats ); | |
3572 | parentNode.childSections.push_back( node ); | |
3573 | } | |
3574 | else | |
3575 | node = *it; | |
3576 | } | |
3577 | m_sectionStack.push_back( node ); | |
3578 | m_deepestSection = std::move(node); | |
3579 | } | |
3580 | ||
3581 | void assertionStarting(AssertionInfo const&) override {} | |
3582 | ||
3583 | bool assertionEnded(AssertionStats const& assertionStats) override { | |
3584 | assert(!m_sectionStack.empty()); | |
3585 | // AssertionResult holds a pointer to a temporary DecomposedExpression, | |
3586 | // which getExpandedExpression() calls to build the expression string. | |
3587 | // Our section stack copy of the assertionResult will likely outlive the | |
3588 | // temporary, so it must be expanded or discarded now to avoid calling | |
3589 | // a destroyed object later. | |
3590 | prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); | |
3591 | SectionNode& sectionNode = *m_sectionStack.back(); | |
3592 | sectionNode.assertions.push_back(assertionStats); | |
3593 | return true; | |
3594 | } | |
3595 | void sectionEnded(SectionStats const& sectionStats) override { | |
3596 | assert(!m_sectionStack.empty()); | |
3597 | SectionNode& node = *m_sectionStack.back(); | |
3598 | node.stats = sectionStats; | |
3599 | m_sectionStack.pop_back(); | |
3600 | } | |
3601 | void testCaseEnded(TestCaseStats const& testCaseStats) override { | |
3602 | auto node = std::make_shared<TestCaseNode>(testCaseStats); | |
3603 | assert(m_sectionStack.size() == 0); | |
3604 | node->children.push_back(m_rootSection); | |
3605 | m_testCases.push_back(node); | |
3606 | m_rootSection.reset(); | |
3607 | ||
3608 | assert(m_deepestSection); | |
3609 | m_deepestSection->stdOut = testCaseStats.stdOut; | |
3610 | m_deepestSection->stdErr = testCaseStats.stdErr; | |
3611 | } | |
3612 | void testGroupEnded(TestGroupStats const& testGroupStats) override { | |
3613 | auto node = std::make_shared<TestGroupNode>(testGroupStats); | |
3614 | node->children.swap(m_testCases); | |
3615 | m_testGroups.push_back(node); | |
3616 | } | |
3617 | void testRunEnded(TestRunStats const& testRunStats) override { | |
3618 | auto node = std::make_shared<TestRunNode>(testRunStats); | |
3619 | node->children.swap(m_testGroups); | |
3620 | m_testRuns.push_back(node); | |
3621 | testRunEndedCumulative(); | |
3622 | } | |
3623 | virtual void testRunEndedCumulative() = 0; | |
3624 | ||
3625 | void skipTest(TestCaseInfo const&) override {} | |
3626 | ||
3627 | IConfigPtr m_config; | |
3628 | std::ostream& stream; | |
3629 | std::vector<AssertionStats> m_assertions; | |
3630 | std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; | |
3631 | std::vector<std::shared_ptr<TestCaseNode>> m_testCases; | |
3632 | std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; | |
3633 | ||
3634 | std::vector<std::shared_ptr<TestRunNode>> m_testRuns; | |
3635 | ||
3636 | std::shared_ptr<SectionNode> m_rootSection; | |
3637 | std::shared_ptr<SectionNode> m_deepestSection; | |
3638 | std::vector<std::shared_ptr<SectionNode>> m_sectionStack; | |
3639 | ReporterPreferences m_reporterPrefs; | |
3640 | }; | |
3641 | ||
3642 | template<char C> | |
3643 | char const* getLineOfChars() { | |
3644 | static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; | |
3645 | if( !*line ) { | |
3646 | std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); | |
3647 | line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; | |
3648 | } | |
3649 | return line; | |
3650 | } | |
3651 | ||
3652 | struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { | |
3653 | TestEventListenerBase( ReporterConfig const& _config ); | |
3654 | ||
3655 | void assertionStarting(AssertionInfo const&) override; | |
3656 | bool assertionEnded(AssertionStats const&) override; | |
3657 | }; | |
3658 | ||
3659 | } // end namespace Catch | |
3660 | ||
3661 | // end catch_reporter_bases.hpp | |
3662 | // start catch_console_colour.h | |
3663 | ||
3664 | namespace Catch { | |
3665 | ||
3666 | struct Colour { | |
3667 | enum Code { | |
3668 | None = 0, | |
3669 | ||
3670 | White, | |
3671 | Red, | |
3672 | Green, | |
3673 | Blue, | |
3674 | Cyan, | |
3675 | Yellow, | |
3676 | Grey, | |
3677 | ||
3678 | Bright = 0x10, | |
3679 | ||
3680 | BrightRed = Bright | Red, | |
3681 | BrightGreen = Bright | Green, | |
3682 | LightGrey = Bright | Grey, | |
3683 | BrightWhite = Bright | White, | |
3684 | ||
3685 | // By intention | |
3686 | FileName = LightGrey, | |
3687 | Warning = Yellow, | |
3688 | ResultError = BrightRed, | |
3689 | ResultSuccess = BrightGreen, | |
3690 | ResultExpectedFailure = Warning, | |
3691 | ||
3692 | Error = BrightRed, | |
3693 | Success = Green, | |
3694 | ||
3695 | OriginalExpression = Cyan, | |
3696 | ReconstructedExpression = Yellow, | |
3697 | ||
3698 | SecondaryText = LightGrey, | |
3699 | Headers = White | |
3700 | }; | |
3701 | ||
3702 | // Use constructed object for RAII guard | |
3703 | Colour( Code _colourCode ); | |
3704 | Colour( Colour&& other ) noexcept; | |
3705 | Colour& operator=( Colour&& other ) noexcept; | |
3706 | ~Colour(); | |
3707 | ||
3708 | // Use static method for one-shot changes | |
3709 | static void use( Code _colourCode ); | |
3710 | ||
3711 | private: | |
3712 | bool m_moved = false; | |
3713 | }; | |
3714 | ||
3715 | std::ostream& operator << ( std::ostream& os, Colour const& ); | |
3716 | ||
3717 | } // end namespace Catch | |
3718 | ||
3719 | // end catch_console_colour.h | |
3720 | // start catch_reporter_registrars.hpp | |
3721 | ||
3722 | ||
3723 | namespace Catch { | |
3724 | ||
3725 | template<typename T> | |
3726 | class ReporterRegistrar { | |
3727 | ||
3728 | class ReporterFactory : public IReporterFactory { | |
3729 | ||
3730 | virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { | |
3731 | return std::unique_ptr<T>( new T( config ) ); | |
3732 | } | |
3733 | ||
3734 | virtual std::string getDescription() const override { | |
3735 | return T::getDescription(); | |
3736 | } | |
3737 | }; | |
3738 | ||
3739 | public: | |
3740 | ||
3741 | ReporterRegistrar( std::string const& name ) { | |
3742 | getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() ); | |
3743 | } | |
3744 | }; | |
3745 | ||
3746 | template<typename T> | |
3747 | class ListenerRegistrar { | |
3748 | ||
3749 | class ListenerFactory : public IReporterFactory { | |
3750 | ||
3751 | virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { | |
3752 | return std::unique_ptr<T>( new T( config ) ); | |
3753 | } | |
3754 | virtual std::string getDescription() const override { | |
3755 | return std::string(); | |
3756 | } | |
3757 | }; | |
3758 | ||
3759 | public: | |
3760 | ||
3761 | ListenerRegistrar() { | |
3762 | getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() ); | |
3763 | } | |
3764 | }; | |
3765 | } | |
3766 | ||
3767 | #if !defined(CATCH_CONFIG_DISABLE) | |
3768 | ||
3769 | #define CATCH_REGISTER_REPORTER( name, reporterType ) \ | |
3770 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
3771 | namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \ | |
3772 | CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS | |
3773 | ||
3774 | #define CATCH_REGISTER_LISTENER( listenerType ) \ | |
3775 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ | |
3776 | namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \ | |
3777 | CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS | |
3778 | #else // CATCH_CONFIG_DISABLE | |
3779 | ||
3780 | #define CATCH_REGISTER_REPORTER(name, reporterType) | |
3781 | #define CATCH_REGISTER_LISTENER(listenerType) | |
3782 | ||
3783 | #endif // CATCH_CONFIG_DISABLE | |
3784 | ||
3785 | // end catch_reporter_registrars.hpp | |
3786 | // end catch_external_interfaces.h | |
3787 | #endif | |
3788 | ||
3789 | #ifdef CATCH_IMPL | |
3790 | // start catch_impl.hpp | |
3791 | ||
3792 | #ifdef __clang__ | |
3793 | #pragma clang diagnostic push | |
3794 | #pragma clang diagnostic ignored "-Wweak-vtables" | |
3795 | #endif | |
3796 | ||
3797 | // Keep these here for external reporters | |
3798 | // start catch_test_case_tracker.h | |
3799 | ||
3800 | #include <string> | |
3801 | #include <vector> | |
3802 | #include <memory> | |
3803 | ||
3804 | namespace Catch { | |
3805 | namespace TestCaseTracking { | |
3806 | ||
3807 | struct NameAndLocation { | |
3808 | std::string name; | |
3809 | SourceLineInfo location; | |
3810 | ||
3811 | NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); | |
3812 | }; | |
3813 | ||
3814 | struct ITracker; | |
3815 | ||
3816 | using ITrackerPtr = std::shared_ptr<ITracker>; | |
3817 | ||
3818 | struct ITracker { | |
3819 | virtual ~ITracker(); | |
3820 | ||
3821 | // static queries | |
3822 | virtual NameAndLocation const& nameAndLocation() const = 0; | |
3823 | ||
3824 | // dynamic queries | |
3825 | virtual bool isComplete() const = 0; // Successfully completed or failed | |
3826 | virtual bool isSuccessfullyCompleted() const = 0; | |
3827 | virtual bool isOpen() const = 0; // Started but not complete | |
3828 | virtual bool hasChildren() const = 0; | |
3829 | ||
3830 | virtual ITracker& parent() = 0; | |
3831 | ||
3832 | // actions | |
3833 | virtual void close() = 0; // Successfully complete | |
3834 | virtual void fail() = 0; | |
3835 | virtual void markAsNeedingAnotherRun() = 0; | |
3836 | ||
3837 | virtual void addChild( ITrackerPtr const& child ) = 0; | |
3838 | virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; | |
3839 | virtual void openChild() = 0; | |
3840 | ||
3841 | // Debug/ checking | |
3842 | virtual bool isSectionTracker() const = 0; | |
3843 | virtual bool isIndexTracker() const = 0; | |
3844 | }; | |
3845 | ||
3846 | class TrackerContext { | |
3847 | ||
3848 | enum RunState { | |
3849 | NotStarted, | |
3850 | Executing, | |
3851 | CompletedCycle | |
3852 | }; | |
3853 | ||
3854 | ITrackerPtr m_rootTracker; | |
3855 | ITracker* m_currentTracker = nullptr; | |
3856 | RunState m_runState = NotStarted; | |
3857 | ||
3858 | public: | |
3859 | ||
3860 | static TrackerContext& instance(); | |
3861 | ||
3862 | ITracker& startRun(); | |
3863 | void endRun(); | |
3864 | ||
3865 | void startCycle(); | |
3866 | void completeCycle(); | |
3867 | ||
3868 | bool completedCycle() const; | |
3869 | ITracker& currentTracker(); | |
3870 | void setCurrentTracker( ITracker* tracker ); | |
3871 | }; | |
3872 | ||
3873 | class TrackerBase : public ITracker { | |
3874 | protected: | |
3875 | enum CycleState { | |
3876 | NotStarted, | |
3877 | Executing, | |
3878 | ExecutingChildren, | |
3879 | NeedsAnotherRun, | |
3880 | CompletedSuccessfully, | |
3881 | Failed | |
3882 | }; | |
3883 | ||
3884 | class TrackerHasName { | |
3885 | NameAndLocation m_nameAndLocation; | |
3886 | public: | |
3887 | TrackerHasName( NameAndLocation const& nameAndLocation ); | |
3888 | bool operator ()( ITrackerPtr const& tracker ) const; | |
3889 | }; | |
3890 | ||
3891 | using Children = std::vector<ITrackerPtr>; | |
3892 | NameAndLocation m_nameAndLocation; | |
3893 | TrackerContext& m_ctx; | |
3894 | ITracker* m_parent; | |
3895 | Children m_children; | |
3896 | CycleState m_runState = NotStarted; | |
3897 | ||
3898 | public: | |
3899 | TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); | |
3900 | ||
3901 | NameAndLocation const& nameAndLocation() const override; | |
3902 | bool isComplete() const override; | |
3903 | bool isSuccessfullyCompleted() const override; | |
3904 | bool isOpen() const override; | |
3905 | bool hasChildren() const override; | |
3906 | ||
3907 | void addChild( ITrackerPtr const& child ) override; | |
3908 | ||
3909 | ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; | |
3910 | ITracker& parent() override; | |
3911 | ||
3912 | void openChild() override; | |
3913 | ||
3914 | bool isSectionTracker() const override; | |
3915 | bool isIndexTracker() const override; | |
3916 | ||
3917 | void open(); | |
3918 | ||
3919 | void close() override; | |
3920 | void fail() override; | |
3921 | void markAsNeedingAnotherRun() override; | |
3922 | ||
3923 | private: | |
3924 | void moveToParent(); | |
3925 | void moveToThis(); | |
3926 | }; | |
3927 | ||
3928 | class SectionTracker : public TrackerBase { | |
3929 | std::vector<std::string> m_filters; | |
3930 | public: | |
3931 | SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); | |
3932 | ||
3933 | bool isSectionTracker() const override; | |
3934 | ||
3935 | static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); | |
3936 | ||
3937 | void tryOpen(); | |
3938 | ||
3939 | void addInitialFilters( std::vector<std::string> const& filters ); | |
3940 | void addNextFilters( std::vector<std::string> const& filters ); | |
3941 | }; | |
3942 | ||
3943 | class IndexTracker : public TrackerBase { | |
3944 | int m_size; | |
3945 | int m_index = -1; | |
3946 | public: | |
3947 | IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); | |
3948 | ||
3949 | bool isIndexTracker() const override; | |
3950 | void close() override; | |
3951 | ||
3952 | static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); | |
3953 | ||
3954 | int index() const; | |
3955 | ||
3956 | void moveNext(); | |
3957 | }; | |
3958 | ||
3959 | } // namespace TestCaseTracking | |
3960 | ||
3961 | using TestCaseTracking::ITracker; | |
3962 | using TestCaseTracking::TrackerContext; | |
3963 | using TestCaseTracking::SectionTracker; | |
3964 | using TestCaseTracking::IndexTracker; | |
3965 | ||
3966 | } // namespace Catch | |
3967 | ||
3968 | // end catch_test_case_tracker.h | |
3969 | ||
3970 | // start catch_leak_detector.h | |
3971 | ||
3972 | namespace Catch { | |
3973 | ||
3974 | struct LeakDetector { | |
3975 | LeakDetector(); | |
3976 | }; | |
3977 | ||
3978 | } | |
3979 | // end catch_leak_detector.h | |
3980 | // Cpp files will be included in the single-header file here | |
3981 | // start catch_approx.cpp | |
3982 | ||
3983 | #include <cmath> | |
3984 | #include <limits> | |
3985 | ||
3986 | namespace { | |
3987 | ||
3988 | // Performs equivalent check of std::fabs(lhs - rhs) <= margin | |
3989 | // But without the subtraction to allow for INFINITY in comparison | |
3990 | bool marginComparison(double lhs, double rhs, double margin) { | |
3991 | return (lhs + margin >= rhs) && (rhs + margin >= lhs); | |
3992 | } | |
3993 | ||
3994 | } | |
3995 | ||
3996 | namespace Catch { | |
3997 | namespace Detail { | |
3998 | ||
3999 | Approx::Approx ( double value ) | |
4000 | : m_epsilon( std::numeric_limits<float>::epsilon()*100 ), | |
4001 | m_margin( 0.0 ), | |
4002 | m_scale( 0.0 ), | |
4003 | m_value( value ) | |
4004 | {} | |
4005 | ||
4006 | Approx Approx::custom() { | |
4007 | return Approx( 0 ); | |
4008 | } | |
4009 | ||
4010 | std::string Approx::toString() const { | |
4011 | std::ostringstream oss; | |
4012 | oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; | |
4013 | return oss.str(); | |
4014 | } | |
4015 | ||
4016 | bool Approx::equalityComparisonImpl(const double other) const { | |
4017 | // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value | |
4018 | // Thanks to Richard Harris for his help refining the scaled margin value | |
4019 | return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); | |
4020 | } | |
4021 | ||
4022 | } // end namespace Detail | |
4023 | ||
4024 | std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) { | |
4025 | return value.toString(); | |
4026 | } | |
4027 | ||
4028 | } // end namespace Catch | |
4029 | // end catch_approx.cpp | |
4030 | // start catch_assertionhandler.cpp | |
4031 | ||
4032 | // start catch_context.h | |
4033 | ||
4034 | #include <memory> | |
4035 | ||
4036 | namespace Catch { | |
4037 | ||
4038 | struct IResultCapture; | |
4039 | struct IRunner; | |
4040 | struct IConfig; | |
4041 | ||
4042 | using IConfigPtr = std::shared_ptr<IConfig const>; | |
4043 | ||
4044 | struct IContext | |
4045 | { | |
4046 | virtual ~IContext(); | |
4047 | ||
4048 | virtual IResultCapture* getResultCapture() = 0; | |
4049 | virtual IRunner* getRunner() = 0; | |
4050 | virtual IConfigPtr getConfig() const = 0; | |
4051 | }; | |
4052 | ||
4053 | struct IMutableContext : IContext | |
4054 | { | |
4055 | virtual ~IMutableContext(); | |
4056 | virtual void setResultCapture( IResultCapture* resultCapture ) = 0; | |
4057 | virtual void setRunner( IRunner* runner ) = 0; | |
4058 | virtual void setConfig( IConfigPtr const& config ) = 0; | |
4059 | }; | |
4060 | ||
4061 | IContext& getCurrentContext(); | |
4062 | IMutableContext& getCurrentMutableContext(); | |
4063 | void cleanUpContext(); | |
4064 | } | |
4065 | ||
4066 | // end catch_context.h | |
4067 | #include <cassert> | |
4068 | ||
4069 | namespace Catch { | |
4070 | ||
4071 | auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { | |
4072 | expr.streamReconstructedExpression( os ); | |
4073 | return os; | |
4074 | } | |
4075 | ||
4076 | LazyExpression::LazyExpression( bool isNegated ) | |
4077 | : m_isNegated( isNegated ) | |
4078 | {} | |
4079 | ||
4080 | LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} | |
4081 | ||
4082 | LazyExpression::operator bool() const { | |
4083 | return m_transientExpression != nullptr; | |
4084 | } | |
4085 | ||
4086 | auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { | |
4087 | if( lazyExpr.m_isNegated ) | |
4088 | os << "!"; | |
4089 | ||
4090 | if( lazyExpr ) { | |
4091 | if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) | |
4092 | os << "(" << *lazyExpr.m_transientExpression << ")"; | |
4093 | else | |
4094 | os << *lazyExpr.m_transientExpression; | |
4095 | } | |
4096 | else { | |
4097 | os << "{** error - unchecked empty expression requested **}"; | |
4098 | } | |
4099 | return os; | |
4100 | } | |
4101 | ||
4102 | AssertionHandler::AssertionHandler | |
4103 | ( StringRef macroName, | |
4104 | SourceLineInfo const& lineInfo, | |
4105 | StringRef capturedExpression, | |
4106 | ResultDisposition::Flags resultDisposition ) | |
4107 | : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } | |
4108 | { | |
4109 | getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); | |
4110 | } | |
4111 | AssertionHandler::~AssertionHandler() { | |
4112 | if ( m_inExceptionGuard ) { | |
4113 | handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" ); | |
4114 | getCurrentContext().getResultCapture()->exceptionEarlyReported(); | |
4115 | } | |
4116 | } | |
4117 | ||
4118 | void AssertionHandler::handle( ITransientExpression const& expr ) { | |
4119 | ||
4120 | bool negated = isFalseTest( m_assertionInfo.resultDisposition ); | |
4121 | bool result = expr.getResult() != negated; | |
4122 | ||
4123 | handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated ); | |
4124 | } | |
4125 | void AssertionHandler::handle( ResultWas::OfType resultType ) { | |
4126 | handle( resultType, nullptr, false ); | |
4127 | } | |
4128 | void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) { | |
4129 | AssertionResultData data( resultType, LazyExpression( false ) ); | |
4130 | data.message = message; | |
4131 | handle( data, nullptr ); | |
4132 | } | |
4133 | void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) { | |
4134 | AssertionResultData data( resultType, LazyExpression( negated ) ); | |
4135 | handle( data, expr ); | |
4136 | } | |
4137 | void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) { | |
4138 | ||
4139 | getResultCapture().assertionRun(); | |
4140 | ||
4141 | AssertionResult assertionResult{ m_assertionInfo, resultData }; | |
4142 | assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; | |
4143 | ||
4144 | getResultCapture().assertionEnded( assertionResult ); | |
4145 | ||
4146 | if( !assertionResult.isOk() ) { | |
4147 | m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); | |
4148 | m_shouldThrow = | |
4149 | getCurrentContext().getRunner()->aborting() || | |
4150 | (m_assertionInfo.resultDisposition & ResultDisposition::Normal); | |
4151 | } | |
4152 | } | |
4153 | ||
4154 | auto AssertionHandler::allowThrows() const -> bool { | |
4155 | return getCurrentContext().getConfig()->allowThrows(); | |
4156 | } | |
4157 | ||
4158 | auto AssertionHandler::shouldDebugBreak() const -> bool { | |
4159 | return m_shouldDebugBreak; | |
4160 | } | |
4161 | void AssertionHandler::reactWithDebugBreak() const { | |
4162 | if (m_shouldDebugBreak) { | |
4163 | /////////////////////////////////////////////////////////////////// | |
4164 | // To inspect the state during test, you need to go one level up the callstack | |
4165 | // To go back to the test and change execution, jump over the reactWithoutDebugBreak() call | |
4166 | /////////////////////////////////////////////////////////////////// | |
4167 | CATCH_BREAK_INTO_DEBUGGER(); | |
4168 | } | |
4169 | reactWithoutDebugBreak(); | |
4170 | } | |
4171 | void AssertionHandler::reactWithoutDebugBreak() const { | |
4172 | if( m_shouldThrow ) | |
4173 | throw Catch::TestFailureException(); | |
4174 | } | |
4175 | ||
4176 | void AssertionHandler::useActiveException() { | |
4177 | handle( ResultWas::ThrewException, Catch::translateActiveException() ); | |
4178 | } | |
4179 | ||
4180 | void AssertionHandler::setExceptionGuard() { | |
4181 | assert( m_inExceptionGuard == false ); | |
4182 | m_inExceptionGuard = true; | |
4183 | } | |
4184 | void AssertionHandler::unsetExceptionGuard() { | |
4185 | assert( m_inExceptionGuard == true ); | |
4186 | m_inExceptionGuard = false; | |
4187 | } | |
4188 | ||
4189 | // This is the overload that takes a string and infers the Equals matcher from it | |
4190 | // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp | |
4191 | void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { | |
4192 | handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); | |
4193 | } | |
4194 | ||
4195 | } // namespace Catch | |
4196 | // end catch_assertionhandler.cpp | |
4197 | // start catch_assertionresult.cpp | |
4198 | ||
4199 | namespace Catch { | |
4200 | AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): | |
4201 | lazyExpression(_lazyExpression), | |
4202 | resultType(_resultType) {} | |
4203 | ||
4204 | std::string AssertionResultData::reconstructExpression() const { | |
4205 | ||
4206 | if( reconstructedExpression.empty() ) { | |
4207 | if( lazyExpression ) { | |
4208 | // !TBD Use stringstream for now, but rework above to pass stream in | |
4209 | std::ostringstream oss; | |
4210 | oss << lazyExpression; | |
4211 | reconstructedExpression = oss.str(); | |
4212 | } | |
4213 | } | |
4214 | return reconstructedExpression; | |
4215 | } | |
4216 | ||
4217 | AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) | |
4218 | : m_info( info ), | |
4219 | m_resultData( data ) | |
4220 | {} | |
4221 | ||
4222 | // Result was a success | |
4223 | bool AssertionResult::succeeded() const { | |
4224 | return Catch::isOk( m_resultData.resultType ); | |
4225 | } | |
4226 | ||
4227 | // Result was a success, or failure is suppressed | |
4228 | bool AssertionResult::isOk() const { | |
4229 | return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); | |
4230 | } | |
4231 | ||
4232 | ResultWas::OfType AssertionResult::getResultType() const { | |
4233 | return m_resultData.resultType; | |
4234 | } | |
4235 | ||
4236 | bool AssertionResult::hasExpression() const { | |
4237 | return m_info.capturedExpression[0] != 0; | |
4238 | } | |
4239 | ||
4240 | bool AssertionResult::hasMessage() const { | |
4241 | return !m_resultData.message.empty(); | |
4242 | } | |
4243 | ||
4244 | std::string AssertionResult::getExpression() const { | |
4245 | if( isFalseTest( m_info.resultDisposition ) ) | |
4246 | return "!(" + std::string(m_info.capturedExpression) + ")"; | |
4247 | else | |
4248 | return m_info.capturedExpression; | |
4249 | } | |
4250 | ||
4251 | std::string AssertionResult::getExpressionInMacro() const { | |
4252 | std::string expr; | |
4253 | if( m_info.macroName[0] == 0 ) | |
4254 | expr = m_info.capturedExpression; | |
4255 | else { | |
4256 | expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); | |
4257 | expr += m_info.macroName; | |
4258 | expr += "( "; | |
4259 | expr += m_info.capturedExpression; | |
4260 | expr += " )"; | |
4261 | } | |
4262 | return expr; | |
4263 | } | |
4264 | ||
4265 | bool AssertionResult::hasExpandedExpression() const { | |
4266 | return hasExpression() && getExpandedExpression() != getExpression(); | |
4267 | } | |
4268 | ||
4269 | std::string AssertionResult::getExpandedExpression() const { | |
4270 | std::string expr = m_resultData.reconstructExpression(); | |
4271 | return expr.empty() | |
4272 | ? getExpression() | |
4273 | : expr; | |
4274 | } | |
4275 | ||
4276 | std::string AssertionResult::getMessage() const { | |
4277 | return m_resultData.message; | |
4278 | } | |
4279 | SourceLineInfo AssertionResult::getSourceInfo() const { | |
4280 | return m_info.lineInfo; | |
4281 | } | |
4282 | ||
4283 | std::string AssertionResult::getTestMacroName() const { | |
4284 | return m_info.macroName; | |
4285 | } | |
4286 | ||
4287 | } // end namespace Catch | |
4288 | // end catch_assertionresult.cpp | |
4289 | // start catch_benchmark.cpp | |
4290 | ||
4291 | namespace Catch { | |
4292 | ||
4293 | auto BenchmarkLooper::getResolution() -> uint64_t { | |
4294 | return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); | |
4295 | } | |
4296 | ||
4297 | void BenchmarkLooper::reportStart() { | |
4298 | getResultCapture().benchmarkStarting( { m_name } ); | |
4299 | } | |
4300 | auto BenchmarkLooper::needsMoreIterations() -> bool { | |
4301 | auto elapsed = m_timer.getElapsedNanoseconds(); | |
4302 | ||
4303 | // Exponentially increasing iterations until we're confident in our timer resolution | |
4304 | if( elapsed < m_resolution ) { | |
4305 | m_iterationsToRun *= 10; | |
4306 | return true; | |
4307 | } | |
4308 | ||
4309 | getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); | |
4310 | return false; | |
4311 | } | |
4312 | ||
4313 | } // end namespace Catch | |
4314 | // end catch_benchmark.cpp | |
4315 | // start catch_capture_matchers.cpp | |
4316 | ||
4317 | namespace Catch { | |
4318 | ||
4319 | using StringMatcher = Matchers::Impl::MatcherBase<std::string>; | |
4320 | ||
4321 | // This is the general overload that takes a any string matcher | |
4322 | // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers | |
4323 | // the Equals matcher (so the header does not mention matchers) | |
4324 | void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { | |
4325 | std::string exceptionMessage = Catch::translateActiveException(); | |
4326 | MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); | |
4327 | handler.handle( expr ); | |
4328 | } | |
4329 | ||
4330 | } // namespace Catch | |
4331 | // end catch_capture_matchers.cpp | |
4332 | // start catch_commandline.cpp | |
4333 | ||
4334 | // start catch_commandline.h | |
4335 | ||
4336 | // start catch_clara.h | |
4337 | ||
4338 | // Use Catch's value for console width (store Clara's off to the side, if present) | |
4339 | #ifdef CLARA_CONFIG_CONSOLE_WIDTH | |
4340 | #define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH | |
4341 | #undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH | |
4342 | #endif | |
4343 | #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 | |
4344 | ||
4345 | #ifdef __clang__ | |
4346 | #pragma clang diagnostic push | |
4347 | #pragma clang diagnostic ignored "-Wweak-vtables" | |
4348 | #pragma clang diagnostic ignored "-Wexit-time-destructors" | |
4349 | #pragma clang diagnostic ignored "-Wshadow" | |
4350 | #endif | |
4351 | ||
4352 | // start clara.hpp | |
4353 | // v1.0-develop.2 | |
4354 | // See https://github.com/philsquared/Clara | |
4355 | ||
4356 | ||
4357 | #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH | |
4358 | #define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 | |
4359 | #endif | |
4360 | ||
4361 | #ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH | |
4362 | #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH | |
4363 | #endif | |
4364 | ||
4365 | // ----------- #included from clara_textflow.hpp ----------- | |
4366 | ||
4367 | // TextFlowCpp | |
4368 | // | |
4369 | // A single-header library for wrapping and laying out basic text, by Phil Nash | |
4370 | // | |
4371 | // This work is licensed under the BSD 2-Clause license. | |
4372 | // See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause | |
4373 | // | |
4374 | // This project is hosted at https://github.com/philsquared/textflowcpp | |
4375 | ||
4376 | ||
4377 | #include <cassert> | |
4378 | #include <ostream> | |
4379 | #include <sstream> | |
4380 | #include <vector> | |
4381 | ||
4382 | #ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH | |
4383 | #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 | |
4384 | #endif | |
4385 | ||
4386 | namespace Catch { namespace clara { namespace TextFlow { | |
4387 | ||
4388 | inline auto isWhitespace( char c ) -> bool { | |
4389 | static std::string chars = " \t\n\r"; | |
4390 | return chars.find( c ) != std::string::npos; | |
4391 | } | |
4392 | inline auto isBreakableBefore( char c ) -> bool { | |
4393 | static std::string chars = "[({<|"; | |
4394 | return chars.find( c ) != std::string::npos; | |
4395 | } | |
4396 | inline auto isBreakableAfter( char c ) -> bool { | |
4397 | static std::string chars = "])}>.,:;*+-=&/\\"; | |
4398 | return chars.find( c ) != std::string::npos; | |
4399 | } | |
4400 | ||
4401 | class Columns; | |
4402 | ||
4403 | class Column { | |
4404 | std::vector<std::string> m_strings; | |
4405 | size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; | |
4406 | size_t m_indent = 0; | |
4407 | size_t m_initialIndent = std::string::npos; | |
4408 | ||
4409 | public: | |
4410 | class iterator { | |
4411 | friend Column; | |
4412 | ||
4413 | Column const& m_column; | |
4414 | size_t m_stringIndex = 0; | |
4415 | size_t m_pos = 0; | |
4416 | ||
4417 | size_t m_len = 0; | |
4418 | size_t m_end = 0; | |
4419 | bool m_suffix = false; | |
4420 | ||
4421 | iterator( Column const& column, size_t stringIndex ) | |
4422 | : m_column( column ), | |
4423 | m_stringIndex( stringIndex ) | |
4424 | {} | |
4425 | ||
4426 | auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } | |
4427 | ||
4428 | auto isBoundary( size_t at ) const -> bool { | |
4429 | assert( at > 0 ); | |
4430 | assert( at <= line().size() ); | |
4431 | ||
4432 | return at == line().size() || | |
4433 | ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || | |
4434 | isBreakableBefore( line()[at] ) || | |
4435 | isBreakableAfter( line()[at-1] ); | |
4436 | } | |
4437 | ||
4438 | void calcLength() { | |
4439 | assert( m_stringIndex < m_column.m_strings.size() ); | |
4440 | ||
4441 | m_suffix = false; | |
4442 | auto width = m_column.m_width-indent(); | |
4443 | m_end = m_pos; | |
4444 | while( m_end < line().size() && line()[m_end] != '\n' ) | |
4445 | ++m_end; | |
4446 | ||
4447 | if( m_end < m_pos + width ) { | |
4448 | m_len = m_end - m_pos; | |
4449 | } | |
4450 | else { | |
4451 | size_t len = width; | |
4452 | while (len > 0 && !isBoundary(m_pos + len)) | |
4453 | --len; | |
4454 | while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) | |
4455 | --len; | |
4456 | ||
4457 | if (len > 0) { | |
4458 | m_len = len; | |
4459 | } else { | |
4460 | m_suffix = true; | |
4461 | m_len = width - 1; | |
4462 | } | |
4463 | } | |
4464 | } | |
4465 | ||
4466 | auto indent() const -> size_t { | |
4467 | auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; | |
4468 | return initial == std::string::npos ? m_column.m_indent : initial; | |
4469 | } | |
4470 | ||
4471 | auto addIndentAndSuffix(std::string const &plain) const -> std::string { | |
4472 | return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); | |
4473 | } | |
4474 | ||
4475 | public: | |
4476 | explicit iterator( Column const& column ) : m_column( column ) { | |
4477 | assert( m_column.m_width > m_column.m_indent ); | |
4478 | assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); | |
4479 | calcLength(); | |
4480 | if( m_len == 0 ) | |
4481 | m_stringIndex++; // Empty string | |
4482 | } | |
4483 | ||
4484 | auto operator *() const -> std::string { | |
4485 | assert( m_stringIndex < m_column.m_strings.size() ); | |
4486 | assert( m_pos <= m_end ); | |
4487 | if( m_pos + m_column.m_width < m_end ) | |
4488 | return addIndentAndSuffix(line().substr(m_pos, m_len)); | |
4489 | else | |
4490 | return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); | |
4491 | } | |
4492 | ||
4493 | auto operator ++() -> iterator& { | |
4494 | m_pos += m_len; | |
4495 | if( m_pos < line().size() && line()[m_pos] == '\n' ) | |
4496 | m_pos += 1; | |
4497 | else | |
4498 | while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) | |
4499 | ++m_pos; | |
4500 | ||
4501 | if( m_pos == line().size() ) { | |
4502 | m_pos = 0; | |
4503 | ++m_stringIndex; | |
4504 | } | |
4505 | if( m_stringIndex < m_column.m_strings.size() ) | |
4506 | calcLength(); | |
4507 | return *this; | |
4508 | } | |
4509 | auto operator ++(int) -> iterator { | |
4510 | iterator prev( *this ); | |
4511 | operator++(); | |
4512 | return prev; | |
4513 | } | |
4514 | ||
4515 | auto operator ==( iterator const& other ) const -> bool { | |
4516 | return | |
4517 | m_pos == other.m_pos && | |
4518 | m_stringIndex == other.m_stringIndex && | |
4519 | &m_column == &other.m_column; | |
4520 | } | |
4521 | auto operator !=( iterator const& other ) const -> bool { | |
4522 | return !operator==( other ); | |
4523 | } | |
4524 | }; | |
4525 | using const_iterator = iterator; | |
4526 | ||
4527 | explicit Column( std::string const& text ) { m_strings.push_back( text ); } | |
4528 | ||
4529 | auto width( size_t newWidth ) -> Column& { | |
4530 | assert( newWidth > 0 ); | |
4531 | m_width = newWidth; | |
4532 | return *this; | |
4533 | } | |
4534 | auto indent( size_t newIndent ) -> Column& { | |
4535 | m_indent = newIndent; | |
4536 | return *this; | |
4537 | } | |
4538 | auto initialIndent( size_t newIndent ) -> Column& { | |
4539 | m_initialIndent = newIndent; | |
4540 | return *this; | |
4541 | } | |
4542 | ||
4543 | auto width() const -> size_t { return m_width; } | |
4544 | auto begin() const -> iterator { return iterator( *this ); } | |
4545 | auto end() const -> iterator { return { *this, m_strings.size() }; } | |
4546 | ||
4547 | inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { | |
4548 | bool first = true; | |
4549 | for( auto line : col ) { | |
4550 | if( first ) | |
4551 | first = false; | |
4552 | else | |
4553 | os << "\n"; | |
4554 | os << line; | |
4555 | } | |
4556 | return os; | |
4557 | } | |
4558 | ||
4559 | auto operator + ( Column const& other ) -> Columns; | |
4560 | ||
4561 | auto toString() const -> std::string { | |
4562 | std::ostringstream oss; | |
4563 | oss << *this; | |
4564 | return oss.str(); | |
4565 | } | |
4566 | }; | |
4567 | ||
4568 | class Spacer : public Column { | |
4569 | ||
4570 | public: | |
4571 | explicit Spacer( size_t spaceWidth ) : Column( "" ) { | |
4572 | width( spaceWidth ); | |
4573 | } | |
4574 | }; | |
4575 | ||
4576 | class Columns { | |
4577 | std::vector<Column> m_columns; | |
4578 | ||
4579 | public: | |
4580 | ||
4581 | class iterator { | |
4582 | friend Columns; | |
4583 | struct EndTag {}; | |
4584 | ||
4585 | std::vector<Column> const& m_columns; | |
4586 | std::vector<Column::iterator> m_iterators; | |
4587 | size_t m_activeIterators; | |
4588 | ||
4589 | iterator( Columns const& columns, EndTag ) | |
4590 | : m_columns( columns.m_columns ), | |
4591 | m_activeIterators( 0 ) | |
4592 | { | |
4593 | m_iterators.reserve( m_columns.size() ); | |
4594 | ||
4595 | for( auto const& col : m_columns ) | |
4596 | m_iterators.push_back( col.end() ); | |
4597 | } | |
4598 | ||
4599 | public: | |
4600 | explicit iterator( Columns const& columns ) | |
4601 | : m_columns( columns.m_columns ), | |
4602 | m_activeIterators( m_columns.size() ) | |
4603 | { | |
4604 | m_iterators.reserve( m_columns.size() ); | |
4605 | ||
4606 | for( auto const& col : m_columns ) | |
4607 | m_iterators.push_back( col.begin() ); | |
4608 | } | |
4609 | ||
4610 | auto operator ==( iterator const& other ) const -> bool { | |
4611 | return m_iterators == other.m_iterators; | |
4612 | } | |
4613 | auto operator !=( iterator const& other ) const -> bool { | |
4614 | return m_iterators != other.m_iterators; | |
4615 | } | |
4616 | auto operator *() const -> std::string { | |
4617 | std::string row, padding; | |
4618 | ||
4619 | for( size_t i = 0; i < m_columns.size(); ++i ) { | |
4620 | auto width = m_columns[i].width(); | |
4621 | if( m_iterators[i] != m_columns[i].end() ) { | |
4622 | std::string col = *m_iterators[i]; | |
4623 | row += padding + col; | |
4624 | if( col.size() < width ) | |
4625 | padding = std::string( width - col.size(), ' ' ); | |
4626 | else | |
4627 | padding = ""; | |
4628 | } | |
4629 | else { | |
4630 | padding += std::string( width, ' ' ); | |
4631 | } | |
4632 | } | |
4633 | return row; | |
4634 | } | |
4635 | auto operator ++() -> iterator& { | |
4636 | for( size_t i = 0; i < m_columns.size(); ++i ) { | |
4637 | if (m_iterators[i] != m_columns[i].end()) | |
4638 | ++m_iterators[i]; | |
4639 | } | |
4640 | return *this; | |
4641 | } | |
4642 | auto operator ++(int) -> iterator { | |
4643 | iterator prev( *this ); | |
4644 | operator++(); | |
4645 | return prev; | |
4646 | } | |
4647 | }; | |
4648 | using const_iterator = iterator; | |
4649 | ||
4650 | auto begin() const -> iterator { return iterator( *this ); } | |
4651 | auto end() const -> iterator { return { *this, iterator::EndTag() }; } | |
4652 | ||
4653 | auto operator += ( Column const& col ) -> Columns& { | |
4654 | m_columns.push_back( col ); | |
4655 | return *this; | |
4656 | } | |
4657 | auto operator + ( Column const& col ) -> Columns { | |
4658 | Columns combined = *this; | |
4659 | combined += col; | |
4660 | return combined; | |
4661 | } | |
4662 | ||
4663 | inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { | |
4664 | ||
4665 | bool first = true; | |
4666 | for( auto line : cols ) { | |
4667 | if( first ) | |
4668 | first = false; | |
4669 | else | |
4670 | os << "\n"; | |
4671 | os << line; | |
4672 | } | |
4673 | return os; | |
4674 | } | |
4675 | ||
4676 | auto toString() const -> std::string { | |
4677 | std::ostringstream oss; | |
4678 | oss << *this; | |
4679 | return oss.str(); | |
4680 | } | |
4681 | }; | |
4682 | ||
4683 | inline auto Column::operator + ( Column const& other ) -> Columns { | |
4684 | Columns cols; | |
4685 | cols += *this; | |
4686 | cols += other; | |
4687 | return cols; | |
4688 | } | |
4689 | }}} // namespace Catch::clara::TextFlow | |
4690 | ||
4691 | // ----------- end of #include from clara_textflow.hpp ----------- | |
4692 | // ........... back in clara.hpp | |
4693 | ||
4694 | #include <memory> | |
4695 | #include <set> | |
4696 | #include <algorithm> | |
4697 | ||
4698 | #if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) | |
4699 | #define CATCH_PLATFORM_WINDOWS | |
4700 | #endif | |
4701 | ||
4702 | namespace Catch { namespace clara { | |
4703 | namespace detail { | |
4704 | ||
4705 | // Traits for extracting arg and return type of lambdas (for single argument lambdas) | |
4706 | template<typename L> | |
4707 | struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {}; | |
4708 | ||
4709 | template<typename ClassT, typename ReturnT, typename... Args> | |
4710 | struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> { | |
4711 | static const bool isValid = false; | |
4712 | }; | |
4713 | ||
4714 | template<typename ClassT, typename ReturnT, typename ArgT> | |
4715 | struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> { | |
4716 | static const bool isValid = true; | |
4717 | using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;; | |
4718 | using ReturnType = ReturnT; | |
4719 | }; | |
4720 | ||
4721 | class TokenStream; | |
4722 | ||
4723 | // Transport for raw args (copied from main args, or supplied via init list for testing) | |
4724 | class Args { | |
4725 | friend TokenStream; | |
4726 | std::string m_exeName; | |
4727 | std::vector<std::string> m_args; | |
4728 | ||
4729 | public: | |
4730 | Args( int argc, char *argv[] ) { | |
4731 | m_exeName = argv[0]; | |
4732 | for( int i = 1; i < argc; ++i ) | |
4733 | m_args.push_back( argv[i] ); | |
4734 | } | |
4735 | ||
4736 | Args( std::initializer_list<std::string> args ) | |
4737 | : m_exeName( *args.begin() ), | |
4738 | m_args( args.begin()+1, args.end() ) | |
4739 | {} | |
4740 | ||
4741 | auto exeName() const -> std::string { | |
4742 | return m_exeName; | |
4743 | } | |
4744 | }; | |
4745 | ||
4746 | // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string | |
4747 | // may encode an option + its argument if the : or = form is used | |
4748 | enum class TokenType { | |
4749 | Option, Argument | |
4750 | }; | |
4751 | struct Token { | |
4752 | TokenType type; | |
4753 | std::string token; | |
4754 | }; | |
4755 | ||
4756 | inline auto isOptPrefix( char c ) -> bool { | |
4757 | return c == '-' | |
4758 | #ifdef CATCH_PLATFORM_WINDOWS | |
4759 | || c == '/' | |
4760 | #endif | |
4761 | ; | |
4762 | } | |
4763 | ||
4764 | // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled | |
4765 | class TokenStream { | |
4766 | using Iterator = std::vector<std::string>::const_iterator; | |
4767 | Iterator it; | |
4768 | Iterator itEnd; | |
4769 | std::vector<Token> m_tokenBuffer; | |
4770 | ||
4771 | void loadBuffer() { | |
4772 | m_tokenBuffer.resize( 0 ); | |
4773 | ||
4774 | // Skip any empty strings | |
4775 | while( it != itEnd && it->empty() ) | |
4776 | ++it; | |
4777 | ||
4778 | if( it != itEnd ) { | |
4779 | auto const &next = *it; | |
4780 | if( isOptPrefix( next[0] ) ) { | |
4781 | auto delimiterPos = next.find_first_of( " :=" ); | |
4782 | if( delimiterPos != std::string::npos ) { | |
4783 | m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); | |
4784 | m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); | |
4785 | } else { | |
4786 | if( next[1] != '-' && next.size() > 2 ) { | |
4787 | std::string opt = "- "; | |
4788 | for( size_t i = 1; i < next.size(); ++i ) { | |
4789 | opt[1] = next[i]; | |
4790 | m_tokenBuffer.push_back( { TokenType::Option, opt } ); | |
4791 | } | |
4792 | } else { | |
4793 | m_tokenBuffer.push_back( { TokenType::Option, next } ); | |
4794 | } | |
4795 | } | |
4796 | } else { | |
4797 | m_tokenBuffer.push_back( { TokenType::Argument, next } ); | |
4798 | } | |
4799 | } | |
4800 | } | |
4801 | ||
4802 | public: | |
4803 | explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} | |
4804 | ||
4805 | TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { | |
4806 | loadBuffer(); | |
4807 | } | |
4808 | ||
4809 | explicit operator bool() const { | |
4810 | return !m_tokenBuffer.empty() || it != itEnd; | |
4811 | } | |
4812 | ||
4813 | auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } | |
4814 | ||
4815 | auto operator*() const -> Token { | |
4816 | assert( !m_tokenBuffer.empty() ); | |
4817 | return m_tokenBuffer.front(); | |
4818 | } | |
4819 | ||
4820 | auto operator->() const -> Token const * { | |
4821 | assert( !m_tokenBuffer.empty() ); | |
4822 | return &m_tokenBuffer.front(); | |
4823 | } | |
4824 | ||
4825 | auto operator++() -> TokenStream & { | |
4826 | if( m_tokenBuffer.size() >= 2 ) { | |
4827 | m_tokenBuffer.erase( m_tokenBuffer.begin() ); | |
4828 | } else { | |
4829 | if( it != itEnd ) | |
4830 | ++it; | |
4831 | loadBuffer(); | |
4832 | } | |
4833 | return *this; | |
4834 | } | |
4835 | }; | |
4836 | ||
4837 | class ResultBase { | |
4838 | public: | |
4839 | enum Type { | |
4840 | Ok, LogicError, RuntimeError | |
4841 | }; | |
4842 | ||
4843 | protected: | |
4844 | ResultBase( Type type ) : m_type( type ) {} | |
4845 | virtual ~ResultBase() = default; | |
4846 | ||
4847 | virtual void enforceOk() const = 0; | |
4848 | ||
4849 | Type m_type; | |
4850 | }; | |
4851 | ||
4852 | template<typename T> | |
4853 | class ResultValueBase : public ResultBase { | |
4854 | public: | |
4855 | auto value() const -> T const & { | |
4856 | enforceOk(); | |
4857 | return m_value; | |
4858 | } | |
4859 | ||
4860 | protected: | |
4861 | ResultValueBase( Type type ) : ResultBase( type ) {} | |
4862 | ||
4863 | ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { | |
4864 | if( m_type == ResultBase::Ok ) | |
4865 | new( &m_value ) T( other.m_value ); | |
4866 | } | |
4867 | ||
4868 | ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { | |
4869 | new( &m_value ) T( value ); | |
4870 | } | |
4871 | ||
4872 | auto operator=( ResultValueBase const &other ) -> ResultValueBase & { | |
4873 | if( m_type == ResultBase::Ok ) | |
4874 | m_value.~T(); | |
4875 | ResultBase::operator=(other); | |
4876 | if( m_type == ResultBase::Ok ) | |
4877 | new( &m_value ) T( other.m_value ); | |
4878 | return *this; | |
4879 | } | |
4880 | ||
4881 | ~ResultValueBase() { | |
4882 | if( m_type == Ok ) | |
4883 | m_value.~T(); | |
4884 | } | |
4885 | ||
4886 | union { | |
4887 | T m_value; | |
4888 | }; | |
4889 | }; | |
4890 | ||
4891 | template<> | |
4892 | class ResultValueBase<void> : public ResultBase { | |
4893 | protected: | |
4894 | using ResultBase::ResultBase; | |
4895 | }; | |
4896 | ||
4897 | template<typename T = void> | |
4898 | class BasicResult : public ResultValueBase<T> { | |
4899 | public: | |
4900 | template<typename U> | |
4901 | explicit BasicResult( BasicResult<U> const &other ) | |
4902 | : ResultValueBase<T>( other.type() ), | |
4903 | m_errorMessage( other.errorMessage() ) | |
4904 | { | |
4905 | assert( type() != ResultBase::Ok ); | |
4906 | } | |
4907 | ||
4908 | template<typename U> | |
4909 | static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } | |
4910 | static auto ok() -> BasicResult { return { ResultBase::Ok }; } | |
4911 | static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } | |
4912 | static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } | |
4913 | ||
4914 | explicit operator bool() const { return m_type == ResultBase::Ok; } | |
4915 | auto type() const -> ResultBase::Type { return m_type; } | |
4916 | auto errorMessage() const -> std::string { return m_errorMessage; } | |
4917 | ||
4918 | protected: | |
4919 | virtual void enforceOk() const { | |
4920 | // !TBD: If no exceptions, std::terminate here or something | |
4921 | switch( m_type ) { | |
4922 | case ResultBase::LogicError: | |
4923 | throw std::logic_error( m_errorMessage ); | |
4924 | case ResultBase::RuntimeError: | |
4925 | throw std::runtime_error( m_errorMessage ); | |
4926 | case ResultBase::Ok: | |
4927 | break; | |
4928 | } | |
4929 | } | |
4930 | ||
4931 | std::string m_errorMessage; // Only populated if resultType is an error | |
4932 | ||
4933 | BasicResult( ResultBase::Type type, std::string const &message ) | |
4934 | : ResultValueBase<T>(type), | |
4935 | m_errorMessage(message) | |
4936 | { | |
4937 | assert( m_type != ResultBase::Ok ); | |
4938 | } | |
4939 | ||
4940 | using ResultValueBase<T>::ResultValueBase; | |
4941 | using ResultBase::m_type; | |
4942 | }; | |
4943 | ||
4944 | enum class ParseResultType { | |
4945 | Matched, NoMatch, ShortCircuitAll, ShortCircuitSame | |
4946 | }; | |
4947 | ||
4948 | class ParseState { | |
4949 | public: | |
4950 | ||
4951 | ParseState( ParseResultType type, TokenStream const &remainingTokens ) | |
4952 | : m_type(type), | |
4953 | m_remainingTokens( remainingTokens ) | |
4954 | {} | |
4955 | ||
4956 | auto type() const -> ParseResultType { return m_type; } | |
4957 | auto remainingTokens() const -> TokenStream { return m_remainingTokens; } | |
4958 | ||
4959 | private: | |
4960 | ParseResultType m_type; | |
4961 | TokenStream m_remainingTokens; | |
4962 | }; | |
4963 | ||
4964 | using Result = BasicResult<void>; | |
4965 | using ParserResult = BasicResult<ParseResultType>; | |
4966 | using InternalParseResult = BasicResult<ParseState>; | |
4967 | ||
4968 | struct HelpColumns { | |
4969 | std::string left; | |
4970 | std::string right; | |
4971 | }; | |
4972 | ||
4973 | template<typename T> | |
4974 | inline auto convertInto( std::string const &source, T& target ) -> ParserResult { | |
4975 | std::stringstream ss; | |
4976 | ss << source; | |
4977 | ss >> target; | |
4978 | if( ss.fail() ) | |
4979 | return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); | |
4980 | else | |
4981 | return ParserResult::ok( ParseResultType::Matched ); | |
4982 | } | |
4983 | inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { | |
4984 | target = source; | |
4985 | return ParserResult::ok( ParseResultType::Matched ); | |
4986 | } | |
4987 | inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { | |
4988 | std::string srcLC = source; | |
4989 | std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } ); | |
4990 | if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") | |
4991 | target = true; | |
4992 | else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") | |
4993 | target = false; | |
4994 | else | |
4995 | return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); | |
4996 | return ParserResult::ok( ParseResultType::Matched ); | |
4997 | } | |
4998 | ||
4999 | struct BoundRefBase { | |
5000 | BoundRefBase() = default; | |
5001 | BoundRefBase( BoundRefBase const & ) = delete; | |
5002 | BoundRefBase( BoundRefBase && ) = delete; | |
5003 | BoundRefBase &operator=( BoundRefBase const & ) = delete; | |
5004 | BoundRefBase &operator=( BoundRefBase && ) = delete; | |
5005 | ||
5006 | virtual ~BoundRefBase() = default; | |
5007 | ||
5008 | virtual auto isFlag() const -> bool = 0; | |
5009 | virtual auto isContainer() const -> bool { return false; } | |
5010 | virtual auto setValue( std::string const &arg ) -> ParserResult = 0; | |
5011 | virtual auto setFlag( bool flag ) -> ParserResult = 0; | |
5012 | }; | |
5013 | ||
5014 | struct BoundValueRefBase : BoundRefBase { | |
5015 | auto isFlag() const -> bool override { return false; } | |
5016 | ||
5017 | auto setFlag( bool ) -> ParserResult override { | |
5018 | return ParserResult::logicError( "Flags can only be set on boolean fields" ); | |
5019 | } | |
5020 | }; | |
5021 | ||
5022 | struct BoundFlagRefBase : BoundRefBase { | |
5023 | auto isFlag() const -> bool override { return true; } | |
5024 | ||
5025 | auto setValue( std::string const &arg ) -> ParserResult override { | |
5026 | bool flag; | |
5027 | auto result = convertInto( arg, flag ); | |
5028 | if( result ) | |
5029 | setFlag( flag ); | |
5030 | return result; | |
5031 | } | |
5032 | }; | |
5033 | ||
5034 | template<typename T> | |
5035 | struct BoundRef : BoundValueRefBase { | |
5036 | T &m_ref; | |
5037 | ||
5038 | explicit BoundRef( T &ref ) : m_ref( ref ) {} | |
5039 | ||
5040 | auto setValue( std::string const &arg ) -> ParserResult override { | |
5041 | return convertInto( arg, m_ref ); | |
5042 | } | |
5043 | }; | |
5044 | ||
5045 | template<typename T> | |
5046 | struct BoundRef<std::vector<T>> : BoundValueRefBase { | |
5047 | std::vector<T> &m_ref; | |
5048 | ||
5049 | explicit BoundRef( std::vector<T> &ref ) : m_ref( ref ) {} | |
5050 | ||
5051 | auto isContainer() const -> bool override { return true; } | |
5052 | ||
5053 | auto setValue( std::string const &arg ) -> ParserResult override { | |
5054 | T temp; | |
5055 | auto result = convertInto( arg, temp ); | |
5056 | if( result ) | |
5057 | m_ref.push_back( temp ); | |
5058 | return result; | |
5059 | } | |
5060 | }; | |
5061 | ||
5062 | struct BoundFlagRef : BoundFlagRefBase { | |
5063 | bool &m_ref; | |
5064 | ||
5065 | explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} | |
5066 | ||
5067 | auto setFlag( bool flag ) -> ParserResult override { | |
5068 | m_ref = flag; | |
5069 | return ParserResult::ok( ParseResultType::Matched ); | |
5070 | } | |
5071 | }; | |
5072 | ||
5073 | template<typename ReturnType> | |
5074 | struct LambdaInvoker { | |
5075 | static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" ); | |
5076 | ||
5077 | template<typename L, typename ArgType> | |
5078 | static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { | |
5079 | return lambda( arg ); | |
5080 | } | |
5081 | }; | |
5082 | ||
5083 | template<> | |
5084 | struct LambdaInvoker<void> { | |
5085 | template<typename L, typename ArgType> | |
5086 | static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { | |
5087 | lambda( arg ); | |
5088 | return ParserResult::ok( ParseResultType::Matched ); | |
5089 | } | |
5090 | }; | |
5091 | ||
5092 | template<typename ArgType, typename L> | |
5093 | inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { | |
5094 | ArgType temp; | |
5095 | auto result = convertInto( arg, temp ); | |
5096 | return !result | |
5097 | ? result | |
5098 | : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp ); | |
5099 | }; | |
5100 | ||
5101 | template<typename L> | |
5102 | struct BoundLambda : BoundValueRefBase { | |
5103 | L m_lambda; | |
5104 | ||
5105 | static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); | |
5106 | explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} | |
5107 | ||
5108 | auto setValue( std::string const &arg ) -> ParserResult override { | |
5109 | return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg ); | |
5110 | } | |
5111 | }; | |
5112 | ||
5113 | template<typename L> | |
5114 | struct BoundFlagLambda : BoundFlagRefBase { | |
5115 | L m_lambda; | |
5116 | ||
5117 | static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); | |
5118 | static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" ); | |
5119 | ||
5120 | explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} | |
5121 | ||
5122 | auto setFlag( bool flag ) -> ParserResult override { | |
5123 | return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag ); | |
5124 | } | |
5125 | }; | |
5126 | ||
5127 | enum class Optionality { Optional, Required }; | |
5128 | ||
5129 | struct Parser; | |
5130 | ||
5131 | class ParserBase { | |
5132 | public: | |
5133 | virtual ~ParserBase() = default; | |
5134 | virtual auto validate() const -> Result { return Result::ok(); } | |
5135 | virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; | |
5136 | virtual auto cardinality() const -> size_t { return 1; } | |
5137 | ||
5138 | auto parse( Args const &args ) const -> InternalParseResult { | |
5139 | return parse( args.exeName(), TokenStream( args ) ); | |
5140 | } | |
5141 | }; | |
5142 | ||
5143 | template<typename DerivedT> | |
5144 | class ComposableParserImpl : public ParserBase { | |
5145 | public: | |
5146 | template<typename T> | |
5147 | auto operator|( T const &other ) const -> Parser; | |
5148 | }; | |
5149 | ||
5150 | // Common code and state for Args and Opts | |
5151 | template<typename DerivedT> | |
5152 | class ParserRefImpl : public ComposableParserImpl<DerivedT> { | |
5153 | protected: | |
5154 | Optionality m_optionality = Optionality::Optional; | |
5155 | std::shared_ptr<BoundRefBase> m_ref; | |
5156 | std::string m_hint; | |
5157 | std::string m_description; | |
5158 | ||
5159 | explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {} | |
5160 | ||
5161 | public: | |
5162 | template<typename T> | |
5163 | ParserRefImpl( T &ref, std::string const &hint ) | |
5164 | : m_ref( std::make_shared<BoundRef<T>>( ref ) ), | |
5165 | m_hint( hint ) | |
5166 | {} | |
5167 | ||
5168 | template<typename LambdaT> | |
5169 | ParserRefImpl( LambdaT const &ref, std::string const &hint ) | |
5170 | : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), | |
5171 | m_hint(hint) | |
5172 | {} | |
5173 | ||
5174 | auto operator()( std::string const &description ) -> DerivedT & { | |
5175 | m_description = description; | |
5176 | return static_cast<DerivedT &>( *this ); | |
5177 | } | |
5178 | ||
5179 | auto optional() -> DerivedT & { | |
5180 | m_optionality = Optionality::Optional; | |
5181 | return static_cast<DerivedT &>( *this ); | |
5182 | }; | |
5183 | ||
5184 | auto required() -> DerivedT & { | |
5185 | m_optionality = Optionality::Required; | |
5186 | return static_cast<DerivedT &>( *this ); | |
5187 | }; | |
5188 | ||
5189 | auto isOptional() const -> bool { | |
5190 | return m_optionality == Optionality::Optional; | |
5191 | } | |
5192 | ||
5193 | auto cardinality() const -> size_t override { | |
5194 | if( m_ref->isContainer() ) | |
5195 | return 0; | |
5196 | else | |
5197 | return 1; | |
5198 | } | |
5199 | ||
5200 | auto hint() const -> std::string { return m_hint; } | |
5201 | }; | |
5202 | ||
5203 | class ExeName : public ComposableParserImpl<ExeName> { | |
5204 | std::shared_ptr<std::string> m_name; | |
5205 | std::shared_ptr<BoundRefBase> m_ref; | |
5206 | ||
5207 | template<typename LambdaT> | |
5208 | static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> { | |
5209 | return std::make_shared<BoundLambda<LambdaT>>( lambda) ; | |
5210 | } | |
5211 | ||
5212 | public: | |
5213 | ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {} | |
5214 | ||
5215 | explicit ExeName( std::string &ref ) : ExeName() { | |
5216 | m_ref = std::make_shared<BoundRef<std::string>>( ref ); | |
5217 | } | |
5218 | ||
5219 | template<typename LambdaT> | |
5220 | explicit ExeName( LambdaT const& lambda ) : ExeName() { | |
5221 | m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda ); | |
5222 | } | |
5223 | ||
5224 | // The exe name is not parsed out of the normal tokens, but is handled specially | |
5225 | auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { | |
5226 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); | |
5227 | } | |
5228 | ||
5229 | auto name() const -> std::string { return *m_name; } | |
5230 | auto set( std::string const& newName ) -> ParserResult { | |
5231 | ||
5232 | auto lastSlash = newName.find_last_of( "\\/" ); | |
5233 | auto filename = ( lastSlash == std::string::npos ) | |
5234 | ? newName | |
5235 | : newName.substr( lastSlash+1 ); | |
5236 | ||
5237 | *m_name = filename; | |
5238 | if( m_ref ) | |
5239 | return m_ref->setValue( filename ); | |
5240 | else | |
5241 | return ParserResult::ok( ParseResultType::Matched ); | |
5242 | } | |
5243 | }; | |
5244 | ||
5245 | class Arg : public ParserRefImpl<Arg> { | |
5246 | public: | |
5247 | using ParserRefImpl::ParserRefImpl; | |
5248 | ||
5249 | auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { | |
5250 | auto validationResult = validate(); | |
5251 | if( !validationResult ) | |
5252 | return InternalParseResult( validationResult ); | |
5253 | ||
5254 | auto remainingTokens = tokens; | |
5255 | auto const &token = *remainingTokens; | |
5256 | if( token.type != TokenType::Argument ) | |
5257 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); | |
5258 | ||
5259 | auto result = m_ref->setValue( remainingTokens->token ); | |
5260 | if( !result ) | |
5261 | return InternalParseResult( result ); | |
5262 | else | |
5263 | return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); | |
5264 | } | |
5265 | }; | |
5266 | ||
5267 | inline auto normaliseOpt( std::string const &optName ) -> std::string { | |
5268 | #ifdef CATCH_PLATFORM_WINDOWS | |
5269 | if( optName[0] == '/' ) | |
5270 | return "-" + optName.substr( 1 ); | |
5271 | else | |
5272 | #endif | |
5273 | return optName; | |
5274 | } | |
5275 | ||
5276 | class Opt : public ParserRefImpl<Opt> { | |
5277 | protected: | |
5278 | std::vector<std::string> m_optNames; | |
5279 | ||
5280 | public: | |
5281 | template<typename LambdaT> | |
5282 | explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {} | |
5283 | ||
5284 | explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {} | |
5285 | ||
5286 | template<typename LambdaT> | |
5287 | Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} | |
5288 | ||
5289 | template<typename T> | |
5290 | Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} | |
5291 | ||
5292 | auto operator[]( std::string const &optName ) -> Opt & { | |
5293 | m_optNames.push_back( optName ); | |
5294 | return *this; | |
5295 | } | |
5296 | ||
5297 | auto getHelpColumns() const -> std::vector<HelpColumns> { | |
5298 | std::ostringstream oss; | |
5299 | bool first = true; | |
5300 | for( auto const &opt : m_optNames ) { | |
5301 | if (first) | |
5302 | first = false; | |
5303 | else | |
5304 | oss << ", "; | |
5305 | oss << opt; | |
5306 | } | |
5307 | if( !m_hint.empty() ) | |
5308 | oss << " <" << m_hint << ">"; | |
5309 | return { { oss.str(), m_description } }; | |
5310 | } | |
5311 | ||
5312 | auto isMatch( std::string const &optToken ) const -> bool { | |
5313 | auto normalisedToken = normaliseOpt( optToken ); | |
5314 | for( auto const &name : m_optNames ) { | |
5315 | if( normaliseOpt( name ) == normalisedToken ) | |
5316 | return true; | |
5317 | } | |
5318 | return false; | |
5319 | } | |
5320 | ||
5321 | using ParserBase::parse; | |
5322 | ||
5323 | auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { | |
5324 | auto validationResult = validate(); | |
5325 | if( !validationResult ) | |
5326 | return InternalParseResult( validationResult ); | |
5327 | ||
5328 | auto remainingTokens = tokens; | |
5329 | if( remainingTokens && remainingTokens->type == TokenType::Option ) { | |
5330 | auto const &token = *remainingTokens; | |
5331 | if( isMatch(token.token ) ) { | |
5332 | if( m_ref->isFlag() ) { | |
5333 | auto result = m_ref->setFlag( true ); | |
5334 | if( !result ) | |
5335 | return InternalParseResult( result ); | |
5336 | if( result.value() == ParseResultType::ShortCircuitAll ) | |
5337 | return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); | |
5338 | } else { | |
5339 | ++remainingTokens; | |
5340 | if( !remainingTokens ) | |
5341 | return InternalParseResult::runtimeError( "Expected argument following " + token.token ); | |
5342 | auto const &argToken = *remainingTokens; | |
5343 | if( argToken.type != TokenType::Argument ) | |
5344 | return InternalParseResult::runtimeError( "Expected argument following " + token.token ); | |
5345 | auto result = m_ref->setValue( argToken.token ); | |
5346 | if( !result ) | |
5347 | return InternalParseResult( result ); | |
5348 | if( result.value() == ParseResultType::ShortCircuitAll ) | |
5349 | return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); | |
5350 | } | |
5351 | return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); | |
5352 | } | |
5353 | } | |
5354 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); | |
5355 | } | |
5356 | ||
5357 | auto validate() const -> Result override { | |
5358 | if( m_optNames.empty() ) | |
5359 | return Result::logicError( "No options supplied to Opt" ); | |
5360 | for( auto const &name : m_optNames ) { | |
5361 | if( name.empty() ) | |
5362 | return Result::logicError( "Option name cannot be empty" ); | |
5363 | #ifdef CATCH_PLATFORM_WINDOWS | |
5364 | if( name[0] != '-' && name[0] != '/' ) | |
5365 | return Result::logicError( "Option name must begin with '-' or '/'" ); | |
5366 | #else | |
5367 | if( name[0] != '-' ) | |
5368 | return Result::logicError( "Option name must begin with '-'" ); | |
5369 | #endif | |
5370 | } | |
5371 | return ParserRefImpl::validate(); | |
5372 | } | |
5373 | }; | |
5374 | ||
5375 | struct Help : Opt { | |
5376 | Help( bool &showHelpFlag ) | |
5377 | : Opt([&]( bool flag ) { | |
5378 | showHelpFlag = flag; | |
5379 | return ParserResult::ok( ParseResultType::ShortCircuitAll ); | |
5380 | }) | |
5381 | { | |
5382 | static_cast<Opt &>( *this ) | |
5383 | ("display usage information") | |
5384 | ["-?"]["-h"]["--help"] | |
5385 | .optional(); | |
5386 | } | |
5387 | }; | |
5388 | ||
5389 | struct Parser : ParserBase { | |
5390 | ||
5391 | mutable ExeName m_exeName; | |
5392 | std::vector<Opt> m_options; | |
5393 | std::vector<Arg> m_args; | |
5394 | ||
5395 | auto operator|=( ExeName const &exeName ) -> Parser & { | |
5396 | m_exeName = exeName; | |
5397 | return *this; | |
5398 | } | |
5399 | ||
5400 | auto operator|=( Arg const &arg ) -> Parser & { | |
5401 | m_args.push_back(arg); | |
5402 | return *this; | |
5403 | } | |
5404 | ||
5405 | auto operator|=( Opt const &opt ) -> Parser & { | |
5406 | m_options.push_back(opt); | |
5407 | return *this; | |
5408 | } | |
5409 | ||
5410 | auto operator|=( Parser const &other ) -> Parser & { | |
5411 | m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); | |
5412 | m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); | |
5413 | return *this; | |
5414 | } | |
5415 | ||
5416 | template<typename T> | |
5417 | auto operator|( T const &other ) const -> Parser { | |
5418 | return Parser( *this ) |= other; | |
5419 | } | |
5420 | ||
5421 | auto getHelpColumns() const -> std::vector<HelpColumns> { | |
5422 | std::vector<HelpColumns> cols; | |
5423 | for (auto const &o : m_options) { | |
5424 | auto childCols = o.getHelpColumns(); | |
5425 | cols.insert( cols.end(), childCols.begin(), childCols.end() ); | |
5426 | } | |
5427 | return cols; | |
5428 | } | |
5429 | ||
5430 | void writeToStream( std::ostream &os ) const { | |
5431 | if (!m_exeName.name().empty()) { | |
5432 | os << "usage:\n" << " " << m_exeName.name() << " "; | |
5433 | bool required = true, first = true; | |
5434 | for( auto const &arg : m_args ) { | |
5435 | if (first) | |
5436 | first = false; | |
5437 | else | |
5438 | os << " "; | |
5439 | if( arg.isOptional() && required ) { | |
5440 | os << "["; | |
5441 | required = false; | |
5442 | } | |
5443 | os << "<" << arg.hint() << ">"; | |
5444 | if( arg.cardinality() == 0 ) | |
5445 | os << " ... "; | |
5446 | } | |
5447 | if( !required ) | |
5448 | os << "]"; | |
5449 | if( !m_options.empty() ) | |
5450 | os << " options"; | |
5451 | os << "\n\nwhere options are:" << std::endl; | |
5452 | } | |
5453 | ||
5454 | auto rows = getHelpColumns(); | |
5455 | size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; | |
5456 | size_t optWidth = 0; | |
5457 | for( auto const &cols : rows ) | |
5458 | optWidth = (std::max)(optWidth, cols.left.size() + 2); | |
5459 | ||
5460 | for( auto const &cols : rows ) { | |
5461 | auto row = | |
5462 | TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + | |
5463 | TextFlow::Spacer(4) + | |
5464 | TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); | |
5465 | os << row << std::endl; | |
5466 | } | |
5467 | } | |
5468 | ||
5469 | friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { | |
5470 | parser.writeToStream( os ); | |
5471 | return os; | |
5472 | } | |
5473 | ||
5474 | auto validate() const -> Result override { | |
5475 | for( auto const &opt : m_options ) { | |
5476 | auto result = opt.validate(); | |
5477 | if( !result ) | |
5478 | return result; | |
5479 | } | |
5480 | for( auto const &arg : m_args ) { | |
5481 | auto result = arg.validate(); | |
5482 | if( !result ) | |
5483 | return result; | |
5484 | } | |
5485 | return Result::ok(); | |
5486 | } | |
5487 | ||
5488 | using ParserBase::parse; | |
5489 | ||
5490 | auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { | |
5491 | ||
5492 | struct ParserInfo { | |
5493 | ParserBase const* parser = nullptr; | |
5494 | size_t count = 0; | |
5495 | }; | |
5496 | const size_t totalParsers = m_options.size() + m_args.size(); | |
5497 | assert( totalParsers < 512 ); | |
5498 | // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do | |
5499 | ParserInfo parseInfos[512]; | |
5500 | ||
5501 | { | |
5502 | size_t i = 0; | |
5503 | for (auto const &opt : m_options) parseInfos[i++].parser = &opt; | |
5504 | for (auto const &arg : m_args) parseInfos[i++].parser = &arg; | |
5505 | } | |
5506 | ||
5507 | m_exeName.set( exeName ); | |
5508 | ||
5509 | auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); | |
5510 | while( result.value().remainingTokens() ) { | |
5511 | bool tokenParsed = false; | |
5512 | ||
5513 | for( size_t i = 0; i < totalParsers; ++i ) { | |
5514 | auto& parseInfo = parseInfos[i]; | |
5515 | if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { | |
5516 | result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); | |
5517 | if (!result) | |
5518 | return result; | |
5519 | if (result.value().type() != ParseResultType::NoMatch) { | |
5520 | tokenParsed = true; | |
5521 | ++parseInfo.count; | |
5522 | break; | |
5523 | } | |
5524 | } | |
5525 | } | |
5526 | ||
5527 | if( result.value().type() == ParseResultType::ShortCircuitAll ) | |
5528 | return result; | |
5529 | if( !tokenParsed ) | |
5530 | return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); | |
5531 | } | |
5532 | // !TBD Check missing required options | |
5533 | return result; | |
5534 | } | |
5535 | }; | |
5536 | ||
5537 | template<typename DerivedT> | |
5538 | template<typename T> | |
5539 | auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser { | |
5540 | return Parser() | static_cast<DerivedT const &>( *this ) | other; | |
5541 | } | |
5542 | } // namespace detail | |
5543 | ||
5544 | // A Combined parser | |
5545 | using detail::Parser; | |
5546 | ||
5547 | // A parser for options | |
5548 | using detail::Opt; | |
5549 | ||
5550 | // A parser for arguments | |
5551 | using detail::Arg; | |
5552 | ||
5553 | // Wrapper for argc, argv from main() | |
5554 | using detail::Args; | |
5555 | ||
5556 | // Specifies the name of the executable | |
5557 | using detail::ExeName; | |
5558 | ||
5559 | // Convenience wrapper for option parser that specifies the help option | |
5560 | using detail::Help; | |
5561 | ||
5562 | // enum of result types from a parse | |
5563 | using detail::ParseResultType; | |
5564 | ||
5565 | // Result type for parser operation | |
5566 | using detail::ParserResult; | |
5567 | ||
5568 | }} // namespace Catch::clara | |
5569 | ||
5570 | // end clara.hpp | |
5571 | #ifdef __clang__ | |
5572 | #pragma clang diagnostic pop | |
5573 | #endif | |
5574 | ||
5575 | // Restore Clara's value for console width, if present | |
5576 | #ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH | |
5577 | #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH | |
5578 | #undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH | |
5579 | #endif | |
5580 | ||
5581 | // end catch_clara.h | |
5582 | namespace Catch { | |
5583 | ||
5584 | clara::Parser makeCommandLineParser( ConfigData& config ); | |
5585 | ||
5586 | } // end namespace Catch | |
5587 | ||
5588 | // end catch_commandline.h | |
5589 | #include <fstream> | |
5590 | #include <ctime> | |
5591 | ||
5592 | namespace Catch { | |
5593 | ||
5594 | clara::Parser makeCommandLineParser( ConfigData& config ) { | |
5595 | ||
5596 | using namespace clara; | |
5597 | ||
5598 | auto const setWarning = [&]( std::string const& warning ) { | |
5599 | if( warning != "NoAssertions" ) | |
5600 | return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); | |
5601 | config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions ); | |
5602 | return ParserResult::ok( ParseResultType::Matched ); | |
5603 | }; | |
5604 | auto const loadTestNamesFromFile = [&]( std::string const& filename ) { | |
5605 | std::ifstream f( filename.c_str() ); | |
5606 | if( !f.is_open() ) | |
5607 | return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); | |
5608 | ||
5609 | std::string line; | |
5610 | while( std::getline( f, line ) ) { | |
5611 | line = trim(line); | |
5612 | if( !line.empty() && !startsWith( line, '#' ) ) { | |
5613 | if( !startsWith( line, '"' ) ) | |
5614 | line = '"' + line + '"'; | |
5615 | config.testsOrTags.push_back( line + ',' ); | |
5616 | } | |
5617 | } | |
5618 | return ParserResult::ok( ParseResultType::Matched ); | |
5619 | }; | |
5620 | auto const setTestOrder = [&]( std::string const& order ) { | |
5621 | if( startsWith( "declared", order ) ) | |
5622 | config.runOrder = RunTests::InDeclarationOrder; | |
5623 | else if( startsWith( "lexical", order ) ) | |
5624 | config.runOrder = RunTests::InLexicographicalOrder; | |
5625 | else if( startsWith( "random", order ) ) | |
5626 | config.runOrder = RunTests::InRandomOrder; | |
5627 | else | |
5628 | return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); | |
5629 | return ParserResult::ok( ParseResultType::Matched ); | |
5630 | }; | |
5631 | auto const setRngSeed = [&]( std::string const& seed ) { | |
5632 | if( seed != "time" ) | |
5633 | return clara::detail::convertInto( seed, config.rngSeed ); | |
5634 | config.rngSeed = static_cast<unsigned int>( std::time(nullptr) ); | |
5635 | return ParserResult::ok( ParseResultType::Matched ); | |
5636 | }; | |
5637 | auto const setColourUsage = [&]( std::string const& useColour ) { | |
5638 | auto mode = toLower( useColour ); | |
5639 | ||
5640 | if( mode == "yes" ) | |
5641 | config.useColour = UseColour::Yes; | |
5642 | else if( mode == "no" ) | |
5643 | config.useColour = UseColour::No; | |
5644 | else if( mode == "auto" ) | |
5645 | config.useColour = UseColour::Auto; | |
5646 | else | |
5647 | return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); | |
5648 | return ParserResult::ok( ParseResultType::Matched ); | |
5649 | }; | |
5650 | auto const setWaitForKeypress = [&]( std::string const& keypress ) { | |
5651 | auto keypressLc = toLower( keypress ); | |
5652 | if( keypressLc == "start" ) | |
5653 | config.waitForKeypress = WaitForKeypress::BeforeStart; | |
5654 | else if( keypressLc == "exit" ) | |
5655 | config.waitForKeypress = WaitForKeypress::BeforeExit; | |
5656 | else if( keypressLc == "both" ) | |
5657 | config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; | |
5658 | else | |
5659 | return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); | |
5660 | return ParserResult::ok( ParseResultType::Matched ); | |
5661 | }; | |
5662 | auto const setVerbosity = [&]( std::string const& verbosity ) { | |
5663 | auto lcVerbosity = toLower( verbosity ); | |
5664 | if( lcVerbosity == "quiet" ) | |
5665 | config.verbosity = Verbosity::Quiet; | |
5666 | else if( lcVerbosity == "normal" ) | |
5667 | config.verbosity = Verbosity::Normal; | |
5668 | else if( lcVerbosity == "high" ) | |
5669 | config.verbosity = Verbosity::High; | |
5670 | else | |
5671 | return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); | |
5672 | return ParserResult::ok( ParseResultType::Matched ); | |
5673 | }; | |
5674 | ||
5675 | auto cli | |
5676 | = ExeName( config.processName ) | |
5677 | | Help( config.showHelp ) | |
5678 | | Opt( config.listTests ) | |
5679 | ["-l"]["--list-tests"] | |
5680 | ( "list all/matching test cases" ) | |
5681 | | Opt( config.listTags ) | |
5682 | ["-t"]["--list-tags"] | |
5683 | ( "list all/matching tags" ) | |
5684 | | Opt( config.showSuccessfulTests ) | |
5685 | ["-s"]["--success"] | |
5686 | ( "include successful tests in output" ) | |
5687 | | Opt( config.shouldDebugBreak ) | |
5688 | ["-b"]["--break"] | |
5689 | ( "break into debugger on failure" ) | |
5690 | | Opt( config.noThrow ) | |
5691 | ["-e"]["--nothrow"] | |
5692 | ( "skip exception tests" ) | |
5693 | | Opt( config.showInvisibles ) | |
5694 | ["-i"]["--invisibles"] | |
5695 | ( "show invisibles (tabs, newlines)" ) | |
5696 | | Opt( config.outputFilename, "filename" ) | |
5697 | ["-o"]["--out"] | |
5698 | ( "output filename" ) | |
5699 | | Opt( config.reporterNames, "name" ) | |
5700 | ["-r"]["--reporter"] | |
5701 | ( "reporter to use (defaults to console)" ) | |
5702 | | Opt( config.name, "name" ) | |
5703 | ["-n"]["--name"] | |
5704 | ( "suite name" ) | |
5705 | | Opt( [&]( bool ){ config.abortAfter = 1; } ) | |
5706 | ["-a"]["--abort"] | |
5707 | ( "abort at first failure" ) | |
5708 | | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) | |
5709 | ["-x"]["--abortx"] | |
5710 | ( "abort after x failures" ) | |
5711 | | Opt( setWarning, "warning name" ) | |
5712 | ["-w"]["--warn"] | |
5713 | ( "enable warnings" ) | |
5714 | | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) | |
5715 | ["-d"]["--durations"] | |
5716 | ( "show test durations" ) | |
5717 | | Opt( loadTestNamesFromFile, "filename" ) | |
5718 | ["-f"]["--input-file"] | |
5719 | ( "load test names to run from a file" ) | |
5720 | | Opt( config.filenamesAsTags ) | |
5721 | ["-#"]["--filenames-as-tags"] | |
5722 | ( "adds a tag for the filename" ) | |
5723 | | Opt( config.sectionsToRun, "section name" ) | |
5724 | ["-c"]["--section"] | |
5725 | ( "specify section to run" ) | |
5726 | | Opt( setVerbosity, "quiet|normal|high" ) | |
5727 | ["-v"]["--verbosity"] | |
5728 | ( "set output verbosity" ) | |
5729 | | Opt( config.listTestNamesOnly ) | |
5730 | ["--list-test-names-only"] | |
5731 | ( "list all/matching test cases names only" ) | |
5732 | | Opt( config.listReporters ) | |
5733 | ["--list-reporters"] | |
5734 | ( "list all reporters" ) | |
5735 | | Opt( setTestOrder, "decl|lex|rand" ) | |
5736 | ["--order"] | |
5737 | ( "test case order (defaults to decl)" ) | |
5738 | | Opt( setRngSeed, "'time'|number" ) | |
5739 | ["--rng-seed"] | |
5740 | ( "set a specific seed for random numbers" ) | |
5741 | | Opt( setColourUsage, "yes|no" ) | |
5742 | ["--use-colour"] | |
5743 | ( "should output be colourised" ) | |
5744 | | Opt( config.libIdentify ) | |
5745 | ["--libidentify"] | |
5746 | ( "report name and version according to libidentify standard" ) | |
5747 | | Opt( setWaitForKeypress, "start|exit|both" ) | |
5748 | ["--wait-for-keypress"] | |
5749 | ( "waits for a keypress before exiting" ) | |
5750 | | Opt( config.benchmarkResolutionMultiple, "multiplier" ) | |
5751 | ["--benchmark-resolution-multiple"] | |
5752 | ( "multiple of clock resolution to run benchmarks" ) | |
5753 | ||
5754 | | Arg( config.testsOrTags, "test name|pattern|tags" ) | |
5755 | ( "which test or tests to use" ); | |
5756 | ||
5757 | return cli; | |
5758 | } | |
5759 | ||
5760 | } // end namespace Catch | |
5761 | // end catch_commandline.cpp | |
5762 | // start catch_common.cpp | |
5763 | ||
5764 | #include <cstring> | |
5765 | #include <ostream> | |
5766 | ||
5767 | namespace Catch { | |
5768 | ||
5769 | SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept | |
5770 | : file( _file ), | |
5771 | line( _line ) | |
5772 | {} | |
5773 | bool SourceLineInfo::empty() const noexcept { | |
5774 | return file[0] == '\0'; | |
5775 | } | |
5776 | bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { | |
5777 | return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); | |
5778 | } | |
5779 | bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { | |
5780 | return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); | |
5781 | } | |
5782 | ||
5783 | std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { | |
5784 | #ifndef __GNUG__ | |
5785 | os << info.file << '(' << info.line << ')'; | |
5786 | #else | |
5787 | os << info.file << ':' << info.line; | |
5788 | #endif | |
5789 | return os; | |
5790 | } | |
5791 | ||
5792 | bool isTrue( bool value ){ return value; } | |
5793 | bool alwaysTrue() { return true; } | |
5794 | bool alwaysFalse() { return false; } | |
5795 | ||
5796 | std::string StreamEndStop::operator+() const { | |
5797 | return std::string(); | |
5798 | } | |
5799 | ||
5800 | NonCopyable::NonCopyable() = default; | |
5801 | NonCopyable::~NonCopyable() = default; | |
5802 | ||
5803 | } | |
5804 | // end catch_common.cpp | |
5805 | // start catch_config.cpp | |
5806 | ||
5807 | namespace Catch { | |
5808 | ||
5809 | Config::Config( ConfigData const& data ) | |
5810 | : m_data( data ), | |
5811 | m_stream( openStream() ) | |
5812 | { | |
5813 | if( !data.testsOrTags.empty() ) { | |
5814 | TestSpecParser parser( ITagAliasRegistry::get() ); | |
5815 | for( auto const& testOrTags : data.testsOrTags ) | |
5816 | parser.parse( testOrTags ); | |
5817 | m_testSpec = parser.testSpec(); | |
5818 | } | |
5819 | } | |
5820 | ||
5821 | std::string const& Config::getFilename() const { | |
5822 | return m_data.outputFilename ; | |
5823 | } | |
5824 | ||
5825 | bool Config::listTests() const { return m_data.listTests; } | |
5826 | bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } | |
5827 | bool Config::listTags() const { return m_data.listTags; } | |
5828 | bool Config::listReporters() const { return m_data.listReporters; } | |
5829 | ||
5830 | std::string Config::getProcessName() const { return m_data.processName; } | |
5831 | ||
5832 | std::vector<std::string> const& Config::getReporterNames() const { return m_data.reporterNames; } | |
5833 | std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } | |
5834 | ||
5835 | TestSpec const& Config::testSpec() const { return m_testSpec; } | |
5836 | ||
5837 | bool Config::showHelp() const { return m_data.showHelp; } | |
5838 | ||
5839 | // IConfig interface | |
5840 | bool Config::allowThrows() const { return !m_data.noThrow; } | |
5841 | std::ostream& Config::stream() const { return m_stream->stream(); } | |
5842 | std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } | |
5843 | bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } | |
5844 | bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } | |
5845 | ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } | |
5846 | RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } | |
5847 | unsigned int Config::rngSeed() const { return m_data.rngSeed; } | |
5848 | int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } | |
5849 | UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } | |
5850 | bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } | |
5851 | int Config::abortAfter() const { return m_data.abortAfter; } | |
5852 | bool Config::showInvisibles() const { return m_data.showInvisibles; } | |
5853 | Verbosity Config::verbosity() const { return m_data.verbosity; } | |
5854 | ||
5855 | IStream const* Config::openStream() { | |
5856 | if( m_data.outputFilename.empty() ) | |
5857 | return new CoutStream(); | |
5858 | else if( m_data.outputFilename[0] == '%' ) { | |
5859 | if( m_data.outputFilename == "%debug" ) | |
5860 | return new DebugOutStream(); | |
5861 | else | |
5862 | CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" ); | |
5863 | } | |
5864 | else | |
5865 | return new FileStream( m_data.outputFilename ); | |
5866 | } | |
5867 | ||
5868 | } // end namespace Catch | |
5869 | // end catch_config.cpp | |
5870 | // start catch_console_colour.cpp | |
5871 | ||
5872 | #if defined(__clang__) | |
5873 | # pragma clang diagnostic push | |
5874 | # pragma clang diagnostic ignored "-Wexit-time-destructors" | |
5875 | #endif | |
5876 | ||
5877 | // start catch_errno_guard.h | |
5878 | ||
5879 | namespace Catch { | |
5880 | ||
5881 | class ErrnoGuard { | |
5882 | public: | |
5883 | ErrnoGuard(); | |
5884 | ~ErrnoGuard(); | |
5885 | private: | |
5886 | int m_oldErrno; | |
5887 | }; | |
5888 | ||
5889 | } | |
5890 | ||
5891 | // end catch_errno_guard.h | |
5892 | // start catch_windows_h_proxy.h | |
5893 | ||
5894 | ||
5895 | #if defined(CATCH_PLATFORM_WINDOWS) | |
5896 | ||
5897 | #if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) | |
5898 | # define CATCH_DEFINED_NOMINMAX | |
5899 | # define NOMINMAX | |
5900 | #endif | |
5901 | #if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) | |
5902 | # define CATCH_DEFINED_WIN32_LEAN_AND_MEAN | |
5903 | # define WIN32_LEAN_AND_MEAN | |
5904 | #endif | |
5905 | ||
5906 | #ifdef __AFXDLL | |
5907 | #include <AfxWin.h> | |
5908 | #else | |
5909 | #include <windows.h> | |
5910 | #endif | |
5911 | ||
5912 | #ifdef CATCH_DEFINED_NOMINMAX | |
5913 | # undef NOMINMAX | |
5914 | #endif | |
5915 | #ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN | |
5916 | # undef WIN32_LEAN_AND_MEAN | |
5917 | #endif | |
5918 | ||
5919 | #endif // defined(CATCH_PLATFORM_WINDOWS) | |
5920 | ||
5921 | // end catch_windows_h_proxy.h | |
5922 | namespace Catch { | |
5923 | namespace { | |
5924 | ||
5925 | struct IColourImpl { | |
5926 | virtual ~IColourImpl() = default; | |
5927 | virtual void use( Colour::Code _colourCode ) = 0; | |
5928 | }; | |
5929 | ||
5930 | struct NoColourImpl : IColourImpl { | |
5931 | void use( Colour::Code ) {} | |
5932 | ||
5933 | static IColourImpl* instance() { | |
5934 | static NoColourImpl s_instance; | |
5935 | return &s_instance; | |
5936 | } | |
5937 | }; | |
5938 | ||
5939 | } // anon namespace | |
5940 | } // namespace Catch | |
5941 | ||
5942 | #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) | |
5943 | # ifdef CATCH_PLATFORM_WINDOWS | |
5944 | # define CATCH_CONFIG_COLOUR_WINDOWS | |
5945 | # else | |
5946 | # define CATCH_CONFIG_COLOUR_ANSI | |
5947 | # endif | |
5948 | #endif | |
5949 | ||
5950 | #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// | |
5951 | ||
5952 | namespace Catch { | |
5953 | namespace { | |
5954 | ||
5955 | class Win32ColourImpl : public IColourImpl { | |
5956 | public: | |
5957 | Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) | |
5958 | { | |
5959 | CONSOLE_SCREEN_BUFFER_INFO csbiInfo; | |
5960 | GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); | |
5961 | originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); | |
5962 | originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); | |
5963 | } | |
5964 | ||
5965 | virtual void use( Colour::Code _colourCode ) override { | |
5966 | switch( _colourCode ) { | |
5967 | case Colour::None: return setTextAttribute( originalForegroundAttributes ); | |
5968 | case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); | |
5969 | case Colour::Red: return setTextAttribute( FOREGROUND_RED ); | |
5970 | case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); | |
5971 | case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); | |
5972 | case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); | |
5973 | case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); | |
5974 | case Colour::Grey: return setTextAttribute( 0 ); | |
5975 | ||
5976 | case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); | |
5977 | case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); | |
5978 | case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); | |
5979 | case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); | |
5980 | ||
5981 | case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); | |
5982 | } | |
5983 | } | |
5984 | ||
5985 | private: | |
5986 | void setTextAttribute( WORD _textAttribute ) { | |
5987 | SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); | |
5988 | } | |
5989 | HANDLE stdoutHandle; | |
5990 | WORD originalForegroundAttributes; | |
5991 | WORD originalBackgroundAttributes; | |
5992 | }; | |
5993 | ||
5994 | IColourImpl* platformColourInstance() { | |
5995 | static Win32ColourImpl s_instance; | |
5996 | ||
5997 | IConfigPtr config = getCurrentContext().getConfig(); | |
5998 | UseColour::YesOrNo colourMode = config | |
5999 | ? config->useColour() | |
6000 | : UseColour::Auto; | |
6001 | if( colourMode == UseColour::Auto ) | |
6002 | colourMode = UseColour::Yes; | |
6003 | return colourMode == UseColour::Yes | |
6004 | ? &s_instance | |
6005 | : NoColourImpl::instance(); | |
6006 | } | |
6007 | ||
6008 | } // end anon namespace | |
6009 | } // end namespace Catch | |
6010 | ||
6011 | #elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// | |
6012 | ||
6013 | #include <unistd.h> | |
6014 | ||
6015 | namespace Catch { | |
6016 | namespace { | |
6017 | ||
6018 | // use POSIX/ ANSI console terminal codes | |
6019 | // Thanks to Adam Strzelecki for original contribution | |
6020 | // (http://github.com/nanoant) | |
6021 | // https://github.com/philsquared/Catch/pull/131 | |
6022 | class PosixColourImpl : public IColourImpl { | |
6023 | public: | |
6024 | virtual void use( Colour::Code _colourCode ) override { | |
6025 | switch( _colourCode ) { | |
6026 | case Colour::None: | |
6027 | case Colour::White: return setColour( "[0m" ); | |
6028 | case Colour::Red: return setColour( "[0;31m" ); | |
6029 | case Colour::Green: return setColour( "[0;32m" ); | |
6030 | case Colour::Blue: return setColour( "[0;34m" ); | |
6031 | case Colour::Cyan: return setColour( "[0;36m" ); | |
6032 | case Colour::Yellow: return setColour( "[0;33m" ); | |
6033 | case Colour::Grey: return setColour( "[1;30m" ); | |
6034 | ||
6035 | case Colour::LightGrey: return setColour( "[0;37m" ); | |
6036 | case Colour::BrightRed: return setColour( "[1;31m" ); | |
6037 | case Colour::BrightGreen: return setColour( "[1;32m" ); | |
6038 | case Colour::BrightWhite: return setColour( "[1;37m" ); | |
6039 | ||
6040 | case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); | |
6041 | } | |
6042 | } | |
6043 | static IColourImpl* instance() { | |
6044 | static PosixColourImpl s_instance; | |
6045 | return &s_instance; | |
6046 | } | |
6047 | ||
6048 | private: | |
6049 | void setColour( const char* _escapeCode ) { | |
6050 | Catch::cout() << '\033' << _escapeCode; | |
6051 | } | |
6052 | }; | |
6053 | ||
6054 | bool useColourOnPlatform() { | |
6055 | return | |
6056 | #ifdef CATCH_PLATFORM_MAC | |
6057 | !isDebuggerActive() && | |
6058 | #endif | |
6059 | isatty(STDOUT_FILENO); | |
6060 | } | |
6061 | IColourImpl* platformColourInstance() { | |
6062 | ErrnoGuard guard; | |
6063 | IConfigPtr config = getCurrentContext().getConfig(); | |
6064 | UseColour::YesOrNo colourMode = config | |
6065 | ? config->useColour() | |
6066 | : UseColour::Auto; | |
6067 | if( colourMode == UseColour::Auto ) | |
6068 | colourMode = useColourOnPlatform() | |
6069 | ? UseColour::Yes | |
6070 | : UseColour::No; | |
6071 | return colourMode == UseColour::Yes | |
6072 | ? PosixColourImpl::instance() | |
6073 | : NoColourImpl::instance(); | |
6074 | } | |
6075 | ||
6076 | } // end anon namespace | |
6077 | } // end namespace Catch | |
6078 | ||
6079 | #else // not Windows or ANSI /////////////////////////////////////////////// | |
6080 | ||
6081 | namespace Catch { | |
6082 | ||
6083 | static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } | |
6084 | ||
6085 | } // end namespace Catch | |
6086 | ||
6087 | #endif // Windows/ ANSI/ None | |
6088 | ||
6089 | namespace Catch { | |
6090 | ||
6091 | Colour::Colour( Code _colourCode ) { use( _colourCode ); } | |
6092 | Colour::Colour( Colour&& rhs ) noexcept { | |
6093 | m_moved = rhs.m_moved; | |
6094 | rhs.m_moved = true; | |
6095 | } | |
6096 | Colour& Colour::operator=( Colour&& rhs ) noexcept { | |
6097 | m_moved = rhs.m_moved; | |
6098 | rhs.m_moved = true; | |
6099 | return *this; | |
6100 | } | |
6101 | ||
6102 | Colour::~Colour(){ if( !m_moved ) use( None ); } | |
6103 | ||
6104 | void Colour::use( Code _colourCode ) { | |
6105 | static IColourImpl* impl = platformColourInstance(); | |
6106 | impl->use( _colourCode ); | |
6107 | } | |
6108 | ||
6109 | std::ostream& operator << ( std::ostream& os, Colour const& ) { | |
6110 | return os; | |
6111 | } | |
6112 | ||
6113 | } // end namespace Catch | |
6114 | ||
6115 | #if defined(__clang__) | |
6116 | # pragma clang diagnostic pop | |
6117 | #endif | |
6118 | ||
6119 | // end catch_console_colour.cpp | |
6120 | // start catch_context.cpp | |
6121 | ||
6122 | namespace Catch { | |
6123 | ||
6124 | class Context : public IMutableContext, NonCopyable { | |
6125 | ||
6126 | public: // IContext | |
6127 | virtual IResultCapture* getResultCapture() override { | |
6128 | return m_resultCapture; | |
6129 | } | |
6130 | virtual IRunner* getRunner() override { | |
6131 | return m_runner; | |
6132 | } | |
6133 | ||
6134 | virtual IConfigPtr getConfig() const override { | |
6135 | return m_config; | |
6136 | } | |
6137 | ||
6138 | virtual ~Context() override; | |
6139 | ||
6140 | public: // IMutableContext | |
6141 | virtual void setResultCapture( IResultCapture* resultCapture ) override { | |
6142 | m_resultCapture = resultCapture; | |
6143 | } | |
6144 | virtual void setRunner( IRunner* runner ) override { | |
6145 | m_runner = runner; | |
6146 | } | |
6147 | virtual void setConfig( IConfigPtr const& config ) override { | |
6148 | m_config = config; | |
6149 | } | |
6150 | ||
6151 | friend IMutableContext& getCurrentMutableContext(); | |
6152 | ||
6153 | private: | |
6154 | IConfigPtr m_config; | |
6155 | IRunner* m_runner = nullptr; | |
6156 | IResultCapture* m_resultCapture = nullptr; | |
6157 | }; | |
6158 | ||
6159 | namespace { | |
6160 | Context* currentContext = nullptr; | |
6161 | } | |
6162 | IMutableContext& getCurrentMutableContext() { | |
6163 | if( !currentContext ) | |
6164 | currentContext = new Context(); | |
6165 | return *currentContext; | |
6166 | } | |
6167 | IContext& getCurrentContext() { | |
6168 | return getCurrentMutableContext(); | |
6169 | } | |
6170 | ||
6171 | void cleanUpContext() { | |
6172 | delete currentContext; | |
6173 | currentContext = nullptr; | |
6174 | } | |
6175 | IContext::~IContext() = default; | |
6176 | IMutableContext::~IMutableContext() = default; | |
6177 | Context::~Context() = default; | |
6178 | } | |
6179 | // end catch_context.cpp | |
6180 | // start catch_debug_console.cpp | |
6181 | ||
6182 | // start catch_debug_console.h | |
6183 | ||
6184 | #include <string> | |
6185 | ||
6186 | namespace Catch { | |
6187 | void writeToDebugConsole( std::string const& text ); | |
6188 | } | |
6189 | ||
6190 | // end catch_debug_console.h | |
6191 | #ifdef CATCH_PLATFORM_WINDOWS | |
6192 | ||
6193 | namespace Catch { | |
6194 | void writeToDebugConsole( std::string const& text ) { | |
6195 | ::OutputDebugStringA( text.c_str() ); | |
6196 | } | |
6197 | } | |
6198 | #else | |
6199 | namespace Catch { | |
6200 | void writeToDebugConsole( std::string const& text ) { | |
6201 | // !TBD: Need a version for Mac/ XCode and other IDEs | |
6202 | Catch::cout() << text; | |
6203 | } | |
6204 | } | |
6205 | #endif // Platform | |
6206 | // end catch_debug_console.cpp | |
6207 | // start catch_debugger.cpp | |
6208 | ||
6209 | #ifdef CATCH_PLATFORM_MAC | |
6210 | ||
6211 | #include <assert.h> | |
6212 | #include <stdbool.h> | |
6213 | #include <sys/types.h> | |
6214 | #include <unistd.h> | |
6215 | #include <sys/sysctl.h> | |
6216 | ||
6217 | namespace Catch { | |
6218 | ||
6219 | // The following function is taken directly from the following technical note: | |
6220 | // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html | |
6221 | ||
6222 | // Returns true if the current process is being debugged (either | |
6223 | // running under the debugger or has a debugger attached post facto). | |
6224 | bool isDebuggerActive(){ | |
6225 | ||
6226 | int mib[4]; | |
6227 | struct kinfo_proc info; | |
6228 | std::size_t size; | |
6229 | ||
6230 | // Initialize the flags so that, if sysctl fails for some bizarre | |
6231 | // reason, we get a predictable result. | |
6232 | ||
6233 | info.kp_proc.p_flag = 0; | |
6234 | ||
6235 | // Initialize mib, which tells sysctl the info we want, in this case | |
6236 | // we're looking for information about a specific process ID. | |
6237 | ||
6238 | mib[0] = CTL_KERN; | |
6239 | mib[1] = KERN_PROC; | |
6240 | mib[2] = KERN_PROC_PID; | |
6241 | mib[3] = getpid(); | |
6242 | ||
6243 | // Call sysctl. | |
6244 | ||
6245 | size = sizeof(info); | |
6246 | if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { | |
6247 | Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; | |
6248 | return false; | |
6249 | } | |
6250 | ||
6251 | // We're being debugged if the P_TRACED flag is set. | |
6252 | ||
6253 | return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); | |
6254 | } | |
6255 | } // namespace Catch | |
6256 | ||
6257 | #elif defined(CATCH_PLATFORM_LINUX) | |
6258 | #include <fstream> | |
6259 | #include <string> | |
6260 | ||
6261 | namespace Catch{ | |
6262 | // The standard POSIX way of detecting a debugger is to attempt to | |
6263 | // ptrace() the process, but this needs to be done from a child and not | |
6264 | // this process itself to still allow attaching to this process later | |
6265 | // if wanted, so is rather heavy. Under Linux we have the PID of the | |
6266 | // "debugger" (which doesn't need to be gdb, of course, it could also | |
6267 | // be strace, for example) in /proc/$PID/status, so just get it from | |
6268 | // there instead. | |
6269 | bool isDebuggerActive(){ | |
6270 | // Libstdc++ has a bug, where std::ifstream sets errno to 0 | |
6271 | // This way our users can properly assert over errno values | |
6272 | ErrnoGuard guard; | |
6273 | std::ifstream in("/proc/self/status"); | |
6274 | for( std::string line; std::getline(in, line); ) { | |
6275 | static const int PREFIX_LEN = 11; | |
6276 | if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { | |
6277 | // We're traced if the PID is not 0 and no other PID starts | |
6278 | // with 0 digit, so it's enough to check for just a single | |
6279 | // character. | |
6280 | return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; | |
6281 | } | |
6282 | } | |
6283 | ||
6284 | return false; | |
6285 | } | |
6286 | } // namespace Catch | |
6287 | #elif defined(_MSC_VER) | |
6288 | extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); | |
6289 | namespace Catch { | |
6290 | bool isDebuggerActive() { | |
6291 | return IsDebuggerPresent() != 0; | |
6292 | } | |
6293 | } | |
6294 | #elif defined(__MINGW32__) | |
6295 | extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); | |
6296 | namespace Catch { | |
6297 | bool isDebuggerActive() { | |
6298 | return IsDebuggerPresent() != 0; | |
6299 | } | |
6300 | } | |
6301 | #else | |
6302 | namespace Catch { | |
6303 | bool isDebuggerActive() { return false; } | |
6304 | } | |
6305 | #endif // Platform | |
6306 | // end catch_debugger.cpp | |
6307 | // start catch_decomposer.cpp | |
6308 | ||
6309 | namespace Catch { | |
6310 | ||
6311 | ITransientExpression::~ITransientExpression() = default; | |
6312 | ||
6313 | void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { | |
6314 | if( lhs.size() + rhs.size() < 40 && | |
6315 | lhs.find('\n') == std::string::npos && | |
6316 | rhs.find('\n') == std::string::npos ) | |
6317 | os << lhs << " " << op << " " << rhs; | |
6318 | else | |
6319 | os << lhs << "\n" << op << "\n" << rhs; | |
6320 | } | |
6321 | } | |
6322 | // end catch_decomposer.cpp | |
6323 | // start catch_errno_guard.cpp | |
6324 | ||
6325 | #include <cerrno> | |
6326 | ||
6327 | namespace Catch { | |
6328 | ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} | |
6329 | ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } | |
6330 | } | |
6331 | // end catch_errno_guard.cpp | |
6332 | // start catch_exception_translator_registry.cpp | |
6333 | ||
6334 | // start catch_exception_translator_registry.h | |
6335 | ||
6336 | #include <vector> | |
6337 | #include <string> | |
6338 | #include <memory> | |
6339 | ||
6340 | namespace Catch { | |
6341 | ||
6342 | class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { | |
6343 | public: | |
6344 | ~ExceptionTranslatorRegistry(); | |
6345 | virtual void registerTranslator( const IExceptionTranslator* translator ); | |
6346 | virtual std::string translateActiveException() const override; | |
6347 | std::string tryTranslators() const; | |
6348 | ||
6349 | private: | |
6350 | std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators; | |
6351 | }; | |
6352 | } | |
6353 | ||
6354 | // end catch_exception_translator_registry.h | |
6355 | #ifdef __OBJC__ | |
6356 | #import "Foundation/Foundation.h" | |
6357 | #endif | |
6358 | ||
6359 | namespace Catch { | |
6360 | ||
6361 | ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { | |
6362 | } | |
6363 | ||
6364 | void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { | |
6365 | m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) ); | |
6366 | } | |
6367 | ||
6368 | std::string ExceptionTranslatorRegistry::translateActiveException() const { | |
6369 | try { | |
6370 | #ifdef __OBJC__ | |
6371 | // In Objective-C try objective-c exceptions first | |
6372 | @try { | |
6373 | return tryTranslators(); | |
6374 | } | |
6375 | @catch (NSException *exception) { | |
6376 | return Catch::Detail::stringify( [exception description] ); | |
6377 | } | |
6378 | #else | |
6379 | return tryTranslators(); | |
6380 | #endif | |
6381 | } | |
6382 | catch( TestFailureException& ) { | |
6383 | std::rethrow_exception(std::current_exception()); | |
6384 | } | |
6385 | catch( std::exception& ex ) { | |
6386 | return ex.what(); | |
6387 | } | |
6388 | catch( std::string& msg ) { | |
6389 | return msg; | |
6390 | } | |
6391 | catch( const char* msg ) { | |
6392 | return msg; | |
6393 | } | |
6394 | catch(...) { | |
6395 | return "Unknown exception"; | |
6396 | } | |
6397 | } | |
6398 | ||
6399 | std::string ExceptionTranslatorRegistry::tryTranslators() const { | |
6400 | if( m_translators.empty() ) | |
6401 | std::rethrow_exception(std::current_exception()); | |
6402 | else | |
6403 | return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); | |
6404 | } | |
6405 | } | |
6406 | // end catch_exception_translator_registry.cpp | |
6407 | // start catch_fatal_condition.cpp | |
6408 | ||
6409 | // start catch_fatal_condition.h | |
6410 | ||
6411 | #include <string> | |
6412 | ||
6413 | #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// | |
6414 | ||
6415 | # if !defined ( CATCH_CONFIG_WINDOWS_SEH ) | |
6416 | ||
6417 | namespace Catch { | |
6418 | struct FatalConditionHandler { | |
6419 | void reset(); | |
6420 | }; | |
6421 | } | |
6422 | ||
6423 | # else // CATCH_CONFIG_WINDOWS_SEH is defined | |
6424 | ||
6425 | namespace Catch { | |
6426 | ||
6427 | struct FatalConditionHandler { | |
6428 | ||
6429 | static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); | |
6430 | FatalConditionHandler(); | |
6431 | static void reset(); | |
6432 | ~FatalConditionHandler(); | |
6433 | ||
6434 | private: | |
6435 | static bool isSet; | |
6436 | static ULONG guaranteeSize; | |
6437 | static PVOID exceptionHandlerHandle; | |
6438 | }; | |
6439 | ||
6440 | } // namespace Catch | |
6441 | ||
6442 | # endif // CATCH_CONFIG_WINDOWS_SEH | |
6443 | ||
6444 | #else // Not Windows - assumed to be POSIX compatible ////////////////////////// | |
6445 | ||
6446 | # if !defined(CATCH_CONFIG_POSIX_SIGNALS) | |
6447 | ||
6448 | namespace Catch { | |
6449 | struct FatalConditionHandler { | |
6450 | void reset(); | |
6451 | }; | |
6452 | } | |
6453 | ||
6454 | # else // CATCH_CONFIG_POSIX_SIGNALS is defined | |
6455 | ||
6456 | #include <signal.h> | |
6457 | ||
6458 | namespace Catch { | |
6459 | ||
6460 | struct FatalConditionHandler { | |
6461 | ||
6462 | static bool isSet; | |
6463 | static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; | |
6464 | static stack_t oldSigStack; | |
6465 | static char altStackMem[]; | |
6466 | ||
6467 | static void handleSignal( int sig ); | |
6468 | ||
6469 | FatalConditionHandler(); | |
6470 | ~FatalConditionHandler(); | |
6471 | static void reset(); | |
6472 | }; | |
6473 | ||
6474 | } // namespace Catch | |
6475 | ||
6476 | # endif // CATCH_CONFIG_POSIX_SIGNALS | |
6477 | ||
6478 | #endif // not Windows | |
6479 | ||
6480 | // end catch_fatal_condition.h | |
6481 | namespace { | |
6482 | // Report the error condition | |
6483 | void reportFatal( char const * const message ) { | |
6484 | Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); | |
6485 | } | |
6486 | } | |
6487 | ||
6488 | #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// | |
6489 | ||
6490 | # if !defined ( CATCH_CONFIG_WINDOWS_SEH ) | |
6491 | ||
6492 | namespace Catch { | |
6493 | void FatalConditionHandler::reset() {} | |
6494 | } | |
6495 | ||
6496 | # else // CATCH_CONFIG_WINDOWS_SEH is defined | |
6497 | ||
6498 | namespace Catch { | |
6499 | struct SignalDefs { DWORD id; const char* name; }; | |
6500 | ||
6501 | // There is no 1-1 mapping between signals and windows exceptions. | |
6502 | // Windows can easily distinguish between SO and SigSegV, | |
6503 | // but SigInt, SigTerm, etc are handled differently. | |
6504 | static SignalDefs signalDefs[] = { | |
6505 | { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, | |
6506 | { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, | |
6507 | { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, | |
6508 | { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, | |
6509 | }; | |
6510 | ||
6511 | LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { | |
6512 | for (auto const& def : signalDefs) { | |
6513 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { | |
6514 | reportFatal(def.name); | |
6515 | } | |
6516 | } | |
6517 | // If its not an exception we care about, pass it along. | |
6518 | // This stops us from eating debugger breaks etc. | |
6519 | return EXCEPTION_CONTINUE_SEARCH; | |
6520 | } | |
6521 | ||
6522 | FatalConditionHandler::FatalConditionHandler() { | |
6523 | isSet = true; | |
6524 | // 32k seems enough for Catch to handle stack overflow, | |
6525 | // but the value was found experimentally, so there is no strong guarantee | |
6526 | guaranteeSize = 32 * 1024; | |
6527 | exceptionHandlerHandle = nullptr; | |
6528 | // Register as first handler in current chain | |
6529 | exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); | |
6530 | // Pass in guarantee size to be filled | |
6531 | SetThreadStackGuarantee(&guaranteeSize); | |
6532 | } | |
6533 | ||
6534 | void FatalConditionHandler::reset() { | |
6535 | if (isSet) { | |
6536 | // Unregister handler and restore the old guarantee | |
6537 | RemoveVectoredExceptionHandler(exceptionHandlerHandle); | |
6538 | SetThreadStackGuarantee(&guaranteeSize); | |
6539 | exceptionHandlerHandle = nullptr; | |
6540 | isSet = false; | |
6541 | } | |
6542 | } | |
6543 | ||
6544 | FatalConditionHandler::~FatalConditionHandler() { | |
6545 | reset(); | |
6546 | } | |
6547 | ||
6548 | bool FatalConditionHandler::isSet = false; | |
6549 | ULONG FatalConditionHandler::guaranteeSize = 0; | |
6550 | PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; | |
6551 | ||
6552 | } // namespace Catch | |
6553 | ||
6554 | # endif // CATCH_CONFIG_WINDOWS_SEH | |
6555 | ||
6556 | #else // Not Windows - assumed to be POSIX compatible ////////////////////////// | |
6557 | ||
6558 | # if !defined(CATCH_CONFIG_POSIX_SIGNALS) | |
6559 | ||
6560 | namespace Catch { | |
6561 | void FatalConditionHandler::reset() {} | |
6562 | } | |
6563 | ||
6564 | # else // CATCH_CONFIG_POSIX_SIGNALS is defined | |
6565 | ||
6566 | #include <signal.h> | |
6567 | ||
6568 | namespace Catch { | |
6569 | ||
6570 | struct SignalDefs { | |
6571 | int id; | |
6572 | const char* name; | |
6573 | }; | |
6574 | static SignalDefs signalDefs[] = { | |
6575 | { SIGINT, "SIGINT - Terminal interrupt signal" }, | |
6576 | { SIGILL, "SIGILL - Illegal instruction signal" }, | |
6577 | { SIGFPE, "SIGFPE - Floating point error signal" }, | |
6578 | { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, | |
6579 | { SIGTERM, "SIGTERM - Termination request signal" }, | |
6580 | { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } | |
6581 | }; | |
6582 | ||
6583 | void FatalConditionHandler::handleSignal( int sig ) { | |
6584 | char const * name = "<unknown signal>"; | |
6585 | for (auto const& def : signalDefs) { | |
6586 | if (sig == def.id) { | |
6587 | name = def.name; | |
6588 | break; | |
6589 | } | |
6590 | } | |
6591 | reset(); | |
6592 | reportFatal(name); | |
6593 | raise( sig ); | |
6594 | } | |
6595 | ||
6596 | FatalConditionHandler::FatalConditionHandler() { | |
6597 | isSet = true; | |
6598 | stack_t sigStack; | |
6599 | sigStack.ss_sp = altStackMem; | |
6600 | sigStack.ss_size = SIGSTKSZ; | |
6601 | sigStack.ss_flags = 0; | |
6602 | sigaltstack(&sigStack, &oldSigStack); | |
6603 | struct sigaction sa = { }; | |
6604 | ||
6605 | sa.sa_handler = handleSignal; | |
6606 | sa.sa_flags = SA_ONSTACK; | |
6607 | for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { | |
6608 | sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); | |
6609 | } | |
6610 | } | |
6611 | ||
6612 | FatalConditionHandler::~FatalConditionHandler() { | |
6613 | reset(); | |
6614 | } | |
6615 | ||
6616 | void FatalConditionHandler::reset() { | |
6617 | if( isSet ) { | |
6618 | // Set signals back to previous values -- hopefully nobody overwrote them in the meantime | |
6619 | for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { | |
6620 | sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); | |
6621 | } | |
6622 | // Return the old stack | |
6623 | sigaltstack(&oldSigStack, nullptr); | |
6624 | isSet = false; | |
6625 | } | |
6626 | } | |
6627 | ||
6628 | bool FatalConditionHandler::isSet = false; | |
6629 | struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; | |
6630 | stack_t FatalConditionHandler::oldSigStack = {}; | |
6631 | char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; | |
6632 | ||
6633 | } // namespace Catch | |
6634 | ||
6635 | # endif // CATCH_CONFIG_POSIX_SIGNALS | |
6636 | ||
6637 | #endif // not Windows | |
6638 | // end catch_fatal_condition.cpp | |
6639 | // start catch_interfaces_capture.cpp | |
6640 | ||
6641 | namespace Catch { | |
6642 | IResultCapture::~IResultCapture() = default; | |
6643 | } | |
6644 | // end catch_interfaces_capture.cpp | |
6645 | // start catch_interfaces_config.cpp | |
6646 | ||
6647 | namespace Catch { | |
6648 | IConfig::~IConfig() = default; | |
6649 | } | |
6650 | // end catch_interfaces_config.cpp | |
6651 | // start catch_interfaces_exception.cpp | |
6652 | ||
6653 | namespace Catch { | |
6654 | IExceptionTranslator::~IExceptionTranslator() = default; | |
6655 | IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; | |
6656 | } | |
6657 | // end catch_interfaces_exception.cpp | |
6658 | // start catch_interfaces_registry_hub.cpp | |
6659 | ||
6660 | namespace Catch { | |
6661 | IRegistryHub::~IRegistryHub() = default; | |
6662 | IMutableRegistryHub::~IMutableRegistryHub() = default; | |
6663 | } | |
6664 | // end catch_interfaces_registry_hub.cpp | |
6665 | // start catch_interfaces_reporter.cpp | |
6666 | ||
6667 | // start catch_reporter_multi.h | |
6668 | ||
6669 | namespace Catch { | |
6670 | ||
6671 | class MultipleReporters : public IStreamingReporter { | |
6672 | using Reporters = std::vector<IStreamingReporterPtr>; | |
6673 | Reporters m_reporters; | |
6674 | ||
6675 | public: | |
6676 | void add( IStreamingReporterPtr&& reporter ); | |
6677 | ||
6678 | public: // IStreamingReporter | |
6679 | ||
6680 | ReporterPreferences getPreferences() const override; | |
6681 | ||
6682 | void noMatchingTestCases( std::string const& spec ) override; | |
6683 | ||
6684 | static std::set<Verbosity> getSupportedVerbosities(); | |
6685 | ||
6686 | void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; | |
6687 | void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; | |
6688 | ||
6689 | void testRunStarting( TestRunInfo const& testRunInfo ) override; | |
6690 | void testGroupStarting( GroupInfo const& groupInfo ) override; | |
6691 | void testCaseStarting( TestCaseInfo const& testInfo ) override; | |
6692 | void sectionStarting( SectionInfo const& sectionInfo ) override; | |
6693 | void assertionStarting( AssertionInfo const& assertionInfo ) override; | |
6694 | ||
6695 | // The return value indicates if the messages buffer should be cleared: | |
6696 | bool assertionEnded( AssertionStats const& assertionStats ) override; | |
6697 | void sectionEnded( SectionStats const& sectionStats ) override; | |
6698 | void testCaseEnded( TestCaseStats const& testCaseStats ) override; | |
6699 | void testGroupEnded( TestGroupStats const& testGroupStats ) override; | |
6700 | void testRunEnded( TestRunStats const& testRunStats ) override; | |
6701 | ||
6702 | void skipTest( TestCaseInfo const& testInfo ) override; | |
6703 | bool isMulti() const override; | |
6704 | ||
6705 | }; | |
6706 | ||
6707 | } // end namespace Catch | |
6708 | ||
6709 | // end catch_reporter_multi.h | |
6710 | namespace Catch { | |
6711 | ||
6712 | ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) | |
6713 | : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} | |
6714 | ||
6715 | ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) | |
6716 | : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} | |
6717 | ||
6718 | std::ostream& ReporterConfig::stream() const { return *m_stream; } | |
6719 | IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } | |
6720 | ||
6721 | TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} | |
6722 | ||
6723 | GroupInfo::GroupInfo( std::string const& _name, | |
6724 | std::size_t _groupIndex, | |
6725 | std::size_t _groupsCount ) | |
6726 | : name( _name ), | |
6727 | groupIndex( _groupIndex ), | |
6728 | groupsCounts( _groupsCount ) | |
6729 | {} | |
6730 | ||
6731 | AssertionStats::AssertionStats( AssertionResult const& _assertionResult, | |
6732 | std::vector<MessageInfo> const& _infoMessages, | |
6733 | Totals const& _totals ) | |
6734 | : assertionResult( _assertionResult ), | |
6735 | infoMessages( _infoMessages ), | |
6736 | totals( _totals ) | |
6737 | { | |
6738 | assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; | |
6739 | ||
6740 | if( assertionResult.hasMessage() ) { | |
6741 | // Copy message into messages list. | |
6742 | // !TBD This should have been done earlier, somewhere | |
6743 | MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); | |
6744 | builder << assertionResult.getMessage(); | |
6745 | builder.m_info.message = builder.m_stream.str(); | |
6746 | ||
6747 | infoMessages.push_back( builder.m_info ); | |
6748 | } | |
6749 | } | |
6750 | ||
6751 | AssertionStats::~AssertionStats() = default; | |
6752 | ||
6753 | SectionStats::SectionStats( SectionInfo const& _sectionInfo, | |
6754 | Counts const& _assertions, | |
6755 | double _durationInSeconds, | |
6756 | bool _missingAssertions ) | |
6757 | : sectionInfo( _sectionInfo ), | |
6758 | assertions( _assertions ), | |
6759 | durationInSeconds( _durationInSeconds ), | |
6760 | missingAssertions( _missingAssertions ) | |
6761 | {} | |
6762 | ||
6763 | SectionStats::~SectionStats() = default; | |
6764 | ||
6765 | TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, | |
6766 | Totals const& _totals, | |
6767 | std::string const& _stdOut, | |
6768 | std::string const& _stdErr, | |
6769 | bool _aborting ) | |
6770 | : testInfo( _testInfo ), | |
6771 | totals( _totals ), | |
6772 | stdOut( _stdOut ), | |
6773 | stdErr( _stdErr ), | |
6774 | aborting( _aborting ) | |
6775 | {} | |
6776 | ||
6777 | TestCaseStats::~TestCaseStats() = default; | |
6778 | ||
6779 | TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, | |
6780 | Totals const& _totals, | |
6781 | bool _aborting ) | |
6782 | : groupInfo( _groupInfo ), | |
6783 | totals( _totals ), | |
6784 | aborting( _aborting ) | |
6785 | {} | |
6786 | ||
6787 | TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) | |
6788 | : groupInfo( _groupInfo ), | |
6789 | aborting( false ) | |
6790 | {} | |
6791 | ||
6792 | TestGroupStats::~TestGroupStats() = default; | |
6793 | ||
6794 | TestRunStats::TestRunStats( TestRunInfo const& _runInfo, | |
6795 | Totals const& _totals, | |
6796 | bool _aborting ) | |
6797 | : runInfo( _runInfo ), | |
6798 | totals( _totals ), | |
6799 | aborting( _aborting ) | |
6800 | {} | |
6801 | ||
6802 | TestRunStats::~TestRunStats() = default; | |
6803 | ||
6804 | void IStreamingReporter::fatalErrorEncountered( StringRef ) {} | |
6805 | bool IStreamingReporter::isMulti() const { return false; } | |
6806 | ||
6807 | IReporterFactory::~IReporterFactory() = default; | |
6808 | IReporterRegistry::~IReporterRegistry() = default; | |
6809 | ||
6810 | void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { | |
6811 | ||
6812 | if( !existingReporter ) { | |
6813 | existingReporter = std::move( additionalReporter ); | |
6814 | return; | |
6815 | } | |
6816 | ||
6817 | MultipleReporters* multi = nullptr; | |
6818 | ||
6819 | if( existingReporter->isMulti() ) { | |
6820 | multi = static_cast<MultipleReporters*>( existingReporter.get() ); | |
6821 | } | |
6822 | else { | |
6823 | auto newMulti = std::unique_ptr<MultipleReporters>( new MultipleReporters ); | |
6824 | newMulti->add( std::move( existingReporter ) ); | |
6825 | multi = newMulti.get(); | |
6826 | existingReporter = std::move( newMulti ); | |
6827 | } | |
6828 | multi->add( std::move( additionalReporter ) ); | |
6829 | } | |
6830 | ||
6831 | } // end namespace Catch | |
6832 | // end catch_interfaces_reporter.cpp | |
6833 | // start catch_interfaces_runner.cpp | |
6834 | ||
6835 | namespace Catch { | |
6836 | IRunner::~IRunner() = default; | |
6837 | } | |
6838 | // end catch_interfaces_runner.cpp | |
6839 | // start catch_interfaces_testcase.cpp | |
6840 | ||
6841 | namespace Catch { | |
6842 | ITestInvoker::~ITestInvoker() = default; | |
6843 | ITestCaseRegistry::~ITestCaseRegistry() = default; | |
6844 | } | |
6845 | // end catch_interfaces_testcase.cpp | |
6846 | // start catch_leak_detector.cpp | |
6847 | ||
6848 | namespace Catch { | |
6849 | ||
6850 | #ifdef CATCH_CONFIG_WINDOWS_CRTDBG | |
6851 | #include <crtdbg.h> | |
6852 | ||
6853 | LeakDetector::LeakDetector() { | |
6854 | int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); | |
6855 | flag |= _CRTDBG_LEAK_CHECK_DF; | |
6856 | flag |= _CRTDBG_ALLOC_MEM_DF; | |
6857 | _CrtSetDbgFlag(flag); | |
6858 | _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); | |
6859 | _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); | |
6860 | // Change this to leaking allocation's number to break there | |
6861 | _CrtSetBreakAlloc(-1); | |
6862 | } | |
6863 | ||
6864 | #else | |
6865 | ||
6866 | LeakDetector::LeakDetector(){} | |
6867 | ||
6868 | #endif | |
6869 | ||
6870 | } | |
6871 | // end catch_leak_detector.cpp | |
6872 | // start catch_list.cpp | |
6873 | ||
6874 | // start catch_list.h | |
6875 | ||
6876 | #include <set> | |
6877 | ||
6878 | namespace Catch { | |
6879 | ||
6880 | std::size_t listTests( Config const& config ); | |
6881 | ||
6882 | std::size_t listTestsNamesOnly( Config const& config ); | |
6883 | ||
6884 | struct TagInfo { | |
6885 | void add( std::string const& spelling ); | |
6886 | std::string all() const; | |
6887 | ||
6888 | std::set<std::string> spellings; | |
6889 | std::size_t count = 0; | |
6890 | }; | |
6891 | ||
6892 | std::size_t listTags( Config const& config ); | |
6893 | ||
6894 | std::size_t listReporters( Config const& /*config*/ ); | |
6895 | ||
6896 | Option<std::size_t> list( Config const& config ); | |
6897 | ||
6898 | } // end namespace Catch | |
6899 | ||
6900 | // end catch_list.h | |
6901 | // start catch_text.h | |
6902 | ||
6903 | namespace Catch { | |
6904 | using namespace clara::TextFlow; | |
6905 | } | |
6906 | ||
6907 | // end catch_text.h | |
6908 | #include <limits> | |
6909 | #include <algorithm> | |
6910 | #include <iomanip> | |
6911 | ||
6912 | namespace Catch { | |
6913 | ||
6914 | std::size_t listTests( Config const& config ) { | |
6915 | TestSpec testSpec = config.testSpec(); | |
6916 | if( config.testSpec().hasFilters() ) | |
6917 | Catch::cout() << "Matching test cases:\n"; | |
6918 | else { | |
6919 | Catch::cout() << "All available test cases:\n"; | |
6920 | testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); | |
6921 | } | |
6922 | ||
6923 | auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); | |
6924 | for( auto const& testCaseInfo : matchedTestCases ) { | |
6925 | Colour::Code colour = testCaseInfo.isHidden() | |
6926 | ? Colour::SecondaryText | |
6927 | : Colour::None; | |
6928 | Colour colourGuard( colour ); | |
6929 | ||
6930 | Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; | |
6931 | if( config.verbosity() >= Verbosity::High ) { | |
6932 | Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; | |
6933 | std::string description = testCaseInfo.description; | |
6934 | if( description.empty() ) | |
6935 | description = "(NO DESCRIPTION)"; | |
6936 | Catch::cout() << Column( description ).indent(4) << std::endl; | |
6937 | } | |
6938 | if( !testCaseInfo.tags.empty() ) | |
6939 | Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; | |
6940 | } | |
6941 | ||
6942 | if( !config.testSpec().hasFilters() ) | |
6943 | Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; | |
6944 | else | |
6945 | Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; | |
6946 | return matchedTestCases.size(); | |
6947 | } | |
6948 | ||
6949 | std::size_t listTestsNamesOnly( Config const& config ) { | |
6950 | TestSpec testSpec = config.testSpec(); | |
6951 | if( !config.testSpec().hasFilters() ) | |
6952 | testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); | |
6953 | std::size_t matchedTests = 0; | |
6954 | std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); | |
6955 | for( auto const& testCaseInfo : matchedTestCases ) { | |
6956 | matchedTests++; | |
6957 | if( startsWith( testCaseInfo.name, '#' ) ) | |
6958 | Catch::cout() << '"' << testCaseInfo.name << '"'; | |
6959 | else | |
6960 | Catch::cout() << testCaseInfo.name; | |
6961 | if ( config.verbosity() >= Verbosity::High ) | |
6962 | Catch::cout() << "\t@" << testCaseInfo.lineInfo; | |
6963 | Catch::cout() << std::endl; | |
6964 | } | |
6965 | return matchedTests; | |
6966 | } | |
6967 | ||
6968 | void TagInfo::add( std::string const& spelling ) { | |
6969 | ++count; | |
6970 | spellings.insert( spelling ); | |
6971 | } | |
6972 | ||
6973 | std::string TagInfo::all() const { | |
6974 | std::string out; | |
6975 | for( auto const& spelling : spellings ) | |
6976 | out += "[" + spelling + "]"; | |
6977 | return out; | |
6978 | } | |
6979 | ||
6980 | std::size_t listTags( Config const& config ) { | |
6981 | TestSpec testSpec = config.testSpec(); | |
6982 | if( config.testSpec().hasFilters() ) | |
6983 | Catch::cout() << "Tags for matching test cases:\n"; | |
6984 | else { | |
6985 | Catch::cout() << "All available tags:\n"; | |
6986 | testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); | |
6987 | } | |
6988 | ||
6989 | std::map<std::string, TagInfo> tagCounts; | |
6990 | ||
6991 | std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); | |
6992 | for( auto const& testCase : matchedTestCases ) { | |
6993 | for( auto const& tagName : testCase.getTestCaseInfo().tags ) { | |
6994 | std::string lcaseTagName = toLower( tagName ); | |
6995 | auto countIt = tagCounts.find( lcaseTagName ); | |
6996 | if( countIt == tagCounts.end() ) | |
6997 | countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; | |
6998 | countIt->second.add( tagName ); | |
6999 | } | |
7000 | } | |
7001 | ||
7002 | for( auto const& tagCount : tagCounts ) { | |
7003 | std::ostringstream oss; | |
7004 | oss << " " << std::setw(2) << tagCount.second.count << " "; | |
7005 | auto wrapper = Column( tagCount.second.all() ) | |
7006 | .initialIndent( 0 ) | |
7007 | .indent( oss.str().size() ) | |
7008 | .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); | |
7009 | Catch::cout() << oss.str() << wrapper << '\n'; | |
7010 | } | |
7011 | Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; | |
7012 | return tagCounts.size(); | |
7013 | } | |
7014 | ||
7015 | std::size_t listReporters( Config const& /*config*/ ) { | |
7016 | Catch::cout() << "Available reporters:\n"; | |
7017 | IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); | |
7018 | std::size_t maxNameLen = 0; | |
7019 | for( auto const& factoryKvp : factories ) | |
7020 | maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); | |
7021 | ||
7022 | for( auto const& factoryKvp : factories ) { | |
7023 | Catch::cout() | |
7024 | << Column( factoryKvp.first + ":" ) | |
7025 | .indent(2) | |
7026 | .width( 5+maxNameLen ) | |
7027 | + Column( factoryKvp.second->getDescription() ) | |
7028 | .initialIndent(0) | |
7029 | .indent(2) | |
7030 | .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) | |
7031 | << "\n"; | |
7032 | } | |
7033 | Catch::cout() << std::endl; | |
7034 | return factories.size(); | |
7035 | } | |
7036 | ||
7037 | Option<std::size_t> list( Config const& config ) { | |
7038 | Option<std::size_t> listedCount; | |
7039 | if( config.listTests() ) | |
7040 | listedCount = listedCount.valueOr(0) + listTests( config ); | |
7041 | if( config.listTestNamesOnly() ) | |
7042 | listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); | |
7043 | if( config.listTags() ) | |
7044 | listedCount = listedCount.valueOr(0) + listTags( config ); | |
7045 | if( config.listReporters() ) | |
7046 | listedCount = listedCount.valueOr(0) + listReporters( config ); | |
7047 | return listedCount; | |
7048 | } | |
7049 | ||
7050 | } // end namespace Catch | |
7051 | // end catch_list.cpp | |
7052 | // start catch_matchers.cpp | |
7053 | ||
7054 | namespace Catch { | |
7055 | namespace Matchers { | |
7056 | namespace Impl { | |
7057 | ||
7058 | std::string MatcherUntypedBase::toString() const { | |
7059 | if( m_cachedToString.empty() ) | |
7060 | m_cachedToString = describe(); | |
7061 | return m_cachedToString; | |
7062 | } | |
7063 | ||
7064 | MatcherUntypedBase::~MatcherUntypedBase() = default; | |
7065 | ||
7066 | } // namespace Impl | |
7067 | } // namespace Matchers | |
7068 | ||
7069 | using namespace Matchers; | |
7070 | using Matchers::Impl::MatcherBase; | |
7071 | ||
7072 | } // namespace Catch | |
7073 | // end catch_matchers.cpp | |
7074 | // start catch_matchers_string.cpp | |
7075 | ||
7076 | namespace Catch { | |
7077 | namespace Matchers { | |
7078 | ||
7079 | namespace StdString { | |
7080 | ||
7081 | CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) | |
7082 | : m_caseSensitivity( caseSensitivity ), | |
7083 | m_str( adjustString( str ) ) | |
7084 | {} | |
7085 | std::string CasedString::adjustString( std::string const& str ) const { | |
7086 | return m_caseSensitivity == CaseSensitive::No | |
7087 | ? toLower( str ) | |
7088 | : str; | |
7089 | } | |
7090 | std::string CasedString::caseSensitivitySuffix() const { | |
7091 | return m_caseSensitivity == CaseSensitive::No | |
7092 | ? " (case insensitive)" | |
7093 | : std::string(); | |
7094 | } | |
7095 | ||
7096 | StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) | |
7097 | : m_comparator( comparator ), | |
7098 | m_operation( operation ) { | |
7099 | } | |
7100 | ||
7101 | std::string StringMatcherBase::describe() const { | |
7102 | std::string description; | |
7103 | description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + | |
7104 | m_comparator.caseSensitivitySuffix().size()); | |
7105 | description += m_operation; | |
7106 | description += ": \""; | |
7107 | description += m_comparator.m_str; | |
7108 | description += "\""; | |
7109 | description += m_comparator.caseSensitivitySuffix(); | |
7110 | return description; | |
7111 | } | |
7112 | ||
7113 | EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} | |
7114 | ||
7115 | bool EqualsMatcher::match( std::string const& source ) const { | |
7116 | return m_comparator.adjustString( source ) == m_comparator.m_str; | |
7117 | } | |
7118 | ||
7119 | ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} | |
7120 | ||
7121 | bool ContainsMatcher::match( std::string const& source ) const { | |
7122 | return contains( m_comparator.adjustString( source ), m_comparator.m_str ); | |
7123 | } | |
7124 | ||
7125 | StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} | |
7126 | ||
7127 | bool StartsWithMatcher::match( std::string const& source ) const { | |
7128 | return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); | |
7129 | } | |
7130 | ||
7131 | EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} | |
7132 | ||
7133 | bool EndsWithMatcher::match( std::string const& source ) const { | |
7134 | return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); | |
7135 | } | |
7136 | ||
7137 | } // namespace StdString | |
7138 | ||
7139 | StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { | |
7140 | return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); | |
7141 | } | |
7142 | StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { | |
7143 | return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); | |
7144 | } | |
7145 | StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { | |
7146 | return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); | |
7147 | } | |
7148 | StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { | |
7149 | return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); | |
7150 | } | |
7151 | ||
7152 | } // namespace Matchers | |
7153 | } // namespace Catch | |
7154 | // end catch_matchers_string.cpp | |
7155 | // start catch_message.cpp | |
7156 | ||
7157 | namespace Catch { | |
7158 | ||
7159 | MessageInfo::MessageInfo( std::string const& _macroName, | |
7160 | SourceLineInfo const& _lineInfo, | |
7161 | ResultWas::OfType _type ) | |
7162 | : macroName( _macroName ), | |
7163 | lineInfo( _lineInfo ), | |
7164 | type( _type ), | |
7165 | sequence( ++globalCount ) | |
7166 | {} | |
7167 | ||
7168 | bool MessageInfo::operator==( MessageInfo const& other ) const { | |
7169 | return sequence == other.sequence; | |
7170 | } | |
7171 | ||
7172 | bool MessageInfo::operator<( MessageInfo const& other ) const { | |
7173 | return sequence < other.sequence; | |
7174 | } | |
7175 | ||
7176 | // This may need protecting if threading support is added | |
7177 | unsigned int MessageInfo::globalCount = 0; | |
7178 | ||
7179 | //////////////////////////////////////////////////////////////////////////// | |
7180 | ||
7181 | Catch::MessageBuilder::MessageBuilder( std::string const& macroName, | |
7182 | SourceLineInfo const& lineInfo, | |
7183 | ResultWas::OfType type ) | |
7184 | :m_info(macroName, lineInfo, type) {} | |
7185 | ||
7186 | //////////////////////////////////////////////////////////////////////////// | |
7187 | ||
7188 | ScopedMessage::ScopedMessage( MessageBuilder const& builder ) | |
7189 | : m_info( builder.m_info ) | |
7190 | { | |
7191 | m_info.message = builder.m_stream.str(); | |
7192 | getResultCapture().pushScopedMessage( m_info ); | |
7193 | } | |
7194 | ||
7195 | ScopedMessage::~ScopedMessage() { | |
7196 | if ( !std::uncaught_exception() ){ | |
7197 | getResultCapture().popScopedMessage(m_info); | |
7198 | } | |
7199 | } | |
7200 | ||
7201 | } // end namespace Catch | |
7202 | // end catch_message.cpp | |
7203 | // start catch_random_number_generator.cpp | |
7204 | ||
7205 | // start catch_random_number_generator.h | |
7206 | ||
7207 | #include <algorithm> | |
7208 | ||
7209 | namespace Catch { | |
7210 | ||
7211 | struct IConfig; | |
7212 | ||
7213 | void seedRng( IConfig const& config ); | |
7214 | ||
7215 | unsigned int rngSeed(); | |
7216 | ||
7217 | struct RandomNumberGenerator { | |
7218 | using result_type = unsigned int; | |
7219 | ||
7220 | static constexpr result_type (min)() { return 0; } | |
7221 | static constexpr result_type (max)() { return 1000000; } | |
7222 | ||
7223 | result_type operator()( result_type n ) const; | |
7224 | result_type operator()() const; | |
7225 | ||
7226 | template<typename V> | |
7227 | static void shuffle( V& vector ) { | |
7228 | RandomNumberGenerator rng; | |
7229 | std::shuffle( vector.begin(), vector.end(), rng ); | |
7230 | } | |
7231 | }; | |
7232 | ||
7233 | } | |
7234 | ||
7235 | // end catch_random_number_generator.h | |
7236 | #include <cstdlib> | |
7237 | ||
7238 | namespace Catch { | |
7239 | ||
7240 | void seedRng( IConfig const& config ) { | |
7241 | if( config.rngSeed() != 0 ) | |
7242 | std::srand( config.rngSeed() ); | |
7243 | } | |
7244 | unsigned int rngSeed() { | |
7245 | return getCurrentContext().getConfig()->rngSeed(); | |
7246 | } | |
7247 | ||
7248 | RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { | |
7249 | return std::rand() % n; | |
7250 | } | |
7251 | RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { | |
7252 | return std::rand() % (max)(); | |
7253 | } | |
7254 | ||
7255 | } | |
7256 | // end catch_random_number_generator.cpp | |
7257 | // start catch_registry_hub.cpp | |
7258 | ||
7259 | // start catch_test_case_registry_impl.h | |
7260 | ||
7261 | #include <vector> | |
7262 | #include <set> | |
7263 | #include <algorithm> | |
7264 | #include <ios> | |
7265 | ||
7266 | namespace Catch { | |
7267 | ||
7268 | class TestCase; | |
7269 | struct IConfig; | |
7270 | ||
7271 | std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ); | |
7272 | bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); | |
7273 | ||
7274 | void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ); | |
7275 | ||
7276 | std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); | |
7277 | std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); | |
7278 | ||
7279 | class TestRegistry : public ITestCaseRegistry { | |
7280 | public: | |
7281 | virtual ~TestRegistry() = default; | |
7282 | ||
7283 | virtual void registerTest( TestCase const& testCase ); | |
7284 | ||
7285 | std::vector<TestCase> const& getAllTests() const override; | |
7286 | std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override; | |
7287 | ||
7288 | private: | |
7289 | std::vector<TestCase> m_functions; | |
7290 | mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; | |
7291 | mutable std::vector<TestCase> m_sortedFunctions; | |
7292 | std::size_t m_unnamedCount = 0; | |
7293 | std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised | |
7294 | }; | |
7295 | ||
7296 | /////////////////////////////////////////////////////////////////////////// | |
7297 | ||
7298 | class TestInvokerAsFunction : public ITestInvoker { | |
7299 | void(*m_testAsFunction)(); | |
7300 | public: | |
7301 | TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; | |
7302 | ||
7303 | void invoke() const override; | |
7304 | }; | |
7305 | ||
7306 | std::string extractClassName( std::string const& classOrQualifiedMethodName ); | |
7307 | ||
7308 | /////////////////////////////////////////////////////////////////////////// | |
7309 | ||
7310 | } // end namespace Catch | |
7311 | ||
7312 | // end catch_test_case_registry_impl.h | |
7313 | // start catch_reporter_registry.h | |
7314 | ||
7315 | #include <map> | |
7316 | ||
7317 | namespace Catch { | |
7318 | ||
7319 | class ReporterRegistry : public IReporterRegistry { | |
7320 | ||
7321 | public: | |
7322 | ||
7323 | ~ReporterRegistry() override; | |
7324 | ||
7325 | IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; | |
7326 | ||
7327 | void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); | |
7328 | void registerListener( IReporterFactoryPtr const& factory ); | |
7329 | ||
7330 | FactoryMap const& getFactories() const override; | |
7331 | Listeners const& getListeners() const override; | |
7332 | ||
7333 | private: | |
7334 | FactoryMap m_factories; | |
7335 | Listeners m_listeners; | |
7336 | }; | |
7337 | } | |
7338 | ||
7339 | // end catch_reporter_registry.h | |
7340 | // start catch_tag_alias_registry.h | |
7341 | ||
7342 | // start catch_tag_alias.h | |
7343 | ||
7344 | #include <string> | |
7345 | ||
7346 | namespace Catch { | |
7347 | ||
7348 | struct TagAlias { | |
7349 | TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); | |
7350 | ||
7351 | std::string tag; | |
7352 | SourceLineInfo lineInfo; | |
7353 | }; | |
7354 | ||
7355 | } // end namespace Catch | |
7356 | ||
7357 | // end catch_tag_alias.h | |
7358 | #include <map> | |
7359 | ||
7360 | namespace Catch { | |
7361 | ||
7362 | class TagAliasRegistry : public ITagAliasRegistry { | |
7363 | public: | |
7364 | ~TagAliasRegistry() override; | |
7365 | TagAlias const* find( std::string const& alias ) const override; | |
7366 | std::string expandAliases( std::string const& unexpandedTestSpec ) const override; | |
7367 | void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); | |
7368 | ||
7369 | private: | |
7370 | std::map<std::string, TagAlias> m_registry; | |
7371 | }; | |
7372 | ||
7373 | } // end namespace Catch | |
7374 | ||
7375 | // end catch_tag_alias_registry.h | |
7376 | // start catch_startup_exception_registry.h | |
7377 | ||
7378 | #include <vector> | |
7379 | #include <exception> | |
7380 | ||
7381 | namespace Catch { | |
7382 | ||
7383 | class StartupExceptionRegistry { | |
7384 | public: | |
7385 | void add(std::exception_ptr const& exception) noexcept; | |
7386 | std::vector<std::exception_ptr> const& getExceptions() const noexcept; | |
7387 | private: | |
7388 | std::vector<std::exception_ptr> m_exceptions; | |
7389 | }; | |
7390 | ||
7391 | } // end namespace Catch | |
7392 | ||
7393 | // end catch_startup_exception_registry.h | |
7394 | namespace Catch { | |
7395 | ||
7396 | namespace { | |
7397 | ||
7398 | class RegistryHub : public IRegistryHub, public IMutableRegistryHub, | |
7399 | private NonCopyable { | |
7400 | ||
7401 | public: // IRegistryHub | |
7402 | RegistryHub() = default; | |
7403 | IReporterRegistry const& getReporterRegistry() const override { | |
7404 | return m_reporterRegistry; | |
7405 | } | |
7406 | ITestCaseRegistry const& getTestCaseRegistry() const override { | |
7407 | return m_testCaseRegistry; | |
7408 | } | |
7409 | IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { | |
7410 | return m_exceptionTranslatorRegistry; | |
7411 | } | |
7412 | ITagAliasRegistry const& getTagAliasRegistry() const override { | |
7413 | return m_tagAliasRegistry; | |
7414 | } | |
7415 | StartupExceptionRegistry const& getStartupExceptionRegistry() const override { | |
7416 | return m_exceptionRegistry; | |
7417 | } | |
7418 | ||
7419 | public: // IMutableRegistryHub | |
7420 | void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { | |
7421 | m_reporterRegistry.registerReporter( name, factory ); | |
7422 | } | |
7423 | void registerListener( IReporterFactoryPtr const& factory ) override { | |
7424 | m_reporterRegistry.registerListener( factory ); | |
7425 | } | |
7426 | void registerTest( TestCase const& testInfo ) override { | |
7427 | m_testCaseRegistry.registerTest( testInfo ); | |
7428 | } | |
7429 | void registerTranslator( const IExceptionTranslator* translator ) override { | |
7430 | m_exceptionTranslatorRegistry.registerTranslator( translator ); | |
7431 | } | |
7432 | void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { | |
7433 | m_tagAliasRegistry.add( alias, tag, lineInfo ); | |
7434 | } | |
7435 | void registerStartupException() noexcept override { | |
7436 | m_exceptionRegistry.add(std::current_exception()); | |
7437 | } | |
7438 | ||
7439 | private: | |
7440 | TestRegistry m_testCaseRegistry; | |
7441 | ReporterRegistry m_reporterRegistry; | |
7442 | ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; | |
7443 | TagAliasRegistry m_tagAliasRegistry; | |
7444 | StartupExceptionRegistry m_exceptionRegistry; | |
7445 | }; | |
7446 | ||
7447 | // Single, global, instance | |
7448 | RegistryHub*& getTheRegistryHub() { | |
7449 | static RegistryHub* theRegistryHub = nullptr; | |
7450 | if( !theRegistryHub ) | |
7451 | theRegistryHub = new RegistryHub(); | |
7452 | return theRegistryHub; | |
7453 | } | |
7454 | } | |
7455 | ||
7456 | IRegistryHub& getRegistryHub() { | |
7457 | return *getTheRegistryHub(); | |
7458 | } | |
7459 | IMutableRegistryHub& getMutableRegistryHub() { | |
7460 | return *getTheRegistryHub(); | |
7461 | } | |
7462 | void cleanUp() { | |
7463 | delete getTheRegistryHub(); | |
7464 | getTheRegistryHub() = nullptr; | |
7465 | cleanUpContext(); | |
7466 | } | |
7467 | std::string translateActiveException() { | |
7468 | return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); | |
7469 | } | |
7470 | ||
7471 | } // end namespace Catch | |
7472 | // end catch_registry_hub.cpp | |
7473 | // start catch_reporter_registry.cpp | |
7474 | ||
7475 | namespace Catch { | |
7476 | ||
7477 | ReporterRegistry::~ReporterRegistry() = default; | |
7478 | ||
7479 | IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { | |
7480 | auto it = m_factories.find( name ); | |
7481 | if( it == m_factories.end() ) | |
7482 | return nullptr; | |
7483 | return it->second->create( ReporterConfig( config ) ); | |
7484 | } | |
7485 | ||
7486 | void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { | |
7487 | m_factories.emplace(name, factory); | |
7488 | } | |
7489 | void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { | |
7490 | m_listeners.push_back( factory ); | |
7491 | } | |
7492 | ||
7493 | IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { | |
7494 | return m_factories; | |
7495 | } | |
7496 | IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { | |
7497 | return m_listeners; | |
7498 | } | |
7499 | ||
7500 | } | |
7501 | // end catch_reporter_registry.cpp | |
7502 | // start catch_result_type.cpp | |
7503 | ||
7504 | namespace Catch { | |
7505 | ||
7506 | bool isOk( ResultWas::OfType resultType ) { | |
7507 | return ( resultType & ResultWas::FailureBit ) == 0; | |
7508 | } | |
7509 | bool isJustInfo( int flags ) { | |
7510 | return flags == ResultWas::Info; | |
7511 | } | |
7512 | ||
7513 | ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { | |
7514 | return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); | |
7515 | } | |
7516 | ||
7517 | bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } | |
7518 | bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } | |
7519 | bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } | |
7520 | ||
7521 | } // end namespace Catch | |
7522 | // end catch_result_type.cpp | |
7523 | // start catch_run_context.cpp | |
7524 | // start catch_run_context.h | |
7525 | ||
7526 | #include <string> | |
7527 | ||
7528 | namespace Catch { | |
7529 | ||
7530 | struct IMutableContext; | |
7531 | ||
7532 | class StreamRedirect { | |
7533 | ||
7534 | public: | |
7535 | StreamRedirect(std::ostream& stream, std::string& targetString); | |
7536 | ||
7537 | ~StreamRedirect(); | |
7538 | ||
7539 | private: | |
7540 | std::ostream& m_stream; | |
7541 | std::streambuf* m_prevBuf; | |
7542 | std::ostringstream m_oss; | |
7543 | std::string& m_targetString; | |
7544 | }; | |
7545 | ||
7546 | // StdErr has two constituent streams in C++, std::cerr and std::clog | |
7547 | // This means that we need to redirect 2 streams into 1 to keep proper | |
7548 | // order of writes and cannot use StreamRedirect on its own | |
7549 | class StdErrRedirect { | |
7550 | public: | |
7551 | StdErrRedirect(std::string& targetString); | |
7552 | ~StdErrRedirect(); | |
7553 | private: | |
7554 | std::streambuf* m_cerrBuf; | |
7555 | std::streambuf* m_clogBuf; | |
7556 | std::ostringstream m_oss; | |
7557 | std::string& m_targetString; | |
7558 | }; | |
7559 | ||
7560 | /////////////////////////////////////////////////////////////////////////// | |
7561 | ||
7562 | class RunContext : public IResultCapture, public IRunner { | |
7563 | ||
7564 | public: | |
7565 | RunContext( RunContext const& ) = delete; | |
7566 | RunContext& operator =( RunContext const& ) = delete; | |
7567 | ||
7568 | explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter); | |
7569 | ||
7570 | virtual ~RunContext(); | |
7571 | ||
7572 | void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount); | |
7573 | void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount); | |
7574 | ||
7575 | Totals runTest(TestCase const& testCase); | |
7576 | ||
7577 | IConfigPtr config() const; | |
7578 | IStreamingReporter& reporter() const; | |
7579 | ||
7580 | private: // IResultCapture | |
7581 | ||
7582 | void assertionStarting(AssertionInfo const& info) override; | |
7583 | void assertionEnded(AssertionResult const& result) override; | |
7584 | ||
7585 | bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; | |
7586 | bool testForMissingAssertions(Counts& assertions); | |
7587 | ||
7588 | void sectionEnded(SectionEndInfo const& endInfo) override; | |
7589 | void sectionEndedEarly(SectionEndInfo const& endInfo) override; | |
7590 | ||
7591 | void benchmarkStarting( BenchmarkInfo const& info ) override; | |
7592 | void benchmarkEnded( BenchmarkStats const& stats ) override; | |
7593 | ||
7594 | void pushScopedMessage(MessageInfo const& message) override; | |
7595 | void popScopedMessage(MessageInfo const& message) override; | |
7596 | ||
7597 | std::string getCurrentTestName() const override; | |
7598 | ||
7599 | const AssertionResult* getLastResult() const override; | |
7600 | ||
7601 | void exceptionEarlyReported() override; | |
7602 | ||
7603 | void handleFatalErrorCondition( StringRef message ) override; | |
7604 | ||
7605 | bool lastAssertionPassed() override; | |
7606 | ||
7607 | void assertionPassed() override; | |
7608 | ||
7609 | void assertionRun() override; | |
7610 | ||
7611 | public: | |
7612 | // !TBD We need to do this another way! | |
7613 | bool aborting() const override; | |
7614 | ||
7615 | private: | |
7616 | ||
7617 | void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr); | |
7618 | void invokeActiveTestCase(); | |
7619 | ||
7620 | private: | |
7621 | ||
7622 | void handleUnfinishedSections(); | |
7623 | ||
7624 | TestRunInfo m_runInfo; | |
7625 | IMutableContext& m_context; | |
7626 | TestCase const* m_activeTestCase = nullptr; | |
7627 | ITracker* m_testCaseTracker; | |
7628 | Option<AssertionResult> m_lastResult; | |
7629 | ||
7630 | IConfigPtr m_config; | |
7631 | Totals m_totals; | |
7632 | IStreamingReporterPtr m_reporter; | |
7633 | std::vector<MessageInfo> m_messages; | |
7634 | AssertionInfo m_lastAssertionInfo; | |
7635 | std::vector<SectionEndInfo> m_unfinishedSections; | |
7636 | std::vector<ITracker*> m_activeSections; | |
7637 | TrackerContext m_trackerContext; | |
7638 | std::size_t m_prevPassed = 0; | |
7639 | bool m_shouldReportUnexpected = true; | |
7640 | }; | |
7641 | ||
7642 | IResultCapture& getResultCapture(); | |
7643 | ||
7644 | } // end namespace Catch | |
7645 | ||
7646 | // end catch_run_context.h | |
7647 | ||
7648 | #include <cassert> | |
7649 | #include <algorithm> | |
7650 | ||
7651 | namespace Catch { | |
7652 | ||
7653 | StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) | |
7654 | : m_stream(stream), | |
7655 | m_prevBuf(stream.rdbuf()), | |
7656 | m_targetString(targetString) { | |
7657 | stream.rdbuf(m_oss.rdbuf()); | |
7658 | } | |
7659 | ||
7660 | StreamRedirect::~StreamRedirect() { | |
7661 | m_targetString += m_oss.str(); | |
7662 | m_stream.rdbuf(m_prevBuf); | |
7663 | } | |
7664 | ||
7665 | StdErrRedirect::StdErrRedirect(std::string & targetString) | |
7666 | :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), | |
7667 | m_targetString(targetString) { | |
7668 | cerr().rdbuf(m_oss.rdbuf()); | |
7669 | clog().rdbuf(m_oss.rdbuf()); | |
7670 | } | |
7671 | ||
7672 | StdErrRedirect::~StdErrRedirect() { | |
7673 | m_targetString += m_oss.str(); | |
7674 | cerr().rdbuf(m_cerrBuf); | |
7675 | clog().rdbuf(m_clogBuf); | |
7676 | } | |
7677 | ||
7678 | RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) | |
7679 | : m_runInfo(_config->name()), | |
7680 | m_context(getCurrentMutableContext()), | |
7681 | m_config(_config), | |
7682 | m_reporter(std::move(reporter)), | |
7683 | m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal } | |
7684 | { | |
7685 | m_context.setRunner(this); | |
7686 | m_context.setConfig(m_config); | |
7687 | m_context.setResultCapture(this); | |
7688 | m_reporter->testRunStarting(m_runInfo); | |
7689 | } | |
7690 | ||
7691 | RunContext::~RunContext() { | |
7692 | m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); | |
7693 | } | |
7694 | ||
7695 | void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { | |
7696 | m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); | |
7697 | } | |
7698 | ||
7699 | void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { | |
7700 | m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); | |
7701 | } | |
7702 | ||
7703 | Totals RunContext::runTest(TestCase const& testCase) { | |
7704 | Totals prevTotals = m_totals; | |
7705 | ||
7706 | std::string redirectedCout; | |
7707 | std::string redirectedCerr; | |
7708 | ||
7709 | TestCaseInfo testInfo = testCase.getTestCaseInfo(); | |
7710 | ||
7711 | m_reporter->testCaseStarting(testInfo); | |
7712 | ||
7713 | m_activeTestCase = &testCase; | |
7714 | ||
7715 | ITracker& rootTracker = m_trackerContext.startRun(); | |
7716 | assert(rootTracker.isSectionTracker()); | |
7717 | static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun()); | |
7718 | do { | |
7719 | m_trackerContext.startCycle(); | |
7720 | m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); | |
7721 | runCurrentTest(redirectedCout, redirectedCerr); | |
7722 | } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); | |
7723 | ||
7724 | Totals deltaTotals = m_totals.delta(prevTotals); | |
7725 | if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { | |
7726 | deltaTotals.assertions.failed++; | |
7727 | deltaTotals.testCases.passed--; | |
7728 | deltaTotals.testCases.failed++; | |
7729 | } | |
7730 | m_totals.testCases += deltaTotals.testCases; | |
7731 | m_reporter->testCaseEnded(TestCaseStats(testInfo, | |
7732 | deltaTotals, | |
7733 | redirectedCout, | |
7734 | redirectedCerr, | |
7735 | aborting())); | |
7736 | ||
7737 | m_activeTestCase = nullptr; | |
7738 | m_testCaseTracker = nullptr; | |
7739 | ||
7740 | return deltaTotals; | |
7741 | } | |
7742 | ||
7743 | IConfigPtr RunContext::config() const { | |
7744 | return m_config; | |
7745 | } | |
7746 | ||
7747 | IStreamingReporter& RunContext::reporter() const { | |
7748 | return *m_reporter; | |
7749 | } | |
7750 | ||
7751 | void RunContext::assertionStarting(AssertionInfo const& info) { | |
7752 | m_reporter->assertionStarting( info ); | |
7753 | } | |
7754 | void RunContext::assertionEnded(AssertionResult const & result) { | |
7755 | if (result.getResultType() == ResultWas::Ok) { | |
7756 | m_totals.assertions.passed++; | |
7757 | } else if (!result.isOk()) { | |
7758 | if( m_activeTestCase->getTestCaseInfo().okToFail() ) | |
7759 | m_totals.assertions.failedButOk++; | |
7760 | else | |
7761 | m_totals.assertions.failed++; | |
7762 | } | |
7763 | ||
7764 | // We have no use for the return value (whether messages should be cleared), because messages were made scoped | |
7765 | // and should be let to clear themselves out. | |
7766 | static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); | |
7767 | ||
7768 | // Reset working state | |
7769 | m_lastAssertionInfo = { "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition }; | |
7770 | m_lastResult = result; | |
7771 | } | |
7772 | ||
7773 | bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { | |
7774 | ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); | |
7775 | if (!sectionTracker.isOpen()) | |
7776 | return false; | |
7777 | m_activeSections.push_back(§ionTracker); | |
7778 | ||
7779 | m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; | |
7780 | ||
7781 | m_reporter->sectionStarting(sectionInfo); | |
7782 | ||
7783 | assertions = m_totals.assertions; | |
7784 | ||
7785 | return true; | |
7786 | } | |
7787 | ||
7788 | bool RunContext::testForMissingAssertions(Counts& assertions) { | |
7789 | if (assertions.total() != 0) | |
7790 | return false; | |
7791 | if (!m_config->warnAboutMissingAssertions()) | |
7792 | return false; | |
7793 | if (m_trackerContext.currentTracker().hasChildren()) | |
7794 | return false; | |
7795 | m_totals.assertions.failed++; | |
7796 | assertions.failed++; | |
7797 | return true; | |
7798 | } | |
7799 | ||
7800 | void RunContext::sectionEnded(SectionEndInfo const & endInfo) { | |
7801 | Counts assertions = m_totals.assertions - endInfo.prevAssertions; | |
7802 | bool missingAssertions = testForMissingAssertions(assertions); | |
7803 | ||
7804 | if (!m_activeSections.empty()) { | |
7805 | m_activeSections.back()->close(); | |
7806 | m_activeSections.pop_back(); | |
7807 | } | |
7808 | ||
7809 | m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); | |
7810 | m_messages.clear(); | |
7811 | } | |
7812 | ||
7813 | void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { | |
7814 | if (m_unfinishedSections.empty()) | |
7815 | m_activeSections.back()->fail(); | |
7816 | else | |
7817 | m_activeSections.back()->close(); | |
7818 | m_activeSections.pop_back(); | |
7819 | ||
7820 | m_unfinishedSections.push_back(endInfo); | |
7821 | } | |
7822 | void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { | |
7823 | m_reporter->benchmarkStarting( info ); | |
7824 | } | |
7825 | void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { | |
7826 | m_reporter->benchmarkEnded( stats ); | |
7827 | } | |
7828 | ||
7829 | void RunContext::pushScopedMessage(MessageInfo const & message) { | |
7830 | m_messages.push_back(message); | |
7831 | } | |
7832 | ||
7833 | void RunContext::popScopedMessage(MessageInfo const & message) { | |
7834 | m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); | |
7835 | } | |
7836 | ||
7837 | std::string RunContext::getCurrentTestName() const { | |
7838 | return m_activeTestCase | |
7839 | ? m_activeTestCase->getTestCaseInfo().name | |
7840 | : std::string(); | |
7841 | } | |
7842 | ||
7843 | const AssertionResult * RunContext::getLastResult() const { | |
7844 | return &(*m_lastResult); | |
7845 | } | |
7846 | ||
7847 | void RunContext::exceptionEarlyReported() { | |
7848 | m_shouldReportUnexpected = false; | |
7849 | } | |
7850 | ||
7851 | void RunContext::handleFatalErrorCondition( StringRef message ) { | |
7852 | // First notify reporter that bad things happened | |
7853 | m_reporter->fatalErrorEncountered(message); | |
7854 | ||
7855 | // Don't rebuild the result -- the stringification itself can cause more fatal errors | |
7856 | // Instead, fake a result data. | |
7857 | AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); | |
7858 | tempResult.message = message; | |
7859 | AssertionResult result(m_lastAssertionInfo, tempResult); | |
7860 | ||
7861 | getResultCapture().assertionEnded(result); | |
7862 | ||
7863 | handleUnfinishedSections(); | |
7864 | ||
7865 | // Recreate section for test case (as we will lose the one that was in scope) | |
7866 | auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); | |
7867 | SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); | |
7868 | ||
7869 | Counts assertions; | |
7870 | assertions.failed = 1; | |
7871 | SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); | |
7872 | m_reporter->sectionEnded(testCaseSectionStats); | |
7873 | ||
7874 | auto const& testInfo = m_activeTestCase->getTestCaseInfo(); | |
7875 | ||
7876 | Totals deltaTotals; | |
7877 | deltaTotals.testCases.failed = 1; | |
7878 | deltaTotals.assertions.failed = 1; | |
7879 | m_reporter->testCaseEnded(TestCaseStats(testInfo, | |
7880 | deltaTotals, | |
7881 | std::string(), | |
7882 | std::string(), | |
7883 | false)); | |
7884 | m_totals.testCases.failed++; | |
7885 | testGroupEnded(std::string(), m_totals, 1, 1); | |
7886 | m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); | |
7887 | } | |
7888 | ||
7889 | bool RunContext::lastAssertionPassed() { | |
7890 | return m_totals.assertions.passed == (m_prevPassed + 1); | |
7891 | } | |
7892 | ||
7893 | void RunContext::assertionPassed() { | |
7894 | ++m_totals.assertions.passed; | |
7895 | m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; | |
7896 | m_lastAssertionInfo.macroName = ""; | |
7897 | } | |
7898 | ||
7899 | void RunContext::assertionRun() { | |
7900 | m_prevPassed = m_totals.assertions.passed; | |
7901 | } | |
7902 | ||
7903 | bool RunContext::aborting() const { | |
7904 | return m_totals.assertions.failed == static_cast<std::size_t>(m_config->abortAfter()); | |
7905 | } | |
7906 | ||
7907 | void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { | |
7908 | auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); | |
7909 | SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); | |
7910 | m_reporter->sectionStarting(testCaseSection); | |
7911 | Counts prevAssertions = m_totals.assertions; | |
7912 | double duration = 0; | |
7913 | m_shouldReportUnexpected = true; | |
7914 | try { | |
7915 | m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; | |
7916 | ||
7917 | seedRng(*m_config); | |
7918 | ||
7919 | Timer timer; | |
7920 | timer.start(); | |
7921 | if (m_reporter->getPreferences().shouldRedirectStdOut) { | |
7922 | StreamRedirect coutRedir(cout(), redirectedCout); | |
7923 | StdErrRedirect errRedir(redirectedCerr); | |
7924 | invokeActiveTestCase(); | |
7925 | } else { | |
7926 | invokeActiveTestCase(); | |
7927 | } | |
7928 | duration = timer.getElapsedSeconds(); | |
7929 | } catch (TestFailureException&) { | |
7930 | // This just means the test was aborted due to failure | |
7931 | } catch (...) { | |
7932 | // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions | |
7933 | // are reported without translation at the point of origin. | |
7934 | if (m_shouldReportUnexpected) { | |
7935 | AssertionHandler | |
7936 | ( m_lastAssertionInfo.macroName, | |
7937 | m_lastAssertionInfo.lineInfo, | |
7938 | m_lastAssertionInfo.capturedExpression, | |
7939 | m_lastAssertionInfo.resultDisposition ).useActiveException(); | |
7940 | } | |
7941 | } | |
7942 | m_testCaseTracker->close(); | |
7943 | handleUnfinishedSections(); | |
7944 | m_messages.clear(); | |
7945 | ||
7946 | Counts assertions = m_totals.assertions - prevAssertions; | |
7947 | bool missingAssertions = testForMissingAssertions(assertions); | |
7948 | SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); | |
7949 | m_reporter->sectionEnded(testCaseSectionStats); | |
7950 | } | |
7951 | ||
7952 | void RunContext::invokeActiveTestCase() { | |
7953 | FatalConditionHandler fatalConditionHandler; // Handle signals | |
7954 | m_activeTestCase->invoke(); | |
7955 | fatalConditionHandler.reset(); | |
7956 | } | |
7957 | ||
7958 | void RunContext::handleUnfinishedSections() { | |
7959 | // If sections ended prematurely due to an exception we stored their | |
7960 | // infos here so we can tear them down outside the unwind process. | |
7961 | for (auto it = m_unfinishedSections.rbegin(), | |
7962 | itEnd = m_unfinishedSections.rend(); | |
7963 | it != itEnd; | |
7964 | ++it) | |
7965 | sectionEnded(*it); | |
7966 | m_unfinishedSections.clear(); | |
7967 | } | |
7968 | ||
7969 | IResultCapture& getResultCapture() { | |
7970 | if (auto* capture = getCurrentContext().getResultCapture()) | |
7971 | return *capture; | |
7972 | else | |
7973 | CATCH_INTERNAL_ERROR("No result capture instance"); | |
7974 | } | |
7975 | } | |
7976 | // end catch_run_context.cpp | |
7977 | // start catch_section.cpp | |
7978 | ||
7979 | namespace Catch { | |
7980 | ||
7981 | Section::Section( SectionInfo const& info ) | |
7982 | : m_info( info ), | |
7983 | m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) | |
7984 | { | |
7985 | m_timer.start(); | |
7986 | } | |
7987 | ||
7988 | #if defined(_MSC_VER) | |
7989 | #pragma warning(push) | |
7990 | #pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 | |
7991 | #endif | |
7992 | Section::~Section() { | |
7993 | if( m_sectionIncluded ) { | |
7994 | SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); | |
7995 | if( std::uncaught_exception() ) | |
7996 | getResultCapture().sectionEndedEarly( endInfo ); | |
7997 | else | |
7998 | getResultCapture().sectionEnded( endInfo ); | |
7999 | } | |
8000 | } | |
8001 | #if defined(_MSC_VER) | |
8002 | #pragma warning(pop) | |
8003 | #endif | |
8004 | ||
8005 | // This indicates whether the section should be executed or not | |
8006 | Section::operator bool() const { | |
8007 | return m_sectionIncluded; | |
8008 | } | |
8009 | ||
8010 | } // end namespace Catch | |
8011 | // end catch_section.cpp | |
8012 | // start catch_section_info.cpp | |
8013 | ||
8014 | namespace Catch { | |
8015 | ||
8016 | SectionInfo::SectionInfo | |
8017 | ( SourceLineInfo const& _lineInfo, | |
8018 | std::string const& _name, | |
8019 | std::string const& _description ) | |
8020 | : name( _name ), | |
8021 | description( _description ), | |
8022 | lineInfo( _lineInfo ) | |
8023 | {} | |
8024 | ||
8025 | SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) | |
8026 | : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) | |
8027 | {} | |
8028 | ||
8029 | } // end namespace Catch | |
8030 | // end catch_section_info.cpp | |
8031 | // start catch_session.cpp | |
8032 | ||
8033 | // start catch_session.h | |
8034 | ||
8035 | #include <memory> | |
8036 | ||
8037 | namespace Catch { | |
8038 | ||
8039 | class Session : NonCopyable { | |
8040 | public: | |
8041 | ||
8042 | Session(); | |
8043 | ~Session() override; | |
8044 | ||
8045 | void showHelp() const; | |
8046 | void libIdentify(); | |
8047 | ||
8048 | int applyCommandLine( int argc, char* argv[] ); | |
8049 | ||
8050 | void useConfigData( ConfigData const& configData ); | |
8051 | ||
8052 | int run( int argc, char* argv[] ); | |
8053 | #if defined(WIN32) && defined(UNICODE) | |
8054 | int run( int argc, wchar_t* const argv[] ); | |
8055 | #endif | |
8056 | int run(); | |
8057 | ||
8058 | clara::Parser const& cli() const; | |
8059 | void cli( clara::Parser const& newParser ); | |
8060 | ConfigData& configData(); | |
8061 | Config& config(); | |
8062 | private: | |
8063 | int runInternal(); | |
8064 | ||
8065 | clara::Parser m_cli; | |
8066 | ConfigData m_configData; | |
8067 | std::shared_ptr<Config> m_config; | |
8068 | bool m_startupExceptions = false; | |
8069 | }; | |
8070 | ||
8071 | } // end namespace Catch | |
8072 | ||
8073 | // end catch_session.h | |
8074 | // start catch_version.h | |
8075 | ||
8076 | #include <iosfwd> | |
8077 | ||
8078 | namespace Catch { | |
8079 | ||
8080 | // Versioning information | |
8081 | struct Version { | |
8082 | Version( Version const& ) = delete; | |
8083 | Version& operator=( Version const& ) = delete; | |
8084 | Version( unsigned int _majorVersion, | |
8085 | unsigned int _minorVersion, | |
8086 | unsigned int _patchNumber, | |
8087 | char const * const _branchName, | |
8088 | unsigned int _buildNumber ); | |
8089 | ||
8090 | unsigned int const majorVersion; | |
8091 | unsigned int const minorVersion; | |
8092 | unsigned int const patchNumber; | |
8093 | ||
8094 | // buildNumber is only used if branchName is not null | |
8095 | char const * const branchName; | |
8096 | unsigned int const buildNumber; | |
8097 | ||
8098 | friend std::ostream& operator << ( std::ostream& os, Version const& version ); | |
8099 | }; | |
8100 | ||
8101 | Version const& libraryVersion(); | |
8102 | } | |
8103 | ||
8104 | // end catch_version.h | |
8105 | #include <cstdlib> | |
8106 | #include <iomanip> | |
8107 | ||
8108 | namespace { | |
8109 | const int MaxExitCode = 255; | |
8110 | using Catch::IStreamingReporterPtr; | |
8111 | using Catch::IConfigPtr; | |
8112 | using Catch::Config; | |
8113 | ||
8114 | IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { | |
8115 | auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); | |
8116 | CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); | |
8117 | ||
8118 | return reporter; | |
8119 | } | |
8120 | ||
8121 | #ifndef CATCH_CONFIG_DEFAULT_REPORTER | |
8122 | #define CATCH_CONFIG_DEFAULT_REPORTER "console" | |
8123 | #endif | |
8124 | ||
8125 | IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { | |
8126 | auto const& reporterNames = config->getReporterNames(); | |
8127 | if (reporterNames.empty()) | |
8128 | return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); | |
8129 | ||
8130 | IStreamingReporterPtr reporter; | |
8131 | for (auto const& name : reporterNames) | |
8132 | addReporter(reporter, createReporter(name, config)); | |
8133 | return reporter; | |
8134 | } | |
8135 | ||
8136 | #undef CATCH_CONFIG_DEFAULT_REPORTER | |
8137 | ||
8138 | void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { | |
8139 | auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); | |
8140 | for (auto const& listener : listeners) | |
8141 | addReporter(reporters, listener->create(Catch::ReporterConfig(config))); | |
8142 | } | |
8143 | ||
8144 | Catch::Totals runTests(std::shared_ptr<Config> const& config) { | |
8145 | using namespace Catch; | |
8146 | IStreamingReporterPtr reporter = makeReporter(config); | |
8147 | addListeners(reporter, config); | |
8148 | ||
8149 | RunContext context(config, std::move(reporter)); | |
8150 | ||
8151 | Totals totals; | |
8152 | ||
8153 | context.testGroupStarting(config->name(), 1, 1); | |
8154 | ||
8155 | TestSpec testSpec = config->testSpec(); | |
8156 | if (!testSpec.hasFilters()) | |
8157 | testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests | |
8158 | ||
8159 | auto const& allTestCases = getAllTestCasesSorted(*config); | |
8160 | for (auto const& testCase : allTestCases) { | |
8161 | if (!context.aborting() && matchTest(testCase, testSpec, *config)) | |
8162 | totals += context.runTest(testCase); | |
8163 | else | |
8164 | context.reporter().skipTest(testCase); | |
8165 | } | |
8166 | ||
8167 | context.testGroupEnded(config->name(), totals, 1, 1); | |
8168 | return totals; | |
8169 | } | |
8170 | ||
8171 | void applyFilenamesAsTags(Catch::IConfig const& config) { | |
8172 | using namespace Catch; | |
8173 | auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); | |
8174 | for (auto& testCase : tests) { | |
8175 | auto tags = testCase.tags; | |
8176 | ||
8177 | std::string filename = testCase.lineInfo.file; | |
8178 | auto lastSlash = filename.find_last_of("\\/"); | |
8179 | if (lastSlash != std::string::npos) { | |
8180 | filename.erase(0, lastSlash); | |
8181 | filename[0] = '#'; | |
8182 | } | |
8183 | ||
8184 | auto lastDot = filename.find_last_of('.'); | |
8185 | if (lastDot != std::string::npos) { | |
8186 | filename.erase(lastDot); | |
8187 | } | |
8188 | ||
8189 | tags.push_back(std::move(filename)); | |
8190 | setTags(testCase, tags); | |
8191 | } | |
8192 | } | |
8193 | ||
8194 | } | |
8195 | ||
8196 | namespace Catch { | |
8197 | ||
8198 | Session::Session() { | |
8199 | static bool alreadyInstantiated = false; | |
8200 | if( alreadyInstantiated ) { | |
8201 | try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } | |
8202 | catch(...) { getMutableRegistryHub().registerStartupException(); } | |
8203 | } | |
8204 | ||
8205 | const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); | |
8206 | if ( !exceptions.empty() ) { | |
8207 | m_startupExceptions = true; | |
8208 | Colour colourGuard( Colour::Red ); | |
8209 | Catch::cerr() << "Errors occured during startup!" << '\n'; | |
8210 | // iterate over all exceptions and notify user | |
8211 | for ( const auto& ex_ptr : exceptions ) { | |
8212 | try { | |
8213 | std::rethrow_exception(ex_ptr); | |
8214 | } catch ( std::exception const& ex ) { | |
8215 | Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; | |
8216 | } | |
8217 | } | |
8218 | } | |
8219 | ||
8220 | alreadyInstantiated = true; | |
8221 | m_cli = makeCommandLineParser( m_configData ); | |
8222 | } | |
8223 | Session::~Session() { | |
8224 | Catch::cleanUp(); | |
8225 | } | |
8226 | ||
8227 | void Session::showHelp() const { | |
8228 | Catch::cout() | |
8229 | << "\nCatch v" << libraryVersion() << "\n" | |
8230 | << m_cli << std::endl | |
8231 | << "For more detailed usage please see the project docs\n" << std::endl; | |
8232 | } | |
8233 | void Session::libIdentify() { | |
8234 | Catch::cout() | |
8235 | << std::left << std::setw(16) << "description: " << "A Catch test executable\n" | |
8236 | << std::left << std::setw(16) << "category: " << "testframework\n" | |
8237 | << std::left << std::setw(16) << "framework: " << "Catch Test\n" | |
8238 | << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; | |
8239 | } | |
8240 | ||
8241 | int Session::applyCommandLine( int argc, char* argv[] ) { | |
8242 | if( m_startupExceptions ) | |
8243 | return 1; | |
8244 | ||
8245 | auto result = m_cli.parse( clara::Args( argc, argv ) ); | |
8246 | if( !result ) { | |
8247 | Catch::cerr() | |
8248 | << Colour( Colour::Red ) | |
8249 | << "\nError(s) in input:\n" | |
8250 | << Column( result.errorMessage() ).indent( 2 ) | |
8251 | << "\n\n"; | |
8252 | Catch::cerr() << "Run with -? for usage\n" << std::endl; | |
8253 | return MaxExitCode; | |
8254 | } | |
8255 | ||
8256 | if( m_configData.showHelp ) | |
8257 | showHelp(); | |
8258 | if( m_configData.libIdentify ) | |
8259 | libIdentify(); | |
8260 | m_config.reset(); | |
8261 | return 0; | |
8262 | } | |
8263 | ||
8264 | void Session::useConfigData( ConfigData const& configData ) { | |
8265 | m_configData = configData; | |
8266 | m_config.reset(); | |
8267 | } | |
8268 | ||
8269 | int Session::run( int argc, char* argv[] ) { | |
8270 | if( m_startupExceptions ) | |
8271 | return 1; | |
8272 | int returnCode = applyCommandLine( argc, argv ); | |
8273 | if( returnCode == 0 ) | |
8274 | returnCode = run(); | |
8275 | return returnCode; | |
8276 | } | |
8277 | ||
8278 | #if defined(WIN32) && defined(UNICODE) | |
8279 | int Session::run( int argc, wchar_t* const argv[] ) { | |
8280 | ||
8281 | char **utf8Argv = new char *[ argc ]; | |
8282 | ||
8283 | for ( int i = 0; i < argc; ++i ) { | |
8284 | int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); | |
8285 | ||
8286 | utf8Argv[ i ] = new char[ bufSize ]; | |
8287 | ||
8288 | WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); | |
8289 | } | |
8290 | ||
8291 | int returnCode = run( argc, utf8Argv ); | |
8292 | ||
8293 | for ( int i = 0; i < argc; ++i ) | |
8294 | delete [] utf8Argv[ i ]; | |
8295 | ||
8296 | delete [] utf8Argv; | |
8297 | ||
8298 | return returnCode; | |
8299 | } | |
8300 | #endif | |
8301 | int Session::run() { | |
8302 | if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { | |
8303 | Catch::cout() << "...waiting for enter/ return before starting" << std::endl; | |
8304 | static_cast<void>(std::getchar()); | |
8305 | } | |
8306 | int exitCode = runInternal(); | |
8307 | if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { | |
8308 | Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; | |
8309 | static_cast<void>(std::getchar()); | |
8310 | } | |
8311 | return exitCode; | |
8312 | } | |
8313 | ||
8314 | clara::Parser const& Session::cli() const { | |
8315 | return m_cli; | |
8316 | } | |
8317 | void Session::cli( clara::Parser const& newParser ) { | |
8318 | m_cli = newParser; | |
8319 | } | |
8320 | ConfigData& Session::configData() { | |
8321 | return m_configData; | |
8322 | } | |
8323 | Config& Session::config() { | |
8324 | if( !m_config ) | |
8325 | m_config = std::make_shared<Config>( m_configData ); | |
8326 | return *m_config; | |
8327 | } | |
8328 | ||
8329 | int Session::runInternal() { | |
8330 | if( m_startupExceptions ) | |
8331 | return 1; | |
8332 | ||
8333 | if( m_configData.showHelp || m_configData.libIdentify ) | |
8334 | return 0; | |
8335 | ||
8336 | try | |
8337 | { | |
8338 | config(); // Force config to be constructed | |
8339 | ||
8340 | seedRng( *m_config ); | |
8341 | ||
8342 | if( m_configData.filenamesAsTags ) | |
8343 | applyFilenamesAsTags( *m_config ); | |
8344 | ||
8345 | // Handle list request | |
8346 | if( Option<std::size_t> listed = list( config() ) ) | |
8347 | return static_cast<int>( *listed ); | |
8348 | ||
8349 | return (std::min)( MaxExitCode, static_cast<int>( runTests( m_config ).assertions.failed ) ); | |
8350 | } | |
8351 | catch( std::exception& ex ) { | |
8352 | Catch::cerr() << ex.what() << std::endl; | |
8353 | return MaxExitCode; | |
8354 | } | |
8355 | } | |
8356 | ||
8357 | } // end namespace Catch | |
8358 | // end catch_session.cpp | |
8359 | // start catch_startup_exception_registry.cpp | |
8360 | ||
8361 | namespace Catch { | |
8362 | void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { | |
8363 | try { | |
8364 | m_exceptions.push_back(exception); | |
8365 | } | |
8366 | catch(...) { | |
8367 | // If we run out of memory during start-up there's really not a lot more we can do about it | |
8368 | std::terminate(); | |
8369 | } | |
8370 | } | |
8371 | ||
8372 | std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept { | |
8373 | return m_exceptions; | |
8374 | } | |
8375 | ||
8376 | } // end namespace Catch | |
8377 | // end catch_startup_exception_registry.cpp | |
8378 | // start catch_stream.cpp | |
8379 | ||
8380 | #include <stdexcept> | |
8381 | #include <cstdio> | |
8382 | #include <iostream> | |
8383 | ||
8384 | namespace Catch { | |
8385 | ||
8386 | template<typename WriterF, std::size_t bufferSize=256> | |
8387 | class StreamBufImpl : public StreamBufBase { | |
8388 | char data[bufferSize]; | |
8389 | WriterF m_writer; | |
8390 | ||
8391 | public: | |
8392 | StreamBufImpl() { | |
8393 | setp( data, data + sizeof(data) ); | |
8394 | } | |
8395 | ||
8396 | ~StreamBufImpl() noexcept { | |
8397 | StreamBufImpl::sync(); | |
8398 | } | |
8399 | ||
8400 | private: | |
8401 | int overflow( int c ) override { | |
8402 | sync(); | |
8403 | ||
8404 | if( c != EOF ) { | |
8405 | if( pbase() == epptr() ) | |
8406 | m_writer( std::string( 1, static_cast<char>( c ) ) ); | |
8407 | else | |
8408 | sputc( static_cast<char>( c ) ); | |
8409 | } | |
8410 | return 0; | |
8411 | } | |
8412 | ||
8413 | int sync() override { | |
8414 | if( pbase() != pptr() ) { | |
8415 | m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); | |
8416 | setp( pbase(), epptr() ); | |
8417 | } | |
8418 | return 0; | |
8419 | } | |
8420 | }; | |
8421 | ||
8422 | /////////////////////////////////////////////////////////////////////////// | |
8423 | ||
8424 | Catch::IStream::~IStream() = default; | |
8425 | ||
8426 | FileStream::FileStream( std::string const& filename ) { | |
8427 | m_ofs.open( filename.c_str() ); | |
8428 | CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); | |
8429 | } | |
8430 | ||
8431 | std::ostream& FileStream::stream() const { | |
8432 | return m_ofs; | |
8433 | } | |
8434 | ||
8435 | struct OutputDebugWriter { | |
8436 | ||
8437 | void operator()( std::string const&str ) { | |
8438 | writeToDebugConsole( str ); | |
8439 | } | |
8440 | }; | |
8441 | ||
8442 | DebugOutStream::DebugOutStream() | |
8443 | : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), | |
8444 | m_os( m_streamBuf.get() ) | |
8445 | {} | |
8446 | ||
8447 | std::ostream& DebugOutStream::stream() const { | |
8448 | return m_os; | |
8449 | } | |
8450 | ||
8451 | // Store the streambuf from cout up-front because | |
8452 | // cout may get redirected when running tests | |
8453 | CoutStream::CoutStream() | |
8454 | : m_os( Catch::cout().rdbuf() ) | |
8455 | {} | |
8456 | ||
8457 | std::ostream& CoutStream::stream() const { | |
8458 | return m_os; | |
8459 | } | |
8460 | ||
8461 | #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions | |
8462 | std::ostream& cout() { | |
8463 | return std::cout; | |
8464 | } | |
8465 | std::ostream& cerr() { | |
8466 | return std::cerr; | |
8467 | } | |
8468 | std::ostream& clog() { | |
8469 | return std::clog; | |
8470 | } | |
8471 | #endif | |
8472 | } | |
8473 | // end catch_stream.cpp | |
8474 | // start catch_streambuf.cpp | |
8475 | ||
8476 | namespace Catch { | |
8477 | StreamBufBase::~StreamBufBase() = default; | |
8478 | } | |
8479 | // end catch_streambuf.cpp | |
8480 | // start catch_string_manip.cpp | |
8481 | ||
8482 | #include <algorithm> | |
8483 | #include <ostream> | |
8484 | #include <cstring> | |
8485 | #include <cctype> | |
8486 | ||
8487 | namespace Catch { | |
8488 | ||
8489 | bool startsWith( std::string const& s, std::string const& prefix ) { | |
8490 | return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); | |
8491 | } | |
8492 | bool startsWith( std::string const& s, char prefix ) { | |
8493 | return !s.empty() && s[0] == prefix; | |
8494 | } | |
8495 | bool endsWith( std::string const& s, std::string const& suffix ) { | |
8496 | return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); | |
8497 | } | |
8498 | bool endsWith( std::string const& s, char suffix ) { | |
8499 | return !s.empty() && s[s.size()-1] == suffix; | |
8500 | } | |
8501 | bool contains( std::string const& s, std::string const& infix ) { | |
8502 | return s.find( infix ) != std::string::npos; | |
8503 | } | |
8504 | char toLowerCh(char c) { | |
8505 | return static_cast<char>( std::tolower( c ) ); | |
8506 | } | |
8507 | void toLowerInPlace( std::string& s ) { | |
8508 | std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); | |
8509 | } | |
8510 | std::string toLower( std::string const& s ) { | |
8511 | std::string lc = s; | |
8512 | toLowerInPlace( lc ); | |
8513 | return lc; | |
8514 | } | |
8515 | std::string trim( std::string const& str ) { | |
8516 | static char const* whitespaceChars = "\n\r\t "; | |
8517 | std::string::size_type start = str.find_first_not_of( whitespaceChars ); | |
8518 | std::string::size_type end = str.find_last_not_of( whitespaceChars ); | |
8519 | ||
8520 | return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); | |
8521 | } | |
8522 | ||
8523 | bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { | |
8524 | bool replaced = false; | |
8525 | std::size_t i = str.find( replaceThis ); | |
8526 | while( i != std::string::npos ) { | |
8527 | replaced = true; | |
8528 | str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); | |
8529 | if( i < str.size()-withThis.size() ) | |
8530 | i = str.find( replaceThis, i+withThis.size() ); | |
8531 | else | |
8532 | i = std::string::npos; | |
8533 | } | |
8534 | return replaced; | |
8535 | } | |
8536 | ||
8537 | pluralise::pluralise( std::size_t count, std::string const& label ) | |
8538 | : m_count( count ), | |
8539 | m_label( label ) | |
8540 | {} | |
8541 | ||
8542 | std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { | |
8543 | os << pluraliser.m_count << ' ' << pluraliser.m_label; | |
8544 | if( pluraliser.m_count != 1 ) | |
8545 | os << 's'; | |
8546 | return os; | |
8547 | } | |
8548 | ||
8549 | } | |
8550 | // end catch_string_manip.cpp | |
8551 | // start catch_stringref.cpp | |
8552 | ||
8553 | #if defined(__clang__) | |
8554 | # pragma clang diagnostic push | |
8555 | # pragma clang diagnostic ignored "-Wexit-time-destructors" | |
8556 | #endif | |
8557 | ||
8558 | #include <ostream> | |
8559 | #include <cassert> | |
8560 | #include <cstring> | |
8561 | ||
8562 | namespace Catch { | |
8563 | ||
8564 | auto getEmptyStringRef() -> StringRef { | |
8565 | static StringRef s_emptyStringRef(""); | |
8566 | return s_emptyStringRef; | |
8567 | } | |
8568 | ||
8569 | StringRef::StringRef() noexcept | |
8570 | : StringRef( getEmptyStringRef() ) | |
8571 | {} | |
8572 | ||
8573 | StringRef::StringRef( StringRef const& other ) noexcept | |
8574 | : m_start( other.m_start ), | |
8575 | m_size( other.m_size ) | |
8576 | {} | |
8577 | ||
8578 | StringRef::StringRef( StringRef&& other ) noexcept | |
8579 | : m_start( other.m_start ), | |
8580 | m_size( other.m_size ), | |
8581 | m_data( other.m_data ) | |
8582 | { | |
8583 | other.m_data = nullptr; | |
8584 | } | |
8585 | ||
8586 | StringRef::StringRef( char const* rawChars ) noexcept | |
8587 | : m_start( rawChars ), | |
8588 | m_size( static_cast<size_type>( std::strlen( rawChars ) ) ) | |
8589 | { | |
8590 | assert( rawChars != nullptr ); | |
8591 | } | |
8592 | ||
8593 | StringRef::StringRef( char const* rawChars, size_type size ) noexcept | |
8594 | : m_start( rawChars ), | |
8595 | m_size( size ) | |
8596 | { | |
8597 | size_type rawSize = rawChars == nullptr ? 0 : static_cast<size_type>( std::strlen( rawChars ) ); | |
8598 | if( rawSize < size ) | |
8599 | m_size = rawSize; | |
8600 | } | |
8601 | ||
8602 | StringRef::StringRef( std::string const& stdString ) noexcept | |
8603 | : m_start( stdString.c_str() ), | |
8604 | m_size( stdString.size() ) | |
8605 | {} | |
8606 | ||
8607 | StringRef::~StringRef() noexcept { | |
8608 | delete[] m_data; | |
8609 | } | |
8610 | ||
8611 | auto StringRef::operator = ( StringRef other ) noexcept -> StringRef& { | |
8612 | swap( other ); | |
8613 | return *this; | |
8614 | } | |
8615 | StringRef::operator std::string() const { | |
8616 | return std::string( m_start, m_size ); | |
8617 | } | |
8618 | ||
8619 | void StringRef::swap( StringRef& other ) noexcept { | |
8620 | std::swap( m_start, other.m_start ); | |
8621 | std::swap( m_size, other.m_size ); | |
8622 | std::swap( m_data, other.m_data ); | |
8623 | } | |
8624 | ||
8625 | auto StringRef::c_str() const -> char const* { | |
8626 | if( isSubstring() ) | |
8627 | const_cast<StringRef*>( this )->takeOwnership(); | |
8628 | return m_start; | |
8629 | } | |
8630 | auto StringRef::data() const noexcept -> char const* { | |
8631 | return m_start; | |
8632 | } | |
8633 | ||
8634 | auto StringRef::isOwned() const noexcept -> bool { | |
8635 | return m_data != nullptr; | |
8636 | } | |
8637 | auto StringRef::isSubstring() const noexcept -> bool { | |
8638 | return m_start[m_size] != '\0'; | |
8639 | } | |
8640 | ||
8641 | void StringRef::takeOwnership() { | |
8642 | if( !isOwned() ) { | |
8643 | m_data = new char[m_size+1]; | |
8644 | memcpy( m_data, m_start, m_size ); | |
8645 | m_data[m_size] = '\0'; | |
8646 | m_start = m_data; | |
8647 | } | |
8648 | } | |
8649 | auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { | |
8650 | if( start < m_size ) | |
8651 | return StringRef( m_start+start, size ); | |
8652 | else | |
8653 | return StringRef(); | |
8654 | } | |
8655 | auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { | |
8656 | return | |
8657 | size() == other.size() && | |
8658 | (std::strncmp( m_start, other.m_start, size() ) == 0); | |
8659 | } | |
8660 | auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { | |
8661 | return !operator==( other ); | |
8662 | } | |
8663 | ||
8664 | auto StringRef::operator[](size_type index) const noexcept -> char { | |
8665 | return m_start[index]; | |
8666 | } | |
8667 | ||
8668 | auto StringRef::empty() const noexcept -> bool { | |
8669 | return m_size == 0; | |
8670 | } | |
8671 | ||
8672 | auto StringRef::size() const noexcept -> size_type { | |
8673 | return m_size; | |
8674 | } | |
8675 | auto StringRef::numberOfCharacters() const noexcept -> size_type { | |
8676 | size_type noChars = m_size; | |
8677 | // Make adjustments for uft encodings | |
8678 | for( size_type i=0; i < m_size; ++i ) { | |
8679 | char c = m_start[i]; | |
8680 | if( ( c & 0b11000000 ) == 0b11000000 ) { | |
8681 | if( ( c & 0b11100000 ) == 0b11000000 ) | |
8682 | noChars--; | |
8683 | else if( ( c & 0b11110000 ) == 0b11100000 ) | |
8684 | noChars-=2; | |
8685 | else if( ( c & 0b11111000 ) == 0b11110000 ) | |
8686 | noChars-=3; | |
8687 | } | |
8688 | } | |
8689 | return noChars; | |
8690 | } | |
8691 | ||
8692 | auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { | |
8693 | std::string str; | |
8694 | str.reserve( lhs.size() + rhs.size() ); | |
8695 | str += lhs; | |
8696 | str += rhs; | |
8697 | return str; | |
8698 | } | |
8699 | auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { | |
8700 | return std::string( lhs ) + std::string( rhs ); | |
8701 | } | |
8702 | auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { | |
8703 | return std::string( lhs ) + std::string( rhs ); | |
8704 | } | |
8705 | ||
8706 | auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { | |
8707 | return os << str.c_str(); | |
8708 | } | |
8709 | ||
8710 | } // namespace Catch | |
8711 | ||
8712 | #if defined(__clang__) | |
8713 | # pragma clang diagnostic pop | |
8714 | #endif | |
8715 | // end catch_stringref.cpp | |
8716 | // start catch_tag_alias.cpp | |
8717 | ||
8718 | namespace Catch { | |
8719 | TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} | |
8720 | } | |
8721 | // end catch_tag_alias.cpp | |
8722 | // start catch_tag_alias_autoregistrar.cpp | |
8723 | ||
8724 | namespace Catch { | |
8725 | ||
8726 | RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { | |
8727 | try { | |
8728 | getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); | |
8729 | } catch (...) { | |
8730 | // Do not throw when constructing global objects, instead register the exception to be processed later | |
8731 | getMutableRegistryHub().registerStartupException(); | |
8732 | } | |
8733 | } | |
8734 | ||
8735 | } | |
8736 | // end catch_tag_alias_autoregistrar.cpp | |
8737 | // start catch_tag_alias_registry.cpp | |
8738 | ||
8739 | namespace Catch { | |
8740 | ||
8741 | TagAliasRegistry::~TagAliasRegistry() {} | |
8742 | ||
8743 | TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { | |
8744 | auto it = m_registry.find( alias ); | |
8745 | if( it != m_registry.end() ) | |
8746 | return &(it->second); | |
8747 | else | |
8748 | return nullptr; | |
8749 | } | |
8750 | ||
8751 | std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { | |
8752 | std::string expandedTestSpec = unexpandedTestSpec; | |
8753 | for( auto const& registryKvp : m_registry ) { | |
8754 | std::size_t pos = expandedTestSpec.find( registryKvp.first ); | |
8755 | if( pos != std::string::npos ) { | |
8756 | expandedTestSpec = expandedTestSpec.substr( 0, pos ) + | |
8757 | registryKvp.second.tag + | |
8758 | expandedTestSpec.substr( pos + registryKvp.first.size() ); | |
8759 | } | |
8760 | } | |
8761 | return expandedTestSpec; | |
8762 | } | |
8763 | ||
8764 | void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { | |
8765 | CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), | |
8766 | "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); | |
8767 | ||
8768 | CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, | |
8769 | "error: tag alias, '" << alias << "' already registered.\n" | |
8770 | << "\tFirst seen at: " << find(alias)->lineInfo << "\n" | |
8771 | << "\tRedefined at: " << lineInfo ); | |
8772 | } | |
8773 | ||
8774 | ITagAliasRegistry::~ITagAliasRegistry() {} | |
8775 | ||
8776 | ITagAliasRegistry const& ITagAliasRegistry::get() { | |
8777 | return getRegistryHub().getTagAliasRegistry(); | |
8778 | } | |
8779 | ||
8780 | } // end namespace Catch | |
8781 | // end catch_tag_alias_registry.cpp | |
8782 | // start catch_test_case_info.cpp | |
8783 | ||
8784 | #include <cctype> | |
8785 | #include <exception> | |
8786 | #include <algorithm> | |
8787 | ||
8788 | namespace Catch { | |
8789 | ||
8790 | TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { | |
8791 | if( startsWith( tag, '.' ) || | |
8792 | tag == "!hide" ) | |
8793 | return TestCaseInfo::IsHidden; | |
8794 | else if( tag == "!throws" ) | |
8795 | return TestCaseInfo::Throws; | |
8796 | else if( tag == "!shouldfail" ) | |
8797 | return TestCaseInfo::ShouldFail; | |
8798 | else if( tag == "!mayfail" ) | |
8799 | return TestCaseInfo::MayFail; | |
8800 | else if( tag == "!nonportable" ) | |
8801 | return TestCaseInfo::NonPortable; | |
8802 | else if( tag == "!benchmark" ) | |
8803 | return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); | |
8804 | else | |
8805 | return TestCaseInfo::None; | |
8806 | } | |
8807 | bool isReservedTag( std::string const& tag ) { | |
8808 | return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); | |
8809 | } | |
8810 | void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { | |
8811 | CATCH_ENFORCE( !isReservedTag(tag), | |
8812 | "Tag name: [" << tag << "] is not allowed.\n" | |
8813 | << "Tag names starting with non alpha-numeric characters are reserved\n" | |
8814 | << _lineInfo ); | |
8815 | } | |
8816 | ||
8817 | TestCase makeTestCase( ITestInvoker* _testCase, | |
8818 | std::string const& _className, | |
8819 | std::string const& _name, | |
8820 | std::string const& _descOrTags, | |
8821 | SourceLineInfo const& _lineInfo ) | |
8822 | { | |
8823 | bool isHidden = false; | |
8824 | ||
8825 | // Parse out tags | |
8826 | std::vector<std::string> tags; | |
8827 | std::string desc, tag; | |
8828 | bool inTag = false; | |
8829 | for (char c : _descOrTags) { | |
8830 | if( !inTag ) { | |
8831 | if( c == '[' ) | |
8832 | inTag = true; | |
8833 | else | |
8834 | desc += c; | |
8835 | } | |
8836 | else { | |
8837 | if( c == ']' ) { | |
8838 | TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); | |
8839 | if( ( prop & TestCaseInfo::IsHidden ) != 0 ) | |
8840 | isHidden = true; | |
8841 | else if( prop == TestCaseInfo::None ) | |
8842 | enforceNotReservedTag( tag, _lineInfo ); | |
8843 | ||
8844 | tags.push_back( tag ); | |
8845 | tag.clear(); | |
8846 | inTag = false; | |
8847 | } | |
8848 | else | |
8849 | tag += c; | |
8850 | } | |
8851 | } | |
8852 | if( isHidden ) { | |
8853 | tags.push_back( "." ); | |
8854 | } | |
8855 | ||
8856 | TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); | |
8857 | return TestCase( _testCase, info ); | |
8858 | } | |
8859 | ||
8860 | void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { | |
8861 | std::sort(begin(tags), end(tags)); | |
8862 | tags.erase(std::unique(begin(tags), end(tags)), end(tags)); | |
8863 | testCaseInfo.lcaseTags.clear(); | |
8864 | ||
8865 | for( auto const& tag : tags ) { | |
8866 | std::string lcaseTag = toLower( tag ); | |
8867 | testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); | |
8868 | testCaseInfo.lcaseTags.push_back( lcaseTag ); | |
8869 | } | |
8870 | testCaseInfo.tags = std::move(tags); | |
8871 | } | |
8872 | ||
8873 | TestCaseInfo::TestCaseInfo( std::string const& _name, | |
8874 | std::string const& _className, | |
8875 | std::string const& _description, | |
8876 | std::vector<std::string> const& _tags, | |
8877 | SourceLineInfo const& _lineInfo ) | |
8878 | : name( _name ), | |
8879 | className( _className ), | |
8880 | description( _description ), | |
8881 | lineInfo( _lineInfo ), | |
8882 | properties( None ) | |
8883 | { | |
8884 | setTags( *this, _tags ); | |
8885 | } | |
8886 | ||
8887 | bool TestCaseInfo::isHidden() const { | |
8888 | return ( properties & IsHidden ) != 0; | |
8889 | } | |
8890 | bool TestCaseInfo::throws() const { | |
8891 | return ( properties & Throws ) != 0; | |
8892 | } | |
8893 | bool TestCaseInfo::okToFail() const { | |
8894 | return ( properties & (ShouldFail | MayFail ) ) != 0; | |
8895 | } | |
8896 | bool TestCaseInfo::expectedToFail() const { | |
8897 | return ( properties & (ShouldFail ) ) != 0; | |
8898 | } | |
8899 | ||
8900 | std::string TestCaseInfo::tagsAsString() const { | |
8901 | std::string ret; | |
8902 | // '[' and ']' per tag | |
8903 | std::size_t full_size = 2 * tags.size(); | |
8904 | for (const auto& tag : tags) { | |
8905 | full_size += tag.size(); | |
8906 | } | |
8907 | ret.reserve(full_size); | |
8908 | for (const auto& tag : tags) { | |
8909 | ret.push_back('['); | |
8910 | ret.append(tag); | |
8911 | ret.push_back(']'); | |
8912 | } | |
8913 | ||
8914 | return ret; | |
8915 | } | |
8916 | ||
8917 | TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} | |
8918 | ||
8919 | TestCase TestCase::withName( std::string const& _newName ) const { | |
8920 | TestCase other( *this ); | |
8921 | other.name = _newName; | |
8922 | return other; | |
8923 | } | |
8924 | ||
8925 | void TestCase::invoke() const { | |
8926 | test->invoke(); | |
8927 | } | |
8928 | ||
8929 | bool TestCase::operator == ( TestCase const& other ) const { | |
8930 | return test.get() == other.test.get() && | |
8931 | name == other.name && | |
8932 | className == other.className; | |
8933 | } | |
8934 | ||
8935 | bool TestCase::operator < ( TestCase const& other ) const { | |
8936 | return name < other.name; | |
8937 | } | |
8938 | ||
8939 | TestCaseInfo const& TestCase::getTestCaseInfo() const | |
8940 | { | |
8941 | return *this; | |
8942 | } | |
8943 | ||
8944 | } // end namespace Catch | |
8945 | // end catch_test_case_info.cpp | |
8946 | // start catch_test_case_registry_impl.cpp | |
8947 | ||
8948 | #include <sstream> | |
8949 | ||
8950 | namespace Catch { | |
8951 | ||
8952 | std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { | |
8953 | ||
8954 | std::vector<TestCase> sorted = unsortedTestCases; | |
8955 | ||
8956 | switch( config.runOrder() ) { | |
8957 | case RunTests::InLexicographicalOrder: | |
8958 | std::sort( sorted.begin(), sorted.end() ); | |
8959 | break; | |
8960 | case RunTests::InRandomOrder: | |
8961 | seedRng( config ); | |
8962 | RandomNumberGenerator::shuffle( sorted ); | |
8963 | break; | |
8964 | case RunTests::InDeclarationOrder: | |
8965 | // already in declaration order | |
8966 | break; | |
8967 | } | |
8968 | return sorted; | |
8969 | } | |
8970 | bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { | |
8971 | return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); | |
8972 | } | |
8973 | ||
8974 | void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { | |
8975 | std::set<TestCase> seenFunctions; | |
8976 | for( auto const& function : functions ) { | |
8977 | auto prev = seenFunctions.insert( function ); | |
8978 | CATCH_ENFORCE( prev.second, | |
8979 | "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" | |
8980 | << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" | |
8981 | << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); | |
8982 | } | |
8983 | } | |
8984 | ||
8985 | std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { | |
8986 | std::vector<TestCase> filtered; | |
8987 | filtered.reserve( testCases.size() ); | |
8988 | for( auto const& testCase : testCases ) | |
8989 | if( matchTest( testCase, testSpec, config ) ) | |
8990 | filtered.push_back( testCase ); | |
8991 | return filtered; | |
8992 | } | |
8993 | std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { | |
8994 | return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); | |
8995 | } | |
8996 | ||
8997 | void TestRegistry::registerTest( TestCase const& testCase ) { | |
8998 | std::string name = testCase.getTestCaseInfo().name; | |
8999 | if( name.empty() ) { | |
9000 | std::ostringstream oss; | |
9001 | oss << "Anonymous test case " << ++m_unnamedCount; | |
9002 | return registerTest( testCase.withName( oss.str() ) ); | |
9003 | } | |
9004 | m_functions.push_back( testCase ); | |
9005 | } | |
9006 | ||
9007 | std::vector<TestCase> const& TestRegistry::getAllTests() const { | |
9008 | return m_functions; | |
9009 | } | |
9010 | std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { | |
9011 | if( m_sortedFunctions.empty() ) | |
9012 | enforceNoDuplicateTestCases( m_functions ); | |
9013 | ||
9014 | if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { | |
9015 | m_sortedFunctions = sortTests( config, m_functions ); | |
9016 | m_currentSortOrder = config.runOrder(); | |
9017 | } | |
9018 | return m_sortedFunctions; | |
9019 | } | |
9020 | ||
9021 | /////////////////////////////////////////////////////////////////////////// | |
9022 | TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} | |
9023 | ||
9024 | void TestInvokerAsFunction::invoke() const { | |
9025 | m_testAsFunction(); | |
9026 | } | |
9027 | ||
9028 | std::string extractClassName( std::string const& classOrQualifiedMethodName ) { | |
9029 | std::string className = classOrQualifiedMethodName; | |
9030 | if( startsWith( className, '&' ) ) | |
9031 | { | |
9032 | std::size_t lastColons = className.rfind( "::" ); | |
9033 | std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); | |
9034 | if( penultimateColons == std::string::npos ) | |
9035 | penultimateColons = 1; | |
9036 | className = className.substr( penultimateColons, lastColons-penultimateColons ); | |
9037 | } | |
9038 | return className; | |
9039 | } | |
9040 | ||
9041 | } // end namespace Catch | |
9042 | // end catch_test_case_registry_impl.cpp | |
9043 | // start catch_test_case_tracker.cpp | |
9044 | ||
9045 | #include <algorithm> | |
9046 | #include <assert.h> | |
9047 | #include <stdexcept> | |
9048 | #include <memory> | |
9049 | ||
9050 | #if defined(__clang__) | |
9051 | # pragma clang diagnostic push | |
9052 | # pragma clang diagnostic ignored "-Wexit-time-destructors" | |
9053 | #endif | |
9054 | ||
9055 | namespace Catch { | |
9056 | namespace TestCaseTracking { | |
9057 | ||
9058 | NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) | |
9059 | : name( _name ), | |
9060 | location( _location ) | |
9061 | {} | |
9062 | ||
9063 | ITracker::~ITracker() = default; | |
9064 | ||
9065 | TrackerContext& TrackerContext::instance() { | |
9066 | static TrackerContext s_instance; | |
9067 | return s_instance; | |
9068 | } | |
9069 | ||
9070 | ITracker& TrackerContext::startRun() { | |
9071 | m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); | |
9072 | m_currentTracker = nullptr; | |
9073 | m_runState = Executing; | |
9074 | return *m_rootTracker; | |
9075 | } | |
9076 | ||
9077 | void TrackerContext::endRun() { | |
9078 | m_rootTracker.reset(); | |
9079 | m_currentTracker = nullptr; | |
9080 | m_runState = NotStarted; | |
9081 | } | |
9082 | ||
9083 | void TrackerContext::startCycle() { | |
9084 | m_currentTracker = m_rootTracker.get(); | |
9085 | m_runState = Executing; | |
9086 | } | |
9087 | void TrackerContext::completeCycle() { | |
9088 | m_runState = CompletedCycle; | |
9089 | } | |
9090 | ||
9091 | bool TrackerContext::completedCycle() const { | |
9092 | return m_runState == CompletedCycle; | |
9093 | } | |
9094 | ITracker& TrackerContext::currentTracker() { | |
9095 | return *m_currentTracker; | |
9096 | } | |
9097 | void TrackerContext::setCurrentTracker( ITracker* tracker ) { | |
9098 | m_currentTracker = tracker; | |
9099 | } | |
9100 | ||
9101 | TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} | |
9102 | bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { | |
9103 | return | |
9104 | tracker->nameAndLocation().name == m_nameAndLocation.name && | |
9105 | tracker->nameAndLocation().location == m_nameAndLocation.location; | |
9106 | } | |
9107 | ||
9108 | TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) | |
9109 | : m_nameAndLocation( nameAndLocation ), | |
9110 | m_ctx( ctx ), | |
9111 | m_parent( parent ) | |
9112 | {} | |
9113 | ||
9114 | NameAndLocation const& TrackerBase::nameAndLocation() const { | |
9115 | return m_nameAndLocation; | |
9116 | } | |
9117 | bool TrackerBase::isComplete() const { | |
9118 | return m_runState == CompletedSuccessfully || m_runState == Failed; | |
9119 | } | |
9120 | bool TrackerBase::isSuccessfullyCompleted() const { | |
9121 | return m_runState == CompletedSuccessfully; | |
9122 | } | |
9123 | bool TrackerBase::isOpen() const { | |
9124 | return m_runState != NotStarted && !isComplete(); | |
9125 | } | |
9126 | bool TrackerBase::hasChildren() const { | |
9127 | return !m_children.empty(); | |
9128 | } | |
9129 | ||
9130 | void TrackerBase::addChild( ITrackerPtr const& child ) { | |
9131 | m_children.push_back( child ); | |
9132 | } | |
9133 | ||
9134 | ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { | |
9135 | auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); | |
9136 | return( it != m_children.end() ) | |
9137 | ? *it | |
9138 | : nullptr; | |
9139 | } | |
9140 | ITracker& TrackerBase::parent() { | |
9141 | assert( m_parent ); // Should always be non-null except for root | |
9142 | return *m_parent; | |
9143 | } | |
9144 | ||
9145 | void TrackerBase::openChild() { | |
9146 | if( m_runState != ExecutingChildren ) { | |
9147 | m_runState = ExecutingChildren; | |
9148 | if( m_parent ) | |
9149 | m_parent->openChild(); | |
9150 | } | |
9151 | } | |
9152 | ||
9153 | bool TrackerBase::isSectionTracker() const { return false; } | |
9154 | bool TrackerBase::isIndexTracker() const { return false; } | |
9155 | ||
9156 | void TrackerBase::open() { | |
9157 | m_runState = Executing; | |
9158 | moveToThis(); | |
9159 | if( m_parent ) | |
9160 | m_parent->openChild(); | |
9161 | } | |
9162 | ||
9163 | void TrackerBase::close() { | |
9164 | ||
9165 | // Close any still open children (e.g. generators) | |
9166 | while( &m_ctx.currentTracker() != this ) | |
9167 | m_ctx.currentTracker().close(); | |
9168 | ||
9169 | switch( m_runState ) { | |
9170 | case NeedsAnotherRun: | |
9171 | break; | |
9172 | ||
9173 | case Executing: | |
9174 | m_runState = CompletedSuccessfully; | |
9175 | break; | |
9176 | case ExecutingChildren: | |
9177 | if( m_children.empty() || m_children.back()->isComplete() ) | |
9178 | m_runState = CompletedSuccessfully; | |
9179 | break; | |
9180 | ||
9181 | case NotStarted: | |
9182 | case CompletedSuccessfully: | |
9183 | case Failed: | |
9184 | CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); | |
9185 | ||
9186 | default: | |
9187 | CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); | |
9188 | } | |
9189 | moveToParent(); | |
9190 | m_ctx.completeCycle(); | |
9191 | } | |
9192 | void TrackerBase::fail() { | |
9193 | m_runState = Failed; | |
9194 | if( m_parent ) | |
9195 | m_parent->markAsNeedingAnotherRun(); | |
9196 | moveToParent(); | |
9197 | m_ctx.completeCycle(); | |
9198 | } | |
9199 | void TrackerBase::markAsNeedingAnotherRun() { | |
9200 | m_runState = NeedsAnotherRun; | |
9201 | } | |
9202 | ||
9203 | void TrackerBase::moveToParent() { | |
9204 | assert( m_parent ); | |
9205 | m_ctx.setCurrentTracker( m_parent ); | |
9206 | } | |
9207 | void TrackerBase::moveToThis() { | |
9208 | m_ctx.setCurrentTracker( this ); | |
9209 | } | |
9210 | ||
9211 | SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) | |
9212 | : TrackerBase( nameAndLocation, ctx, parent ) | |
9213 | { | |
9214 | if( parent ) { | |
9215 | while( !parent->isSectionTracker() ) | |
9216 | parent = &parent->parent(); | |
9217 | ||
9218 | SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); | |
9219 | addNextFilters( parentSection.m_filters ); | |
9220 | } | |
9221 | } | |
9222 | ||
9223 | bool SectionTracker::isSectionTracker() const { return true; } | |
9224 | ||
9225 | SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { | |
9226 | std::shared_ptr<SectionTracker> section; | |
9227 | ||
9228 | ITracker& currentTracker = ctx.currentTracker(); | |
9229 | if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { | |
9230 | assert( childTracker ); | |
9231 | assert( childTracker->isSectionTracker() ); | |
9232 | section = std::static_pointer_cast<SectionTracker>( childTracker ); | |
9233 | } | |
9234 | else { | |
9235 | section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker ); | |
9236 | currentTracker.addChild( section ); | |
9237 | } | |
9238 | if( !ctx.completedCycle() ) | |
9239 | section->tryOpen(); | |
9240 | return *section; | |
9241 | } | |
9242 | ||
9243 | void SectionTracker::tryOpen() { | |
9244 | if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) | |
9245 | open(); | |
9246 | } | |
9247 | ||
9248 | void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { | |
9249 | if( !filters.empty() ) { | |
9250 | m_filters.push_back(""); // Root - should never be consulted | |
9251 | m_filters.push_back(""); // Test Case - not a section filter | |
9252 | m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); | |
9253 | } | |
9254 | } | |
9255 | void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { | |
9256 | if( filters.size() > 1 ) | |
9257 | m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); | |
9258 | } | |
9259 | ||
9260 | IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) | |
9261 | : TrackerBase( nameAndLocation, ctx, parent ), | |
9262 | m_size( size ) | |
9263 | {} | |
9264 | ||
9265 | bool IndexTracker::isIndexTracker() const { return true; } | |
9266 | ||
9267 | IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { | |
9268 | std::shared_ptr<IndexTracker> tracker; | |
9269 | ||
9270 | ITracker& currentTracker = ctx.currentTracker(); | |
9271 | if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { | |
9272 | assert( childTracker ); | |
9273 | assert( childTracker->isIndexTracker() ); | |
9274 | tracker = std::static_pointer_cast<IndexTracker>( childTracker ); | |
9275 | } | |
9276 | else { | |
9277 | tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, ¤tTracker, size ); | |
9278 | currentTracker.addChild( tracker ); | |
9279 | } | |
9280 | ||
9281 | if( !ctx.completedCycle() && !tracker->isComplete() ) { | |
9282 | if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) | |
9283 | tracker->moveNext(); | |
9284 | tracker->open(); | |
9285 | } | |
9286 | ||
9287 | return *tracker; | |
9288 | } | |
9289 | ||
9290 | int IndexTracker::index() const { return m_index; } | |
9291 | ||
9292 | void IndexTracker::moveNext() { | |
9293 | m_index++; | |
9294 | m_children.clear(); | |
9295 | } | |
9296 | ||
9297 | void IndexTracker::close() { | |
9298 | TrackerBase::close(); | |
9299 | if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) | |
9300 | m_runState = Executing; | |
9301 | } | |
9302 | ||
9303 | } // namespace TestCaseTracking | |
9304 | ||
9305 | using TestCaseTracking::ITracker; | |
9306 | using TestCaseTracking::TrackerContext; | |
9307 | using TestCaseTracking::SectionTracker; | |
9308 | using TestCaseTracking::IndexTracker; | |
9309 | ||
9310 | } // namespace Catch | |
9311 | ||
9312 | #if defined(__clang__) | |
9313 | # pragma clang diagnostic pop | |
9314 | #endif | |
9315 | // end catch_test_case_tracker.cpp | |
9316 | // start catch_test_registry.cpp | |
9317 | ||
9318 | namespace Catch { | |
9319 | ||
9320 | auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { | |
9321 | return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); | |
9322 | } | |
9323 | ||
9324 | NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} | |
9325 | ||
9326 | AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { | |
9327 | try { | |
9328 | getMutableRegistryHub() | |
9329 | .registerTest( | |
9330 | makeTestCase( | |
9331 | invoker, | |
9332 | extractClassName( classOrMethod ), | |
9333 | nameAndTags.name, | |
9334 | nameAndTags.tags, | |
9335 | lineInfo)); | |
9336 | } catch (...) { | |
9337 | // Do not throw when constructing global objects, instead register the exception to be processed later | |
9338 | getMutableRegistryHub().registerStartupException(); | |
9339 | } | |
9340 | } | |
9341 | ||
9342 | AutoReg::~AutoReg() = default; | |
9343 | } | |
9344 | // end catch_test_registry.cpp | |
9345 | // start catch_test_spec.cpp | |
9346 | ||
9347 | #include <algorithm> | |
9348 | #include <string> | |
9349 | #include <vector> | |
9350 | #include <memory> | |
9351 | ||
9352 | namespace Catch { | |
9353 | ||
9354 | TestSpec::Pattern::~Pattern() = default; | |
9355 | TestSpec::NamePattern::~NamePattern() = default; | |
9356 | TestSpec::TagPattern::~TagPattern() = default; | |
9357 | TestSpec::ExcludedPattern::~ExcludedPattern() = default; | |
9358 | ||
9359 | TestSpec::NamePattern::NamePattern( std::string const& name ) | |
9360 | : m_wildcardPattern( toLower( name ), CaseSensitive::No ) | |
9361 | {} | |
9362 | bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { | |
9363 | return m_wildcardPattern.matches( toLower( testCase.name ) ); | |
9364 | } | |
9365 | ||
9366 | TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} | |
9367 | bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { | |
9368 | return std::find(begin(testCase.lcaseTags), | |
9369 | end(testCase.lcaseTags), | |
9370 | m_tag) != end(testCase.lcaseTags); | |
9371 | } | |
9372 | ||
9373 | TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} | |
9374 | bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } | |
9375 | ||
9376 | bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { | |
9377 | // All patterns in a filter must match for the filter to be a match | |
9378 | for( auto const& pattern : m_patterns ) { | |
9379 | if( !pattern->matches( testCase ) ) | |
9380 | return false; | |
9381 | } | |
9382 | return true; | |
9383 | } | |
9384 | ||
9385 | bool TestSpec::hasFilters() const { | |
9386 | return !m_filters.empty(); | |
9387 | } | |
9388 | bool TestSpec::matches( TestCaseInfo const& testCase ) const { | |
9389 | // A TestSpec matches if any filter matches | |
9390 | for( auto const& filter : m_filters ) | |
9391 | if( filter.matches( testCase ) ) | |
9392 | return true; | |
9393 | return false; | |
9394 | } | |
9395 | } | |
9396 | // end catch_test_spec.cpp | |
9397 | // start catch_test_spec_parser.cpp | |
9398 | ||
9399 | namespace Catch { | |
9400 | ||
9401 | TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} | |
9402 | ||
9403 | TestSpecParser& TestSpecParser::parse( std::string const& arg ) { | |
9404 | m_mode = None; | |
9405 | m_exclusion = false; | |
9406 | m_start = std::string::npos; | |
9407 | m_arg = m_tagAliases->expandAliases( arg ); | |
9408 | m_escapeChars.clear(); | |
9409 | for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) | |
9410 | visitChar( m_arg[m_pos] ); | |
9411 | if( m_mode == Name ) | |
9412 | addPattern<TestSpec::NamePattern>(); | |
9413 | return *this; | |
9414 | } | |
9415 | TestSpec TestSpecParser::testSpec() { | |
9416 | addFilter(); | |
9417 | return m_testSpec; | |
9418 | } | |
9419 | ||
9420 | void TestSpecParser::visitChar( char c ) { | |
9421 | if( m_mode == None ) { | |
9422 | switch( c ) { | |
9423 | case ' ': return; | |
9424 | case '~': m_exclusion = true; return; | |
9425 | case '[': return startNewMode( Tag, ++m_pos ); | |
9426 | case '"': return startNewMode( QuotedName, ++m_pos ); | |
9427 | case '\\': return escape(); | |
9428 | default: startNewMode( Name, m_pos ); break; | |
9429 | } | |
9430 | } | |
9431 | if( m_mode == Name ) { | |
9432 | if( c == ',' ) { | |
9433 | addPattern<TestSpec::NamePattern>(); | |
9434 | addFilter(); | |
9435 | } | |
9436 | else if( c == '[' ) { | |
9437 | if( subString() == "exclude:" ) | |
9438 | m_exclusion = true; | |
9439 | else | |
9440 | addPattern<TestSpec::NamePattern>(); | |
9441 | startNewMode( Tag, ++m_pos ); | |
9442 | } | |
9443 | else if( c == '\\' ) | |
9444 | escape(); | |
9445 | } | |
9446 | else if( m_mode == EscapedName ) | |
9447 | m_mode = Name; | |
9448 | else if( m_mode == QuotedName && c == '"' ) | |
9449 | addPattern<TestSpec::NamePattern>(); | |
9450 | else if( m_mode == Tag && c == ']' ) | |
9451 | addPattern<TestSpec::TagPattern>(); | |
9452 | } | |
9453 | void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { | |
9454 | m_mode = mode; | |
9455 | m_start = start; | |
9456 | } | |
9457 | void TestSpecParser::escape() { | |
9458 | if( m_mode == None ) | |
9459 | m_start = m_pos; | |
9460 | m_mode = EscapedName; | |
9461 | m_escapeChars.push_back( m_pos ); | |
9462 | } | |
9463 | std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } | |
9464 | ||
9465 | void TestSpecParser::addFilter() { | |
9466 | if( !m_currentFilter.m_patterns.empty() ) { | |
9467 | m_testSpec.m_filters.push_back( m_currentFilter ); | |
9468 | m_currentFilter = TestSpec::Filter(); | |
9469 | } | |
9470 | } | |
9471 | ||
9472 | TestSpec parseTestSpec( std::string const& arg ) { | |
9473 | return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); | |
9474 | } | |
9475 | ||
9476 | } // namespace Catch | |
9477 | // end catch_test_spec_parser.cpp | |
9478 | // start catch_timer.cpp | |
9479 | ||
9480 | #include <chrono> | |
9481 | ||
9482 | namespace Catch { | |
9483 | ||
9484 | auto getCurrentNanosecondsSinceEpoch() -> uint64_t { | |
9485 | return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); | |
9486 | } | |
9487 | ||
9488 | auto estimateClockResolution() -> uint64_t { | |
9489 | uint64_t sum = 0; | |
9490 | static const uint64_t iterations = 1000000; | |
9491 | ||
9492 | for( std::size_t i = 0; i < iterations; ++i ) { | |
9493 | ||
9494 | uint64_t ticks; | |
9495 | uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); | |
9496 | do { | |
9497 | ticks = getCurrentNanosecondsSinceEpoch(); | |
9498 | } | |
9499 | while( ticks == baseTicks ); | |
9500 | ||
9501 | auto delta = ticks - baseTicks; | |
9502 | sum += delta; | |
9503 | } | |
9504 | ||
9505 | // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers | |
9506 | // - and potentially do more iterations if there's a high variance. | |
9507 | return sum/iterations; | |
9508 | } | |
9509 | auto getEstimatedClockResolution() -> uint64_t { | |
9510 | static auto s_resolution = estimateClockResolution(); | |
9511 | return s_resolution; | |
9512 | } | |
9513 | ||
9514 | void Timer::start() { | |
9515 | m_nanoseconds = getCurrentNanosecondsSinceEpoch(); | |
9516 | } | |
9517 | auto Timer::getElapsedNanoseconds() const -> unsigned int { | |
9518 | return static_cast<unsigned int>(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); | |
9519 | } | |
9520 | auto Timer::getElapsedMicroseconds() const -> unsigned int { | |
9521 | return static_cast<unsigned int>(getElapsedNanoseconds()/1000); | |
9522 | } | |
9523 | auto Timer::getElapsedMilliseconds() const -> unsigned int { | |
9524 | return static_cast<unsigned int>(getElapsedMicroseconds()/1000); | |
9525 | } | |
9526 | auto Timer::getElapsedSeconds() const -> double { | |
9527 | return getElapsedMicroseconds()/1000000.0; | |
9528 | } | |
9529 | ||
9530 | } // namespace Catch | |
9531 | // end catch_timer.cpp | |
9532 | // start catch_tostring.cpp | |
9533 | ||
9534 | #if defined(__clang__) | |
9535 | # pragma clang diagnostic push | |
9536 | # pragma clang diagnostic ignored "-Wexit-time-destructors" | |
9537 | # pragma clang diagnostic ignored "-Wglobal-constructors" | |
9538 | #endif | |
9539 | ||
9540 | #include <iomanip> | |
9541 | ||
9542 | namespace Catch { | |
9543 | ||
9544 | namespace Detail { | |
9545 | ||
9546 | const std::string unprintableString = "{?}"; | |
9547 | ||
9548 | namespace { | |
9549 | const int hexThreshold = 255; | |
9550 | ||
9551 | struct Endianness { | |
9552 | enum Arch { Big, Little }; | |
9553 | ||
9554 | static Arch which() { | |
9555 | union _{ | |
9556 | int asInt; | |
9557 | char asChar[sizeof (int)]; | |
9558 | } u; | |
9559 | ||
9560 | u.asInt = 1; | |
9561 | return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; | |
9562 | } | |
9563 | }; | |
9564 | } | |
9565 | ||
9566 | std::string rawMemoryToString( const void *object, std::size_t size ) { | |
9567 | // Reverse order for little endian architectures | |
9568 | int i = 0, end = static_cast<int>( size ), inc = 1; | |
9569 | if( Endianness::which() == Endianness::Little ) { | |
9570 | i = end-1; | |
9571 | end = inc = -1; | |
9572 | } | |
9573 | ||
9574 | unsigned char const *bytes = static_cast<unsigned char const *>(object); | |
9575 | std::ostringstream os; | |
9576 | os << "0x" << std::setfill('0') << std::hex; | |
9577 | for( ; i != end; i += inc ) | |
9578 | os << std::setw(2) << static_cast<unsigned>(bytes[i]); | |
9579 | return os.str(); | |
9580 | } | |
9581 | } | |
9582 | ||
9583 | template<typename T> | |
9584 | std::string fpToString( T value, int precision ) { | |
9585 | std::ostringstream oss; | |
9586 | oss << std::setprecision( precision ) | |
9587 | << std::fixed | |
9588 | << value; | |
9589 | std::string d = oss.str(); | |
9590 | std::size_t i = d.find_last_not_of( '0' ); | |
9591 | if( i != std::string::npos && i != d.size()-1 ) { | |
9592 | if( d[i] == '.' ) | |
9593 | i++; | |
9594 | d = d.substr( 0, i+1 ); | |
9595 | } | |
9596 | return d; | |
9597 | } | |
9598 | ||
9599 | //// ======================================================= //// | |
9600 | // | |
9601 | // Out-of-line defs for full specialization of StringMaker | |
9602 | // | |
9603 | //// ======================================================= //// | |
9604 | ||
9605 | std::string StringMaker<std::string>::convert(const std::string& str) { | |
9606 | if (!getCurrentContext().getConfig()->showInvisibles()) { | |
9607 | return '"' + str + '"'; | |
9608 | } | |
9609 | ||
9610 | std::string s("\""); | |
9611 | for (char c : str) { | |
9612 | switch (c) { | |
9613 | case '\n': | |
9614 | s.append("\\n"); | |
9615 | break; | |
9616 | case '\t': | |
9617 | s.append("\\t"); | |
9618 | break; | |
9619 | default: | |
9620 | s.push_back(c); | |
9621 | break; | |
9622 | } | |
9623 | } | |
9624 | s.append("\""); | |
9625 | return s; | |
9626 | } | |
9627 | ||
9628 | std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { | |
9629 | std::string s; | |
9630 | s.reserve(wstr.size()); | |
9631 | for (auto c : wstr) { | |
9632 | s += (c <= 0xff) ? static_cast<char>(c) : '?'; | |
9633 | } | |
9634 | return ::Catch::Detail::stringify(s); | |
9635 | } | |
9636 | ||
9637 | std::string StringMaker<char const*>::convert(char const* str) { | |
9638 | if (str) { | |
9639 | return ::Catch::Detail::stringify(std::string{ str }); | |
9640 | } else { | |
9641 | return{ "{null string}" }; | |
9642 | } | |
9643 | } | |
9644 | std::string StringMaker<char*>::convert(char* str) { | |
9645 | if (str) { | |
9646 | return ::Catch::Detail::stringify(std::string{ str }); | |
9647 | } else { | |
9648 | return{ "{null string}" }; | |
9649 | } | |
9650 | } | |
9651 | std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { | |
9652 | if (str) { | |
9653 | return ::Catch::Detail::stringify(std::wstring{ str }); | |
9654 | } else { | |
9655 | return{ "{null string}" }; | |
9656 | } | |
9657 | } | |
9658 | std::string StringMaker<wchar_t *>::convert(wchar_t * str) { | |
9659 | if (str) { | |
9660 | return ::Catch::Detail::stringify(std::wstring{ str }); | |
9661 | } else { | |
9662 | return{ "{null string}" }; | |
9663 | } | |
9664 | } | |
9665 | ||
9666 | std::string StringMaker<int>::convert(int value) { | |
9667 | return ::Catch::Detail::stringify(static_cast<long long>(value)); | |
9668 | } | |
9669 | std::string StringMaker<long>::convert(long value) { | |
9670 | return ::Catch::Detail::stringify(static_cast<long long>(value)); | |
9671 | } | |
9672 | std::string StringMaker<long long>::convert(long long value) { | |
9673 | std::ostringstream oss; | |
9674 | oss << value; | |
9675 | if (value > Detail::hexThreshold) { | |
9676 | oss << " (0x" << std::hex << value << ')'; | |
9677 | } | |
9678 | return oss.str(); | |
9679 | } | |
9680 | ||
9681 | std::string StringMaker<unsigned int>::convert(unsigned int value) { | |
9682 | return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); | |
9683 | } | |
9684 | std::string StringMaker<unsigned long>::convert(unsigned long value) { | |
9685 | return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); | |
9686 | } | |
9687 | std::string StringMaker<unsigned long long>::convert(unsigned long long value) { | |
9688 | std::ostringstream oss; | |
9689 | oss << value; | |
9690 | if (value > Detail::hexThreshold) { | |
9691 | oss << " (0x" << std::hex << value << ')'; | |
9692 | } | |
9693 | return oss.str(); | |
9694 | } | |
9695 | ||
9696 | std::string StringMaker<bool>::convert(bool b) { | |
9697 | return b ? "true" : "false"; | |
9698 | } | |
9699 | ||
9700 | std::string StringMaker<char>::convert(char value) { | |
9701 | if (value == '\r') { | |
9702 | return "'\\r'"; | |
9703 | } else if (value == '\f') { | |
9704 | return "'\\f'"; | |
9705 | } else if (value == '\n') { | |
9706 | return "'\\n'"; | |
9707 | } else if (value == '\t') { | |
9708 | return "'\\t'"; | |
9709 | } else if ('\0' <= value && value < ' ') { | |
9710 | return ::Catch::Detail::stringify(static_cast<unsigned int>(value)); | |
9711 | } else { | |
9712 | char chstr[] = "' '"; | |
9713 | chstr[1] = value; | |
9714 | return chstr; | |
9715 | } | |
9716 | } | |
9717 | std::string StringMaker<signed char>::convert(signed char c) { | |
9718 | return ::Catch::Detail::stringify(static_cast<char>(c)); | |
9719 | } | |
9720 | std::string StringMaker<unsigned char>::convert(unsigned char c) { | |
9721 | return ::Catch::Detail::stringify(static_cast<char>(c)); | |
9722 | } | |
9723 | ||
9724 | std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) { | |
9725 | return "nullptr"; | |
9726 | } | |
9727 | ||
9728 | std::string StringMaker<float>::convert(float value) { | |
9729 | return fpToString(value, 5) + 'f'; | |
9730 | } | |
9731 | std::string StringMaker<double>::convert(double value) { | |
9732 | return fpToString(value, 10); | |
9733 | } | |
9734 | ||
9735 | } // end namespace Catch | |
9736 | ||
9737 | #if defined(__clang__) | |
9738 | # pragma clang diagnostic pop | |
9739 | #endif | |
9740 | ||
9741 | // end catch_tostring.cpp | |
9742 | // start catch_totals.cpp | |
9743 | ||
9744 | namespace Catch { | |
9745 | ||
9746 | Counts Counts::operator - ( Counts const& other ) const { | |
9747 | Counts diff; | |
9748 | diff.passed = passed - other.passed; | |
9749 | diff.failed = failed - other.failed; | |
9750 | diff.failedButOk = failedButOk - other.failedButOk; | |
9751 | return diff; | |
9752 | } | |
9753 | ||
9754 | Counts& Counts::operator += ( Counts const& other ) { | |
9755 | passed += other.passed; | |
9756 | failed += other.failed; | |
9757 | failedButOk += other.failedButOk; | |
9758 | return *this; | |
9759 | } | |
9760 | ||
9761 | std::size_t Counts::total() const { | |
9762 | return passed + failed + failedButOk; | |
9763 | } | |
9764 | bool Counts::allPassed() const { | |
9765 | return failed == 0 && failedButOk == 0; | |
9766 | } | |
9767 | bool Counts::allOk() const { | |
9768 | return failed == 0; | |
9769 | } | |
9770 | ||
9771 | Totals Totals::operator - ( Totals const& other ) const { | |
9772 | Totals diff; | |
9773 | diff.assertions = assertions - other.assertions; | |
9774 | diff.testCases = testCases - other.testCases; | |
9775 | return diff; | |
9776 | } | |
9777 | ||
9778 | Totals& Totals::operator += ( Totals const& other ) { | |
9779 | assertions += other.assertions; | |
9780 | testCases += other.testCases; | |
9781 | return *this; | |
9782 | } | |
9783 | ||
9784 | Totals Totals::delta( Totals const& prevTotals ) const { | |
9785 | Totals diff = *this - prevTotals; | |
9786 | if( diff.assertions.failed > 0 ) | |
9787 | ++diff.testCases.failed; | |
9788 | else if( diff.assertions.failedButOk > 0 ) | |
9789 | ++diff.testCases.failedButOk; | |
9790 | else | |
9791 | ++diff.testCases.passed; | |
9792 | return diff; | |
9793 | } | |
9794 | ||
9795 | } | |
9796 | // end catch_totals.cpp | |
9797 | // start catch_version.cpp | |
9798 | ||
9799 | #include <ostream> | |
9800 | ||
9801 | namespace Catch { | |
9802 | ||
9803 | Version::Version | |
9804 | ( unsigned int _majorVersion, | |
9805 | unsigned int _minorVersion, | |
9806 | unsigned int _patchNumber, | |
9807 | char const * const _branchName, | |
9808 | unsigned int _buildNumber ) | |
9809 | : majorVersion( _majorVersion ), | |
9810 | minorVersion( _minorVersion ), | |
9811 | patchNumber( _patchNumber ), | |
9812 | branchName( _branchName ), | |
9813 | buildNumber( _buildNumber ) | |
9814 | {} | |
9815 | ||
9816 | std::ostream& operator << ( std::ostream& os, Version const& version ) { | |
9817 | os << version.majorVersion << '.' | |
9818 | << version.minorVersion << '.' | |
9819 | << version.patchNumber; | |
9820 | // branchName is never null -> 0th char is \0 if it is empty | |
9821 | if (version.branchName[0]) { | |
9822 | os << '-' << version.branchName | |
9823 | << '.' << version.buildNumber; | |
9824 | } | |
9825 | return os; | |
9826 | } | |
9827 | ||
9828 | Version const& libraryVersion() { | |
9829 | static Version version( 2, 0, 1, "", 0 ); | |
9830 | return version; | |
9831 | } | |
9832 | ||
9833 | } | |
9834 | // end catch_version.cpp | |
9835 | // start catch_wildcard_pattern.cpp | |
9836 | ||
9837 | namespace Catch { | |
9838 | ||
9839 | WildcardPattern::WildcardPattern( std::string const& pattern, | |
9840 | CaseSensitive::Choice caseSensitivity ) | |
9841 | : m_caseSensitivity( caseSensitivity ), | |
9842 | m_pattern( adjustCase( pattern ) ) | |
9843 | { | |
9844 | if( startsWith( m_pattern, '*' ) ) { | |
9845 | m_pattern = m_pattern.substr( 1 ); | |
9846 | m_wildcard = WildcardAtStart; | |
9847 | } | |
9848 | if( endsWith( m_pattern, '*' ) ) { | |
9849 | m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); | |
9850 | m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); | |
9851 | } | |
9852 | } | |
9853 | ||
9854 | bool WildcardPattern::matches( std::string const& str ) const { | |
9855 | switch( m_wildcard ) { | |
9856 | case NoWildcard: | |
9857 | return m_pattern == adjustCase( str ); | |
9858 | case WildcardAtStart: | |
9859 | return endsWith( adjustCase( str ), m_pattern ); | |
9860 | case WildcardAtEnd: | |
9861 | return startsWith( adjustCase( str ), m_pattern ); | |
9862 | case WildcardAtBothEnds: | |
9863 | return contains( adjustCase( str ), m_pattern ); | |
9864 | default: | |
9865 | CATCH_INTERNAL_ERROR( "Unknown enum" ); | |
9866 | } | |
9867 | } | |
9868 | ||
9869 | std::string WildcardPattern::adjustCase( std::string const& str ) const { | |
9870 | return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; | |
9871 | } | |
9872 | } | |
9873 | // end catch_wildcard_pattern.cpp | |
9874 | // start catch_xmlwriter.cpp | |
9875 | ||
9876 | // start catch_xmlwriter.h | |
9877 | ||
9878 | #include <sstream> | |
9879 | #include <vector> | |
9880 | ||
9881 | namespace Catch { | |
9882 | ||
9883 | class XmlEncode { | |
9884 | public: | |
9885 | enum ForWhat { ForTextNodes, ForAttributes }; | |
9886 | ||
9887 | XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); | |
9888 | ||
9889 | void encodeTo( std::ostream& os ) const; | |
9890 | ||
9891 | friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); | |
9892 | ||
9893 | private: | |
9894 | std::string m_str; | |
9895 | ForWhat m_forWhat; | |
9896 | }; | |
9897 | ||
9898 | class XmlWriter { | |
9899 | public: | |
9900 | ||
9901 | class ScopedElement { | |
9902 | public: | |
9903 | ScopedElement( XmlWriter* writer ); | |
9904 | ||
9905 | ScopedElement( ScopedElement&& other ) noexcept; | |
9906 | ScopedElement& operator=( ScopedElement&& other ) noexcept; | |
9907 | ||
9908 | ~ScopedElement(); | |
9909 | ||
9910 | ScopedElement& writeText( std::string const& text, bool indent = true ); | |
9911 | ||
9912 | template<typename T> | |
9913 | ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { | |
9914 | m_writer->writeAttribute( name, attribute ); | |
9915 | return *this; | |
9916 | } | |
9917 | ||
9918 | private: | |
9919 | mutable XmlWriter* m_writer = nullptr; | |
9920 | }; | |
9921 | ||
9922 | XmlWriter( std::ostream& os = Catch::cout() ); | |
9923 | ~XmlWriter(); | |
9924 | ||
9925 | XmlWriter( XmlWriter const& ) = delete; | |
9926 | XmlWriter& operator=( XmlWriter const& ) = delete; | |
9927 | ||
9928 | XmlWriter& startElement( std::string const& name ); | |
9929 | ||
9930 | ScopedElement scopedElement( std::string const& name ); | |
9931 | ||
9932 | XmlWriter& endElement(); | |
9933 | ||
9934 | XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); | |
9935 | ||
9936 | XmlWriter& writeAttribute( std::string const& name, bool attribute ); | |
9937 | ||
9938 | template<typename T> | |
9939 | XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { | |
9940 | m_oss.clear(); | |
9941 | m_oss.str(std::string()); | |
9942 | m_oss << attribute; | |
9943 | return writeAttribute( name, m_oss.str() ); | |
9944 | } | |
9945 | ||
9946 | XmlWriter& writeText( std::string const& text, bool indent = true ); | |
9947 | ||
9948 | XmlWriter& writeComment( std::string const& text ); | |
9949 | ||
9950 | void writeStylesheetRef( std::string const& url ); | |
9951 | ||
9952 | XmlWriter& writeBlankLine(); | |
9953 | ||
9954 | void ensureTagClosed(); | |
9955 | ||
9956 | private: | |
9957 | ||
9958 | void writeDeclaration(); | |
9959 | ||
9960 | void newlineIfNecessary(); | |
9961 | ||
9962 | bool m_tagIsOpen = false; | |
9963 | bool m_needsNewline = false; | |
9964 | std::vector<std::string> m_tags; | |
9965 | std::string m_indent; | |
9966 | std::ostream& m_os; | |
9967 | std::ostringstream m_oss; | |
9968 | }; | |
9969 | ||
9970 | } | |
9971 | ||
9972 | // end catch_xmlwriter.h | |
9973 | #include <iomanip> | |
9974 | ||
9975 | namespace Catch { | |
9976 | ||
9977 | XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) | |
9978 | : m_str( str ), | |
9979 | m_forWhat( forWhat ) | |
9980 | {} | |
9981 | ||
9982 | void XmlEncode::encodeTo( std::ostream& os ) const { | |
9983 | ||
9984 | // Apostrophe escaping not necessary if we always use " to write attributes | |
9985 | // (see: http://www.w3.org/TR/xml/#syntax) | |
9986 | ||
9987 | for( std::size_t i = 0; i < m_str.size(); ++ i ) { | |
9988 | char c = m_str[i]; | |
9989 | switch( c ) { | |
9990 | case '<': os << "<"; break; | |
9991 | case '&': os << "&"; break; | |
9992 | ||
9993 | case '>': | |
9994 | // See: http://www.w3.org/TR/xml/#syntax | |
9995 | if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) | |
9996 | os << ">"; | |
9997 | else | |
9998 | os << c; | |
9999 | break; | |
10000 | ||
10001 | case '\"': | |
10002 | if( m_forWhat == ForAttributes ) | |
10003 | os << """; | |
10004 | else | |
10005 | os << c; | |
10006 | break; | |
10007 | ||
10008 | default: | |
10009 | // Escape control chars - based on contribution by @espenalb in PR #465 and | |
10010 | // by @mrpi PR #588 | |
10011 | if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { | |
10012 | // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 | |
10013 | os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) | |
10014 | << static_cast<int>( c ); | |
10015 | } | |
10016 | else | |
10017 | os << c; | |
10018 | } | |
10019 | } | |
10020 | } | |
10021 | ||
10022 | std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { | |
10023 | xmlEncode.encodeTo( os ); | |
10024 | return os; | |
10025 | } | |
10026 | ||
10027 | XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) | |
10028 | : m_writer( writer ) | |
10029 | {} | |
10030 | ||
10031 | XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept | |
10032 | : m_writer( other.m_writer ){ | |
10033 | other.m_writer = nullptr; | |
10034 | } | |
10035 | XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { | |
10036 | if ( m_writer ) { | |
10037 | m_writer->endElement(); | |
10038 | } | |
10039 | m_writer = other.m_writer; | |
10040 | other.m_writer = nullptr; | |
10041 | return *this; | |
10042 | } | |
10043 | ||
10044 | XmlWriter::ScopedElement::~ScopedElement() { | |
10045 | if( m_writer ) | |
10046 | m_writer->endElement(); | |
10047 | } | |
10048 | ||
10049 | XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { | |
10050 | m_writer->writeText( text, indent ); | |
10051 | return *this; | |
10052 | } | |
10053 | ||
10054 | XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) | |
10055 | { | |
10056 | writeDeclaration(); | |
10057 | } | |
10058 | ||
10059 | XmlWriter::~XmlWriter() { | |
10060 | while( !m_tags.empty() ) | |
10061 | endElement(); | |
10062 | } | |
10063 | ||
10064 | XmlWriter& XmlWriter::startElement( std::string const& name ) { | |
10065 | ensureTagClosed(); | |
10066 | newlineIfNecessary(); | |
10067 | m_os << m_indent << '<' << name; | |
10068 | m_tags.push_back( name ); | |
10069 | m_indent += " "; | |
10070 | m_tagIsOpen = true; | |
10071 | return *this; | |
10072 | } | |
10073 | ||
10074 | XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { | |
10075 | ScopedElement scoped( this ); | |
10076 | startElement( name ); | |
10077 | return scoped; | |
10078 | } | |
10079 | ||
10080 | XmlWriter& XmlWriter::endElement() { | |
10081 | newlineIfNecessary(); | |
10082 | m_indent = m_indent.substr( 0, m_indent.size()-2 ); | |
10083 | if( m_tagIsOpen ) { | |
10084 | m_os << "/>"; | |
10085 | m_tagIsOpen = false; | |
10086 | } | |
10087 | else { | |
10088 | m_os << m_indent << "</" << m_tags.back() << ">"; | |
10089 | } | |
10090 | m_os << std::endl; | |
10091 | m_tags.pop_back(); | |
10092 | return *this; | |
10093 | } | |
10094 | ||
10095 | XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { | |
10096 | if( !name.empty() && !attribute.empty() ) | |
10097 | m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; | |
10098 | return *this; | |
10099 | } | |
10100 | ||
10101 | XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { | |
10102 | m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; | |
10103 | return *this; | |
10104 | } | |
10105 | ||
10106 | XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { | |
10107 | if( !text.empty() ){ | |
10108 | bool tagWasOpen = m_tagIsOpen; | |
10109 | ensureTagClosed(); | |
10110 | if( tagWasOpen && indent ) | |
10111 | m_os << m_indent; | |
10112 | m_os << XmlEncode( text ); | |
10113 | m_needsNewline = true; | |
10114 | } | |
10115 | return *this; | |
10116 | } | |
10117 | ||
10118 | XmlWriter& XmlWriter::writeComment( std::string const& text ) { | |
10119 | ensureTagClosed(); | |
10120 | m_os << m_indent << "<!--" << text << "-->"; | |
10121 | m_needsNewline = true; | |
10122 | return *this; | |
10123 | } | |
10124 | ||
10125 | void XmlWriter::writeStylesheetRef( std::string const& url ) { | |
10126 | m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n"; | |
10127 | } | |
10128 | ||
10129 | XmlWriter& XmlWriter::writeBlankLine() { | |
10130 | ensureTagClosed(); | |
10131 | m_os << '\n'; | |
10132 | return *this; | |
10133 | } | |
10134 | ||
10135 | void XmlWriter::ensureTagClosed() { | |
10136 | if( m_tagIsOpen ) { | |
10137 | m_os << ">" << std::endl; | |
10138 | m_tagIsOpen = false; | |
10139 | } | |
10140 | } | |
10141 | ||
10142 | void XmlWriter::writeDeclaration() { | |
10143 | m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | |
10144 | } | |
10145 | ||
10146 | void XmlWriter::newlineIfNecessary() { | |
10147 | if( m_needsNewline ) { | |
10148 | m_os << std::endl; | |
10149 | m_needsNewline = false; | |
10150 | } | |
10151 | } | |
10152 | } | |
10153 | // end catch_xmlwriter.cpp | |
10154 | // start catch_reporter_bases.cpp | |
10155 | ||
10156 | #include <cstring> | |
10157 | #include <cfloat> | |
10158 | #include <cstdio> | |
10159 | #include <assert.h> | |
10160 | #include <memory> | |
10161 | ||
10162 | namespace Catch { | |
10163 | void prepareExpandedExpression(AssertionResult& result) { | |
10164 | result.getExpandedExpression(); | |
10165 | } | |
10166 | ||
10167 | // Because formatting using c++ streams is stateful, drop down to C is required | |
10168 | // Alternatively we could use stringstream, but its performance is... not good. | |
10169 | std::string getFormattedDuration( double duration ) { | |
10170 | // Max exponent + 1 is required to represent the whole part | |
10171 | // + 1 for decimal point | |
10172 | // + 3 for the 3 decimal places | |
10173 | // + 1 for null terminator | |
10174 | const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; | |
10175 | char buffer[maxDoubleSize]; | |
10176 | ||
10177 | // Save previous errno, to prevent sprintf from overwriting it | |
10178 | ErrnoGuard guard; | |
10179 | #ifdef _MSC_VER | |
10180 | sprintf_s(buffer, "%.3f", duration); | |
10181 | #else | |
10182 | sprintf(buffer, "%.3f", duration); | |
10183 | #endif | |
10184 | return std::string(buffer); | |
10185 | } | |
10186 | ||
10187 | TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) | |
10188 | :StreamingReporterBase(_config) {} | |
10189 | ||
10190 | void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} | |
10191 | ||
10192 | bool TestEventListenerBase::assertionEnded(AssertionStats const &) { | |
10193 | return false; | |
10194 | } | |
10195 | ||
10196 | } // end namespace Catch | |
10197 | // end catch_reporter_bases.cpp | |
10198 | // start catch_reporter_compact.cpp | |
10199 | ||
10200 | namespace { | |
10201 | ||
10202 | #ifdef CATCH_PLATFORM_MAC | |
10203 | const char* failedString() { return "FAILED"; } | |
10204 | const char* passedString() { return "PASSED"; } | |
10205 | #else | |
10206 | const char* failedString() { return "failed"; } | |
10207 | const char* passedString() { return "passed"; } | |
10208 | #endif | |
10209 | ||
10210 | // Colour::LightGrey | |
10211 | Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } | |
10212 | ||
10213 | std::string bothOrAll( std::size_t count ) { | |
10214 | return count == 1 ? std::string() : | |
10215 | count == 2 ? "both " : "all " ; | |
10216 | } | |
10217 | } | |
10218 | ||
10219 | namespace Catch { | |
10220 | ||
10221 | struct CompactReporter : StreamingReporterBase<CompactReporter> { | |
10222 | ||
10223 | using StreamingReporterBase::StreamingReporterBase; | |
10224 | ||
10225 | ~CompactReporter() override; | |
10226 | ||
10227 | static std::string getDescription() { | |
10228 | return "Reports test results on a single line, suitable for IDEs"; | |
10229 | } | |
10230 | ||
10231 | ReporterPreferences getPreferences() const override { | |
10232 | ReporterPreferences prefs; | |
10233 | prefs.shouldRedirectStdOut = false; | |
10234 | return prefs; | |
10235 | } | |
10236 | ||
10237 | void noMatchingTestCases( std::string const& spec ) override { | |
10238 | stream << "No test cases matched '" << spec << '\'' << std::endl; | |
10239 | } | |
10240 | ||
10241 | void assertionStarting( AssertionInfo const& ) override {} | |
10242 | ||
10243 | bool assertionEnded( AssertionStats const& _assertionStats ) override { | |
10244 | AssertionResult const& result = _assertionStats.assertionResult; | |
10245 | ||
10246 | bool printInfoMessages = true; | |
10247 | ||
10248 | // Drop out if result was successful and we're not printing those | |
10249 | if( !m_config->includeSuccessfulResults() && result.isOk() ) { | |
10250 | if( result.getResultType() != ResultWas::Warning ) | |
10251 | return false; | |
10252 | printInfoMessages = false; | |
10253 | } | |
10254 | ||
10255 | AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); | |
10256 | printer.print(); | |
10257 | ||
10258 | stream << std::endl; | |
10259 | return true; | |
10260 | } | |
10261 | ||
10262 | void sectionEnded(SectionStats const& _sectionStats) override { | |
10263 | if (m_config->showDurations() == ShowDurations::Always) { | |
10264 | stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; | |
10265 | } | |
10266 | } | |
10267 | ||
10268 | void testRunEnded( TestRunStats const& _testRunStats ) override { | |
10269 | printTotals( _testRunStats.totals ); | |
10270 | stream << '\n' << std::endl; | |
10271 | StreamingReporterBase::testRunEnded( _testRunStats ); | |
10272 | } | |
10273 | ||
10274 | private: | |
10275 | class AssertionPrinter { | |
10276 | public: | |
10277 | AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; | |
10278 | AssertionPrinter( AssertionPrinter const& ) = delete; | |
10279 | AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) | |
10280 | : stream( _stream ) | |
10281 | , result( _stats.assertionResult ) | |
10282 | , messages( _stats.infoMessages ) | |
10283 | , itMessage( _stats.infoMessages.begin() ) | |
10284 | , printInfoMessages( _printInfoMessages ) | |
10285 | {} | |
10286 | ||
10287 | void print() { | |
10288 | printSourceInfo(); | |
10289 | ||
10290 | itMessage = messages.begin(); | |
10291 | ||
10292 | switch( result.getResultType() ) { | |
10293 | case ResultWas::Ok: | |
10294 | printResultType( Colour::ResultSuccess, passedString() ); | |
10295 | printOriginalExpression(); | |
10296 | printReconstructedExpression(); | |
10297 | if ( ! result.hasExpression() ) | |
10298 | printRemainingMessages( Colour::None ); | |
10299 | else | |
10300 | printRemainingMessages(); | |
10301 | break; | |
10302 | case ResultWas::ExpressionFailed: | |
10303 | if( result.isOk() ) | |
10304 | printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); | |
10305 | else | |
10306 | printResultType( Colour::Error, failedString() ); | |
10307 | printOriginalExpression(); | |
10308 | printReconstructedExpression(); | |
10309 | printRemainingMessages(); | |
10310 | break; | |
10311 | case ResultWas::ThrewException: | |
10312 | printResultType( Colour::Error, failedString() ); | |
10313 | printIssue( "unexpected exception with message:" ); | |
10314 | printMessage(); | |
10315 | printExpressionWas(); | |
10316 | printRemainingMessages(); | |
10317 | break; | |
10318 | case ResultWas::FatalErrorCondition: | |
10319 | printResultType( Colour::Error, failedString() ); | |
10320 | printIssue( "fatal error condition with message:" ); | |
10321 | printMessage(); | |
10322 | printExpressionWas(); | |
10323 | printRemainingMessages(); | |
10324 | break; | |
10325 | case ResultWas::DidntThrowException: | |
10326 | printResultType( Colour::Error, failedString() ); | |
10327 | printIssue( "expected exception, got none" ); | |
10328 | printExpressionWas(); | |
10329 | printRemainingMessages(); | |
10330 | break; | |
10331 | case ResultWas::Info: | |
10332 | printResultType( Colour::None, "info" ); | |
10333 | printMessage(); | |
10334 | printRemainingMessages(); | |
10335 | break; | |
10336 | case ResultWas::Warning: | |
10337 | printResultType( Colour::None, "warning" ); | |
10338 | printMessage(); | |
10339 | printRemainingMessages(); | |
10340 | break; | |
10341 | case ResultWas::ExplicitFailure: | |
10342 | printResultType( Colour::Error, failedString() ); | |
10343 | printIssue( "explicitly" ); | |
10344 | printRemainingMessages( Colour::None ); | |
10345 | break; | |
10346 | // These cases are here to prevent compiler warnings | |
10347 | case ResultWas::Unknown: | |
10348 | case ResultWas::FailureBit: | |
10349 | case ResultWas::Exception: | |
10350 | printResultType( Colour::Error, "** internal error **" ); | |
10351 | break; | |
10352 | } | |
10353 | } | |
10354 | ||
10355 | private: | |
10356 | void printSourceInfo() const { | |
10357 | Colour colourGuard( Colour::FileName ); | |
10358 | stream << result.getSourceInfo() << ':'; | |
10359 | } | |
10360 | ||
10361 | void printResultType( Colour::Code colour, std::string const& passOrFail ) const { | |
10362 | if( !passOrFail.empty() ) { | |
10363 | { | |
10364 | Colour colourGuard( colour ); | |
10365 | stream << ' ' << passOrFail; | |
10366 | } | |
10367 | stream << ':'; | |
10368 | } | |
10369 | } | |
10370 | ||
10371 | void printIssue( std::string const& issue ) const { | |
10372 | stream << ' ' << issue; | |
10373 | } | |
10374 | ||
10375 | void printExpressionWas() { | |
10376 | if( result.hasExpression() ) { | |
10377 | stream << ';'; | |
10378 | { | |
10379 | Colour colour( dimColour() ); | |
10380 | stream << " expression was:"; | |
10381 | } | |
10382 | printOriginalExpression(); | |
10383 | } | |
10384 | } | |
10385 | ||
10386 | void printOriginalExpression() const { | |
10387 | if( result.hasExpression() ) { | |
10388 | stream << ' ' << result.getExpression(); | |
10389 | } | |
10390 | } | |
10391 | ||
10392 | void printReconstructedExpression() const { | |
10393 | if( result.hasExpandedExpression() ) { | |
10394 | { | |
10395 | Colour colour( dimColour() ); | |
10396 | stream << " for: "; | |
10397 | } | |
10398 | stream << result.getExpandedExpression(); | |
10399 | } | |
10400 | } | |
10401 | ||
10402 | void printMessage() { | |
10403 | if ( itMessage != messages.end() ) { | |
10404 | stream << " '" << itMessage->message << '\''; | |
10405 | ++itMessage; | |
10406 | } | |
10407 | } | |
10408 | ||
10409 | void printRemainingMessages( Colour::Code colour = dimColour() ) { | |
10410 | if ( itMessage == messages.end() ) | |
10411 | return; | |
10412 | ||
10413 | // using messages.end() directly yields (or auto) compilation error: | |
10414 | std::vector<MessageInfo>::const_iterator itEnd = messages.end(); | |
10415 | const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) ); | |
10416 | ||
10417 | { | |
10418 | Colour colourGuard( colour ); | |
10419 | stream << " with " << pluralise( N, "message" ) << ':'; | |
10420 | } | |
10421 | ||
10422 | for(; itMessage != itEnd; ) { | |
10423 | // If this assertion is a warning ignore any INFO messages | |
10424 | if( printInfoMessages || itMessage->type != ResultWas::Info ) { | |
10425 | stream << " '" << itMessage->message << '\''; | |
10426 | if ( ++itMessage != itEnd ) { | |
10427 | Colour colourGuard( dimColour() ); | |
10428 | stream << " and"; | |
10429 | } | |
10430 | } | |
10431 | } | |
10432 | } | |
10433 | ||
10434 | private: | |
10435 | std::ostream& stream; | |
10436 | AssertionResult const& result; | |
10437 | std::vector<MessageInfo> messages; | |
10438 | std::vector<MessageInfo>::const_iterator itMessage; | |
10439 | bool printInfoMessages; | |
10440 | }; | |
10441 | ||
10442 | // Colour, message variants: | |
10443 | // - white: No tests ran. | |
10444 | // - red: Failed [both/all] N test cases, failed [both/all] M assertions. | |
10445 | // - white: Passed [both/all] N test cases (no assertions). | |
10446 | // - red: Failed N tests cases, failed M assertions. | |
10447 | // - green: Passed [both/all] N tests cases with M assertions. | |
10448 | ||
10449 | void printTotals( const Totals& totals ) const { | |
10450 | if( totals.testCases.total() == 0 ) { | |
10451 | stream << "No tests ran."; | |
10452 | } | |
10453 | else if( totals.testCases.failed == totals.testCases.total() ) { | |
10454 | Colour colour( Colour::ResultError ); | |
10455 | const std::string qualify_assertions_failed = | |
10456 | totals.assertions.failed == totals.assertions.total() ? | |
10457 | bothOrAll( totals.assertions.failed ) : std::string(); | |
10458 | stream << | |
10459 | "Failed " << bothOrAll( totals.testCases.failed ) | |
10460 | << pluralise( totals.testCases.failed, "test case" ) << ", " | |
10461 | "failed " << qualify_assertions_failed << | |
10462 | pluralise( totals.assertions.failed, "assertion" ) << '.'; | |
10463 | } | |
10464 | else if( totals.assertions.total() == 0 ) { | |
10465 | stream << | |
10466 | "Passed " << bothOrAll( totals.testCases.total() ) | |
10467 | << pluralise( totals.testCases.total(), "test case" ) | |
10468 | << " (no assertions)."; | |
10469 | } | |
10470 | else if( totals.assertions.failed ) { | |
10471 | Colour colour( Colour::ResultError ); | |
10472 | stream << | |
10473 | "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " | |
10474 | "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; | |
10475 | } | |
10476 | else { | |
10477 | Colour colour( Colour::ResultSuccess ); | |
10478 | stream << | |
10479 | "Passed " << bothOrAll( totals.testCases.passed ) | |
10480 | << pluralise( totals.testCases.passed, "test case" ) << | |
10481 | " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; | |
10482 | } | |
10483 | } | |
10484 | }; | |
10485 | ||
10486 | CompactReporter::~CompactReporter() {} | |
10487 | ||
10488 | CATCH_REGISTER_REPORTER( "compact", CompactReporter ) | |
10489 | ||
10490 | } // end namespace Catch | |
10491 | // end catch_reporter_compact.cpp | |
10492 | // start catch_reporter_console.cpp | |
10493 | ||
10494 | #include <cfloat> | |
10495 | #include <cstdio> | |
10496 | ||
10497 | #if defined(_MSC_VER) | |
10498 | #pragma warning(push) | |
10499 | #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch | |
10500 | // Note that 4062 (not all labels are handled | |
10501 | // and default is missing) is enabled | |
10502 | #endif | |
10503 | ||
10504 | namespace Catch { | |
10505 | ||
10506 | namespace { | |
10507 | std::size_t makeRatio( std::size_t number, std::size_t total ) { | |
10508 | std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; | |
10509 | return ( ratio == 0 && number > 0 ) ? 1 : ratio; | |
10510 | } | |
10511 | ||
10512 | std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { | |
10513 | if( i > j && i > k ) | |
10514 | return i; | |
10515 | else if( j > k ) | |
10516 | return j; | |
10517 | else | |
10518 | return k; | |
10519 | } | |
10520 | ||
10521 | struct ColumnInfo { | |
10522 | enum Justification { Left, Right }; | |
10523 | std::string name; | |
10524 | int width; | |
10525 | Justification justification; | |
10526 | }; | |
10527 | struct ColumnBreak {}; | |
10528 | struct RowBreak {}; | |
10529 | ||
10530 | class TablePrinter { | |
10531 | std::ostream& m_os; | |
10532 | std::vector<ColumnInfo> m_columnInfos; | |
10533 | std::ostringstream m_oss; | |
10534 | int m_currentColumn = -1; | |
10535 | bool m_isOpen = false; | |
10536 | ||
10537 | public: | |
10538 | TablePrinter( std::ostream& os, std::vector<ColumnInfo> const& columnInfos ) | |
10539 | : m_os( os ), | |
10540 | m_columnInfos( columnInfos ) | |
10541 | {} | |
10542 | ||
10543 | auto columnInfos() const -> std::vector<ColumnInfo> const& { | |
10544 | return m_columnInfos; | |
10545 | } | |
10546 | ||
10547 | void open() { | |
10548 | if( !m_isOpen ) { | |
10549 | m_isOpen = true; | |
10550 | *this << RowBreak(); | |
10551 | for( auto const& info : m_columnInfos ) | |
10552 | *this << info.name << ColumnBreak(); | |
10553 | *this << RowBreak(); | |
10554 | m_os << Catch::getLineOfChars<'-'>() << "\n"; | |
10555 | } | |
10556 | } | |
10557 | void close() { | |
10558 | if( m_isOpen ) { | |
10559 | *this << RowBreak(); | |
10560 | m_os << std::endl; | |
10561 | m_isOpen = false; | |
10562 | } | |
10563 | } | |
10564 | ||
10565 | template<typename T> | |
10566 | friend TablePrinter& operator << ( TablePrinter& tp, T const& value ) { | |
10567 | tp.m_oss << value; | |
10568 | return tp; | |
10569 | } | |
10570 | ||
10571 | friend TablePrinter& operator << ( TablePrinter& tp, ColumnBreak ) { | |
10572 | auto colStr = tp.m_oss.str(); | |
10573 | // This takes account of utf8 encodings | |
10574 | auto strSize = Catch::StringRef( colStr ).numberOfCharacters(); | |
10575 | tp.m_oss.str(""); | |
10576 | tp.open(); | |
10577 | if( tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size()-1) ) { | |
10578 | tp.m_currentColumn = -1; | |
10579 | tp.m_os << "\n"; | |
10580 | } | |
10581 | tp.m_currentColumn++; | |
10582 | ||
10583 | auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; | |
10584 | auto padding = ( strSize+2 < static_cast<std::size_t>( colInfo.width ) ) | |
10585 | ? std::string( colInfo.width-(strSize+2), ' ' ) | |
10586 | : std::string(); | |
10587 | if( colInfo.justification == ColumnInfo::Left ) | |
10588 | tp.m_os << colStr << padding << " "; | |
10589 | else | |
10590 | tp.m_os << padding << colStr << " "; | |
10591 | return tp; | |
10592 | } | |
10593 | ||
10594 | friend TablePrinter& operator << ( TablePrinter& tp, RowBreak ) { | |
10595 | if( tp.m_currentColumn > 0 ) { | |
10596 | tp.m_os << "\n"; | |
10597 | tp.m_currentColumn = -1; | |
10598 | } | |
10599 | return tp; | |
10600 | } | |
10601 | }; | |
10602 | ||
10603 | class Duration { | |
10604 | enum class Unit { | |
10605 | Auto, | |
10606 | Nanoseconds, | |
10607 | Microseconds, | |
10608 | Milliseconds, | |
10609 | Seconds, | |
10610 | Minutes | |
10611 | }; | |
10612 | static const uint64_t s_nanosecondsInAMicrosecond = 1000; | |
10613 | static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond; | |
10614 | static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond; | |
10615 | static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond; | |
10616 | ||
10617 | uint64_t m_inNanoseconds; | |
10618 | Unit m_units; | |
10619 | ||
10620 | public: | |
10621 | Duration( uint64_t inNanoseconds, Unit units = Unit::Auto ) | |
10622 | : m_inNanoseconds( inNanoseconds ), | |
10623 | m_units( units ) | |
10624 | { | |
10625 | if( m_units == Unit::Auto ) { | |
10626 | if( m_inNanoseconds < s_nanosecondsInAMicrosecond ) | |
10627 | m_units = Unit::Nanoseconds; | |
10628 | else if( m_inNanoseconds < s_nanosecondsInAMillisecond ) | |
10629 | m_units = Unit::Microseconds; | |
10630 | else if( m_inNanoseconds < s_nanosecondsInASecond ) | |
10631 | m_units = Unit::Milliseconds; | |
10632 | else if( m_inNanoseconds < s_nanosecondsInAMinute ) | |
10633 | m_units = Unit::Seconds; | |
10634 | else | |
10635 | m_units = Unit::Minutes; | |
10636 | } | |
10637 | ||
10638 | } | |
10639 | ||
10640 | auto value() const -> double { | |
10641 | switch( m_units ) { | |
10642 | case Unit::Microseconds: | |
10643 | return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMicrosecond ); | |
10644 | case Unit::Milliseconds: | |
10645 | return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMillisecond ); | |
10646 | case Unit::Seconds: | |
10647 | return m_inNanoseconds / static_cast<double>( s_nanosecondsInASecond ); | |
10648 | case Unit::Minutes: | |
10649 | return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMinute ); | |
10650 | default: | |
10651 | return static_cast<double>( m_inNanoseconds ); | |
10652 | } | |
10653 | } | |
10654 | auto unitsAsString() const -> std::string { | |
10655 | switch( m_units ) { | |
10656 | case Unit::Nanoseconds: | |
10657 | return "ns"; | |
10658 | case Unit::Microseconds: | |
10659 | return "µs"; | |
10660 | case Unit::Milliseconds: | |
10661 | return "ms"; | |
10662 | case Unit::Seconds: | |
10663 | return "s"; | |
10664 | case Unit::Minutes: | |
10665 | return "m"; | |
10666 | default: | |
10667 | return "** internal error **"; | |
10668 | } | |
10669 | ||
10670 | } | |
10671 | friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& { | |
10672 | return os << duration.value() << " " << duration.unitsAsString(); | |
10673 | } | |
10674 | }; | |
10675 | } // end anon namespace | |
10676 | ||
10677 | struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> { | |
10678 | TablePrinter m_tablePrinter; | |
10679 | ||
10680 | ConsoleReporter( ReporterConfig const& config ) | |
10681 | : StreamingReporterBase( config ), | |
10682 | m_tablePrinter( config.stream(), | |
10683 | { | |
10684 | { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-32, ColumnInfo::Left }, | |
10685 | { "iters", 8, ColumnInfo::Right }, | |
10686 | { "elapsed ns", 14, ColumnInfo::Right }, | |
10687 | { "average", 14, ColumnInfo::Right } | |
10688 | } ) | |
10689 | {} | |
10690 | ~ConsoleReporter() override; | |
10691 | static std::string getDescription() { | |
10692 | return "Reports test results as plain lines of text"; | |
10693 | } | |
10694 | ||
10695 | void noMatchingTestCases( std::string const& spec ) override { | |
10696 | stream << "No test cases matched '" << spec << '\'' << std::endl; | |
10697 | } | |
10698 | ||
10699 | void assertionStarting( AssertionInfo const& ) override { | |
10700 | } | |
10701 | ||
10702 | bool assertionEnded( AssertionStats const& _assertionStats ) override { | |
10703 | AssertionResult const& result = _assertionStats.assertionResult; | |
10704 | ||
10705 | bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); | |
10706 | ||
10707 | // Drop out if result was successful but we're not printing them. | |
10708 | if( !includeResults && result.getResultType() != ResultWas::Warning ) | |
10709 | return false; | |
10710 | ||
10711 | lazyPrint(); | |
10712 | ||
10713 | AssertionPrinter printer( stream, _assertionStats, includeResults ); | |
10714 | printer.print(); | |
10715 | stream << std::endl; | |
10716 | return true; | |
10717 | } | |
10718 | ||
10719 | void sectionStarting( SectionInfo const& _sectionInfo ) override { | |
10720 | m_headerPrinted = false; | |
10721 | StreamingReporterBase::sectionStarting( _sectionInfo ); | |
10722 | } | |
10723 | void sectionEnded( SectionStats const& _sectionStats ) override { | |
10724 | m_tablePrinter.close(); | |
10725 | if( _sectionStats.missingAssertions ) { | |
10726 | lazyPrint(); | |
10727 | Colour colour( Colour::ResultError ); | |
10728 | if( m_sectionStack.size() > 1 ) | |
10729 | stream << "\nNo assertions in section"; | |
10730 | else | |
10731 | stream << "\nNo assertions in test case"; | |
10732 | stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; | |
10733 | } | |
10734 | if( m_config->showDurations() == ShowDurations::Always ) { | |
10735 | stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; | |
10736 | } | |
10737 | if( m_headerPrinted ) { | |
10738 | m_headerPrinted = false; | |
10739 | } | |
10740 | StreamingReporterBase::sectionEnded( _sectionStats ); | |
10741 | } | |
10742 | ||
10743 | void benchmarkStarting( BenchmarkInfo const& info ) override { | |
10744 | lazyPrintWithoutClosingBenchmarkTable(); | |
10745 | ||
10746 | auto nameCol = Column( info.name ).width( m_tablePrinter.columnInfos()[0].width-2 ); | |
10747 | ||
10748 | bool firstLine = true; | |
10749 | for( auto line : nameCol ) { | |
10750 | if( !firstLine ) | |
10751 | m_tablePrinter << ColumnBreak() << ColumnBreak() << ColumnBreak(); | |
10752 | else | |
10753 | firstLine = false; | |
10754 | ||
10755 | m_tablePrinter << line << ColumnBreak(); | |
10756 | } | |
10757 | } | |
10758 | void benchmarkEnded( BenchmarkStats const& stats ) override { | |
10759 | Duration average( stats.elapsedTimeInNanoseconds/stats.iterations ); | |
10760 | m_tablePrinter | |
10761 | << stats.iterations << ColumnBreak() | |
10762 | << stats.elapsedTimeInNanoseconds << ColumnBreak() | |
10763 | << average << ColumnBreak(); | |
10764 | } | |
10765 | ||
10766 | void testCaseEnded( TestCaseStats const& _testCaseStats ) override { | |
10767 | m_tablePrinter.close(); | |
10768 | StreamingReporterBase::testCaseEnded( _testCaseStats ); | |
10769 | m_headerPrinted = false; | |
10770 | } | |
10771 | void testGroupEnded( TestGroupStats const& _testGroupStats ) override { | |
10772 | if( currentGroupInfo.used ) { | |
10773 | printSummaryDivider(); | |
10774 | stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; | |
10775 | printTotals( _testGroupStats.totals ); | |
10776 | stream << '\n' << std::endl; | |
10777 | } | |
10778 | StreamingReporterBase::testGroupEnded( _testGroupStats ); | |
10779 | } | |
10780 | void testRunEnded( TestRunStats const& _testRunStats ) override { | |
10781 | printTotalsDivider( _testRunStats.totals ); | |
10782 | printTotals( _testRunStats.totals ); | |
10783 | stream << std::endl; | |
10784 | StreamingReporterBase::testRunEnded( _testRunStats ); | |
10785 | } | |
10786 | ||
10787 | private: | |
10788 | ||
10789 | class AssertionPrinter { | |
10790 | public: | |
10791 | AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; | |
10792 | AssertionPrinter( AssertionPrinter const& ) = delete; | |
10793 | AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) | |
10794 | : stream( _stream ), | |
10795 | stats( _stats ), | |
10796 | result( _stats.assertionResult ), | |
10797 | colour( Colour::None ), | |
10798 | message( result.getMessage() ), | |
10799 | messages( _stats.infoMessages ), | |
10800 | printInfoMessages( _printInfoMessages ) | |
10801 | { | |
10802 | switch( result.getResultType() ) { | |
10803 | case ResultWas::Ok: | |
10804 | colour = Colour::Success; | |
10805 | passOrFail = "PASSED"; | |
10806 | //if( result.hasMessage() ) | |
10807 | if( _stats.infoMessages.size() == 1 ) | |
10808 | messageLabel = "with message"; | |
10809 | if( _stats.infoMessages.size() > 1 ) | |
10810 | messageLabel = "with messages"; | |
10811 | break; | |
10812 | case ResultWas::ExpressionFailed: | |
10813 | if( result.isOk() ) { | |
10814 | colour = Colour::Success; | |
10815 | passOrFail = "FAILED - but was ok"; | |
10816 | } | |
10817 | else { | |
10818 | colour = Colour::Error; | |
10819 | passOrFail = "FAILED"; | |
10820 | } | |
10821 | if( _stats.infoMessages.size() == 1 ) | |
10822 | messageLabel = "with message"; | |
10823 | if( _stats.infoMessages.size() > 1 ) | |
10824 | messageLabel = "with messages"; | |
10825 | break; | |
10826 | case ResultWas::ThrewException: | |
10827 | colour = Colour::Error; | |
10828 | passOrFail = "FAILED"; | |
10829 | messageLabel = "due to unexpected exception with "; | |
10830 | if (_stats.infoMessages.size() == 1) | |
10831 | messageLabel += "message"; | |
10832 | if (_stats.infoMessages.size() > 1) | |
10833 | messageLabel += "messages"; | |
10834 | break; | |
10835 | case ResultWas::FatalErrorCondition: | |
10836 | colour = Colour::Error; | |
10837 | passOrFail = "FAILED"; | |
10838 | messageLabel = "due to a fatal error condition"; | |
10839 | break; | |
10840 | case ResultWas::DidntThrowException: | |
10841 | colour = Colour::Error; | |
10842 | passOrFail = "FAILED"; | |
10843 | messageLabel = "because no exception was thrown where one was expected"; | |
10844 | break; | |
10845 | case ResultWas::Info: | |
10846 | messageLabel = "info"; | |
10847 | break; | |
10848 | case ResultWas::Warning: | |
10849 | messageLabel = "warning"; | |
10850 | break; | |
10851 | case ResultWas::ExplicitFailure: | |
10852 | passOrFail = "FAILED"; | |
10853 | colour = Colour::Error; | |
10854 | if( _stats.infoMessages.size() == 1 ) | |
10855 | messageLabel = "explicitly with message"; | |
10856 | if( _stats.infoMessages.size() > 1 ) | |
10857 | messageLabel = "explicitly with messages"; | |
10858 | break; | |
10859 | // These cases are here to prevent compiler warnings | |
10860 | case ResultWas::Unknown: | |
10861 | case ResultWas::FailureBit: | |
10862 | case ResultWas::Exception: | |
10863 | passOrFail = "** internal error **"; | |
10864 | colour = Colour::Error; | |
10865 | break; | |
10866 | } | |
10867 | } | |
10868 | ||
10869 | void print() const { | |
10870 | printSourceInfo(); | |
10871 | if( stats.totals.assertions.total() > 0 ) { | |
10872 | if( result.isOk() ) | |
10873 | stream << '\n'; | |
10874 | printResultType(); | |
10875 | printOriginalExpression(); | |
10876 | printReconstructedExpression(); | |
10877 | } | |
10878 | else { | |
10879 | stream << '\n'; | |
10880 | } | |
10881 | printMessage(); | |
10882 | } | |
10883 | ||
10884 | private: | |
10885 | void printResultType() const { | |
10886 | if( !passOrFail.empty() ) { | |
10887 | Colour colourGuard( colour ); | |
10888 | stream << passOrFail << ":\n"; | |
10889 | } | |
10890 | } | |
10891 | void printOriginalExpression() const { | |
10892 | if( result.hasExpression() ) { | |
10893 | Colour colourGuard( Colour::OriginalExpression ); | |
10894 | stream << " "; | |
10895 | stream << result.getExpressionInMacro(); | |
10896 | stream << '\n'; | |
10897 | } | |
10898 | } | |
10899 | void printReconstructedExpression() const { | |
10900 | if( result.hasExpandedExpression() ) { | |
10901 | stream << "with expansion:\n"; | |
10902 | Colour colourGuard( Colour::ReconstructedExpression ); | |
10903 | stream << Column( result.getExpandedExpression() ).indent(2) << '\n'; | |
10904 | } | |
10905 | } | |
10906 | void printMessage() const { | |
10907 | if( !messageLabel.empty() ) | |
10908 | stream << messageLabel << ':' << '\n'; | |
10909 | for( auto const& msg : messages ) { | |
10910 | // If this assertion is a warning ignore any INFO messages | |
10911 | if( printInfoMessages || msg.type != ResultWas::Info ) | |
10912 | stream << Column( msg.message ).indent(2) << '\n'; | |
10913 | } | |
10914 | } | |
10915 | void printSourceInfo() const { | |
10916 | Colour colourGuard( Colour::FileName ); | |
10917 | stream << result.getSourceInfo() << ": "; | |
10918 | } | |
10919 | ||
10920 | std::ostream& stream; | |
10921 | AssertionStats const& stats; | |
10922 | AssertionResult const& result; | |
10923 | Colour::Code colour; | |
10924 | std::string passOrFail; | |
10925 | std::string messageLabel; | |
10926 | std::string message; | |
10927 | std::vector<MessageInfo> messages; | |
10928 | bool printInfoMessages; | |
10929 | }; | |
10930 | ||
10931 | void lazyPrint() { | |
10932 | ||
10933 | m_tablePrinter.close(); | |
10934 | lazyPrintWithoutClosingBenchmarkTable(); | |
10935 | } | |
10936 | ||
10937 | void lazyPrintWithoutClosingBenchmarkTable() { | |
10938 | ||
10939 | if( !currentTestRunInfo.used ) | |
10940 | lazyPrintRunInfo(); | |
10941 | if( !currentGroupInfo.used ) | |
10942 | lazyPrintGroupInfo(); | |
10943 | ||
10944 | if( !m_headerPrinted ) { | |
10945 | printTestCaseAndSectionHeader(); | |
10946 | m_headerPrinted = true; | |
10947 | } | |
10948 | } | |
10949 | void lazyPrintRunInfo() { | |
10950 | stream << '\n' << getLineOfChars<'~'>() << '\n'; | |
10951 | Colour colour( Colour::SecondaryText ); | |
10952 | stream << currentTestRunInfo->name | |
10953 | << " is a Catch v" << libraryVersion() << " host application.\n" | |
10954 | << "Run with -? for options\n\n"; | |
10955 | ||
10956 | if( m_config->rngSeed() != 0 ) | |
10957 | stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; | |
10958 | ||
10959 | currentTestRunInfo.used = true; | |
10960 | } | |
10961 | void lazyPrintGroupInfo() { | |
10962 | if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { | |
10963 | printClosedHeader( "Group: " + currentGroupInfo->name ); | |
10964 | currentGroupInfo.used = true; | |
10965 | } | |
10966 | } | |
10967 | void printTestCaseAndSectionHeader() { | |
10968 | assert( !m_sectionStack.empty() ); | |
10969 | printOpenHeader( currentTestCaseInfo->name ); | |
10970 | ||
10971 | if( m_sectionStack.size() > 1 ) { | |
10972 | Colour colourGuard( Colour::Headers ); | |
10973 | ||
10974 | auto | |
10975 | it = m_sectionStack.begin()+1, // Skip first section (test case) | |
10976 | itEnd = m_sectionStack.end(); | |
10977 | for( ; it != itEnd; ++it ) | |
10978 | printHeaderString( it->name, 2 ); | |
10979 | } | |
10980 | ||
10981 | SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; | |
10982 | ||
10983 | if( !lineInfo.empty() ){ | |
10984 | stream << getLineOfChars<'-'>() << '\n'; | |
10985 | Colour colourGuard( Colour::FileName ); | |
10986 | stream << lineInfo << '\n'; | |
10987 | } | |
10988 | stream << getLineOfChars<'.'>() << '\n' << std::endl; | |
10989 | } | |
10990 | ||
10991 | void printClosedHeader( std::string const& _name ) { | |
10992 | printOpenHeader( _name ); | |
10993 | stream << getLineOfChars<'.'>() << '\n'; | |
10994 | } | |
10995 | void printOpenHeader( std::string const& _name ) { | |
10996 | stream << getLineOfChars<'-'>() << '\n'; | |
10997 | { | |
10998 | Colour colourGuard( Colour::Headers ); | |
10999 | printHeaderString( _name ); | |
11000 | } | |
11001 | } | |
11002 | ||
11003 | // if string has a : in first line will set indent to follow it on | |
11004 | // subsequent lines | |
11005 | void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { | |
11006 | std::size_t i = _string.find( ": " ); | |
11007 | if( i != std::string::npos ) | |
11008 | i+=2; | |
11009 | else | |
11010 | i = 0; | |
11011 | stream << Column( _string ).indent( indent+i ).initialIndent( indent ) << '\n'; | |
11012 | } | |
11013 | ||
11014 | struct SummaryColumn { | |
11015 | ||
11016 | SummaryColumn( std::string const& _label, Colour::Code _colour ) | |
11017 | : label( _label ), | |
11018 | colour( _colour ) | |
11019 | {} | |
11020 | SummaryColumn addRow( std::size_t count ) { | |
11021 | std::ostringstream oss; | |
11022 | oss << count; | |
11023 | std::string row = oss.str(); | |
11024 | for( auto& oldRow : rows ) { | |
11025 | while( oldRow.size() < row.size() ) | |
11026 | oldRow = ' ' + oldRow; | |
11027 | while( oldRow.size() > row.size() ) | |
11028 | row = ' ' + row; | |
11029 | } | |
11030 | rows.push_back( row ); | |
11031 | return *this; | |
11032 | } | |
11033 | ||
11034 | std::string label; | |
11035 | Colour::Code colour; | |
11036 | std::vector<std::string> rows; | |
11037 | ||
11038 | }; | |
11039 | ||
11040 | void printTotals( Totals const& totals ) { | |
11041 | if( totals.testCases.total() == 0 ) { | |
11042 | stream << Colour( Colour::Warning ) << "No tests ran\n"; | |
11043 | } | |
11044 | else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { | |
11045 | stream << Colour( Colour::ResultSuccess ) << "All tests passed"; | |
11046 | stream << " (" | |
11047 | << pluralise( totals.assertions.passed, "assertion" ) << " in " | |
11048 | << pluralise( totals.testCases.passed, "test case" ) << ')' | |
11049 | << '\n'; | |
11050 | } | |
11051 | else { | |
11052 | ||
11053 | std::vector<SummaryColumn> columns; | |
11054 | columns.push_back( SummaryColumn( "", Colour::None ) | |
11055 | .addRow( totals.testCases.total() ) | |
11056 | .addRow( totals.assertions.total() ) ); | |
11057 | columns.push_back( SummaryColumn( "passed", Colour::Success ) | |
11058 | .addRow( totals.testCases.passed ) | |
11059 | .addRow( totals.assertions.passed ) ); | |
11060 | columns.push_back( SummaryColumn( "failed", Colour::ResultError ) | |
11061 | .addRow( totals.testCases.failed ) | |
11062 | .addRow( totals.assertions.failed ) ); | |
11063 | columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) | |
11064 | .addRow( totals.testCases.failedButOk ) | |
11065 | .addRow( totals.assertions.failedButOk ) ); | |
11066 | ||
11067 | printSummaryRow( "test cases", columns, 0 ); | |
11068 | printSummaryRow( "assertions", columns, 1 ); | |
11069 | } | |
11070 | } | |
11071 | void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) { | |
11072 | for( auto col : cols ) { | |
11073 | std::string value = col.rows[row]; | |
11074 | if( col.label.empty() ) { | |
11075 | stream << label << ": "; | |
11076 | if( value != "0" ) | |
11077 | stream << value; | |
11078 | else | |
11079 | stream << Colour( Colour::Warning ) << "- none -"; | |
11080 | } | |
11081 | else if( value != "0" ) { | |
11082 | stream << Colour( Colour::LightGrey ) << " | "; | |
11083 | stream << Colour( col.colour ) | |
11084 | << value << ' ' << col.label; | |
11085 | } | |
11086 | } | |
11087 | stream << '\n'; | |
11088 | } | |
11089 | ||
11090 | void printTotalsDivider( Totals const& totals ) { | |
11091 | if( totals.testCases.total() > 0 ) { | |
11092 | std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); | |
11093 | std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); | |
11094 | std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); | |
11095 | while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) | |
11096 | findMax( failedRatio, failedButOkRatio, passedRatio )++; | |
11097 | while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) | |
11098 | findMax( failedRatio, failedButOkRatio, passedRatio )--; | |
11099 | ||
11100 | stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); | |
11101 | stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); | |
11102 | if( totals.testCases.allPassed() ) | |
11103 | stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); | |
11104 | else | |
11105 | stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); | |
11106 | } | |
11107 | else { | |
11108 | stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); | |
11109 | } | |
11110 | stream << '\n'; | |
11111 | } | |
11112 | void printSummaryDivider() { | |
11113 | stream << getLineOfChars<'-'>() << '\n'; | |
11114 | } | |
11115 | ||
11116 | private: | |
11117 | bool m_headerPrinted = false; | |
11118 | }; | |
11119 | ||
11120 | CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) | |
11121 | ||
11122 | ConsoleReporter::~ConsoleReporter() {} | |
11123 | ||
11124 | } // end namespace Catch | |
11125 | ||
11126 | #if defined(_MSC_VER) | |
11127 | #pragma warning(pop) | |
11128 | #endif | |
11129 | // end catch_reporter_console.cpp | |
11130 | // start catch_reporter_junit.cpp | |
11131 | ||
11132 | #include <assert.h> | |
11133 | ||
11134 | #include <ctime> | |
11135 | #include <algorithm> | |
11136 | ||
11137 | namespace Catch { | |
11138 | ||
11139 | namespace { | |
11140 | std::string getCurrentTimestamp() { | |
11141 | // Beware, this is not reentrant because of backward compatibility issues | |
11142 | // Also, UTC only, again because of backward compatibility (%z is C++11) | |
11143 | time_t rawtime; | |
11144 | std::time(&rawtime); | |
11145 | auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); | |
11146 | ||
11147 | #ifdef _MSC_VER | |
11148 | std::tm timeInfo = {}; | |
11149 | gmtime_s(&timeInfo, &rawtime); | |
11150 | #else | |
11151 | std::tm* timeInfo; | |
11152 | timeInfo = std::gmtime(&rawtime); | |
11153 | #endif | |
11154 | ||
11155 | char timeStamp[timeStampSize]; | |
11156 | const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; | |
11157 | ||
11158 | #ifdef _MSC_VER | |
11159 | std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); | |
11160 | #else | |
11161 | std::strftime(timeStamp, timeStampSize, fmt, timeInfo); | |
11162 | #endif | |
11163 | return std::string(timeStamp); | |
11164 | } | |
11165 | ||
11166 | std::string fileNameTag(const std::vector<std::string> &tags) { | |
11167 | auto it = std::find_if(begin(tags), | |
11168 | end(tags), | |
11169 | [] (std::string const& tag) {return tag.front() == '#'; }); | |
11170 | if (it != tags.end()) | |
11171 | return it->substr(1); | |
11172 | return std::string(); | |
11173 | } | |
11174 | } | |
11175 | ||
11176 | class JunitReporter : public CumulativeReporterBase<JunitReporter> { | |
11177 | public: | |
11178 | JunitReporter( ReporterConfig const& _config ) | |
11179 | : CumulativeReporterBase( _config ), | |
11180 | xml( _config.stream() ) | |
11181 | { | |
11182 | m_reporterPrefs.shouldRedirectStdOut = true; | |
11183 | } | |
11184 | ||
11185 | ~JunitReporter() override; | |
11186 | ||
11187 | static std::string getDescription() { | |
11188 | return "Reports test results in an XML format that looks like Ant's junitreport target"; | |
11189 | } | |
11190 | ||
11191 | void noMatchingTestCases( std::string const& /*spec*/ ) override {} | |
11192 | ||
11193 | void testRunStarting( TestRunInfo const& runInfo ) override { | |
11194 | CumulativeReporterBase::testRunStarting( runInfo ); | |
11195 | xml.startElement( "testsuites" ); | |
11196 | } | |
11197 | ||
11198 | void testGroupStarting( GroupInfo const& groupInfo ) override { | |
11199 | suiteTimer.start(); | |
11200 | stdOutForSuite.str(""); | |
11201 | stdErrForSuite.str(""); | |
11202 | unexpectedExceptions = 0; | |
11203 | CumulativeReporterBase::testGroupStarting( groupInfo ); | |
11204 | } | |
11205 | ||
11206 | void testCaseStarting( TestCaseInfo const& testCaseInfo ) override { | |
11207 | m_okToFail = testCaseInfo.okToFail(); | |
11208 | } | |
11209 | bool assertionEnded( AssertionStats const& assertionStats ) override { | |
11210 | if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) | |
11211 | unexpectedExceptions++; | |
11212 | return CumulativeReporterBase::assertionEnded( assertionStats ); | |
11213 | } | |
11214 | ||
11215 | void testCaseEnded( TestCaseStats const& testCaseStats ) override { | |
11216 | stdOutForSuite << testCaseStats.stdOut; | |
11217 | stdErrForSuite << testCaseStats.stdErr; | |
11218 | CumulativeReporterBase::testCaseEnded( testCaseStats ); | |
11219 | } | |
11220 | ||
11221 | void testGroupEnded( TestGroupStats const& testGroupStats ) override { | |
11222 | double suiteTime = suiteTimer.getElapsedSeconds(); | |
11223 | CumulativeReporterBase::testGroupEnded( testGroupStats ); | |
11224 | writeGroup( *m_testGroups.back(), suiteTime ); | |
11225 | } | |
11226 | ||
11227 | void testRunEndedCumulative() override { | |
11228 | xml.endElement(); | |
11229 | } | |
11230 | ||
11231 | void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { | |
11232 | XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); | |
11233 | TestGroupStats const& stats = groupNode.value; | |
11234 | xml.writeAttribute( "name", stats.groupInfo.name ); | |
11235 | xml.writeAttribute( "errors", unexpectedExceptions ); | |
11236 | xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); | |
11237 | xml.writeAttribute( "tests", stats.totals.assertions.total() ); | |
11238 | xml.writeAttribute( "hostname", "tbd" ); // !TBD | |
11239 | if( m_config->showDurations() == ShowDurations::Never ) | |
11240 | xml.writeAttribute( "time", "" ); | |
11241 | else | |
11242 | xml.writeAttribute( "time", suiteTime ); | |
11243 | xml.writeAttribute( "timestamp", getCurrentTimestamp() ); | |
11244 | ||
11245 | // Write test cases | |
11246 | for( auto const& child : groupNode.children ) | |
11247 | writeTestCase( *child ); | |
11248 | ||
11249 | xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); | |
11250 | xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); | |
11251 | } | |
11252 | ||
11253 | void writeTestCase( TestCaseNode const& testCaseNode ) { | |
11254 | TestCaseStats const& stats = testCaseNode.value; | |
11255 | ||
11256 | // All test cases have exactly one section - which represents the | |
11257 | // test case itself. That section may have 0-n nested sections | |
11258 | assert( testCaseNode.children.size() == 1 ); | |
11259 | SectionNode const& rootSection = *testCaseNode.children.front(); | |
11260 | ||
11261 | std::string className = stats.testInfo.className; | |
11262 | ||
11263 | if( className.empty() ) { | |
11264 | className = fileNameTag(stats.testInfo.tags); | |
11265 | if ( className.empty() ) | |
11266 | className = "global"; | |
11267 | } | |
11268 | ||
11269 | if ( !m_config->name().empty() ) | |
11270 | className = m_config->name() + "." + className; | |
11271 | ||
11272 | writeSection( className, "", rootSection ); | |
11273 | } | |
11274 | ||
11275 | void writeSection( std::string const& className, | |
11276 | std::string const& rootName, | |
11277 | SectionNode const& sectionNode ) { | |
11278 | std::string name = trim( sectionNode.stats.sectionInfo.name ); | |
11279 | if( !rootName.empty() ) | |
11280 | name = rootName + '/' + name; | |
11281 | ||
11282 | if( !sectionNode.assertions.empty() || | |
11283 | !sectionNode.stdOut.empty() || | |
11284 | !sectionNode.stdErr.empty() ) { | |
11285 | XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); | |
11286 | if( className.empty() ) { | |
11287 | xml.writeAttribute( "classname", name ); | |
11288 | xml.writeAttribute( "name", "root" ); | |
11289 | } | |
11290 | else { | |
11291 | xml.writeAttribute( "classname", className ); | |
11292 | xml.writeAttribute( "name", name ); | |
11293 | } | |
11294 | xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); | |
11295 | ||
11296 | writeAssertions( sectionNode ); | |
11297 | ||
11298 | if( !sectionNode.stdOut.empty() ) | |
11299 | xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); | |
11300 | if( !sectionNode.stdErr.empty() ) | |
11301 | xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); | |
11302 | } | |
11303 | for( auto const& childNode : sectionNode.childSections ) | |
11304 | if( className.empty() ) | |
11305 | writeSection( name, "", *childNode ); | |
11306 | else | |
11307 | writeSection( className, name, *childNode ); | |
11308 | } | |
11309 | ||
11310 | void writeAssertions( SectionNode const& sectionNode ) { | |
11311 | for( auto const& assertion : sectionNode.assertions ) | |
11312 | writeAssertion( assertion ); | |
11313 | } | |
11314 | void writeAssertion( AssertionStats const& stats ) { | |
11315 | AssertionResult const& result = stats.assertionResult; | |
11316 | if( !result.isOk() ) { | |
11317 | std::string elementName; | |
11318 | switch( result.getResultType() ) { | |
11319 | case ResultWas::ThrewException: | |
11320 | case ResultWas::FatalErrorCondition: | |
11321 | elementName = "error"; | |
11322 | break; | |
11323 | case ResultWas::ExplicitFailure: | |
11324 | elementName = "failure"; | |
11325 | break; | |
11326 | case ResultWas::ExpressionFailed: | |
11327 | elementName = "failure"; | |
11328 | break; | |
11329 | case ResultWas::DidntThrowException: | |
11330 | elementName = "failure"; | |
11331 | break; | |
11332 | ||
11333 | // We should never see these here: | |
11334 | case ResultWas::Info: | |
11335 | case ResultWas::Warning: | |
11336 | case ResultWas::Ok: | |
11337 | case ResultWas::Unknown: | |
11338 | case ResultWas::FailureBit: | |
11339 | case ResultWas::Exception: | |
11340 | elementName = "internalError"; | |
11341 | break; | |
11342 | } | |
11343 | ||
11344 | XmlWriter::ScopedElement e = xml.scopedElement( elementName ); | |
11345 | ||
11346 | xml.writeAttribute( "message", result.getExpandedExpression() ); | |
11347 | xml.writeAttribute( "type", result.getTestMacroName() ); | |
11348 | ||
11349 | std::ostringstream oss; | |
11350 | if( !result.getMessage().empty() ) | |
11351 | oss << result.getMessage() << '\n'; | |
11352 | for( auto const& msg : stats.infoMessages ) | |
11353 | if( msg.type == ResultWas::Info ) | |
11354 | oss << msg.message << '\n'; | |
11355 | ||
11356 | oss << "at " << result.getSourceInfo(); | |
11357 | xml.writeText( oss.str(), false ); | |
11358 | } | |
11359 | } | |
11360 | ||
11361 | XmlWriter xml; | |
11362 | Timer suiteTimer; | |
11363 | std::ostringstream stdOutForSuite; | |
11364 | std::ostringstream stdErrForSuite; | |
11365 | unsigned int unexpectedExceptions = 0; | |
11366 | bool m_okToFail = false; | |
11367 | }; | |
11368 | ||
11369 | JunitReporter::~JunitReporter() {} | |
11370 | CATCH_REGISTER_REPORTER( "junit", JunitReporter ) | |
11371 | ||
11372 | } // end namespace Catch | |
11373 | // end catch_reporter_junit.cpp | |
11374 | // start catch_reporter_multi.cpp | |
11375 | ||
11376 | namespace Catch { | |
11377 | ||
11378 | void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { | |
11379 | m_reporters.push_back( std::move( reporter ) ); | |
11380 | } | |
11381 | ||
11382 | ReporterPreferences MultipleReporters::getPreferences() const { | |
11383 | return m_reporters[0]->getPreferences(); | |
11384 | } | |
11385 | ||
11386 | std::set<Verbosity> MultipleReporters::getSupportedVerbosities() { | |
11387 | return std::set<Verbosity>{ }; | |
11388 | } | |
11389 | ||
11390 | void MultipleReporters::noMatchingTestCases( std::string const& spec ) { | |
11391 | for( auto const& reporter : m_reporters ) | |
11392 | reporter->noMatchingTestCases( spec ); | |
11393 | } | |
11394 | ||
11395 | void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { | |
11396 | for( auto const& reporter : m_reporters ) | |
11397 | reporter->benchmarkStarting( benchmarkInfo ); | |
11398 | } | |
11399 | void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { | |
11400 | for( auto const& reporter : m_reporters ) | |
11401 | reporter->benchmarkEnded( benchmarkStats ); | |
11402 | } | |
11403 | ||
11404 | void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { | |
11405 | for( auto const& reporter : m_reporters ) | |
11406 | reporter->testRunStarting( testRunInfo ); | |
11407 | } | |
11408 | ||
11409 | void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { | |
11410 | for( auto const& reporter : m_reporters ) | |
11411 | reporter->testGroupStarting( groupInfo ); | |
11412 | } | |
11413 | ||
11414 | void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { | |
11415 | for( auto const& reporter : m_reporters ) | |
11416 | reporter->testCaseStarting( testInfo ); | |
11417 | } | |
11418 | ||
11419 | void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { | |
11420 | for( auto const& reporter : m_reporters ) | |
11421 | reporter->sectionStarting( sectionInfo ); | |
11422 | } | |
11423 | ||
11424 | void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { | |
11425 | for( auto const& reporter : m_reporters ) | |
11426 | reporter->assertionStarting( assertionInfo ); | |
11427 | } | |
11428 | ||
11429 | // The return value indicates if the messages buffer should be cleared: | |
11430 | bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { | |
11431 | bool clearBuffer = false; | |
11432 | for( auto const& reporter : m_reporters ) | |
11433 | clearBuffer |= reporter->assertionEnded( assertionStats ); | |
11434 | return clearBuffer; | |
11435 | } | |
11436 | ||
11437 | void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { | |
11438 | for( auto const& reporter : m_reporters ) | |
11439 | reporter->sectionEnded( sectionStats ); | |
11440 | } | |
11441 | ||
11442 | void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { | |
11443 | for( auto const& reporter : m_reporters ) | |
11444 | reporter->testCaseEnded( testCaseStats ); | |
11445 | } | |
11446 | ||
11447 | void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { | |
11448 | for( auto const& reporter : m_reporters ) | |
11449 | reporter->testGroupEnded( testGroupStats ); | |
11450 | } | |
11451 | ||
11452 | void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { | |
11453 | for( auto const& reporter : m_reporters ) | |
11454 | reporter->testRunEnded( testRunStats ); | |
11455 | } | |
11456 | ||
11457 | void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { | |
11458 | for( auto const& reporter : m_reporters ) | |
11459 | reporter->skipTest( testInfo ); | |
11460 | } | |
11461 | ||
11462 | bool MultipleReporters::isMulti() const { | |
11463 | return true; | |
11464 | } | |
11465 | ||
11466 | } // end namespace Catch | |
11467 | // end catch_reporter_multi.cpp | |
11468 | // start catch_reporter_xml.cpp | |
11469 | ||
11470 | #if defined(_MSC_VER) | |
11471 | #pragma warning(push) | |
11472 | #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch | |
11473 | // Note that 4062 (not all labels are handled | |
11474 | // and default is missing) is enabled | |
11475 | #endif | |
11476 | ||
11477 | namespace Catch { | |
11478 | class XmlReporter : public StreamingReporterBase<XmlReporter> { | |
11479 | public: | |
11480 | XmlReporter( ReporterConfig const& _config ) | |
11481 | : StreamingReporterBase( _config ), | |
11482 | m_xml(_config.stream()) | |
11483 | { | |
11484 | m_reporterPrefs.shouldRedirectStdOut = true; | |
11485 | } | |
11486 | ||
11487 | ~XmlReporter() override; | |
11488 | ||
11489 | static std::string getDescription() { | |
11490 | return "Reports test results as an XML document"; | |
11491 | } | |
11492 | ||
11493 | virtual std::string getStylesheetRef() const { | |
11494 | return std::string(); | |
11495 | } | |
11496 | ||
11497 | void writeSourceInfo( SourceLineInfo const& sourceInfo ) { | |
11498 | m_xml | |
11499 | .writeAttribute( "filename", sourceInfo.file ) | |
11500 | .writeAttribute( "line", sourceInfo.line ); | |
11501 | } | |
11502 | ||
11503 | public: // StreamingReporterBase | |
11504 | ||
11505 | void noMatchingTestCases( std::string const& s ) override { | |
11506 | StreamingReporterBase::noMatchingTestCases( s ); | |
11507 | } | |
11508 | ||
11509 | void testRunStarting( TestRunInfo const& testInfo ) override { | |
11510 | StreamingReporterBase::testRunStarting( testInfo ); | |
11511 | std::string stylesheetRef = getStylesheetRef(); | |
11512 | if( !stylesheetRef.empty() ) | |
11513 | m_xml.writeStylesheetRef( stylesheetRef ); | |
11514 | m_xml.startElement( "Catch" ); | |
11515 | if( !m_config->name().empty() ) | |
11516 | m_xml.writeAttribute( "name", m_config->name() ); | |
11517 | } | |
11518 | ||
11519 | void testGroupStarting( GroupInfo const& groupInfo ) override { | |
11520 | StreamingReporterBase::testGroupStarting( groupInfo ); | |
11521 | m_xml.startElement( "Group" ) | |
11522 | .writeAttribute( "name", groupInfo.name ); | |
11523 | } | |
11524 | ||
11525 | void testCaseStarting( TestCaseInfo const& testInfo ) override { | |
11526 | StreamingReporterBase::testCaseStarting(testInfo); | |
11527 | m_xml.startElement( "TestCase" ) | |
11528 | .writeAttribute( "name", trim( testInfo.name ) ) | |
11529 | .writeAttribute( "description", testInfo.description ) | |
11530 | .writeAttribute( "tags", testInfo.tagsAsString() ); | |
11531 | ||
11532 | writeSourceInfo( testInfo.lineInfo ); | |
11533 | ||
11534 | if ( m_config->showDurations() == ShowDurations::Always ) | |
11535 | m_testCaseTimer.start(); | |
11536 | m_xml.ensureTagClosed(); | |
11537 | } | |
11538 | ||
11539 | void sectionStarting( SectionInfo const& sectionInfo ) override { | |
11540 | StreamingReporterBase::sectionStarting( sectionInfo ); | |
11541 | if( m_sectionDepth++ > 0 ) { | |
11542 | m_xml.startElement( "Section" ) | |
11543 | .writeAttribute( "name", trim( sectionInfo.name ) ) | |
11544 | .writeAttribute( "description", sectionInfo.description ); | |
11545 | writeSourceInfo( sectionInfo.lineInfo ); | |
11546 | m_xml.ensureTagClosed(); | |
11547 | } | |
11548 | } | |
11549 | ||
11550 | void assertionStarting( AssertionInfo const& ) override { } | |
11551 | ||
11552 | bool assertionEnded( AssertionStats const& assertionStats ) override { | |
11553 | ||
11554 | AssertionResult const& result = assertionStats.assertionResult; | |
11555 | ||
11556 | bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); | |
11557 | ||
11558 | if( includeResults ) { | |
11559 | // Print any info messages in <Info> tags. | |
11560 | for( auto const& msg : assertionStats.infoMessages ) { | |
11561 | if( msg.type == ResultWas::Info ) { | |
11562 | m_xml.scopedElement( "Info" ) | |
11563 | .writeText( msg.message ); | |
11564 | } else if ( msg.type == ResultWas::Warning ) { | |
11565 | m_xml.scopedElement( "Warning" ) | |
11566 | .writeText( msg.message ); | |
11567 | } | |
11568 | } | |
11569 | } | |
11570 | ||
11571 | // Drop out if result was successful but we're not printing them. | |
11572 | if( !includeResults && result.getResultType() != ResultWas::Warning ) | |
11573 | return true; | |
11574 | ||
11575 | // Print the expression if there is one. | |
11576 | if( result.hasExpression() ) { | |
11577 | m_xml.startElement( "Expression" ) | |
11578 | .writeAttribute( "success", result.succeeded() ) | |
11579 | .writeAttribute( "type", result.getTestMacroName() ); | |
11580 | ||
11581 | writeSourceInfo( result.getSourceInfo() ); | |
11582 | ||
11583 | m_xml.scopedElement( "Original" ) | |
11584 | .writeText( result.getExpression() ); | |
11585 | m_xml.scopedElement( "Expanded" ) | |
11586 | .writeText( result.getExpandedExpression() ); | |
11587 | } | |
11588 | ||
11589 | // And... Print a result applicable to each result type. | |
11590 | switch( result.getResultType() ) { | |
11591 | case ResultWas::ThrewException: | |
11592 | m_xml.startElement( "Exception" ); | |
11593 | writeSourceInfo( result.getSourceInfo() ); | |
11594 | m_xml.writeText( result.getMessage() ); | |
11595 | m_xml.endElement(); | |
11596 | break; | |
11597 | case ResultWas::FatalErrorCondition: | |
11598 | m_xml.startElement( "FatalErrorCondition" ); | |
11599 | writeSourceInfo( result.getSourceInfo() ); | |
11600 | m_xml.writeText( result.getMessage() ); | |
11601 | m_xml.endElement(); | |
11602 | break; | |
11603 | case ResultWas::Info: | |
11604 | m_xml.scopedElement( "Info" ) | |
11605 | .writeText( result.getMessage() ); | |
11606 | break; | |
11607 | case ResultWas::Warning: | |
11608 | // Warning will already have been written | |
11609 | break; | |
11610 | case ResultWas::ExplicitFailure: | |
11611 | m_xml.startElement( "Failure" ); | |
11612 | writeSourceInfo( result.getSourceInfo() ); | |
11613 | m_xml.writeText( result.getMessage() ); | |
11614 | m_xml.endElement(); | |
11615 | break; | |
11616 | default: | |
11617 | break; | |
11618 | } | |
11619 | ||
11620 | if( result.hasExpression() ) | |
11621 | m_xml.endElement(); | |
11622 | ||
11623 | return true; | |
11624 | } | |
11625 | ||
11626 | void sectionEnded( SectionStats const& sectionStats ) override { | |
11627 | StreamingReporterBase::sectionEnded( sectionStats ); | |
11628 | if( --m_sectionDepth > 0 ) { | |
11629 | XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); | |
11630 | e.writeAttribute( "successes", sectionStats.assertions.passed ); | |
11631 | e.writeAttribute( "failures", sectionStats.assertions.failed ); | |
11632 | e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); | |
11633 | ||
11634 | if ( m_config->showDurations() == ShowDurations::Always ) | |
11635 | e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); | |
11636 | ||
11637 | m_xml.endElement(); | |
11638 | } | |
11639 | } | |
11640 | ||
11641 | void testCaseEnded( TestCaseStats const& testCaseStats ) override { | |
11642 | StreamingReporterBase::testCaseEnded( testCaseStats ); | |
11643 | XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); | |
11644 | e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); | |
11645 | ||
11646 | if ( m_config->showDurations() == ShowDurations::Always ) | |
11647 | e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); | |
11648 | ||
11649 | if( !testCaseStats.stdOut.empty() ) | |
11650 | m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); | |
11651 | if( !testCaseStats.stdErr.empty() ) | |
11652 | m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); | |
11653 | ||
11654 | m_xml.endElement(); | |
11655 | } | |
11656 | ||
11657 | void testGroupEnded( TestGroupStats const& testGroupStats ) override { | |
11658 | StreamingReporterBase::testGroupEnded( testGroupStats ); | |
11659 | // TODO: Check testGroupStats.aborting and act accordingly. | |
11660 | m_xml.scopedElement( "OverallResults" ) | |
11661 | .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) | |
11662 | .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) | |
11663 | .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); | |
11664 | m_xml.endElement(); | |
11665 | } | |
11666 | ||
11667 | void testRunEnded( TestRunStats const& testRunStats ) override { | |
11668 | StreamingReporterBase::testRunEnded( testRunStats ); | |
11669 | m_xml.scopedElement( "OverallResults" ) | |
11670 | .writeAttribute( "successes", testRunStats.totals.assertions.passed ) | |
11671 | .writeAttribute( "failures", testRunStats.totals.assertions.failed ) | |
11672 | .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); | |
11673 | m_xml.endElement(); | |
11674 | } | |
11675 | ||
11676 | private: | |
11677 | Timer m_testCaseTimer; | |
11678 | XmlWriter m_xml; | |
11679 | int m_sectionDepth = 0; | |
11680 | }; | |
11681 | ||
11682 | XmlReporter::~XmlReporter() {} | |
11683 | CATCH_REGISTER_REPORTER( "xml", XmlReporter ) | |
11684 | ||
11685 | } // end namespace Catch | |
11686 | ||
11687 | #if defined(_MSC_VER) | |
11688 | #pragma warning(pop) | |
11689 | #endif | |
11690 | // end catch_reporter_xml.cpp | |
11691 | ||
11692 | namespace Catch { | |
11693 | LeakDetector leakDetector; | |
11694 | } | |
11695 | ||
11696 | #ifdef __clang__ | |
11697 | #pragma clang diagnostic pop | |
11698 | #endif | |
11699 | ||
11700 | // end catch_impl.hpp | |
11701 | #endif | |
11702 | ||
11703 | #ifdef CATCH_CONFIG_MAIN | |
11704 | // start catch_default_main.hpp | |
11705 | ||
11706 | #ifndef __OBJC__ | |
11707 | ||
11708 | #if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) | |
11709 | // Standard C/C++ Win32 Unicode wmain entry point | |
11710 | extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { | |
11711 | #else | |
11712 | // Standard C/C++ main entry point | |
11713 | int main (int argc, char * argv[]) { | |
11714 | #endif | |
11715 | ||
11716 | return Catch::Session().run( argc, argv ); | |
11717 | } | |
11718 | ||
11719 | #else // __OBJC__ | |
11720 | ||
11721 | // Objective-C entry point | |
11722 | int main (int argc, char * const argv[]) { | |
11723 | #if !CATCH_ARC_ENABLED | |
11724 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | |
11725 | #endif | |
11726 | ||
11727 | Catch::registerTestMethods(); | |
11728 | int result = Catch::Session().run( argc, (char**)argv ); | |
11729 | ||
11730 | #if !CATCH_ARC_ENABLED | |
11731 | [pool drain]; | |
11732 | #endif | |
11733 | ||
11734 | return result; | |
11735 | } | |
11736 | ||
11737 | #endif // __OBJC__ | |
11738 | ||
11739 | // end catch_default_main.hpp | |
11740 | #endif | |
11741 | ||
11742 | #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED | |
11743 | # undef CLARA_CONFIG_MAIN | |
11744 | #endif | |
11745 | ||
11746 | #if !defined(CATCH_CONFIG_DISABLE) | |
11747 | ////// | |
11748 | // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ | |
11749 | #ifdef CATCH_CONFIG_PREFIX_ALL | |
11750 | ||
11751 | #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11752 | #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) | |
11753 | ||
11754 | #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) | |
11755 | #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) | |
11756 | #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) | |
11757 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11758 | #define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) | |
11759 | #endif// CATCH_CONFIG_DISABLE_MATCHERS | |
11760 | #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11761 | ||
11762 | #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11763 | #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) | |
11764 | #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11765 | #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11766 | #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) | |
11767 | ||
11768 | #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) | |
11769 | #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) | |
11770 | #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) | |
11771 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11772 | #define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) | |
11773 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11774 | #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11775 | ||
11776 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11777 | #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) | |
11778 | ||
11779 | #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) | |
11780 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11781 | ||
11782 | #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) | |
11783 | #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) | |
11784 | #define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) | |
11785 | ||
11786 | #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) | |
11787 | #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) | |
11788 | #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) | |
11789 | #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) | |
11790 | #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) | |
11791 | #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11792 | #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11793 | #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11794 | ||
11795 | #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() | |
11796 | ||
11797 | // "BDD-style" convenience wrappers | |
11798 | #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) | |
11799 | #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) | |
11800 | #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) | |
11801 | #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) | |
11802 | #define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) | |
11803 | #define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) | |
11804 | #define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) | |
11805 | ||
11806 | // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required | |
11807 | #else | |
11808 | ||
11809 | #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11810 | #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) | |
11811 | ||
11812 | #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11813 | #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) | |
11814 | #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) | |
11815 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11816 | #define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) | |
11817 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11818 | #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11819 | ||
11820 | #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11821 | #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) | |
11822 | #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11823 | #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11824 | #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) | |
11825 | ||
11826 | #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11827 | #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) | |
11828 | #define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) | |
11829 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11830 | #define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) | |
11831 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11832 | #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11833 | ||
11834 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11835 | #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) | |
11836 | ||
11837 | #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) | |
11838 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11839 | ||
11840 | #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) | |
11841 | #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) | |
11842 | #define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) | |
11843 | ||
11844 | #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) | |
11845 | #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) | |
11846 | #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) | |
11847 | #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) | |
11848 | #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) | |
11849 | #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) | |
11850 | #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11851 | #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) | |
11852 | #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() | |
11853 | ||
11854 | #endif | |
11855 | ||
11856 | #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) | |
11857 | ||
11858 | // "BDD-style" convenience wrappers | |
11859 | #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) | |
11860 | #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) | |
11861 | ||
11862 | #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) | |
11863 | #define WHEN( desc ) SECTION( std::string(" When: ") + desc ) | |
11864 | #define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) | |
11865 | #define THEN( desc ) SECTION( std::string(" Then: ") + desc ) | |
11866 | #define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) | |
11867 | ||
11868 | using Catch::Detail::Approx; | |
11869 | ||
11870 | #else | |
11871 | ////// | |
11872 | // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ | |
11873 | #ifdef CATCH_CONFIG_PREFIX_ALL | |
11874 | ||
11875 | #define CATCH_REQUIRE( ... ) (void)(0) | |
11876 | #define CATCH_REQUIRE_FALSE( ... ) (void)(0) | |
11877 | ||
11878 | #define CATCH_REQUIRE_THROWS( ... ) (void)(0) | |
11879 | #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) | |
11880 | #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) | |
11881 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11882 | #define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) | |
11883 | #endif// CATCH_CONFIG_DISABLE_MATCHERS | |
11884 | #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) | |
11885 | ||
11886 | #define CATCH_CHECK( ... ) (void)(0) | |
11887 | #define CATCH_CHECK_FALSE( ... ) (void)(0) | |
11888 | #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) | |
11889 | #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) | |
11890 | #define CATCH_CHECK_NOFAIL( ... ) (void)(0) | |
11891 | ||
11892 | #define CATCH_CHECK_THROWS( ... ) (void)(0) | |
11893 | #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) | |
11894 | #define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) | |
11895 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11896 | #define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) | |
11897 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11898 | #define CATCH_CHECK_NOTHROW( ... ) (void)(0) | |
11899 | ||
11900 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11901 | #define CATCH_CHECK_THAT( arg, matcher ) (void)(0) | |
11902 | ||
11903 | #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) | |
11904 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11905 | ||
11906 | #define CATCH_INFO( msg ) (void)(0) | |
11907 | #define CATCH_WARN( msg ) (void)(0) | |
11908 | #define CATCH_CAPTURE( msg ) (void)(0) | |
11909 | ||
11910 | #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11911 | #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11912 | #define CATCH_METHOD_AS_TEST_CASE( method, ... ) | |
11913 | #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) | |
11914 | #define CATCH_SECTION( ... ) | |
11915 | #define CATCH_FAIL( ... ) (void)(0) | |
11916 | #define CATCH_FAIL_CHECK( ... ) (void)(0) | |
11917 | #define CATCH_SUCCEED( ... ) (void)(0) | |
11918 | ||
11919 | #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11920 | ||
11921 | // "BDD-style" convenience wrappers | |
11922 | #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11923 | #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) | |
11924 | #define CATCH_GIVEN( desc ) | |
11925 | #define CATCH_WHEN( desc ) | |
11926 | #define CATCH_AND_WHEN( desc ) | |
11927 | #define CATCH_THEN( desc ) | |
11928 | #define CATCH_AND_THEN( desc ) | |
11929 | ||
11930 | // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required | |
11931 | #else | |
11932 | ||
11933 | #define REQUIRE( ... ) (void)(0) | |
11934 | #define REQUIRE_FALSE( ... ) (void)(0) | |
11935 | ||
11936 | #define REQUIRE_THROWS( ... ) (void)(0) | |
11937 | #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) | |
11938 | #define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) | |
11939 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11940 | #define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) | |
11941 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11942 | #define REQUIRE_NOTHROW( ... ) (void)(0) | |
11943 | ||
11944 | #define CHECK( ... ) (void)(0) | |
11945 | #define CHECK_FALSE( ... ) (void)(0) | |
11946 | #define CHECKED_IF( ... ) if (__VA_ARGS__) | |
11947 | #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) | |
11948 | #define CHECK_NOFAIL( ... ) (void)(0) | |
11949 | ||
11950 | #define CHECK_THROWS( ... ) (void)(0) | |
11951 | #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) | |
11952 | #define CHECK_THROWS_WITH( expr, matcher ) (void)(0) | |
11953 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11954 | #define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) | |
11955 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11956 | #define CHECK_NOTHROW( ... ) (void)(0) | |
11957 | ||
11958 | #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) | |
11959 | #define CHECK_THAT( arg, matcher ) (void)(0) | |
11960 | ||
11961 | #define REQUIRE_THAT( arg, matcher ) (void)(0) | |
11962 | #endif // CATCH_CONFIG_DISABLE_MATCHERS | |
11963 | ||
11964 | #define INFO( msg ) (void)(0) | |
11965 | #define WARN( msg ) (void)(0) | |
11966 | #define CAPTURE( msg ) (void)(0) | |
11967 | ||
11968 | #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11969 | #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11970 | #define METHOD_AS_TEST_CASE( method, ... ) | |
11971 | #define REGISTER_TEST_CASE( Function, ... ) (void)(0) | |
11972 | #define SECTION( ... ) | |
11973 | #define FAIL( ... ) (void)(0) | |
11974 | #define FAIL_CHECK( ... ) (void)(0) | |
11975 | #define SUCCEED( ... ) (void)(0) | |
11976 | #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) | |
11977 | ||
11978 | #endif | |
11979 | ||
11980 | #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) | |
11981 | ||
11982 | // "BDD-style" convenience wrappers | |
11983 | #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) | |
11984 | #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) | |
11985 | ||
11986 | #define GIVEN( desc ) | |
11987 | #define WHEN( desc ) | |
11988 | #define AND_WHEN( desc ) | |
11989 | #define THEN( desc ) | |
11990 | #define AND_THEN( desc ) | |
11991 | ||
11992 | using Catch::Detail::Approx; | |
11993 | ||
11994 | #endif | |
11995 | ||
11996 | // start catch_reenable_warnings.h | |
11997 | ||
11998 | ||
11999 | #ifdef __clang__ | |
12000 | # ifdef __ICC // icpc defines the __clang__ macro | |
12001 | # pragma warning(pop) | |
12002 | # else | |
12003 | # pragma clang diagnostic pop | |
12004 | # endif | |
12005 | #elif defined __GNUC__ | |
12006 | # pragma GCC diagnostic pop | |
12007 | #endif | |
12008 | ||
12009 | // end catch_reenable_warnings.h | |
12010 | // end catch.hpp | |
12011 | #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED | |
12012 |