Parcourir la source

Update catch.hpp (fixes #1856)

See also catchorg/Catch2#2627
Benoit Blanchon il y a 3 ans
Parent
commit
bf93779b4f
1 fichiers modifiés avec 282 ajouts et 145 suppressions
  1. 282 145
      extras/tests/catch/catch.hpp

+ 282 - 145
extras/tests/catch/catch.hpp

@@ -1,6 +1,6 @@
 /*
  *  Catch v1.12.2
- *  Generated: 2018-05-14 15:10:01.112442
+ *  Generated: 2023-01-17 08:45:40.979381
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -214,7 +214,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 // Use variadic macros if the compiler supports them
-#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+#if ( defined _MSC_VER && _MSC_VER >= 1400 && !defined __EDGE__) || \
     ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
     ( defined __GNUC__ && __GNUC__ >= 3 ) || \
     ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
@@ -2129,6 +2129,9 @@ namespace Catch{
         #define CATCH_TRAP() \
                 __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
                 : : : "memory","r0","r3","r4" ) /* NOLINT */
+    #elif defined(__aarch64__)
+        // Backport of https://github.com/catchorg/Catch2/commit/a25c1a24af8bffd35727a888a307ff0280cf9387
+        #define CATCH_TRAP() __asm__(".inst 0xd4200000")
     #else
         #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ )
     #endif
@@ -6392,18 +6395,21 @@ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
 // #included from: catch_fatal_condition.hpp
 #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
 
+#include <cassert>
+#include <stdexcept>
+
 namespace Catch {
 
-    // Report the error condition
-    inline void reportFatal( std::string const& message ) {
-        IContext& context = Catch::getCurrentContext();
-        IResultCapture* resultCapture = context.getResultCapture();
-        resultCapture->handleFatalErrorCondition( message );
-    }
+//! Signals fatal error message to the run context
+inline void reportFatal(std::string const &message) {
+  IContext &context = Catch::getCurrentContext();
+  IResultCapture *resultCapture = context.getResultCapture();
+  resultCapture->handleFatalErrorCondition(message);
+}
 
 } // namespace Catch
 
-#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+#if defined(CATCH_PLATFORM_WINDOWS) /////////////////////////////////////////
 // #included from: catch_windows_h_proxy.h
 
 #define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
@@ -6429,176 +6435,307 @@ namespace Catch {
 #endif
 
 
-#  if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+#if !defined(CATCH_CONFIG_WINDOWS_SEH)
 
 namespace Catch {
-    struct FatalConditionHandler {
-        void reset() {}
-    };
-}
+class FatalConditionHandler {
+  bool m_started;
+
+  // Install/disengage implementation for specific platform.
+  // Should be if-defed to work on current platform, can assume
+  // engage-disengage 1:1 pairing.
+  void engage_platform() {}
+  void disengage_platform() {}
+
+public:
+  // Should also have platform-specific implementations as needed
+  FatalConditionHandler() : m_started(false) {}
+  ~FatalConditionHandler() {}
+
+  void engage() {
+    assert(!m_started && "Handler cannot be installed twice.");
+    m_started = true;
+    engage_platform();
+  }
+
+  void disengage() {
+    assert(m_started &&
+           "Handler cannot be uninstalled without being installed first");
+    m_started = false;
+    disengage_platform();
+  }
+};
+} // namespace Catch
 
-#  else // CATCH_CONFIG_WINDOWS_SEH is defined
+#else // CATCH_CONFIG_WINDOWS_SEH is defined
 
 namespace Catch {
 
-    struct SignalDefs { DWORD id; const char* name; };
-    extern SignalDefs signalDefs[];
-    // There is no 1-1 mapping between signals and windows exceptions.
-    // Windows can easily distinguish between SO and SigSegV,
-    // but SigInt, SigTerm, etc are handled differently.
-    SignalDefs signalDefs[] = {
-        { EXCEPTION_ILLEGAL_INSTRUCTION,  "SIGILL - Illegal instruction signal" },
-        { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
-        { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
-        { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
-    };
+struct SignalDefs {
+  DWORD id;
+  const char *name;
+};
+extern SignalDefs signalDefs[];
+// There is no 1-1 mapping between signals and windows exceptions.
+// Windows can easily distinguish between SO and SigSegV,
+// but SigInt, SigTerm, etc are handled differently.
+SignalDefs signalDefs[] = {
+    {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"},
+    {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"},
+    {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"},
+    {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"},
+};
 
-    struct FatalConditionHandler {
+static LONG CALLBACK
+handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+  for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+    if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+      reportFatal(signalDefs[i].name);
+    }
+  }
+  // If its not an exception we care about, pass it along.
+  // This stops us from eating debugger breaks etc.
+  return EXCEPTION_CONTINUE_SEARCH;
+}
 
-        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
-            for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
-                if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
-                    reportFatal(signalDefs[i].name);
-                }
-            }
-            // If its not an exception we care about, pass it along.
-            // This stops us from eating debugger breaks etc.
-            return EXCEPTION_CONTINUE_SEARCH;
-        }
+// Since we do not support multiple instantiations, we put these
+// into global variables and rely on cleaning them up in outlined
+// constructors/destructors
+static PVOID exceptionHandlerHandle = CATCH_NULL;
 
-        FatalConditionHandler() {
-            isSet = true;
-            // 32k seems enough for Catch to handle stack overflow,
-            // but the value was found experimentally, so there is no strong guarantee
-            guaranteeSize = 32 * 1024;
-            exceptionHandlerHandle = CATCH_NULL;
-            // Register as first handler in current chain
-            exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
-            // Pass in guarantee size to be filled
-            SetThreadStackGuarantee(&guaranteeSize);
-        }
+class FatalConditionHandler {
+  bool m_started;
 
-        static void reset() {
-            if (isSet) {
-                // Unregister handler and restore the old guarantee
-                RemoveVectoredExceptionHandler(exceptionHandlerHandle);
-                SetThreadStackGuarantee(&guaranteeSize);
-                exceptionHandlerHandle = CATCH_NULL;
-                isSet = false;
-            }
-        }
+  // Install/disengage implementation for specific platform.
+  // Should be if-defed to work on current platform, can assume
+  // engage-disengage 1:1 pairing.
 
-        ~FatalConditionHandler() {
-            reset();
-        }
-    private:
-        static bool isSet;
-        static ULONG guaranteeSize;
-        static PVOID exceptionHandlerHandle;
-    };
+  void engage_platform() {
+    // Register as first handler in current chain
+    exceptionHandlerHandle =
+        AddVectoredExceptionHandler(1, handleVectoredException);
+    if (!exceptionHandlerHandle) {
+      throw std::runtime_error("Could not register vectored exception handler");
+    }
+  }
+
+  void disengage_platform() {
+    if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
+      throw std::runtime_error(
+          "Could not unregister vectored exception handler");
+    }
+    exceptionHandlerHandle = CATCH_NULL;
+  }
 
-    bool FatalConditionHandler::isSet = false;
-    ULONG FatalConditionHandler::guaranteeSize = 0;
-    PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
+public:
+  FatalConditionHandler() : m_started(false) {
+    ULONG guaranteeSize = static_cast<ULONG>(32 * 1024);
+    if (!SetThreadStackGuarantee(&guaranteeSize)) {
+      // We do not want to fully error out, because needing
+      // the stack reserve should be rare enough anyway.
+      Catch::cerr() << "Failed to reserve piece of stack."
+                    << " Stack overflows will not be reported successfully.";
+    }
+  }
+
+  // We do not attempt to unset the stack guarantee, because
+  // Windows does not support lowering the stack size guarantee.
+  ~FatalConditionHandler() {}
+
+  void engage() {
+    assert(!m_started && "Handler cannot be installed twice.");
+    m_started = true;
+    engage_platform();
+  }
+
+  void disengage() {
+    assert(m_started &&
+           "Handler cannot be uninstalled without being installed first");
+    m_started = false;
+    disengage_platform();
+  }
+};
 
 } // namespace Catch
 
-#  endif // CATCH_CONFIG_WINDOWS_SEH
+#endif // CATCH_CONFIG_WINDOWS_SEH
 
 #else // Not Windows - assumed to be POSIX compatible //////////////////////////
 
-#  if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#if !defined(CATCH_CONFIG_POSIX_SIGNALS)
 
 namespace Catch {
-    struct FatalConditionHandler {
-        void reset() {}
-    };
-}
+class FatalConditionHandler {
+  bool m_started;
 
-#  else // CATCH_CONFIG_POSIX_SIGNALS is defined
+  // Install/disengage implementation for specific platform.
+  // Should be if-defed to work on current platform, can assume
+  // engage-disengage 1:1 pairing.
+  void engage_platform() {}
+  void disengage_platform() {}
+
+public:
+  // Should also have platform-specific implementations as needed
+  FatalConditionHandler() : m_started(false) {}
+  ~FatalConditionHandler() {}
+
+  void engage() {
+    assert(!m_started && "Handler cannot be installed twice.");
+    m_started = true;
+    engage_platform();
+  }
+
+  void disengage() {
+    assert(m_started &&
+           "Handler cannot be uninstalled without being installed first");
+    m_started = false;
+    disengage_platform();
+  }
+};
+} // namespace Catch
+
+#else // CATCH_CONFIG_POSIX_SIGNALS is defined
 
 #include <signal.h>
 
 namespace Catch {
 
-    struct SignalDefs {
-        int id;
-        const char* name;
-    };
-    extern SignalDefs signalDefs[];
-    SignalDefs signalDefs[] = {
-            { SIGINT,  "SIGINT - Terminal interrupt signal" },
-            { SIGILL,  "SIGILL - Illegal instruction signal" },
-            { SIGFPE,  "SIGFPE - Floating point error signal" },
-            { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
-            { SIGTERM, "SIGTERM - Termination request signal" },
-            { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
-    };
-
-    struct FatalConditionHandler {
-
-        static bool isSet;
-        static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
-        static stack_t oldSigStack;
-        static char altStackMem[SIGSTKSZ];
-
-        static void handleSignal( int sig ) {
-            std::string name = "<unknown signal>";
-            for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
-                SignalDefs &def = signalDefs[i];
-                if (sig == def.id) {
-                    name = def.name;
-                    break;
-                }
-            }
-            reset();
-            reportFatal(name);
-            raise( sig );
-        }
+struct SignalDefs {
+  int id;
+  const char *name;
+};
+extern SignalDefs signalDefs[];
+SignalDefs signalDefs[] = {
+    {SIGINT, "SIGINT - Terminal interrupt signal"},
+    {SIGILL, "SIGILL - Illegal instruction signal"},
+    {SIGFPE, "SIGFPE - Floating point error signal"},
+    {SIGSEGV, "SIGSEGV - Segmentation violation signal"},
+    {SIGTERM, "SIGTERM - Termination request signal"},
+    {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}};
+
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+static char *altStackMem = CATCH_NULL;
+static std::size_t altStackSize = 0;
+static stack_t oldSigStack;
+static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)];
+
+static void restorePreviousSignalHandlers() {
+  // We set signal handlers back to the previous ones. Hopefully
+  // nobody overwrote them in the meantime, and doesn't expect
+  // their signal handlers to live past ours given that they
+  // installed them after ours..
+  for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+    sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
+  }
+  // Return the old stack
+  sigaltstack(&oldSigStack, CATCH_NULL);
+}
 
