xref: /aosp_15_r20/external/cronet/third_party/libc++/src/test/support/check_assertion.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 <array>
13 #include <cassert>
14 #include <csignal>
15 #include <cstdarg>
16 #include <cstddef>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <exception>
20 #include <functional>
21 #include <regex>
22 #include <sstream>
23 #include <string>
24 #include <string_view>
25 #include <utility>
26 
27 #include <unistd.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <sys/wait.h>
31 
32 #include "test_macros.h"
33 #include "test_allocator.h"
34 
35 #if TEST_STD_VER < 11
36 #  error "C++11 or greater is required to use this header"
37 #endif
38 
39 // When printing the assertion message to `stderr`, delimit it with a marker to make it easier to match the message
40 // later.
41 static constexpr const char* Marker = "###";
42 
43 // (success, error-message-if-failed)
44 using MatchResult = std::pair<bool, std::string>;
45 using Matcher     = std::function<MatchResult(const std::string& /*text*/)>;
46 
MatchAssertionMessage(const std::string & text,std::string_view expected_message)47 MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message) {
48   // Extract information from the error message. This has to stay synchronized with how we format assertions in the
49   // library.
50   std::regex assertion_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
51 
52   std::smatch match_result;
53   bool has_match = std::regex_match(text, match_result, assertion_format);
54   assert(has_match);
55   assert(match_result.size() == 5);
56 
57   const std::string& file = match_result[1];
58   int line                = std::stoi(match_result[2]);
59   // Omitting `expression` in `match_result[3]`
60   const std::string& assertion_message = match_result[4];
61 
62   bool result = assertion_message == expected_message;
63   if (!result) {
64     std::stringstream matching_error;
65     matching_error                                                       //
66         << "Expected message:   '" << expected_message.data() << "'\n"   //
67         << "Actual message:     '" << assertion_message.c_str() << "'\n" //
68         << "Source location:     " << file << ":" << std::to_string(line) << "\n";
69     return MatchResult(/*success=*/false, matching_error.str());
70   }
71 
72   return MatchResult(/*success=*/true, /*maybe_error=*/"");
73 }
74 
MakeAssertionMessageMatcher(std::string_view assertion_message)75 Matcher MakeAssertionMessageMatcher(std::string_view assertion_message) {
76   return [=](const std::string& text) { //
77     return MatchAssertionMessage(text, assertion_message);
78   };
79 }
80 
MakeAnyMatcher()81 Matcher MakeAnyMatcher() {
82   return [](const std::string&) { //
83     return MatchResult(/*success=*/true, /*maybe_error=*/"");
84   };
85 }
86 
87 enum class DeathCause {
88   // Valid causes
89   VerboseAbort = 1,
90   StdAbort,
91   StdTerminate,
92   Trap,
93   // Invalid causes
94   DidNotDie,
95   SetupFailure,
96   Unknown
97 };
98 
IsValidCause(DeathCause cause)99 bool IsValidCause(DeathCause cause) {
100   switch (cause) {
101   case DeathCause::VerboseAbort:
102   case DeathCause::StdAbort:
103   case DeathCause::StdTerminate:
104   case DeathCause::Trap:
105     return true;
106   default:
107     return false;
108   }
109 }
110 
ToString(DeathCause cause)111 std::string ToString(DeathCause cause) {
112   switch (cause) {
113   case DeathCause::VerboseAbort:
114     return "verbose abort";
115   case DeathCause::StdAbort:
116     return "`std::abort`";
117   case DeathCause::StdTerminate:
118     return "`std::terminate`";
119   case DeathCause::Trap:
120     return "trap";
121   case DeathCause::DidNotDie:
122     return "<invalid cause (child did not die)>";
123   case DeathCause::SetupFailure:
124     return "<invalid cause (child failed to set up test environment)>";
125   case DeathCause::Unknown:
126     return "<invalid cause (cause unknown)>";
127   }
128 
129   assert(false && "Unreachable");
130 }
131 
132 template <std::size_t N>
ToString(std::array<DeathCause,N> const & causes)133 std::string ToString(std::array<DeathCause, N> const& causes) {
134   std::stringstream ss;
135   ss << "{";
136   for (std::size_t i = 0; i != N; ++i) {
137     ss << ToString(causes[i]);
138     if (i + 1 != N)
139       ss << ", ";
140   }
141   ss << "}";
142   return ss.str();
143 }
144 
StopChildProcess(DeathCause cause)145 TEST_NORETURN void StopChildProcess(DeathCause cause) { std::exit(static_cast<int>(cause)); }
146 
ConvertToDeathCause(int val)147 DeathCause ConvertToDeathCause(int val) {
148   if (val < static_cast<int>(DeathCause::VerboseAbort) || val > static_cast<int>(DeathCause::Unknown)) {
149     return DeathCause::Unknown;
150   }
151   return static_cast<DeathCause>(val);
152 }
153 
154 enum class Outcome {
155   Success,
156   UnexpectedCause,
157   UnexpectedErrorMessage,
158   InvalidCause,
159 };
160 
ToString(Outcome outcome)161 std::string ToString(Outcome outcome) {
162   switch (outcome) {
163   case Outcome::Success:
164     return "success";
165   case Outcome::UnexpectedCause:
166     return "unexpected death cause";
167   case Outcome::UnexpectedErrorMessage:
168     return "unexpected error message";
169   case Outcome::InvalidCause:
170     return "invalid death cause";
171   }
172 
173   assert(false && "Unreachable");
174 }
175 
176 class DeathTestResult {
177 public:
178   DeathTestResult() = default;
179   DeathTestResult(Outcome set_outcome, DeathCause set_cause, const std::string& set_failure_description = "")
outcome_(set_outcome)180       : outcome_(set_outcome), cause_(set_cause), failure_description_(set_failure_description) {}
181 
success()182   bool success() const { return outcome() == Outcome::Success; }
outcome()183   Outcome outcome() const { return outcome_; }
cause()184   DeathCause cause() const { return cause_; }
failure_description()185   const std::string& failure_description() const { return failure_description_; }
186 
187 private:
188   Outcome outcome_  = Outcome::Success;
189   DeathCause cause_ = DeathCause::Unknown;
190   std::string failure_description_;
191 };
192 
193 class DeathTest {
194 public:
195   DeathTest()                            = default;
196   DeathTest(DeathTest const&)            = delete;
197   DeathTest& operator=(DeathTest const&) = delete;
198 
199   template <std::size_t N, class Func>
Run(const std::array<DeathCause,N> & expected_causes,Func && func,const Matcher & matcher)200   DeathTestResult Run(const std::array<DeathCause, N>& expected_causes, Func&& func, const Matcher& matcher) {
201     std::signal(SIGABRT, [](int) { StopChildProcess(DeathCause::StdAbort); });
202     std::set_terminate([] { StopChildProcess(DeathCause::StdTerminate); });
203 
204     DeathCause cause = Run(func);
205 
206     if (!IsValidCause(cause)) {
207       return DeathTestResult(Outcome::InvalidCause, cause, ToString(cause));
208     }
209 
210     if (std::find(expected_causes.begin(), expected_causes.end(), cause) == expected_causes.end()) {
211       std::stringstream failure_description;
212       failure_description                                               //
213           << "Child died, but with a different death cause\n"           //
214           << "Expected cause(s): " << ToString(expected_causes) << "\n" //
215           << "Actual cause:      " << ToString(cause) << "\n";
216       return DeathTestResult(Outcome::UnexpectedCause, cause, failure_description.str());
217     }
218 
219     MatchResult match_result = matcher(GetChildStdErr());
220     if (!match_result.first) {
221       auto failure_description = std::string("Child died, but with a different error message\n") + match_result.second;
222       return DeathTestResult(Outcome::UnexpectedErrorMessage, cause, failure_description);
223     }
224 
225     return DeathTestResult(Outcome::Success, cause);
226   }
227 
PrintFailureDetails(std::string_view failure_description,std::string_view stmt,DeathCause cause)228   void PrintFailureDetails(std::string_view failure_description, std::string_view stmt, DeathCause cause) const {
229     std::fprintf(
230         stderr, "Failure: EXPECT_DEATH( %s ) failed!\n(reason: %s)\n\n", stmt.data(), failure_description.data());
231 
232     if (cause != DeathCause::Unknown) {
233       std::fprintf(stderr, "child exit code: %d\n", GetChildExitCode());
234     }
235     std::fprintf(stderr, "---------- standard err ----------\n%s", GetChildStdErr().c_str());
236     std::fprintf(stderr, "\n----------------------------------\n");
237     std::fprintf(stderr, "---------- standard out ----------\n%s", GetChildStdOut().c_str());
238     std::fprintf(stderr, "\n----------------------------------\n");
239   };
240 
241 private:
GetChildExitCode()242   int GetChildExitCode() const { return exit_code_; }
GetChildStdOut()243   std::string const& GetChildStdOut() const { return stdout_from_child_; }
GetChildStdErr()244   std::string const& GetChildStdErr() const { return stderr_from_child_; }
245 
246   template <class Func>
Run(Func && f)247   DeathCause Run(Func&& f) {
248     int pipe_res = pipe(stdout_pipe_fd_);
249     assert(pipe_res != -1 && "failed to create pipe");
250     pipe_res = pipe(stderr_pipe_fd_);
251     assert(pipe_res != -1 && "failed to create pipe");
252     pid_t child_pid = fork();
253     assert(child_pid != -1 && "failed to fork a process to perform a death test");
254     child_pid_ = child_pid;
255     if (child_pid_ == 0) {
256       RunForChild(std::forward<Func>(f));
257       assert(false && "unreachable");
258     }
259     return RunForParent();
260   }
261 
262   template <class Func>
RunForChild(Func && f)263   TEST_NORETURN void RunForChild(Func&& f) {
264     close(GetStdOutReadFD()); // don't need to read from the pipe in the child.
265     close(GetStdErrReadFD());
266     auto DupFD = [](int DestFD, int TargetFD) {
267       int dup_result = dup2(DestFD, TargetFD);
268       if (dup_result == -1)
269         StopChildProcess(DeathCause::SetupFailure);
270     };
271     DupFD(GetStdOutWriteFD(), STDOUT_FILENO);
272     DupFD(GetStdErrWriteFD(), STDERR_FILENO);
273 
274     f();
275     StopChildProcess(DeathCause::DidNotDie);
276   }
277 
ReadChildIOUntilEnd(int FD)278   static std::string ReadChildIOUntilEnd(int FD) {
279     std::string error_msg;
280     char buffer[256];
281     int num_read;
282     do {
283       while ((num_read = read(FD, buffer, 255)) > 0) {
284         buffer[num_read] = '\0';
285         error_msg += buffer;
286       }
287     } while (num_read == -1 && errno == EINTR);
288     return error_msg;
289   }
290 
CaptureIOFromChild()291   void CaptureIOFromChild() {
292     close(GetStdOutWriteFD()); // no need to write from the parent process
293     close(GetStdErrWriteFD());
294     stdout_from_child_ = ReadChildIOUntilEnd(GetStdOutReadFD());
295     stderr_from_child_ = ReadChildIOUntilEnd(GetStdErrReadFD());
296     close(GetStdOutReadFD());
297     close(GetStdErrReadFD());
298   }
299 
RunForParent()300   DeathCause RunForParent() {
301     CaptureIOFromChild();
302 
303     int status_value;
304     pid_t result = waitpid(child_pid_, &status_value, 0);
305     assert(result != -1 && "there is no child process to wait for");
306 
307     if (WIFEXITED(status_value)) {
308       exit_code_ = WEXITSTATUS(status_value);
309       return ConvertToDeathCause(exit_code_);
310     }
311 
312     if (WIFSIGNALED(status_value)) {
313       exit_code_ = WTERMSIG(status_value);
314       // `__builtin_trap` generqtes `SIGILL` on x86 and `SIGTRAP` on ARM.
315       if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
316         return DeathCause::Trap;
317       }
318     }
319 
320     return DeathCause::Unknown;
321   }
322 
GetStdOutReadFD()323   int GetStdOutReadFD() const { return stdout_pipe_fd_[0]; }
GetStdOutWriteFD()324   int GetStdOutWriteFD() const { return stdout_pipe_fd_[1]; }
GetStdErrReadFD()325   int GetStdErrReadFD() const { return stderr_pipe_fd_[0]; }
GetStdErrWriteFD()326   int GetStdErrWriteFD() const { return stderr_pipe_fd_[1]; }
327 
328   pid_t child_pid_ = -1;
329   int exit_code_   = -1;
330   int stdout_pipe_fd_[2];
331   int stderr_pipe_fd_[2];
332   std::string stdout_from_child_;
333   std::string stderr_from_child_;
334 };
335 
336 #ifdef _LIBCPP_VERSION
__libcpp_verbose_abort(char const * format,...)337 void std::__libcpp_verbose_abort(char const* format, ...) {
338   va_list args;
339   va_start(args, format);
340 
341   std::fprintf(stderr, "%s\n", Marker);
342   std::vfprintf(stderr, format, args);
343   std::fprintf(stderr, "%s", Marker);
344 
345   va_end(args);
346 
347   StopChildProcess(DeathCause::VerboseAbort);
348 }
349 #endif // _LIBCPP_VERSION
350 
351 template <std::size_t N, class Func>
ExpectDeath(const std::array<DeathCause,N> & expected_causes,const char * stmt,Func && func,const Matcher & matcher)352 bool ExpectDeath(
353     const std::array<DeathCause, N>& expected_causes, const char* stmt, Func&& func, const Matcher& matcher) {
354   for (auto cause : expected_causes)
355     assert(IsValidCause(cause));
356 
357   DeathTest test_case;
358   DeathTestResult test_result = test_case.Run(expected_causes, func, matcher);
359   if (!test_result.success()) {
360     test_case.PrintFailureDetails(test_result.failure_description(), stmt, test_result.cause());
361   }
362 
363   return test_result.success();
364 }
365 
366 template <class Func>
ExpectDeath(DeathCause expected_cause,const char * stmt,Func && func,const Matcher & matcher)367 bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func, const Matcher& matcher) {
368   return ExpectDeath(std::array<DeathCause, 1>{expected_cause}, stmt, func, matcher);
369 }
370 
371 template <std::size_t N, class Func>
ExpectDeath(const std::array<DeathCause,N> & expected_causes,const char * stmt,Func && func)372 bool ExpectDeath(const std::array<DeathCause, N>& expected_causes, const char* stmt, Func&& func) {
373   return ExpectDeath(expected_causes, stmt, func, MakeAnyMatcher());
374 }
375 
376 template <class Func>
ExpectDeath(DeathCause expected_cause,const char * stmt,Func && func)377 bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
378   return ExpectDeath(std::array<DeathCause, 1>{expected_cause}, stmt, func, MakeAnyMatcher());
379 }
380 
381 // clang-format off
382 
383 /// Assert that the specified expression aborts with the expected cause and, optionally, error message.
384 #define EXPECT_ANY_DEATH(...)                         \
385     assert(( ExpectDeath(std::array<DeathCause, 4>{DeathCause::VerboseAbort, DeathCause::StdAbort, DeathCause::StdTerminate, DeathCause::Trap}, #__VA_ARGS__, [&]() { __VA_ARGS__; } ) ))
386 #define EXPECT_DEATH(...)                         \
387     assert(( ExpectDeath(DeathCause::VerboseAbort, #__VA_ARGS__, [&]() { __VA_ARGS__; } ) ))
388 #define EXPECT_DEATH_MATCHES(matcher, ...)        \
389     assert(( ExpectDeath(DeathCause::VerboseAbort, #__VA_ARGS__, [&]() { __VA_ARGS__; }, matcher) ))
390 #define EXPECT_STD_ABORT(...)                 \
391     assert(  ExpectDeath(DeathCause::StdAbort, #__VA_ARGS__, [&]() { __VA_ARGS__; })  )
392 #define EXPECT_STD_TERMINATE(...)                 \
393     assert(  ExpectDeath(DeathCause::StdTerminate, #__VA_ARGS__, __VA_ARGS__)  )
394 
395 #if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
396 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
397     assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
398 #else
399 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
400     assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
401 #endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
402 
403 // clang-format on
404 
405 #endif // TEST_SUPPORT_CHECK_ASSERTION_H
406