capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

test-helpers.c++ (5313B)


      1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #ifndef _GNU_SOURCE
     23 #define _GNU_SOURCE
     24 #endif
     25 
     26 #include "test.h"
     27 
     28 #include <string.h>
     29 #ifndef _WIN32
     30 #include <unistd.h>
     31 #include <sys/types.h>
     32 #include <sys/wait.h>
     33 #else
     34 #include <process.h>
     35 #endif
     36 
     37 namespace kj {
     38 namespace _ {  // private
     39 
     40 bool hasSubstring(StringPtr haystack, StringPtr needle) {
     41   if (needle.size() <= haystack.size()) {
     42     // Boyer Moore Horspool wins https://quick-bench.com/q/RiKdKduhdLb6x_DfS1fHaksqwdQ
     43     // https://quick-bench.com/q/KV8irwXrkvsNMbNpP8ENR_tBEPY but libc++ only has default_searcher
     44     // which performs *drastically worse* than the naiive algorithm (seriously - why even bother?).
     45     // Hell, doing a query for an embedded null & dispatching to strstr is still cheaper & only
     46     // marginally slower than the purely naiive implementation.
     47 
     48 #if !defined(_WIN32)
     49     return memmem(haystack.begin(), haystack.size(), needle.begin(), needle.size()) != nullptr;
     50 #else
     51     // TODO(perf): This is not the best algorithm for substring matching. strstr can't be used
     52     //   because this is supposed to be safe to call on strings with embedded nulls.
     53     // Amusingly this naiive algorithm some times outperforms std::default_searcher, even if we need
     54     // to double-check first if the needle has an embedded null (indicating std::search ).
     55     for (size_t i = 0; i <= haystack.size() - needle.size(); i++) {
     56       if (haystack.slice(i).startsWith(needle)) {
     57         return true;
     58       }
     59     }
     60 #endif
     61   }
     62   return false;
     63 }
     64 
     65 LogExpectation::LogExpectation(LogSeverity severity, StringPtr substring)
     66     : severity(severity), substring(substring), seen(false) {}
     67 LogExpectation::~LogExpectation() {
     68   if (!unwindDetector.isUnwinding()) {
     69     KJ_ASSERT(seen, "expected log message not seen", severity, substring);
     70   }
     71 }
     72 
     73 void LogExpectation::logMessage(
     74     LogSeverity severity, const char* file, int line, int contextDepth,
     75     String&& text) {
     76   if (!seen && severity == this->severity) {
     77     if (hasSubstring(text, substring)) {
     78       // Match. Ignore it.
     79       seen = true;
     80       return;
     81     }
     82   }
     83 
     84   // Pass up the chain.
     85   ExceptionCallback::logMessage(severity, file, line, contextDepth, kj::mv(text));
     86 }
     87 
     88 // =======================================================================================
     89 
     90 namespace {
     91 
     92 class FatalThrowExpectation: public ExceptionCallback {
     93 public:
     94   FatalThrowExpectation(Maybe<Exception::Type> type,
     95                         Maybe<StringPtr> message)
     96       : type(type), message(message) {}
     97 
     98   virtual void onFatalException(Exception&& exception) {
     99     KJ_IF_MAYBE(expectedType, type) {
    100       if (exception.getType() != *expectedType) {
    101         KJ_LOG(ERROR, "threw exception of wrong type", exception, *expectedType);
    102         _exit(1);
    103       }
    104     }
    105     KJ_IF_MAYBE(expectedSubstring, message) {
    106       if (!hasSubstring(exception.getDescription(), *expectedSubstring)) {
    107         KJ_LOG(ERROR, "threw exception with wrong message", exception, *expectedSubstring);
    108         _exit(1);
    109       }
    110     }
    111     _exit(0);
    112   }
    113 
    114 private:
    115   Maybe<Exception::Type> type;
    116   Maybe<StringPtr> message;
    117 };
    118 
    119 }  // namespace
    120 
    121 bool expectFatalThrow(kj::Maybe<Exception::Type> type, kj::Maybe<StringPtr> message,
    122                       Function<void()> code) {
    123 #if _WIN32
    124   // We don't support death tests on Windows due to lack of efficient fork.
    125   return true;
    126 #else
    127   pid_t child;
    128   KJ_SYSCALL(child = fork());
    129   if (child == 0) {
    130     KJ_DEFER(_exit(1));
    131     FatalThrowExpectation expectation(type, message);
    132     KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
    133       code();
    134     })) {
    135       KJ_LOG(ERROR, "a non-fatal exception was thrown, but we expected fatal", *e);
    136     } else {
    137       KJ_LOG(ERROR, "no fatal exception was thrown");
    138     }
    139   }
    140 
    141   int status;
    142   KJ_SYSCALL(waitpid(child, &status, 0));
    143 
    144   if (WIFEXITED(status)) {
    145     return WEXITSTATUS(status) == 0;
    146   } else if (WIFSIGNALED(status)) {
    147     KJ_FAIL_EXPECT("subprocess crashed without throwing exception", WTERMSIG(status));
    148     return false;
    149   } else {
    150     KJ_FAIL_EXPECT("subprocess neiter excited nor crashed?", status);
    151     return false;
    152   }
    153 #endif
    154 }
    155 
    156 }  // namespace _ (private)
    157 }  // namespace kj