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#include "test.h"
23#ifndef _WIN32
24#include <unistd.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#else
28#include <process.h>
29#endif
30
31namespace kj {
32namespace _ { // private
33
34bool hasSubstring(StringPtr haystack, StringPtr needle) {
35 // TODO(perf): This is not the best algorithm for substring matching.
36 if (needle.size() <= haystack.size()) {
37 for (size_t i = 0; i <= haystack.size() - needle.size(); i++) {
38 if (haystack.slice(i).startsWith(needle)) {
39 return true;
40 }
41 }
42 }
43 return false;
44}
45
46LogExpectation::LogExpectation(LogSeverity severity, StringPtr substring)
47 : severity(severity), substring(substring), seen(false) {}
48LogExpectation::~LogExpectation() {
49 if (!unwindDetector.isUnwinding()) {
50 KJ_ASSERT(seen, "expected log message not seen", severity, substring);
51 }
52}
53
54void LogExpectation::logMessage(
55 LogSeverity severity, const char* file, int line, int contextDepth,
56 String&& text) {
57 if (!seen && severity == this->severity) {
58 if (hasSubstring(text, substring)) {
59 // Match. Ignore it.
60 seen = true;
61 return;
62 }
63 }
64
65 // Pass up the chain.
66 ExceptionCallback::logMessage(severity, file, line, contextDepth, kj::mv(text));
67}
68
69// =======================================================================================
70
71namespace {
72
73class FatalThrowExpectation: public ExceptionCallback {
74public:
75 FatalThrowExpectation(Maybe<Exception::Type> type,
76 Maybe<StringPtr> message)
77 : type(type), message(message) {}
78
79 virtual void onFatalException(Exception&& exception) {
80 KJ_IF_MAYBE(expectedType, type) {
81 if (exception.getType() != *expectedType) {
82 KJ_LOG(ERROR, "threw exception of wrong type", exception, *expectedType);
83 _exit(1);
84 }
85 }
86 KJ_IF_MAYBE(expectedSubstring, message) {
87 if (!hasSubstring(exception.getDescription(), *expectedSubstring)) {
88 KJ_LOG(ERROR, "threw exception with wrong message", exception, *expectedSubstring);
89 _exit(1);
90 }
91 }
92 _exit(0);
93 }
94
95private:
96 Maybe<Exception::Type> type;
97 Maybe<StringPtr> message;
98};
99
100} // namespace
101
102bool expectFatalThrow(kj::Maybe<Exception::Type> type, kj::Maybe<StringPtr> message,
103 Function<void()> code) {
104#if _WIN32
105 // We don't support death tests on Windows due to lack of efficient fork.
106 return true;
107#else
108 pid_t child;
109 KJ_SYSCALL(child = fork());
110 if (child == 0) {
111 KJ_DEFER(_exit(1));
112 FatalThrowExpectation expectation(type, message);
113 KJ_IF_MAYBE(e, kj::runCatchingExceptions([&]() {
114 code();
115 })) {
116 KJ_LOG(ERROR, "a non-fatal exception was thrown, but we expected fatal", *e);
117 } else {
118 KJ_LOG(ERROR, "no fatal exception was thrown");
119 }
120 }
121
122 int status;
123 KJ_SYSCALL(waitpid(child, &status, 0));
124
125 if (WIFEXITED(status)) {
126 return WEXITSTATUS(status) == 0;
127 } else if (WIFSIGNALED(status)) {
128 KJ_FAIL_EXPECT("subprocess crashed without throwing exception", WTERMSIG(status));
129 return false;
130 } else {
131 KJ_FAIL_EXPECT("subprocess neiter excited nor crashed?", status);
132 return false;
133 }
134#endif
135}
136
137} // namespace _ (private)
138} // namespace kj
139