-        FatalConditionHandler() {
-            isSet = true;
-            stack_t sigStack;
-            sigStack.ss_sp = altStackMem;
-            sigStack.ss_size = SIGSTKSZ;
-            sigStack.ss_flags = 0;
-            sigaltstack(&sigStack, &oldSigStack);
-            struct sigaction sa = { 0 };
+static void handleSignal(int sig) {
+  char const *name = "<unknown signal>";
+  for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+    SignalDefs &def = signalDefs[i];
+    if (sig == def.id) {
+      name = def.name;
+      break;
+    }
+  }
+  // We need to restore previous signal handlers and let them do
+  // their thing, so that the users can have the debugger break
+  // when a signal is raised, and so on.
+  restorePreviousSignalHandlers();
+  reportFatal(name);
+  raise(sig);
+}
 
-            sa.sa_handler = handleSignal;
-            sa.sa_flags = SA_ONSTACK;
-            for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
-                sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
-            }
-        }
+class FatalConditionHandler {
+  bool m_started;
 
-        ~FatalConditionHandler() {
-            reset();
-        }
-        static void reset() {
-            if( isSet ) {
-                // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
-                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
-                    sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
-                }
-                // Return the old stack
-                sigaltstack(&oldSigStack, CATCH_NULL);
-                isSet = false;
-            }
-        }
-    };
+  // Install/disengage implementation for specific platform.
+  // Should be if-defed to work on current platform, can assume
+  // engage-disengage 1:1 pairing.
+
+  void engage_platform() {
+    stack_t sigStack;
+    sigStack.ss_sp = altStackMem;
+    sigStack.ss_size = SIGSTKSZ;
+    sigStack.ss_flags = 0;
+    sigaltstack(&sigStack, &oldSigStack);
+    struct sigaction sa = {0};
+
+    sa.sa_handler = handleSignal;
+    sa.sa_flags = SA_ONSTACK;
+    for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+      sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+    }
+  }
+
+  void disengage_platform() { restorePreviousSignalHandlers(); }
 
