// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include "test.h" #ifndef _WIN32 #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #else #include <process.h> #endif namespace kj { namespace _ { // private bool hasSubstring(StringPtr haystack, StringPtr needle) { // TODO(perf): This is not the best algorithm for substring matching. if (needle.size() <= haystack.size()) { for (size_t i = 0; i <= haystack.size() - needle.size(); i++) { if (haystack.slice(i).startsWith(needle)) { return true; } } } return false; } LogExpectation::LogExpectation(LogSeverity severity, StringPtr substring) : severity(severity), substring(substring), seen(false) {} LogExpectation::~LogExpectation() { if (!unwindDetector.isUnwinding()) { KJ_ASSERT(seen, "expected log message not seen", severity, substring); } } void LogExpectation::logMessage( LogSeverity severity, const char* file, int line, int contextDepth, String&& text) { if (!seen && severity == this->severity) { if (hasSubstring(text, substring)) { // Match. Ignore it. seen = true; return; } } // Pass up the chain. ExceptionCallback::logMessage(severity, file, line, contextDepth, kj::mv(text)); } // ======================================================================================= namespace { class FatalThrowExpectation: public ExceptionCallback { public: FatalThrowExpectation(Maybe<Exception::Type> type, Maybe<StringPtr> message) : type(type), message(message) {} virtual void onFatalException(Exception&& exception) { KJ_IF_MAYBE(expectedType, type) { if (exception.getType() != *expectedType) { KJ_LOG(ERROR, "threw exception of wrong type", exception, *expectedType); _exit(1); } } KJ_IF_MAYBE(expectedSubstring, message) { if (!hasSubstring(exception.getDescription(), *expectedSubstring)) { KJ_LOG(ERROR, "threw exception with wrong message", exception, *expectedSubstring); _exit(1); } } _exit(0); } private: Maybe<Exception::Type> type; Maybe<StringPtr> message; }; } // namespace bool expectFatalThrow(kj::Maybe<Exception::Type> type, kj::Maybe<StringPtr> message, Function<void()> code) { #if _WIN32 // We don't support death tests on Windows due to lack of efficient fork. return true; #else pid_t child; KJ_SYSCALL(child = fork()); if (child == 0) { KJ_DEFER(_exit(1)); FatalThrowExpectation expectation(type, message); KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() { code(); })) { KJ_LOG(ERROR, "a non-fatal exception was thrown, but we expected fatal", *e); } else { KJ_LOG(ERROR, "no fatal exception was thrown"); } } int status; KJ_SYSCALL(waitpid(child, &status, 0)); if (WIFEXITED(status)) { return WEXITSTATUS(status) == 0; } else if (WIFSIGNALED(status)) { KJ_FAIL_EXPECT("subprocess crashed without throwing exception", WTERMSIG(status)); return false; } else { KJ_FAIL_EXPECT("subprocess neiter excited nor crashed?", status); return false; } #endif } } // namespace _ (private) } // namespace kj