1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef TEST_SUPPORT_CHECK_ASSERTION_H 10 #define TEST_SUPPORT_CHECK_ASSERTION_H 11 12 #ifndef _LIBCPP_DEBUG 13 #error _LIBCPP_DEBUG must be defined before including this header 14 #endif 15 16 #include <ciso646> 17 #ifndef _LIBCPP_VERSION 18 #error "This header may only be used for libc++ tests" 19 #endif 20 21 #include <__debug> 22 #include <cassert> 23 #include <cstddef> 24 #include <cstdio> 25 #include <cstdlib> 26 #include <string> 27 #include <string_view> 28 #include <utility> 29 30 #include <unistd.h> 31 #include <errno.h> 32 #include <sys/wait.h> 33 #include "test_macros.h" 34 #include "test_allocator.h" 35 36 #if TEST_STD_VER < 11 37 # error "C++11 or greater is required to use this header" 38 #endif 39 40 struct DebugInfoMatcher { 41 static const int any_line = -1; 42 static constexpr const char* any_file = "*"; 43 static constexpr const char* any_msg = "*"; 44 45 constexpr DebugInfoMatcher() : is_empty_(true), msg_(any_msg, __builtin_strlen(any_msg)), file_(any_file, __builtin_strlen(any_file)), line_(any_line) { } 46 constexpr DebugInfoMatcher(const char* msg, const char* file = any_file, int line = any_line) 47 : is_empty_(false), msg_(msg, __builtin_strlen(msg)), file_(file, __builtin_strlen(file)), line_(line) {} 48 49 bool Matches(std::__libcpp_debug_info const& got) const { 50 assert(!empty() && "empty matcher"); 51 52 if (CheckLineMatches(got.__line_) && CheckFileMatches(got.__file_) && 53 CheckMessageMatches(got.__msg_)) 54 return true; 55 // Write to stdout because that's the file descriptor captured by the parent 56 // process. 57 std::printf("Failed to match debug info!\n%s\nVS\n%s\n", ToString().data(), got.what().data()); 58 return false; 59 } 60 61 std::string ToString() const { 62 std::string result = "msg = \""; result += msg_; result += "\"\n"; 63 result += "line = " + (line_ == any_line ? "'*'" : std::to_string(line_)) + "\n"; 64 result += "file = " + (file_ == any_file ? "'*'" : std::string(file_)); 65 return result; 66 } 67 68 bool empty() const { return is_empty_; } 69 private: 70 bool CheckLineMatches(int got_line) const { 71 if (line_ == any_line) 72 return true; 73 return got_line == line_; 74 } 75 76 bool CheckFileMatches(std::string_view got_file) const { 77 assert(!empty() && "empty matcher"); 78 if (file_ == any_file) 79 return true; 80 std::size_t found_at = got_file.find(file_); 81 if (found_at == std::string_view::npos) 82 return false; 83 // require the match start at the beginning of the file or immediately after 84 // a directory separator. 85 if (found_at != 0) { 86 char last_char = got_file[found_at - 1]; 87 if (last_char != '/' && last_char != '\\') 88 return false; 89 } 90 // require the match goes until the end of the string. 91 return got_file.substr(found_at) == file_; 92 } 93 94 bool CheckMessageMatches(std::string_view got_msg) const { 95 assert(!empty() && "empty matcher"); 96 if (msg_ == any_msg) 97 return true; 98 std::size_t found_at = got_msg.find(msg_); 99 if (found_at == std::string_view::npos) 100 return false; 101 // Allow any match 102 return true; 103 } 104 private: 105 bool is_empty_; 106 std::string_view msg_; 107 std::string_view file_; 108 int line_; 109 }; 110 111 static constexpr DebugInfoMatcher AnyMatcher(DebugInfoMatcher::any_msg); 112 113 inline DebugInfoMatcher& GlobalMatcher() { 114 static DebugInfoMatcher GMatch; 115 return GMatch; 116 } 117 118 struct DeathTest { 119 enum ResultKind { 120 RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_SetupFailure, RK_Unknown 121 }; 122 123 static const char* ResultKindToString(ResultKind RK) { 124 #define CASE(K) case K: return #K 125 switch (RK) { 126 CASE(RK_MatchFailure); 127 CASE(RK_DidNotDie); 128 CASE(RK_SetupFailure); 129 CASE(RK_MatchFound); 130 CASE(RK_Unknown); 131 } 132 return "not a result kind"; 133 } 134 135 static bool IsValidResultKind(int val) { 136 return val >= RK_DidNotDie && val <= RK_Unknown; 137 } 138 139 TEST_NORETURN static void DeathTestDebugHandler(std::__libcpp_debug_info const& info) { 140 assert(!GlobalMatcher().empty()); 141 if (GlobalMatcher().Matches(info)) { 142 std::exit(RK_MatchFound); 143 } 144 std::exit(RK_MatchFailure); 145 } 146 147 DeathTest(DebugInfoMatcher const& Matcher) : matcher_(Matcher) {} 148 149 template <class Func> 150 ResultKind Run(Func&& f) { 151 int pipe_res = pipe(stdout_pipe_fd_); 152 assert(pipe_res != -1 && "failed to create pipe"); 153 pipe_res = pipe(stderr_pipe_fd_); 154 assert(pipe_res != -1 && "failed to create pipe"); 155 pid_t child_pid = fork(); 156 assert(child_pid != -1 && 157 "failed to fork a process to perform a death test"); 158 child_pid_ = child_pid; 159 if (child_pid_ == 0) { 160 RunForChild(std::forward<Func>(f)); 161 assert(false && "unreachable"); 162 } 163 return RunForParent(); 164 } 165 166 int getChildExitCode() const { return exit_code_; } 167 std::string const& getChildStdOut() const { return stdout_from_child_; } 168 std::string const& getChildStdErr() const { return stderr_from_child_; } 169 private: 170 template <class Func> 171 TEST_NORETURN void RunForChild(Func&& f) { 172 close(GetStdOutReadFD()); // don't need to read from the pipe in the child. 173 close(GetStdErrReadFD()); 174 auto DupFD = [](int DestFD, int TargetFD) { 175 int dup_result = dup2(DestFD, TargetFD); 176 if (dup_result == -1) 177 std::exit(RK_SetupFailure); 178 }; 179 DupFD(GetStdOutWriteFD(), STDOUT_FILENO); 180 DupFD(GetStdErrWriteFD(), STDERR_FILENO); 181 182 GlobalMatcher() = matcher_; 183 std::__libcpp_set_debug_function(&DeathTestDebugHandler); 184 f(); 185 std::exit(RK_DidNotDie); 186 } 187 188 static std::string ReadChildIOUntilEnd(int FD) { 189 std::string error_msg; 190 char buffer[256]; 191 int num_read; 192 do { 193 while ((num_read = read(FD, buffer, 255)) > 0) { 194 buffer[num_read] = '\0'; 195 error_msg += buffer; 196 } 197 } while (num_read == -1 && errno == EINTR); 198 return error_msg; 199 } 200 201 void CaptureIOFromChild() { 202 close(GetStdOutWriteFD()); // no need to write from the parent process 203 close(GetStdErrWriteFD()); 204 stdout_from_child_ = ReadChildIOUntilEnd(GetStdOutReadFD()); 205 stderr_from_child_ = ReadChildIOUntilEnd(GetStdErrReadFD()); 206 close(GetStdOutReadFD()); 207 close(GetStdErrReadFD()); 208 } 209 210 ResultKind RunForParent() { 211 CaptureIOFromChild(); 212 213 int status_value; 214 pid_t result = waitpid(child_pid_, &status_value, 0); 215 assert(result != -1 && "there is no child process to wait for"); 216 217 if (WIFEXITED(status_value)) { 218 exit_code_ = WEXITSTATUS(status_value); 219 if (!IsValidResultKind(exit_code_)) 220 return RK_Unknown; 221 return static_cast<ResultKind>(exit_code_); 222 } 223 return RK_Unknown; 224 } 225 226 DeathTest(DeathTest const&) = delete; 227 DeathTest& operator=(DeathTest const&) = delete; 228 229 int GetStdOutReadFD() const { 230 return stdout_pipe_fd_[0]; 231 } 232 233 int GetStdOutWriteFD() const { 234 return stdout_pipe_fd_[1]; 235 } 236 237 int GetStdErrReadFD() const { 238 return stderr_pipe_fd_[0]; 239 } 240 241 int GetStdErrWriteFD() const { 242 return stderr_pipe_fd_[1]; 243 } 244 private: 245 DebugInfoMatcher matcher_; 246 pid_t child_pid_ = -1; 247 int exit_code_ = -1; 248 int stdout_pipe_fd_[2]; 249 int stderr_pipe_fd_[2]; 250 std::string stdout_from_child_; 251 std::string stderr_from_child_; 252 }; 253 254 template <class Func> 255 inline bool ExpectDeath(const char* stmt, Func&& func, DebugInfoMatcher Matcher) { 256 DeathTest DT(Matcher); 257 DeathTest::ResultKind RK = DT.Run(func); 258 auto OnFailure = [&](const char* msg) { 259 std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg); 260 if (RK != DeathTest::RK_Unknown) { 261 std::fprintf(stderr, "child exit code: %d\n", DT.getChildExitCode()); 262 } 263 if (!DT.getChildStdErr().empty()) { 264 std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str()); 265 } 266 if (!DT.getChildStdOut().empty()) { 267 std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str()); 268 } 269 return false; 270 }; 271 switch (RK) { 272 case DeathTest::RK_MatchFound: 273 return true; 274 case DeathTest::RK_SetupFailure: 275 return OnFailure("child failed to setup test environment"); 276 case DeathTest::RK_Unknown: 277 return OnFailure("reason unknown"); 278 case DeathTest::RK_DidNotDie: 279 return OnFailure("child did not die"); 280 case DeathTest::RK_MatchFailure: 281 return OnFailure("matcher failed"); 282 } 283 assert(false && "unreachable"); 284 } 285 286 template <class Func> 287 inline bool ExpectDeath(const char* stmt, Func&& func) { 288 return ExpectDeath(stmt, func, AnyMatcher); 289 } 290 291 /// Assert that the specified expression throws a libc++ debug exception. 292 #define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } ))) 293 294 #define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher))) 295 296 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) assert((ExpectDeath(#expr, [&]() { (void)(expr); }, DebugInfoMatcher(message)))) 297 298 #endif // TEST_SUPPORT_CHECK_ASSERTION_H 299