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