-    bool FatalConditionHandler::isSet = false;
-    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
-    stack_t FatalConditionHandler::oldSigStack = {};
-    char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+public:
+  FatalConditionHandler() : m_started(false) {
+    assert(!altStackMem &&
+           "Cannot initialize POSIX signal handler when one already exists");
+    if (altStackSize == 0) {
+      altStackSize = SIGSTKSZ;
+    }
+    altStackMem = new char[altStackSize]();
+  }
+
+  ~FatalConditionHandler() {
+    delete[] altStackMem;
+    // We signal that another instance can be constructed by zeroing
+    // out the pointer.
+    altStackMem = CATCH_NULL;
+  }
+
+  void engage() {
+    assert(!m_started && "Handler cannot be installed twice.");
+    m_started = true;
+    engage_platform();
+  }
+
+  void disengage() {
+    assert(m_started &&
+           "Handler cannot be uninstalled without being installed first");
+    m_started = false;
+    disengage_platform();
+  }
+};
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
 
 } // namespace Catch
 
-#  endif // CATCH_CONFIG_POSIX_SIGNALS
+#endif // CATCH_CONFIG_POSIX_SIGNALS
 
 #endif // not Windows
 
+namespace Catch {
+
+//! Simple RAII guard for (dis)engaging the FatalConditionHandler
+class FatalConditionHandlerGuard {
+  FatalConditionHandler *m_handler;
+
+public:
+  FatalConditionHandlerGuard(FatalConditionHandler *handler)
+      : m_handler(handler) {
+    m_handler->engage();
+  }
+  ~FatalConditionHandlerGuard() { m_handler->disengage(); }
+};
+
+} // end namespace Catch
+
 #include <cassert>
 #include <set>
 #include <string>
@@ -6938,9 +7075,8 @@ namespace Catch {
         }
 
         void invokeActiveTestCase() {
-            FatalConditionHandler fatalConditionHandler; // Handle signals
+            FatalConditionHandlerGuard _(&m_fatalConditionhandler);
             m_activeTestCase->invoke();
-            fatalConditionHandler.reset();
         }
 
     private:
@@ -6978,6 +7114,7 @@ namespace Catch {
         std::vector<SectionEndInfo> m_unfinishedSections;
         std::vector<ITracker*> m_activeSections;
         TrackerContext m_trackerContext;
+        FatalConditionHandler m_fatalConditionhandler;
         size_t m_prevPassed;
         bool m_shouldReportUnexpected;
     };