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