xref: /aosp_15_r20/system/core/cli-test/cli-test.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1*00c7fec1SAndroid Build Coastguard Worker /*
2*00c7fec1SAndroid Build Coastguard Worker  * Copyright (C) 2019 The Android Open Source Project
3*00c7fec1SAndroid Build Coastguard Worker  *
4*00c7fec1SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*00c7fec1SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*00c7fec1SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*00c7fec1SAndroid Build Coastguard Worker  *
8*00c7fec1SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*00c7fec1SAndroid Build Coastguard Worker  *
10*00c7fec1SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*00c7fec1SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*00c7fec1SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*00c7fec1SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*00c7fec1SAndroid Build Coastguard Worker  * limitations under the License.
15*00c7fec1SAndroid Build Coastguard Worker  */
16*00c7fec1SAndroid Build Coastguard Worker 
17*00c7fec1SAndroid Build Coastguard Worker #include <errno.h>
18*00c7fec1SAndroid Build Coastguard Worker #include <getopt.h>
19*00c7fec1SAndroid Build Coastguard Worker #include <inttypes.h>
20*00c7fec1SAndroid Build Coastguard Worker #include <libgen.h>
21*00c7fec1SAndroid Build Coastguard Worker #include <stdarg.h>
22*00c7fec1SAndroid Build Coastguard Worker #include <stdio.h>
23*00c7fec1SAndroid Build Coastguard Worker #include <stdlib.h>
24*00c7fec1SAndroid Build Coastguard Worker #include <string.h>
25*00c7fec1SAndroid Build Coastguard Worker #include <sys/wait.h>
26*00c7fec1SAndroid Build Coastguard Worker #include <time.h>
27*00c7fec1SAndroid Build Coastguard Worker #include <unistd.h>
28*00c7fec1SAndroid Build Coastguard Worker 
29*00c7fec1SAndroid Build Coastguard Worker #include <string>
30*00c7fec1SAndroid Build Coastguard Worker #include <vector>
31*00c7fec1SAndroid Build Coastguard Worker 
32*00c7fec1SAndroid Build Coastguard Worker #include <android-base/chrono_utils.h>
33*00c7fec1SAndroid Build Coastguard Worker #include <android-base/file.h>
34*00c7fec1SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
35*00c7fec1SAndroid Build Coastguard Worker #include <android-base/strings.h>
36*00c7fec1SAndroid Build Coastguard Worker #include <android-base/test_utils.h>
37*00c7fec1SAndroid Build Coastguard Worker 
38*00c7fec1SAndroid Build Coastguard Worker // Example:
39*00c7fec1SAndroid Build Coastguard Worker 
40*00c7fec1SAndroid Build Coastguard Worker // name: unzip -n
41*00c7fec1SAndroid Build Coastguard Worker // before: mkdir -p d1/d2
42*00c7fec1SAndroid Build Coastguard Worker // before: echo b > d1/d2/a.txt
43*00c7fec1SAndroid Build Coastguard Worker // command: unzip -q -n $FILES/zip/example.zip d1/d2/a.txt && cat d1/d2/a.txt
44*00c7fec1SAndroid Build Coastguard Worker // expected-stdout:
45*00c7fec1SAndroid Build Coastguard Worker // 	b
46*00c7fec1SAndroid Build Coastguard Worker 
47*00c7fec1SAndroid Build Coastguard Worker struct Test {
48*00c7fec1SAndroid Build Coastguard Worker   std::string test_filename;
49*00c7fec1SAndroid Build Coastguard Worker   std::string name;
50*00c7fec1SAndroid Build Coastguard Worker   std::string command;
51*00c7fec1SAndroid Build Coastguard Worker   std::vector<std::string> befores;
52*00c7fec1SAndroid Build Coastguard Worker   std::vector<std::string> afters;
53*00c7fec1SAndroid Build Coastguard Worker   std::string expected_stdout;
54*00c7fec1SAndroid Build Coastguard Worker   std::string expected_stderr;
55*00c7fec1SAndroid Build Coastguard Worker   int exit_status = 0;
56*00c7fec1SAndroid Build Coastguard Worker };
57*00c7fec1SAndroid Build Coastguard Worker 
58*00c7fec1SAndroid Build Coastguard Worker static const char* g_progname;
59*00c7fec1SAndroid Build Coastguard Worker static bool g_verbose;
60*00c7fec1SAndroid Build Coastguard Worker 
61*00c7fec1SAndroid Build Coastguard Worker static const char* g_file;
62*00c7fec1SAndroid Build Coastguard Worker static size_t g_line;
63*00c7fec1SAndroid Build Coastguard Worker 
64*00c7fec1SAndroid Build Coastguard Worker enum Color { kRed, kGreen };
65*00c7fec1SAndroid Build Coastguard Worker 
Print(Color c,const char * lhs,const char * fmt,...)66*00c7fec1SAndroid Build Coastguard Worker static void Print(Color c, const char* lhs, const char* fmt, ...) {
67*00c7fec1SAndroid Build Coastguard Worker   va_list ap;
68*00c7fec1SAndroid Build Coastguard Worker   va_start(ap, fmt);
69*00c7fec1SAndroid Build Coastguard Worker   if (isatty(0)) printf("%s", (c == kRed) ? "\e[31m" : "\e[32m");
70*00c7fec1SAndroid Build Coastguard Worker   printf("%s%s", lhs, isatty(0) ? "\e[0m" : "");
71*00c7fec1SAndroid Build Coastguard Worker   vfprintf(stdout, fmt, ap);
72*00c7fec1SAndroid Build Coastguard Worker   putchar('\n');
73*00c7fec1SAndroid Build Coastguard Worker   va_end(ap);
74*00c7fec1SAndroid Build Coastguard Worker }
75*00c7fec1SAndroid Build Coastguard Worker 
Die(int error,const char * fmt,...)76*00c7fec1SAndroid Build Coastguard Worker static void Die(int error, const char* fmt, ...) {
77*00c7fec1SAndroid Build Coastguard Worker   va_list ap;
78*00c7fec1SAndroid Build Coastguard Worker   va_start(ap, fmt);
79*00c7fec1SAndroid Build Coastguard Worker   fprintf(stderr, "%s: ", g_progname);
80*00c7fec1SAndroid Build Coastguard Worker   vfprintf(stderr, fmt, ap);
81*00c7fec1SAndroid Build Coastguard Worker   if (error != 0) fprintf(stderr, ": %s", strerror(error));
82*00c7fec1SAndroid Build Coastguard Worker   fprintf(stderr, "\n");
83*00c7fec1SAndroid Build Coastguard Worker   va_end(ap);
84*00c7fec1SAndroid Build Coastguard Worker   _exit(1);
85*00c7fec1SAndroid Build Coastguard Worker }
86*00c7fec1SAndroid Build Coastguard Worker 
V(const char * fmt,...)87*00c7fec1SAndroid Build Coastguard Worker static void V(const char* fmt, ...) {
88*00c7fec1SAndroid Build Coastguard Worker   if (!g_verbose) return;
89*00c7fec1SAndroid Build Coastguard Worker 
90*00c7fec1SAndroid Build Coastguard Worker   va_list ap;
91*00c7fec1SAndroid Build Coastguard Worker   va_start(ap, fmt);
92*00c7fec1SAndroid Build Coastguard Worker   fprintf(stderr, "           - ");
93*00c7fec1SAndroid Build Coastguard Worker   vfprintf(stderr, fmt, ap);
94*00c7fec1SAndroid Build Coastguard Worker   fprintf(stderr, "\n");
95*00c7fec1SAndroid Build Coastguard Worker   va_end(ap);
96*00c7fec1SAndroid Build Coastguard Worker }
97*00c7fec1SAndroid Build Coastguard Worker 
SetField(const char * what,std::string * field,std::string_view value)98*00c7fec1SAndroid Build Coastguard Worker static void SetField(const char* what, std::string* field, std::string_view value) {
99*00c7fec1SAndroid Build Coastguard Worker   if (!field->empty()) {
100*00c7fec1SAndroid Build Coastguard Worker     Die(0, "%s:%zu: %s already set to '%s'", g_file, g_line, what, field->c_str());
101*00c7fec1SAndroid Build Coastguard Worker   }
102*00c7fec1SAndroid Build Coastguard Worker   field->assign(value);
103*00c7fec1SAndroid Build Coastguard Worker }
104*00c7fec1SAndroid Build Coastguard Worker 
105*00c7fec1SAndroid Build Coastguard Worker // Similar to ConsumePrefix, but also trims, so "key:value" and "key: value"
106*00c7fec1SAndroid Build Coastguard Worker // are equivalent.
Match(std::string * s,const std::string & prefix)107*00c7fec1SAndroid Build Coastguard Worker static bool Match(std::string* s, const std::string& prefix) {
108*00c7fec1SAndroid Build Coastguard Worker   if (!android::base::StartsWith(*s, prefix)) return false;
109*00c7fec1SAndroid Build Coastguard Worker   s->assign(android::base::Trim(s->substr(prefix.length())));
110*00c7fec1SAndroid Build Coastguard Worker   return true;
111*00c7fec1SAndroid Build Coastguard Worker }
112*00c7fec1SAndroid Build Coastguard Worker 
CollectTests(std::vector<Test> * tests,const char * test_filename)113*00c7fec1SAndroid Build Coastguard Worker static void CollectTests(std::vector<Test>* tests, const char* test_filename) {
114*00c7fec1SAndroid Build Coastguard Worker   std::string absolute_test_filename;
115*00c7fec1SAndroid Build Coastguard Worker   if (!android::base::Realpath(test_filename, &absolute_test_filename)) {
116*00c7fec1SAndroid Build Coastguard Worker     Die(errno, "realpath '%s'", test_filename);
117*00c7fec1SAndroid Build Coastguard Worker   }
118*00c7fec1SAndroid Build Coastguard Worker 
119*00c7fec1SAndroid Build Coastguard Worker   std::string content;
120*00c7fec1SAndroid Build Coastguard Worker   if (!android::base::ReadFileToString(test_filename, &content)) {
121*00c7fec1SAndroid Build Coastguard Worker     Die(errno, "couldn't read '%s'", test_filename);
122*00c7fec1SAndroid Build Coastguard Worker   }
123*00c7fec1SAndroid Build Coastguard Worker 
124*00c7fec1SAndroid Build Coastguard Worker   size_t count = 0;
125*00c7fec1SAndroid Build Coastguard Worker   g_file = test_filename;
126*00c7fec1SAndroid Build Coastguard Worker   g_line = 0;
127*00c7fec1SAndroid Build Coastguard Worker   auto lines = android::base::Split(content, "\n");
128*00c7fec1SAndroid Build Coastguard Worker   std::unique_ptr<Test> test(new Test);
129*00c7fec1SAndroid Build Coastguard Worker   while (g_line < lines.size()) {
130*00c7fec1SAndroid Build Coastguard Worker     auto line = lines[g_line++];
131*00c7fec1SAndroid Build Coastguard Worker     if (line.empty() || line[0] == '#') continue;
132*00c7fec1SAndroid Build Coastguard Worker 
133*00c7fec1SAndroid Build Coastguard Worker     if (line[0] == '-') {
134*00c7fec1SAndroid Build Coastguard Worker       if (test->name.empty() || test->command.empty()) {
135*00c7fec1SAndroid Build Coastguard Worker         Die(0, "%s:%zu: each test requires both a name and a command", g_file, g_line);
136*00c7fec1SAndroid Build Coastguard Worker       }
137*00c7fec1SAndroid Build Coastguard Worker       test->test_filename = absolute_test_filename;
138*00c7fec1SAndroid Build Coastguard Worker       tests->push_back(*test.release());
139*00c7fec1SAndroid Build Coastguard Worker       test.reset(new Test);
140*00c7fec1SAndroid Build Coastguard Worker       ++count;
141*00c7fec1SAndroid Build Coastguard Worker     } else if (Match(&line, "name:")) {
142*00c7fec1SAndroid Build Coastguard Worker       SetField("name", &test->name, line);
143*00c7fec1SAndroid Build Coastguard Worker     } else if (Match(&line, "command:")) {
144*00c7fec1SAndroid Build Coastguard Worker       SetField("command", &test->command, line);
145*00c7fec1SAndroid Build Coastguard Worker     } else if (Match(&line, "before:")) {
146*00c7fec1SAndroid Build Coastguard Worker       test->befores.push_back(line);
147*00c7fec1SAndroid Build Coastguard Worker     } else if (Match(&line, "after:")) {
148*00c7fec1SAndroid Build Coastguard Worker       test->afters.push_back(line);
149*00c7fec1SAndroid Build Coastguard Worker     } else if (Match(&line, "expected-exit-status:")) {
150*00c7fec1SAndroid Build Coastguard Worker       char* end_p;
151*00c7fec1SAndroid Build Coastguard Worker       errno = 0;
152*00c7fec1SAndroid Build Coastguard Worker       test->exit_status = strtol(line.c_str(), &end_p, 10);
153*00c7fec1SAndroid Build Coastguard Worker       if (errno != 0 || *end_p != '\0') {
154*00c7fec1SAndroid Build Coastguard Worker         Die(0, "%s:%zu: bad exit status: \"%s\"", g_file, g_line, line.c_str());
155*00c7fec1SAndroid Build Coastguard Worker       }
156*00c7fec1SAndroid Build Coastguard Worker     } else if (Match(&line, "expected-stdout:")) {
157*00c7fec1SAndroid Build Coastguard Worker       // Collect tab-indented lines.
158*00c7fec1SAndroid Build Coastguard Worker       std::string text;
159*00c7fec1SAndroid Build Coastguard Worker       while (g_line < lines.size() && !lines[g_line].empty() && lines[g_line][0] == '\t') {
160*00c7fec1SAndroid Build Coastguard Worker         text += lines[g_line++].substr(1) + "\n";
161*00c7fec1SAndroid Build Coastguard Worker       }
162*00c7fec1SAndroid Build Coastguard Worker       SetField("expected stdout", &test->expected_stdout, text);
163*00c7fec1SAndroid Build Coastguard Worker     } else {
164*00c7fec1SAndroid Build Coastguard Worker       Die(0, "%s:%zu: syntax error: \"%s\"", g_file, g_line, line.c_str());
165*00c7fec1SAndroid Build Coastguard Worker     }
166*00c7fec1SAndroid Build Coastguard Worker   }
167*00c7fec1SAndroid Build Coastguard Worker   if (count == 0) Die(0, "no tests found in '%s'", g_file);
168*00c7fec1SAndroid Build Coastguard Worker }
169*00c7fec1SAndroid Build Coastguard Worker 
Plural(size_t n)170*00c7fec1SAndroid Build Coastguard Worker static const char* Plural(size_t n) {
171*00c7fec1SAndroid Build Coastguard Worker   return (n == 1) ? "" : "s";
172*00c7fec1SAndroid Build Coastguard Worker }
173*00c7fec1SAndroid Build Coastguard Worker 
ExitStatusToString(int status)174*00c7fec1SAndroid Build Coastguard Worker static std::string ExitStatusToString(int status) {
175*00c7fec1SAndroid Build Coastguard Worker   if (WIFSIGNALED(status)) {
176*00c7fec1SAndroid Build Coastguard Worker     return android::base::StringPrintf("was killed by signal %d (%s)", WTERMSIG(status),
177*00c7fec1SAndroid Build Coastguard Worker                                        strsignal(WTERMSIG(status)));
178*00c7fec1SAndroid Build Coastguard Worker   }
179*00c7fec1SAndroid Build Coastguard Worker   if (WIFSTOPPED(status)) {
180*00c7fec1SAndroid Build Coastguard Worker     return android::base::StringPrintf("was stopped by signal %d (%s)", WSTOPSIG(status),
181*00c7fec1SAndroid Build Coastguard Worker                                        strsignal(WSTOPSIG(status)));
182*00c7fec1SAndroid Build Coastguard Worker   }
183*00c7fec1SAndroid Build Coastguard Worker   return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
184*00c7fec1SAndroid Build Coastguard Worker }
185*00c7fec1SAndroid Build Coastguard Worker 
RunCommands(const char * what,const std::vector<std::string> & commands)186*00c7fec1SAndroid Build Coastguard Worker static bool RunCommands(const char* what, const std::vector<std::string>& commands) {
187*00c7fec1SAndroid Build Coastguard Worker   bool result = true;
188*00c7fec1SAndroid Build Coastguard Worker   for (auto& command : commands) {
189*00c7fec1SAndroid Build Coastguard Worker     V("running %s \"%s\"", what, command.c_str());
190*00c7fec1SAndroid Build Coastguard Worker     int exit_status = system(command.c_str());
191*00c7fec1SAndroid Build Coastguard Worker     if (exit_status != 0) {
192*00c7fec1SAndroid Build Coastguard Worker       result = false;
193*00c7fec1SAndroid Build Coastguard Worker       fprintf(stderr, "Command (%s) \"%s\" %s\n", what, command.c_str(),
194*00c7fec1SAndroid Build Coastguard Worker               ExitStatusToString(exit_status).c_str());
195*00c7fec1SAndroid Build Coastguard Worker     }
196*00c7fec1SAndroid Build Coastguard Worker   }
197*00c7fec1SAndroid Build Coastguard Worker   return result;
198*00c7fec1SAndroid Build Coastguard Worker }
199*00c7fec1SAndroid Build Coastguard Worker 
CheckOutput(const char * what,std::string actual_output,const std::string & expected_output,const std::string & FILES)200*00c7fec1SAndroid Build Coastguard Worker static bool CheckOutput(const char* what, std::string actual_output,
201*00c7fec1SAndroid Build Coastguard Worker                         const std::string& expected_output, const std::string& FILES) {
202*00c7fec1SAndroid Build Coastguard Worker   // Rewrite the output to reverse any expansion of $FILES.
203*00c7fec1SAndroid Build Coastguard Worker   actual_output = android::base::StringReplace(actual_output, FILES, "$FILES", true);
204*00c7fec1SAndroid Build Coastguard Worker 
205*00c7fec1SAndroid Build Coastguard Worker   bool result = (actual_output == expected_output);
206*00c7fec1SAndroid Build Coastguard Worker   if (!result) {
207*00c7fec1SAndroid Build Coastguard Worker     fprintf(stderr, "Incorrect %s.\nExpected:\n%s\nActual:\n%s\n", what, expected_output.c_str(),
208*00c7fec1SAndroid Build Coastguard Worker             actual_output.c_str());
209*00c7fec1SAndroid Build Coastguard Worker   }
210*00c7fec1SAndroid Build Coastguard Worker   return result;
211*00c7fec1SAndroid Build Coastguard Worker }
212*00c7fec1SAndroid Build Coastguard Worker 
RunTests(const std::vector<Test> & tests)213*00c7fec1SAndroid Build Coastguard Worker static int RunTests(const std::vector<Test>& tests) {
214*00c7fec1SAndroid Build Coastguard Worker   std::vector<std::string> failures;
215*00c7fec1SAndroid Build Coastguard Worker 
216*00c7fec1SAndroid Build Coastguard Worker   Print(kGreen, "[==========]", " Running %zu tests.", tests.size());
217*00c7fec1SAndroid Build Coastguard Worker   android::base::Timer total_timer;
218*00c7fec1SAndroid Build Coastguard Worker   for (const auto& test : tests) {
219*00c7fec1SAndroid Build Coastguard Worker     bool failed = false;
220*00c7fec1SAndroid Build Coastguard Worker 
221*00c7fec1SAndroid Build Coastguard Worker     Print(kGreen, "[ RUN      ]", " %s", test.name.c_str());
222*00c7fec1SAndroid Build Coastguard Worker     android::base::Timer test_timer;
223*00c7fec1SAndroid Build Coastguard Worker 
224*00c7fec1SAndroid Build Coastguard Worker     // Set $FILES for this test.
225*00c7fec1SAndroid Build Coastguard Worker     std::string FILES = android::base::Dirname(test.test_filename) + "/files";
226*00c7fec1SAndroid Build Coastguard Worker     V("setenv(\"FILES\", \"%s\")", FILES.c_str());
227*00c7fec1SAndroid Build Coastguard Worker     setenv("FILES", FILES.c_str(), 1);
228*00c7fec1SAndroid Build Coastguard Worker 
229*00c7fec1SAndroid Build Coastguard Worker     // Make a safe space to run the test.
230*00c7fec1SAndroid Build Coastguard Worker     TemporaryDir td;
231*00c7fec1SAndroid Build Coastguard Worker     V("chdir(\"%s\")", td.path);
232*00c7fec1SAndroid Build Coastguard Worker     if (chdir(td.path)) Die(errno, "chdir(\"%s\")", td.path);
233*00c7fec1SAndroid Build Coastguard Worker 
234*00c7fec1SAndroid Build Coastguard Worker     // Perform any setup specified for this test.
235*00c7fec1SAndroid Build Coastguard Worker     if (!RunCommands("before", test.befores)) failed = true;
236*00c7fec1SAndroid Build Coastguard Worker 
237*00c7fec1SAndroid Build Coastguard Worker     if (!failed) {
238*00c7fec1SAndroid Build Coastguard Worker       V("running command \"%s\"", test.command.c_str());
239*00c7fec1SAndroid Build Coastguard Worker       CapturedStdout test_stdout;
240*00c7fec1SAndroid Build Coastguard Worker       CapturedStderr test_stderr;
241*00c7fec1SAndroid Build Coastguard Worker       int status = system(test.command.c_str());
242*00c7fec1SAndroid Build Coastguard Worker       test_stdout.Stop();
243*00c7fec1SAndroid Build Coastguard Worker       test_stderr.Stop();
244*00c7fec1SAndroid Build Coastguard Worker 
245*00c7fec1SAndroid Build Coastguard Worker       V("system() returned status %d", status);
246*00c7fec1SAndroid Build Coastguard Worker       if (WEXITSTATUS(status) != test.exit_status) {
247*00c7fec1SAndroid Build Coastguard Worker         failed = true;
248*00c7fec1SAndroid Build Coastguard Worker         fprintf(stderr, "Incorrect exit status: expected %d but %s\n", test.exit_status,
249*00c7fec1SAndroid Build Coastguard Worker                 ExitStatusToString(status).c_str());
250*00c7fec1SAndroid Build Coastguard Worker       }
251*00c7fec1SAndroid Build Coastguard Worker 
252*00c7fec1SAndroid Build Coastguard Worker       if (!CheckOutput("stdout", test_stdout.str(), test.expected_stdout, FILES)) failed = true;
253*00c7fec1SAndroid Build Coastguard Worker       if (!CheckOutput("stderr", test_stderr.str(), test.expected_stderr, FILES)) failed = true;
254*00c7fec1SAndroid Build Coastguard Worker 
255*00c7fec1SAndroid Build Coastguard Worker       if (!RunCommands("after", test.afters)) failed = true;
256*00c7fec1SAndroid Build Coastguard Worker     }
257*00c7fec1SAndroid Build Coastguard Worker 
258*00c7fec1SAndroid Build Coastguard Worker     std::stringstream duration;
259*00c7fec1SAndroid Build Coastguard Worker     duration << test_timer;
260*00c7fec1SAndroid Build Coastguard Worker     if (failed) {
261*00c7fec1SAndroid Build Coastguard Worker       failures.push_back(test.name);
262*00c7fec1SAndroid Build Coastguard Worker       Print(kRed, "[  FAILED  ]", " %s (%s)", test.name.c_str(), duration.str().c_str());
263*00c7fec1SAndroid Build Coastguard Worker     } else {
264*00c7fec1SAndroid Build Coastguard Worker       Print(kGreen, "[       OK ]", " %s (%s)", test.name.c_str(), duration.str().c_str());
265*00c7fec1SAndroid Build Coastguard Worker     }
266*00c7fec1SAndroid Build Coastguard Worker   }
267*00c7fec1SAndroid Build Coastguard Worker 
268*00c7fec1SAndroid Build Coastguard Worker   // Summarize the whole run and explicitly list all the failures.
269*00c7fec1SAndroid Build Coastguard Worker 
270*00c7fec1SAndroid Build Coastguard Worker   std::stringstream duration;
271*00c7fec1SAndroid Build Coastguard Worker   duration << total_timer;
272*00c7fec1SAndroid Build Coastguard Worker   Print(kGreen, "[==========]", " %zu tests ran. (%s total)", tests.size(), duration.str().c_str());
273*00c7fec1SAndroid Build Coastguard Worker 
274*00c7fec1SAndroid Build Coastguard Worker   size_t fail_count = failures.size();
275*00c7fec1SAndroid Build Coastguard Worker   size_t pass_count = tests.size() - fail_count;
276*00c7fec1SAndroid Build Coastguard Worker   Print(kGreen, "[  PASSED  ]", " %zu test%s.", pass_count, Plural(pass_count));
277*00c7fec1SAndroid Build Coastguard Worker   if (!failures.empty()) {
278*00c7fec1SAndroid Build Coastguard Worker     Print(kRed, "[  FAILED  ]", " %zu test%s.", fail_count, Plural(fail_count));
279*00c7fec1SAndroid Build Coastguard Worker     for (auto& failure : failures) {
280*00c7fec1SAndroid Build Coastguard Worker       Print(kRed, "[  FAILED  ]", " %s", failure.c_str());
281*00c7fec1SAndroid Build Coastguard Worker     }
282*00c7fec1SAndroid Build Coastguard Worker   }
283*00c7fec1SAndroid Build Coastguard Worker   return (fail_count == 0) ? 0 : 1;
284*00c7fec1SAndroid Build Coastguard Worker }
285*00c7fec1SAndroid Build Coastguard Worker 
ShowHelp(bool full)286*00c7fec1SAndroid Build Coastguard Worker static void ShowHelp(bool full) {
287*00c7fec1SAndroid Build Coastguard Worker   fprintf(full ? stdout : stderr, "usage: %s [-v] FILE...\n", g_progname);
288*00c7fec1SAndroid Build Coastguard Worker   if (!full) exit(EXIT_FAILURE);
289*00c7fec1SAndroid Build Coastguard Worker 
290*00c7fec1SAndroid Build Coastguard Worker   printf(
291*00c7fec1SAndroid Build Coastguard Worker       "\n"
292*00c7fec1SAndroid Build Coastguard Worker       "Run tests.\n"
293*00c7fec1SAndroid Build Coastguard Worker       "\n"
294*00c7fec1SAndroid Build Coastguard Worker       "-v\tVerbose (show workings)\n");
295*00c7fec1SAndroid Build Coastguard Worker   exit(EXIT_SUCCESS);
296*00c7fec1SAndroid Build Coastguard Worker }
297*00c7fec1SAndroid Build Coastguard Worker 
main(int argc,char * argv[])298*00c7fec1SAndroid Build Coastguard Worker int main(int argc, char* argv[]) {
299*00c7fec1SAndroid Build Coastguard Worker   g_progname = basename(argv[0]);
300*00c7fec1SAndroid Build Coastguard Worker 
301*00c7fec1SAndroid Build Coastguard Worker   static const struct option opts[] = {
302*00c7fec1SAndroid Build Coastguard Worker       {"help", no_argument, 0, 'h'},
303*00c7fec1SAndroid Build Coastguard Worker       {"verbose", no_argument, 0, 'v'},
304*00c7fec1SAndroid Build Coastguard Worker       {},
305*00c7fec1SAndroid Build Coastguard Worker   };
306*00c7fec1SAndroid Build Coastguard Worker 
307*00c7fec1SAndroid Build Coastguard Worker   int opt;
308*00c7fec1SAndroid Build Coastguard Worker   while ((opt = getopt_long(argc, argv, "hv", opts, nullptr)) != -1) {
309*00c7fec1SAndroid Build Coastguard Worker     switch (opt) {
310*00c7fec1SAndroid Build Coastguard Worker       case 'h':
311*00c7fec1SAndroid Build Coastguard Worker         ShowHelp(true);
312*00c7fec1SAndroid Build Coastguard Worker         break;
313*00c7fec1SAndroid Build Coastguard Worker       case 'v':
314*00c7fec1SAndroid Build Coastguard Worker         g_verbose = true;
315*00c7fec1SAndroid Build Coastguard Worker         break;
316*00c7fec1SAndroid Build Coastguard Worker       default:
317*00c7fec1SAndroid Build Coastguard Worker         ShowHelp(false);
318*00c7fec1SAndroid Build Coastguard Worker         break;
319*00c7fec1SAndroid Build Coastguard Worker     }
320*00c7fec1SAndroid Build Coastguard Worker   }
321*00c7fec1SAndroid Build Coastguard Worker 
322*00c7fec1SAndroid Build Coastguard Worker   argv += optind;
323*00c7fec1SAndroid Build Coastguard Worker   if (!*argv) Die(0, "no test files provided");
324*00c7fec1SAndroid Build Coastguard Worker   std::vector<Test> tests;
325*00c7fec1SAndroid Build Coastguard Worker   for (; *argv; ++argv) CollectTests(&tests, *argv);
326*00c7fec1SAndroid Build Coastguard Worker   return RunTests(tests);
327*00c7fec1SAndroid Build Coastguard Worker }
328