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