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