blob: 5e72b6ec82cff7c1f5d41d5028cef59a2a04c4f5 [file] [log] [blame]
Austin Schuhf2a50ba2016-12-24 16:16:26 -08001#include <libgen.h>
Brian Silverman4c04efd2013-09-07 14:51:33 -07002#include <sys/types.h>
3#include <sys/wait.h>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08004#include <unistd.h>
Brian Silverman4c04efd2013-09-07 14:51:33 -07005
Tyler Chatowbf0609c2021-07-31 16:13:27 -07006#include <cerrno>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08007#include <chrono>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07008#include <cstdio>
9#include <cstring>
Brian Silverman4c04efd2013-09-07 14:51:33 -070010#include <string>
11
John Park33858a32018-09-28 23:05:48 -070012#include "aos/die.h"
Tyler Chatowbf0609c2021-07-31 16:13:27 -070013#include "aos/ipc_lib/core_lib.h"
John Park33858a32018-09-28 23:05:48 -070014#include "aos/libc/aos_strsignal.h"
15#include "aos/libc/dirname.h"
16#include "aos/logging/logging.h"
17#include "aos/mutex/mutex.h"
Tyler Chatowbf0609c2021-07-31 16:13:27 -070018#include "aos/testing/test_shm.h"
John Park33858a32018-09-28 23:05:48 -070019#include "aos/time/time.h"
20#include "aos/type_traits/type_traits.h"
Brian Silverman4c04efd2013-09-07 14:51:33 -070021
22// This runs all of the IPC-related tests in a bunch of parallel processes for a
23// while and makes sure that they don't fail. It also captures the stdout and
24// stderr output from each test run and only prints it out (not interleaved with
25// the output from any other run) if the test fails.
26//
Brian Silverman5f8c4922014-02-11 21:22:38 -080027// They have to be run in separate processes because (in addition to various
28// parts of our code not being thread-safe...) gtest does not like multiple
29// threads.
30//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -070031// It's written in C++ for performance. We need actual OS-level parallelism for
Brian Silverman4c04efd2013-09-07 14:51:33 -070032// this to work, which means that Ruby's out because it doesn't have good
33// support for doing that. My Python implementation ended up pretty heavily disk
Brian Silvermaneeb62ca2013-09-11 15:08:03 -070034// IO-bound, which is a bad way to test CPU contention.
Brian Silverman4c04efd2013-09-07 14:51:33 -070035
36namespace aos {
37
Austin Schuhf2a50ba2016-12-24 16:16:26 -080038namespace chrono = ::std::chrono;
39
Brian Silverman4c04efd2013-09-07 14:51:33 -070040// Each test is represented by the name of the test binary and then any
41// arguments to pass to it.
42// Using --gtest_filter is a bad idea because it seems to result in a lot of
Brian Silvermaneeb62ca2013-09-11 15:08:03 -070043// swapping which causes everything to be disk-bound (at least for me).
Brian Silvermanac5cd382014-02-12 14:50:23 -080044static const size_t kTestMaxArgs = 10;
Tyler Chatowbf0609c2021-07-31 16:13:27 -070045static const char *kTests[][kTestMaxArgs] = {
46 {"queue_test"},
47 {"condition_test"},
48 {"mutex_test"},
49 {"raw_queue_test"},
Brian Silverman4c04efd2013-09-07 14:51:33 -070050};
Brian Silvermanac5cd382014-02-12 14:50:23 -080051static const size_t kTestsLength = sizeof(kTests) / sizeof(kTests[0]);
Brian Silverman4c04efd2013-09-07 14:51:33 -070052// These arguments get inserted before any per-test arguments.
Brian Silverman5f8c4922014-02-11 21:22:38 -080053static const char *kDefaultArgs[] = {
Tyler Chatowbf0609c2021-07-31 16:13:27 -070054 "--gtest_repeat=30",
55 "--gtest_shuffle",
Brian Silverman4c04efd2013-09-07 14:51:33 -070056};
57
58// How many test processes to run at a time.
Brian Silvermaneeb62ca2013-09-11 15:08:03 -070059static const int kTesters = 100;
Brian Silverman4c04efd2013-09-07 14:51:33 -070060// How long to test for.
Austin Schuhf2a50ba2016-12-24 16:16:26 -080061static constexpr monotonic_clock::duration kTestTime = chrono::seconds(30);
Brian Silverman4c04efd2013-09-07 14:51:33 -070062
63// The structure that gets put into shared memory and then referenced by all of
64// the child processes.
65struct Shared {
Austin Schuhf2a50ba2016-12-24 16:16:26 -080066 Shared(const monotonic_clock::time_point stop_time)
Tyler Chatowbf0609c2021-07-31 16:13:27 -070067 : stop_time(stop_time), total_iterations(0) {}
Brian Silverman4c04efd2013-09-07 14:51:33 -070068
69 // Synchronizes access to stdout/stderr to avoid interleaving failure
70 // messages.
71 Mutex output_mutex;
72
73 // When to stop.
Austin Schuhf2a50ba2016-12-24 16:16:26 -080074 monotonic_clock::time_point stop_time;
Brian Silverman4c04efd2013-09-07 14:51:33 -070075
76 // The total number of iterations. Updated by each child as it finishes.
77 int total_iterations;
78 // Sychronizes writes to total_iterations
79 Mutex total_iterations_mutex;
80
81 const char *path;
82};
83static_assert(shm_ok<Shared>::value,
84 "it's going to get shared between forked processes");
85
86// Gets called after each child forks to run a test.
Tyler Chatowbf0609c2021-07-31 16:13:27 -070087void __attribute__((noreturn))
88DoRunTest(Shared *shared, const char *(*test)[kTestMaxArgs], int pipes[2]) {
Brian Silverman4c04efd2013-09-07 14:51:33 -070089 if (close(pipes[0]) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -070090 PDie("close(%d) of read end of pipe failed", pipes[0]);
Brian Silverman4c04efd2013-09-07 14:51:33 -070091 }
92 if (close(STDIN_FILENO) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -070093 PDie("close(STDIN_FILENO(=%d)) failed", STDIN_FILENO);
Brian Silverman4c04efd2013-09-07 14:51:33 -070094 }
95 if (dup2(pipes[1], STDOUT_FILENO) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -070096 PDie("dup2(%d, STDOUT_FILENO(=%d)) failed", pipes[1], STDOUT_FILENO);
Brian Silverman4c04efd2013-09-07 14:51:33 -070097 }
98 if (dup2(pipes[1], STDERR_FILENO) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -070099 PDie("dup2(%d, STDERR_FILENO(=%d)) failed", pipes[1], STDERR_FILENO);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700100 }
101
Brian Silvermanac5cd382014-02-12 14:50:23 -0800102 size_t size = kTestMaxArgs;
103 size_t default_size = sizeof(kDefaultArgs) / sizeof(kDefaultArgs[0]);
Brian Silverman5f8c4922014-02-11 21:22:38 -0800104 // There's no chance to free this because we either exec or Die.
Brian Silverman4c04efd2013-09-07 14:51:33 -0700105 const char **args = new const char *[size + default_size + 1];
106 // The actual executable to run.
107 ::std::string executable;
108 int i = 0;
Brian Silvermanac5cd382014-02-12 14:50:23 -0800109 for (size_t test_i = 0; test_i < size; ++test_i) {
110 const char *c = (*test)[test_i];
Brian Silverman4c04efd2013-09-07 14:51:33 -0700111 if (i == 0) {
112 executable = ::std::string(shared->path) + "/" + c;
113 args[0] = executable.c_str();
114 for (const ::std::string &ci : kDefaultArgs) {
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700115 args[++i] = ci.c_str();
Brian Silverman4c04efd2013-09-07 14:51:33 -0700116 }
117 } else {
Brian Silvermanac5cd382014-02-12 14:50:23 -0800118 args[i] = c;
Brian Silverman4c04efd2013-09-07 14:51:33 -0700119 }
120 ++i;
121 }
122 args[size] = NULL;
123 execv(executable.c_str(), const_cast<char *const *>(args));
Brian Silverman01be0002014-05-10 15:44:38 -0700124 PDie("execv(%s, %p) failed", executable.c_str(), args);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700125}
126
127void DoRun(Shared *shared) {
128 int iterations = 0;
129 // An iterator pointing to a random one of the tests.
Brian Silverman5f8c4922014-02-11 21:22:38 -0800130 // We randomize based on PID because otherwise they all end up running the
131 // same test at the same time for the whole test.
Brian Silvermanac5cd382014-02-12 14:50:23 -0800132 const char *(*test)[kTestMaxArgs] = &kTests[getpid() % kTestsLength];
Brian Silverman4c04efd2013-09-07 14:51:33 -0700133 int pipes[2];
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800134 while (monotonic_clock::now() < shared->stop_time) {
Brian Silverman4c04efd2013-09-07 14:51:33 -0700135 if (pipe(pipes) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700136 PDie("pipe(%p) failed", &pipes);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700137 }
138 switch (fork()) {
139 case 0: // in runner
Brian Silvermanac5cd382014-02-12 14:50:23 -0800140 DoRunTest(shared, test, pipes);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700141 case -1:
Brian Silverman01be0002014-05-10 15:44:38 -0700142 PDie("fork() failed");
Brian Silverman4c04efd2013-09-07 14:51:33 -0700143 }
144
145 if (close(pipes[1]) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700146 PDie("close(%d) of write end of pipe failed", pipes[1]);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700147 }
148
149 ::std::string output;
150 char buffer[2048];
151 while (true) {
152 ssize_t ret = read(pipes[0], &buffer, sizeof(buffer));
153 if (ret == 0) { // EOF
154 if (close(pipes[0]) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700155 PDie("close(%d) of pipe at EOF failed", pipes[0]);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700156 }
157 break;
158 } else if (ret == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700159 PDie("read(%d, %p, %zd) failed", pipes[0], &buffer, sizeof(buffer));
Brian Silverman4c04efd2013-09-07 14:51:33 -0700160 }
161 output += ::std::string(buffer, ret);
162 }
163
164 int status;
165 while (true) {
166 if (wait(&status) == -1) {
167 if (errno == EINTR) continue;
Brian Silverman01be0002014-05-10 15:44:38 -0700168 PDie("wait(%p) in child failed", &status);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700169 } else {
170 break;
171 }
172 }
173 if (WIFEXITED(status)) {
174 if (WEXITSTATUS(status) != 0) {
175 MutexLocker sync(&shared->output_mutex);
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700176 fprintf(stderr, "Test %s exited with status %d. output:\n", (*test)[0],
177 WEXITSTATUS(status));
Brian Silverman4c04efd2013-09-07 14:51:33 -0700178 fputs(output.c_str(), stderr);
179 }
180 } else if (WIFSIGNALED(status)) {
181 MutexLocker sync(&shared->output_mutex);
Brian Silvermanac5cd382014-02-12 14:50:23 -0800182 fprintf(stderr, "Test %s terminated by signal %d: %s.\n", (*test)[0],
Brian Silvermanaf784862014-05-13 08:14:55 -0700183 WTERMSIG(status), aos_strsignal(WTERMSIG(status)));
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700184 fputs(output.c_str(), stderr);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700185 } else {
Brian Silvermanfe457de2014-05-26 22:04:08 -0700186 CHECK(WIFSTOPPED(status));
Brian Silvermanac5cd382014-02-12 14:50:23 -0800187 Die("Test %s was stopped.\n", (*test)[0]);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700188 }
189
190 ++test;
Brian Silvermanac5cd382014-02-12 14:50:23 -0800191 if (test == kTests + 1) test = kTests;
Brian Silverman4c04efd2013-09-07 14:51:33 -0700192 ++iterations;
193 }
194 {
195 MutexLocker sync(&shared->total_iterations_mutex);
196 shared->total_iterations += iterations;
197 }
198}
199
200void Run(Shared *shared) {
201 switch (fork()) {
202 case 0: // in child
203 DoRun(shared);
204 _exit(EXIT_SUCCESS);
205 case -1:
Brian Silverman01be0002014-05-10 15:44:38 -0700206 PDie("fork() of child failed");
Brian Silverman4c04efd2013-09-07 14:51:33 -0700207 }
208}
209
210int Main(int argc, char **argv) {
Brian Silvermanfe457de2014-05-26 22:04:08 -0700211 if (argc < 1) {
212 fputs("need an argument\n", stderr);
213 return EXIT_FAILURE;
214 }
Brian Silverman4c04efd2013-09-07 14:51:33 -0700215
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -0500216 ::aos::testing::TestSharedMemory my_shm_;
Brian Silverman4c04efd2013-09-07 14:51:33 -0700217
218 Shared *shared = static_cast<Shared *>(shm_malloc(sizeof(Shared)));
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800219 new (shared) Shared(monotonic_clock::now() + kTestTime);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700220
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700221 if (asprintf(const_cast<char **>(&shared->path), "%s/../tests",
222 ::aos::libc::Dirname(argv[0]).c_str()) == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700223 PDie("asprintf failed");
Brian Silverman5f8c4922014-02-11 21:22:38 -0800224 }
Brian Silverman4c04efd2013-09-07 14:51:33 -0700225
226 for (int i = 0; i < kTesters; ++i) {
227 Run(shared);
228 }
229
230 bool error = false;
231 for (int i = 0; i < kTesters; ++i) {
232 int status;
233 if (wait(&status) == -1) {
234 if (errno == EINTR) {
235 --i;
236 } else {
Brian Silverman01be0002014-05-10 15:44:38 -0700237 PDie("wait(%p) failed", &status);
Brian Silverman4c04efd2013-09-07 14:51:33 -0700238 }
239 }
240 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
241 error = true;
242 }
243 }
244
245 printf("Ran a total of %d tests.\n", shared->total_iterations);
246 if (error) {
247 printf("A child had a problem during the test.\n");
248 }
249 return error ? EXIT_FAILURE : EXIT_SUCCESS;
250}
251
252} // namespace aos
253
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700254int main(int argc, char **argv) { return ::aos::Main(argc, argv); }