From 2c1e71b6ad2e7f718f7fa18bd00850620eabe990 Mon Sep 17 00:00:00 2001 From: Lukasz Forynski Date: Tue, 17 Dec 2013 14:46:57 +0000 Subject: [PATCH] Added optional global timeouts for test suite --- README.md | 2 +- include/internal/catch_default_main.hpp | 80 +++++++++++++++++------ include/internal/catch_version.hpp | 2 +- single_include/catch.hpp | 86 ++++++++++++++++++------- 4 files changed, 125 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index dba8e0c2..42767531 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![catch logo](catch-logo-small.png) -*v1.0 build 23 (master branch)* +*v1.0 build 24 (master branch)* Build status (on Travis CI) [![Build Status](https://travis-ci.org/philsquared/Catch.png)](https://travis-ci.org/philsquared/Catch) diff --git a/include/internal/catch_default_main.hpp b/include/internal/catch_default_main.hpp index 1e89f43d..95955ad9 100644 --- a/include/internal/catch_default_main.hpp +++ b/include/internal/catch_default_main.hpp @@ -10,7 +10,16 @@ #ifndef __OBJC__ -#if !defined(DO_NOT_USE_SIGNALS) +#ifdef TIMEOUT_FOR_ALL_TESTS_IN_SECONDS +#ifdef __GNUC__ +#define USING_TIMEOUTS +#else /*!__GNUC__ = for MSVC pthreads-win32 is needed ( + unless this library will go to C++11 and use std::thread, in which case below could be updated accordingly)*/ +#pragma message ("\nWARNING: Specified TIMEOUT_FOR_ALL_TESTS_IN_SECONDS, but it's only supported with GCC compilers..\n") +#endif /*__GNUC__*/ +#endif /*TIMEOUT_FOR_ALL_TESTS_IN_SECONDS*/ + +#if defined(USING_TIMEOUTS) || !defined(DO_NOT_USE_SIGNALS) #include #include void add_test_info(std::stringstream& s) { @@ -29,6 +38,38 @@ void add_test_info(std::stringstream& s) { } #endif +#ifdef USING_TIMEOUTS +#include +#ifdef CATCH_PLATFORM_WINDOWS +#define SLEEP(x) Sleep(x) +#else +#include +#define SLEEP(x) sleep(x/1000) +#endif + +void* timeout_thread_fcn(void* arg) { + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + unsigned int elapsed_s = 0; + volatile bool& exit_timeout_thread = *static_cast(arg); + while(!exit_timeout_thread) { + SLEEP(1000); + elapsed_s++; + if (elapsed_s == TIMEOUT_FOR_ALL_TESTS_IN_SECONDS) + { + std::stringstream info; + info << "\n\n=================\n\n"; + info << "Test execution timed out"; + info << " (timeout was: " << TIMEOUT_FOR_ALL_TESTS_IN_SECONDS << " seconds)\n"; + add_test_info(info); + std::cerr << info.str(); + exit(-1); + } + } + return NULL; +} +#endif /*!TIMEOUT_FOR_ALL_TESTS_IN_SECONDS*/ + #ifndef DO_NOT_USE_SIGNALS static bool testing_finished = false; #include @@ -66,16 +107,15 @@ void handle_signal(int sig) { default: break; } - add_test_info(s); std::cout << s.str(); - exit(-sig); } #endif /*!DO_NOT_USE_SIGNALS*/ // Standard C/C++ main entry point int main (int argc, char * const argv[]) { + int ret = 0; #ifndef DO_NOT_USE_SIGNALS testing_finished = false; signal(SIGSEGV, handle_signal); @@ -86,25 +126,25 @@ int main (int argc, char * const argv[]) { signal(SIGFPE, handle_signal); #endif /*!DO_NOT_USE_SIGNALS*/ - return Catch::Session().run( argc, argv ); -} +#ifdef USING_TIMEOUTS + pthread_t timeout_thread; + volatile bool exit_timeout_thread = false; + ret = pthread_create(&timeout_thread, + NULL, + timeout_thread_fcn, + (void*)&exit_timeout_thread); + if (ret) { + std::cerr << "Catch failed to start timeout thread (pthread_create failed with %d" << ret << ")\n"; + return ret; + } +#endif /*!USING_TIMEOUTS*/ -#else // __OBJC__ + ret = Catch::Session().run( argc, argv ); -// Objective-C entry point -int main (int argc, char * const argv[]) { -#if !CATCH_ARC_ENABLED - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; -#endif - - Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); - -#if !CATCH_ARC_ENABLED - [pool drain]; -#endif - - return result; +#ifndef DO_NOT_USE_SIGNALS + testing_finished = true; +#endif /*!DO_NOT_USE_SIGNALS*/ + return ret; } #endif // __OBJC__ diff --git a/include/internal/catch_version.hpp b/include/internal/catch_version.hpp index f615b3ea..67390ce8 100644 --- a/include/internal/catch_version.hpp +++ b/include/internal/catch_version.hpp @@ -13,7 +13,7 @@ namespace Catch { // These numbers are maintained by a script - Version libraryVersion( 1, 0, 23, "master" ); + Version libraryVersion( 1, 0, 24, "master" ); } #endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED diff --git a/single_include/catch.hpp b/single_include/catch.hpp index e3e40cae..fdda4825 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,6 +1,6 @@ /* - * CATCH v1.0 build 23 (master branch) - * Generated: 2013-12-17 13:44:13.801508 + * CATCH v1.0 build 24 (master branch) + * Generated: 2013-12-17 14:45:19.329049 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -6166,7 +6166,7 @@ namespace Catch { namespace Catch { // These numbers are maintained by a script - Version libraryVersion( 1, 0, 23, "master" ); + Version libraryVersion( 1, 0, 24, "master" ); } // #included from: catch_text.hpp @@ -7904,7 +7904,16 @@ namespace Catch { #ifndef __OBJC__ -#if !defined(DO_NOT_USE_SIGNALS) +#ifdef TIMEOUT_FOR_ALL_TESTS_IN_SECONDS +#ifdef __GNUC__ +#define USING_TIMEOUTS +#else /*!__GNUC__ = for MSVC pthreads-win32 is needed ( + unless this library will go to C++11 and use std::thread, in which case below could be updated accordingly)*/ +#pragma message ("\nWARNING: Specified TIMEOUT_FOR_ALL_TESTS_IN_SECONDS, but it's only supported with GCC compilers..\n") +#endif /*__GNUC__*/ +#endif /*TIMEOUT_FOR_ALL_TESTS_IN_SECONDS*/ + +#if defined(USING_TIMEOUTS) || !defined(DO_NOT_USE_SIGNALS) #include #include void add_test_info(std::stringstream& s) { @@ -7923,6 +7932,38 @@ void add_test_info(std::stringstream& s) { } #endif +#ifdef USING_TIMEOUTS +#include +#ifdef CATCH_PLATFORM_WINDOWS +#define SLEEP(x) Sleep(x) +#else +#include +#define SLEEP(x) sleep(x/1000) +#endif + +void* timeout_thread_fcn(void* arg) { + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + unsigned int elapsed_s = 0; + volatile bool& exit_timeout_thread = *static_cast(arg); + while(!exit_timeout_thread) { + SLEEP(1000); + elapsed_s++; + if (elapsed_s == TIMEOUT_FOR_ALL_TESTS_IN_SECONDS) + { + std::stringstream info; + info << "\n\n=================\n\n"; + info << "Test execution timed out"; + info << " (timeout was: " << TIMEOUT_FOR_ALL_TESTS_IN_SECONDS << " seconds)\n"; + add_test_info(info); + std::cerr << info.str(); + exit(-1); + } + } + return NULL; +} +#endif /*!TIMEOUT_FOR_ALL_TESTS_IN_SECONDS*/ + #ifndef DO_NOT_USE_SIGNALS static bool testing_finished = false; #include @@ -7960,16 +8001,15 @@ void handle_signal(int sig) { default: break; } - add_test_info(s); std::cout << s.str(); - exit(-sig); } #endif /*!DO_NOT_USE_SIGNALS*/ // Standard C/C++ main entry point int main (int argc, char * const argv[]) { + int ret = 0; #ifndef DO_NOT_USE_SIGNALS testing_finished = false; signal(SIGSEGV, handle_signal); @@ -7980,25 +8020,25 @@ int main (int argc, char * const argv[]) { signal(SIGFPE, handle_signal); #endif /*!DO_NOT_USE_SIGNALS*/ - return Catch::Session().run( argc, argv ); -} +#ifdef USING_TIMEOUTS + pthread_t timeout_thread; + volatile bool exit_timeout_thread = false; + ret = pthread_create(&timeout_thread, + NULL, + timeout_thread_fcn, + (void*)&exit_timeout_thread); + if (ret) { + std::cerr << "Catch failed to start timeout thread (pthread_create failed with %d" << ret << ")\n"; + return ret; + } +#endif /*!USING_TIMEOUTS*/ -#else // __OBJC__ + ret = Catch::Session().run( argc, argv ); -// Objective-C entry point -int main (int argc, char * const argv[]) { -#if !CATCH_ARC_ENABLED - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; -#endif - - Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); - -#if !CATCH_ARC_ENABLED - [pool drain]; -#endif - - return result; +#ifndef DO_NOT_USE_SIGNALS + testing_finished = true; +#endif /*!DO_NOT_USE_SIGNALS*/ + return ret; } #endif // __OBJC__