1*cf5a6c84SAndroid Build Coastguard Worker /* toybox-gtests.cpp - Wrapper around scripts/runtest.sh to run each toy test as a gtest
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2023 The Android Open Source Project
4*cf5a6c84SAndroid Build Coastguard Worker */
5*cf5a6c84SAndroid Build Coastguard Worker
6*cf5a6c84SAndroid Build Coastguard Worker #include <dirent.h>
7*cf5a6c84SAndroid Build Coastguard Worker #include <paths.h>
8*cf5a6c84SAndroid Build Coastguard Worker #include <signal.h>
9*cf5a6c84SAndroid Build Coastguard Worker #include <stdlib.h>
10*cf5a6c84SAndroid Build Coastguard Worker #include <sys/stat.h>
11*cf5a6c84SAndroid Build Coastguard Worker #include <sys/types.h>
12*cf5a6c84SAndroid Build Coastguard Worker #include <unistd.h>
13*cf5a6c84SAndroid Build Coastguard Worker
14*cf5a6c84SAndroid Build Coastguard Worker #include <algorithm>
15*cf5a6c84SAndroid Build Coastguard Worker #include <iostream>
16*cf5a6c84SAndroid Build Coastguard Worker #include <functional>
17*cf5a6c84SAndroid Build Coastguard Worker #include <memory>
18*cf5a6c84SAndroid Build Coastguard Worker #include <stdlib.h>
19*cf5a6c84SAndroid Build Coastguard Worker #include <string>
20*cf5a6c84SAndroid Build Coastguard Worker #include <vector>
21*cf5a6c84SAndroid Build Coastguard Worker
22*cf5a6c84SAndroid Build Coastguard Worker #include <gtest/gtest.h>
23*cf5a6c84SAndroid Build Coastguard Worker
24*cf5a6c84SAndroid Build Coastguard Worker #include <android-base/file.h>
25*cf5a6c84SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
26*cf5a6c84SAndroid Build Coastguard Worker #include <android-base/strings.h>
27*cf5a6c84SAndroid Build Coastguard Worker #include <android-base/test_utils.h>
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker const std::string kShell =
30*cf5a6c84SAndroid Build Coastguard Worker #ifdef __ANDROID__
31*cf5a6c84SAndroid Build Coastguard Worker _PATH_BSHELL;
32*cf5a6c84SAndroid Build Coastguard Worker #else
33*cf5a6c84SAndroid Build Coastguard Worker // /bin/sh doesn't work when running on the host, the tests require /bin/bash
34*cf5a6c84SAndroid Build Coastguard Worker "/bin/bash";
35*cf5a6c84SAndroid Build Coastguard Worker #endif
36*cf5a6c84SAndroid Build Coastguard Worker
MkdirOrFatal(std::string dir)37*cf5a6c84SAndroid Build Coastguard Worker static void MkdirOrFatal(std::string dir) {
38*cf5a6c84SAndroid Build Coastguard Worker int ret = mkdir(dir.c_str(), 0777);
39*cf5a6c84SAndroid Build Coastguard Worker ASSERT_EQ(ret, 0) << "Failed to make directory " << dir << ": " << strerror(errno);
40*cf5a6c84SAndroid Build Coastguard Worker }
41*cf5a6c84SAndroid Build Coastguard Worker
SystemStdoutOrFatal(std::string cmd)42*cf5a6c84SAndroid Build Coastguard Worker static std::string SystemStdoutOrFatal(std::string cmd) {
43*cf5a6c84SAndroid Build Coastguard Worker CapturedStdout stdout_str;
44*cf5a6c84SAndroid Build Coastguard Worker int ret = system(cmd.c_str());
45*cf5a6c84SAndroid Build Coastguard Worker stdout_str.Stop();
46*cf5a6c84SAndroid Build Coastguard Worker EXPECT_GE(ret, 0) << "Failed to run " << cmd << ": " << strerror(errno);
47*cf5a6c84SAndroid Build Coastguard Worker EXPECT_EQ(ret, 0) << "Failed to run " << cmd << ": exited with status " << ret;
48*cf5a6c84SAndroid Build Coastguard Worker return android::base::Trim(stdout_str.str());
49*cf5a6c84SAndroid Build Coastguard Worker }
50*cf5a6c84SAndroid Build Coastguard Worker
51*cf5a6c84SAndroid Build Coastguard Worker // ExecTest sets up the environemnt and then execs the toybox test scripts for a single toy.
52*cf5a6c84SAndroid Build Coastguard Worker // It is run in a subprocess as a gtest death test.
ExecTest(std::string toy,std::string toy_path,std::string test_file,std::string temp_dir)53*cf5a6c84SAndroid Build Coastguard Worker static void ExecTest(std::string toy,
54*cf5a6c84SAndroid Build Coastguard Worker std::string toy_path,
55*cf5a6c84SAndroid Build Coastguard Worker std::string test_file,
56*cf5a6c84SAndroid Build Coastguard Worker std::string temp_dir) {
57*cf5a6c84SAndroid Build Coastguard Worker std::string test_binary_dir = android::base::GetExecutableDirectory();
58*cf5a6c84SAndroid Build Coastguard Worker
59*cf5a6c84SAndroid Build Coastguard Worker std::string working_dir = temp_dir + "/" + toy;
60*cf5a6c84SAndroid Build Coastguard Worker MkdirOrFatal(working_dir);
61*cf5a6c84SAndroid Build Coastguard Worker
62*cf5a6c84SAndroid Build Coastguard Worker #ifndef __ANDROID__
63*cf5a6c84SAndroid Build Coastguard Worker std::string path_env = getenv("PATH");
64*cf5a6c84SAndroid Build Coastguard Worker path_env = temp_dir + "/path:" + path_env;
65*cf5a6c84SAndroid Build Coastguard Worker setenv("PATH", path_env.c_str(), true);
66*cf5a6c84SAndroid Build Coastguard Worker #endif
67*cf5a6c84SAndroid Build Coastguard Worker
68*cf5a6c84SAndroid Build Coastguard Worker setenv("C", toy_path.c_str(), true);
69*cf5a6c84SAndroid Build Coastguard Worker setenv("CMDNAME", toy.c_str(), true);
70*cf5a6c84SAndroid Build Coastguard Worker setenv("TESTDIR", temp_dir.c_str(), true);
71*cf5a6c84SAndroid Build Coastguard Worker setenv("FILES", (test_binary_dir + "/tests/files").c_str(), true);
72*cf5a6c84SAndroid Build Coastguard Worker setenv("LANG", "en_US.UTF-8", true);
73*cf5a6c84SAndroid Build Coastguard Worker setenv("VERBOSE", "1", true);
74*cf5a6c84SAndroid Build Coastguard Worker
75*cf5a6c84SAndroid Build Coastguard Worker std::string test_cmd = android::base::StringPrintf(
76*cf5a6c84SAndroid Build Coastguard Worker "cd %s && source %s/scripts/runtest.sh && source %s/tests/%s",
77*cf5a6c84SAndroid Build Coastguard Worker working_dir.c_str(),
78*cf5a6c84SAndroid Build Coastguard Worker test_binary_dir.c_str(),
79*cf5a6c84SAndroid Build Coastguard Worker test_binary_dir.c_str(),
80*cf5a6c84SAndroid Build Coastguard Worker test_file.c_str());
81*cf5a6c84SAndroid Build Coastguard Worker
82*cf5a6c84SAndroid Build Coastguard Worker std::vector<const char*> args;
83*cf5a6c84SAndroid Build Coastguard Worker args.push_back(kShell.c_str());
84*cf5a6c84SAndroid Build Coastguard Worker args.push_back("-c");
85*cf5a6c84SAndroid Build Coastguard Worker args.push_back(test_cmd.c_str());
86*cf5a6c84SAndroid Build Coastguard Worker args.push_back(NULL);
87*cf5a6c84SAndroid Build Coastguard Worker
88*cf5a6c84SAndroid Build Coastguard Worker // When running in atest something is configure the SIGQUIT signal as blocked, which
89*cf5a6c84SAndroid Build Coastguard Worker // causes some missed signals in toybox tests that leave dangling "sleep 100" processes
90*cf5a6c84SAndroid Build Coastguard Worker // lying around. These processes have the gtest pipe fd open, and keep gtest from
91*cf5a6c84SAndroid Build Coastguard Worker // considering the death test to have exited until the sleep ends.
92*cf5a6c84SAndroid Build Coastguard Worker sigset_t signal_set;
93*cf5a6c84SAndroid Build Coastguard Worker sigemptyset(&signal_set);
94*cf5a6c84SAndroid Build Coastguard Worker sigaddset(&signal_set, SIGQUIT);
95*cf5a6c84SAndroid Build Coastguard Worker sigprocmask(SIG_UNBLOCK, &signal_set, nullptr);
96*cf5a6c84SAndroid Build Coastguard Worker
97*cf5a6c84SAndroid Build Coastguard Worker execv(args[0], const_cast<char**>(args.data()));
98*cf5a6c84SAndroid Build Coastguard Worker FAIL() << "Failed to exec " << kShell << " -c '" << test_cmd << "'" << strerror(errno);
99*cf5a6c84SAndroid Build Coastguard Worker }
100*cf5a6c84SAndroid Build Coastguard Worker
101*cf5a6c84SAndroid Build Coastguard Worker class ToyboxTest : public testing::Test {
102*cf5a6c84SAndroid Build Coastguard Worker public:
ToyboxTest(std::string toy,std::string test_file)103*cf5a6c84SAndroid Build Coastguard Worker ToyboxTest(std::string toy, std::string test_file) : toy_(toy), test_file_(test_file) { }
104*cf5a6c84SAndroid Build Coastguard Worker void TestBody();
105*cf5a6c84SAndroid Build Coastguard Worker private:
106*cf5a6c84SAndroid Build Coastguard Worker std::string toy_;
107*cf5a6c84SAndroid Build Coastguard Worker std::string test_file_;
108*cf5a6c84SAndroid Build Coastguard Worker };
109*cf5a6c84SAndroid Build Coastguard Worker
110*cf5a6c84SAndroid Build Coastguard Worker
TestBody()111*cf5a6c84SAndroid Build Coastguard Worker void ToyboxTest::TestBody() {
112*cf5a6c84SAndroid Build Coastguard Worker // This test function is run once for each toy.
113*cf5a6c84SAndroid Build Coastguard Worker TemporaryDir temp_dir{};
114*cf5a6c84SAndroid Build Coastguard Worker bool ignore_failures = false;
115*cf5a6c84SAndroid Build Coastguard Worker
116*cf5a6c84SAndroid Build Coastguard Worker #ifdef __ANDROID__
117*cf5a6c84SAndroid Build Coastguard Worker // On the device, check whether the toy exists
118*cf5a6c84SAndroid Build Coastguard Worker std::string toy_path = SystemStdoutOrFatal(std::string("which ") + toy_ + " || true");
119*cf5a6c84SAndroid Build Coastguard Worker if (toy_path.empty()) {
120*cf5a6c84SAndroid Build Coastguard Worker GTEST_SKIP() << toy_ << " not present";
121*cf5a6c84SAndroid Build Coastguard Worker }
122*cf5a6c84SAndroid Build Coastguard Worker
123*cf5a6c84SAndroid Build Coastguard Worker // And whether it is uses toybox as its implementation.
124*cf5a6c84SAndroid Build Coastguard Worker std::string implementation = SystemStdoutOrFatal(std::string("realpath ") + toy_path);
125*cf5a6c84SAndroid Build Coastguard Worker if (!android::base::EndsWith(implementation, "/toybox")) {
126*cf5a6c84SAndroid Build Coastguard Worker std::cout << toy_ << " is *not* toybox; this does not count as a test failure";
127*cf5a6c84SAndroid Build Coastguard Worker // If there is no symlink for the toy on the device then run the tests but don't report
128*cf5a6c84SAndroid Build Coastguard Worker // failures.
129*cf5a6c84SAndroid Build Coastguard Worker ignore_failures = true;
130*cf5a6c84SAndroid Build Coastguard Worker }
131*cf5a6c84SAndroid Build Coastguard Worker #else
132*cf5a6c84SAndroid Build Coastguard Worker // On the host toybox is packaged with the test so that it can be run in CI, which won't
133*cf5a6c84SAndroid Build Coastguard Worker // have access to the prebuilt toybox or path symlinks. It is packaged without any toy
134*cf5a6c84SAndroid Build Coastguard Worker // symlinks, so a symlink is created for each test.
135*cf5a6c84SAndroid Build Coastguard Worker
136*cf5a6c84SAndroid Build Coastguard Worker // Test if the toy is supported by the packaged toybox, and skip the test if not.
137*cf5a6c84SAndroid Build Coastguard Worker std::string toybox_path = android::base::GetExecutableDirectory() + "/toybox";
138*cf5a6c84SAndroid Build Coastguard Worker std::string supported_toys_str = SystemStdoutOrFatal(toybox_path);
139*cf5a6c84SAndroid Build Coastguard Worker std::vector<std::string> supported_toys = android::base::Split(supported_toys_str, " \n");
140*cf5a6c84SAndroid Build Coastguard Worker if (std::find(supported_toys.begin(), supported_toys.end(), toy_) == supported_toys.end()) {
141*cf5a6c84SAndroid Build Coastguard Worker GTEST_SKIP() << toy_ << " not compiled into toybox";
142*cf5a6c84SAndroid Build Coastguard Worker }
143*cf5a6c84SAndroid Build Coastguard Worker
144*cf5a6c84SAndroid Build Coastguard Worker // Create a directory with a symlinks for all the toys that will be prepended to PATH.
145*cf5a6c84SAndroid Build Coastguard Worker // Some tests have interdependencies on other toys that may not be available in
146*cf5a6c84SAndroid Build Coastguard Worker // the host system, e.g. the tar tests depend on the file tool.
147*cf5a6c84SAndroid Build Coastguard Worker std::string path_dir = std::string(temp_dir.path) + "/path";
148*cf5a6c84SAndroid Build Coastguard Worker MkdirOrFatal(path_dir);
149*cf5a6c84SAndroid Build Coastguard Worker for (auto& toy : supported_toys) {
150*cf5a6c84SAndroid Build Coastguard Worker std::string toy_path = path_dir + "/" + toy;
151*cf5a6c84SAndroid Build Coastguard Worker int ret = symlink(toybox_path.c_str(), toy_path.c_str());
152*cf5a6c84SAndroid Build Coastguard Worker ASSERT_EQ(ret, 0) <<
153*cf5a6c84SAndroid Build Coastguard Worker "Failed to symlink " << toy_path << " to " << toybox_path << ": " << strerror(errno);
154*cf5a6c84SAndroid Build Coastguard Worker }
155*cf5a6c84SAndroid Build Coastguard Worker std::string toy_path = path_dir + "/" + toy_;
156*cf5a6c84SAndroid Build Coastguard Worker #endif
157*cf5a6c84SAndroid Build Coastguard Worker
158*cf5a6c84SAndroid Build Coastguard Worker pid_t pid = fork();
159*cf5a6c84SAndroid Build Coastguard Worker ASSERT_GE(pid, 0) << "Failed to fork";
160*cf5a6c84SAndroid Build Coastguard Worker if (pid > 0) {
161*cf5a6c84SAndroid Build Coastguard Worker // parent
162*cf5a6c84SAndroid Build Coastguard Worker int status = 0;
163*cf5a6c84SAndroid Build Coastguard Worker int ret = waitpid(pid, &status, 0);
164*cf5a6c84SAndroid Build Coastguard Worker ASSERT_GT(pid, 0) << "Failed to wait for child " << pid << ": " << strerror(errno);
165*cf5a6c84SAndroid Build Coastguard Worker ASSERT_TRUE(WIFEXITED(status));
166*cf5a6c84SAndroid Build Coastguard Worker if (!ignore_failures) {
167*cf5a6c84SAndroid Build Coastguard Worker int exit_status = WEXITSTATUS(status);
168*cf5a6c84SAndroid Build Coastguard Worker ASSERT_EQ(exit_status, 0);
169*cf5a6c84SAndroid Build Coastguard Worker }
170*cf5a6c84SAndroid Build Coastguard Worker } else {
171*cf5a6c84SAndroid Build Coastguard Worker // child
172*cf5a6c84SAndroid Build Coastguard Worker ExecTest(toy_, toy_path, test_file_, temp_dir.path);
173*cf5a6c84SAndroid Build Coastguard Worker _exit(1);
174*cf5a6c84SAndroid Build Coastguard Worker }
175*cf5a6c84SAndroid Build Coastguard Worker }
176*cf5a6c84SAndroid Build Coastguard Worker
initTests()177*cf5a6c84SAndroid Build Coastguard Worker __attribute__((constructor)) static void initTests() {
178*cf5a6c84SAndroid Build Coastguard Worker // Find all the "tests/*.test" files packaged alongside the gtest.
179*cf5a6c84SAndroid Build Coastguard Worker std::string test_dir = android::base::GetExecutableDirectory() + "/tests";
180*cf5a6c84SAndroid Build Coastguard Worker std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(test_dir.c_str()), closedir);
181*cf5a6c84SAndroid Build Coastguard Worker if (!dir) {
182*cf5a6c84SAndroid Build Coastguard Worker std::cerr << "Cannot open test executable directory " << test_dir;
183*cf5a6c84SAndroid Build Coastguard Worker exit(1);
184*cf5a6c84SAndroid Build Coastguard Worker }
185*cf5a6c84SAndroid Build Coastguard Worker
186*cf5a6c84SAndroid Build Coastguard Worker std::vector<std::string> test_files;
187*cf5a6c84SAndroid Build Coastguard Worker dirent* de;
188*cf5a6c84SAndroid Build Coastguard Worker while ((de = readdir(dir.get())) != nullptr) {
189*cf5a6c84SAndroid Build Coastguard Worker std::string file = de->d_name;
190*cf5a6c84SAndroid Build Coastguard Worker if (android::base::EndsWith(file, ".test")) {
191*cf5a6c84SAndroid Build Coastguard Worker test_files.push_back(file);
192*cf5a6c84SAndroid Build Coastguard Worker }
193*cf5a6c84SAndroid Build Coastguard Worker }
194*cf5a6c84SAndroid Build Coastguard Worker
195*cf5a6c84SAndroid Build Coastguard Worker std::sort(test_files.begin(), test_files.end());
196*cf5a6c84SAndroid Build Coastguard Worker
197*cf5a6c84SAndroid Build Coastguard Worker // Register each test file as an individual gtest.
198*cf5a6c84SAndroid Build Coastguard Worker for (auto& test_file : test_files) {
199*cf5a6c84SAndroid Build Coastguard Worker std::string toy = test_file.substr(0, test_file.size() - strlen(".test"));
200*cf5a6c84SAndroid Build Coastguard Worker
201*cf5a6c84SAndroid Build Coastguard Worker testing::RegisterTest("toybox", toy.c_str(), nullptr, nullptr, __FILE__, __LINE__,
202*cf5a6c84SAndroid Build Coastguard Worker [=]() -> ToyboxTest* {
203*cf5a6c84SAndroid Build Coastguard Worker return new ToyboxTest(toy, test_file);
204*cf5a6c84SAndroid Build Coastguard Worker });
205*cf5a6c84SAndroid Build Coastguard Worker }
206*cf5a6c84SAndroid Build Coastguard Worker }
207