xref: /llvm-project/libcxx/test/support/check_assertion.h (revision 4001b82b15d85691dfe3cbacd519772a706cd846)
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