1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include <algorithm>
24 #include <cctype>
25 #include <string>
26 #include <unordered_map>
27 #include <vector>
28
29 #include <android-base/parseint.h>
30 #include <gtest/gtest.h>
31
32 #include "Options.h"
33
34 namespace android {
35 namespace gtest_extras {
36
37 // The total time each test can run before timing out and being killed.
38 constexpr uint64_t kDefaultDeadlineThresholdMs = 90000;
39
40 // The total time each test can run before a warning is issued.
41 constexpr uint64_t kDefaultSlowThresholdMs = 2000;
42
43 const std::unordered_map<std::string, Options::ArgInfo> Options::kArgs = {
44 {"deadline_threshold_ms", {FLAG_REQUIRES_VALUE, &Options::SetNumeric}},
45 {"slow_threshold_ms", {FLAG_REQUIRES_VALUE, &Options::SetNumeric}},
46 {"gtest_format", {FLAG_NONE, &Options::SetBool}},
47 {"no_gtest_format", {FLAG_NONE, &Options::SetBool}},
48 {"gtest_list_tests", {FLAG_NONE, &Options::SetBool}},
49 {"gtest_filter", {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE, &Options::SetString}},
50 {
51 "gtest_repeat",
52 {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE, &Options::SetIterations},
53 },
54 {"gtest_output", {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE, &Options::SetXmlFile}},
55 {"gtest_print_time", {FLAG_ENVIRONMENT_VARIABLE | FLAG_OPTIONAL_VALUE, &Options::SetPrintTime}},
56 {
57 "gtest_also_run_disabled_tests",
58 {FLAG_ENVIRONMENT_VARIABLE | FLAG_CHILD, &Options::SetBool},
59 },
60 {"gtest_color",
61 {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE | FLAG_CHILD, &Options::SetString}},
62 {"gtest_death_test_style",
63 {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE | FLAG_CHILD, nullptr}},
64 {"gtest_break_on_failure", {FLAG_ENVIRONMENT_VARIABLE | FLAG_INCOMPATIBLE, nullptr}},
65 {"gtest_catch_exceptions", {FLAG_ENVIRONMENT_VARIABLE | FLAG_INCOMPATIBLE, nullptr}},
66 {"gtest_random_seed", {FLAG_ENVIRONMENT_VARIABLE | FLAG_INCOMPATIBLE, nullptr}},
67 {"gtest_shuffle", {FLAG_ENVIRONMENT_VARIABLE | FLAG_INCOMPATIBLE, nullptr}},
68 {"gtest_stream_result_to", {FLAG_ENVIRONMENT_VARIABLE | FLAG_INCOMPATIBLE, nullptr}},
69 {"gtest_throw_on_failure", {FLAG_ENVIRONMENT_VARIABLE | FLAG_INCOMPATIBLE, nullptr}},
70 {"gtest_shard_index",
71 {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE, &Options::SetNumericEnvOnly}},
72 {"gtest_total_shards",
73 {FLAG_ENVIRONMENT_VARIABLE | FLAG_REQUIRES_VALUE, &Options::SetNumericEnvOnly}},
74 };
75
PrintError(const std::string & arg,std::string msg,bool from_env)76 static void PrintError(const std::string& arg, std::string msg, bool from_env) {
77 if (from_env) {
78 std::string variable(arg);
79 std::transform(variable.begin(), variable.end(), variable.begin(),
80 [](char c) { return std::toupper(c); });
81 printf("env[%s] %s\n", variable.c_str(), msg.c_str());
82 } else if (arg[0] == '-') {
83 printf("%s %s\n", arg.c_str(), msg.c_str());
84 } else {
85 printf("--%s %s\n", arg.c_str(), msg.c_str());
86 }
87 }
88
89 template <typename IntType>
GetNumeric(const char * arg,const char * value,IntType * numeric_value,bool from_env)90 static bool GetNumeric(const char* arg, const char* value, IntType* numeric_value, bool from_env) {
91 bool result = false;
92 if constexpr (std::is_unsigned<IntType>::value) {
93 result = android::base::ParseUint<IntType>(value, numeric_value);
94 } else {
95 result = android::base::ParseInt<IntType>(value, numeric_value);
96 }
97 if (!result) {
98 if (errno == ERANGE) {
99 PrintError(arg, std::string("value overflows (") + value + ")", from_env);
100 } else {
101 PrintError(arg, std::string("value is not formatted as a numeric value (") + value + ")",
102 from_env);
103 }
104 return false;
105 }
106 return true;
107 }
108
SetPrintTime(const std::string &,const std::string & value,bool)109 bool Options::SetPrintTime(const std::string&, const std::string& value, bool) {
110 if (!value.empty() && strtol(value.c_str(), nullptr, 10) == 0) {
111 bools_.find("gtest_print_time")->second = false;
112 }
113 return true;
114 }
115
SetNumeric(const std::string & arg,const std::string & value,bool from_env)116 bool Options::SetNumeric(const std::string& arg, const std::string& value, bool from_env) {
117 uint64_t* numeric = &numerics_.find(arg)->second;
118 if (!GetNumeric<uint64_t>(arg.c_str(), value.c_str(), numeric, from_env)) {
119 return false;
120 }
121 if (*numeric == 0) {
122 PrintError(arg, "requires a number greater than zero.", from_env);
123 return false;
124 }
125 return true;
126 }
127
SetNumericEnvOnly(const std::string & arg,const std::string & value,bool from_env)128 bool Options::SetNumericEnvOnly(const std::string& arg, const std::string& value, bool from_env) {
129 if (!from_env) {
130 PrintError(arg, "is only supported as an environment variable.", false);
131 return false;
132 }
133 uint64_t* numeric = &numerics_.find(arg)->second;
134 if (!GetNumeric<uint64_t>(arg.c_str(), value.c_str(), numeric, from_env)) {
135 return false;
136 }
137 return true;
138 }
139
SetBool(const std::string & arg,const std::string &,bool)140 bool Options::SetBool(const std::string& arg, const std::string&, bool) {
141 bools_.find(arg)->second = true;
142 return true;
143 }
144
SetIterations(const std::string & arg,const std::string & value,bool from_env)145 bool Options::SetIterations(const std::string& arg, const std::string& value, bool from_env) {
146 if (!GetNumeric<int>(arg.c_str(), value.c_str(), &num_iterations_, from_env)) {
147 return false;
148 }
149 return true;
150 }
151
SetString(const std::string & arg,const std::string & value,bool)152 bool Options::SetString(const std::string& arg, const std::string& value, bool) {
153 strings_.find(arg)->second = value;
154 return true;
155 }
156
SetXmlFile(const std::string & arg,const std::string & value,bool from_env)157 bool Options::SetXmlFile(const std::string& arg, const std::string& value, bool from_env) {
158 if (value.substr(0, 4) != "xml:") {
159 PrintError(arg, "only supports an xml output file.", from_env);
160 return false;
161 }
162 std::string xml_file(value.substr(4));
163 if (xml_file.empty()) {
164 PrintError(arg, "requires a file name after xml:", from_env);
165 return false;
166 }
167 // Need an absolute file.
168 if (xml_file[0] != '/') {
169 char* cwd = getcwd(nullptr, 0);
170 if (cwd == nullptr) {
171 PrintError(arg,
172 std::string("cannot get absolute pathname, getcwd() is failing: ") +
173 strerror(errno) + '\n',
174 from_env);
175 return false;
176 }
177 xml_file = std::string(cwd) + '/' + xml_file;
178 free(cwd);
179 }
180
181 // If the output file is a directory, add the name of a file.
182 if (xml_file.back() == '/') {
183 xml_file += "test_details.xml";
184 }
185 strings_.find("xml_file")->second = xml_file;
186 return true;
187 }
188
HandleArg(const std::string & arg,const std::string & value,const ArgInfo & info,bool from_env)189 bool Options::HandleArg(const std::string& arg, const std::string& value, const ArgInfo& info,
190 bool from_env) {
191 if (info.flags & FLAG_INCOMPATIBLE) {
192 PrintError(arg, "is not compatible with isolation runs.", from_env);
193 return false;
194 }
195
196 if (info.flags & FLAG_TAKES_VALUE) {
197 if ((info.flags & FLAG_REQUIRES_VALUE) && value.empty()) {
198 PrintError(arg, "requires an argument.", from_env);
199 return false;
200 }
201
202 if (info.func != nullptr && !(this->*(info.func))(arg, value, from_env)) {
203 return false;
204 }
205 } else if (!value.empty()) {
206 PrintError(arg, "does not take an argument.", from_env);
207 return false;
208 } else if (info.func != nullptr) {
209 return (this->*(info.func))(arg, value, from_env);
210 }
211 return true;
212 }
213
Process(const std::vector<const char * > & args,std::vector<const char * > * child_args)214 bool Options::Process(const std::vector<const char*>& args, std::vector<const char*>* child_args) {
215 // Initialize the variables.
216 job_count_ = static_cast<size_t>(sysconf(_SC_NPROCESSORS_ONLN));
217 num_iterations_ = ::testing::GTEST_FLAG(repeat);
218 numerics_.clear();
219 numerics_["deadline_threshold_ms"] = kDefaultDeadlineThresholdMs;
220 numerics_["slow_threshold_ms"] = kDefaultSlowThresholdMs;
221 numerics_["gtest_shard_index"] = 0;
222 numerics_["gtest_total_shards"] = 0;
223 strings_.clear();
224 strings_["gtest_color"] = ::testing::GTEST_FLAG(color);
225 strings_["xml_file"] = ::testing::GTEST_FLAG(output);
226 strings_["gtest_filter"] = "";
227 bools_.clear();
228 bools_["gtest_print_time"] = ::testing::GTEST_FLAG(print_time);
229 bools_["gtest_format"] = true;
230 bools_["no_gtest_format"] = false;
231 bools_["gtest_also_run_disabled_tests"] = ::testing::GTEST_FLAG(also_run_disabled_tests);
232 bools_["gtest_list_tests"] = false;
233
234 child_args->clear();
235
236 // Loop through all of the possible environment variables.
237 for (const auto& entry : kArgs) {
238 if (entry.second.flags & FLAG_ENVIRONMENT_VARIABLE) {
239 std::string variable(entry.first);
240 std::transform(variable.begin(), variable.end(), variable.begin(),
241 [](char c) { return std::toupper(c); });
242 char* env = getenv(variable.c_str());
243 if (env == nullptr) {
244 continue;
245 }
246 std::string value(env);
247 if (!HandleArg(entry.first, value, entry.second, true)) {
248 return false;
249 }
250 }
251 }
252
253 child_args->push_back(args[0]);
254
255 // Assumes the first value is not an argument, so skip it.
256 for (size_t i = 1; i < args.size(); i++) {
257 // Special handle of -j or -jXX.
258 if (strncmp(args[i], "-j", 2) == 0) {
259 const char* value = &args[i][2];
260 if (*value == '\0') {
261 // Get the next argument.
262 if (i == args.size() - 1) {
263 printf("-j requires an argument.\n");
264 return false;
265 }
266 i++;
267 value = args[i];
268 }
269 if (!GetNumeric<size_t>("-j", value, &job_count_, false)) {
270 return false;
271 }
272 } else if (strncmp("--", args[i], 2) == 0) {
273 // See if this is a name=value argument.
274 std::string name;
275 std::string value;
276 const char* equal = strchr(args[i], '=');
277 if (equal != nullptr) {
278 name = std::string(&args[i][2], static_cast<size_t>(equal - args[i]) - 2);
279 value = equal + 1;
280 } else {
281 name = args[i] + 2;
282 }
283 auto entry = kArgs.find(name);
284 if (entry == kArgs.end()) {
285 printf("Unknown argument: %s\n", args[i]);
286 return false;
287 }
288
289 if (entry->second.flags & FLAG_CHILD) {
290 child_args->push_back(args[i]);
291 }
292
293 if (!HandleArg(name, value, entry->second)) {
294 return false;
295 }
296 } else if (args[i][0] == '-') {
297 printf("Unknown argument: %s\n", args[i]);
298 return false;
299 } else {
300 printf("Unexpected argument '%s'\n", args[i]);
301 return false;
302 }
303 }
304
305 // If no_gtest_format was specified, it overrides gtest_format.
306 if (bools_.at("no_gtest_format")) {
307 bools_["gtest_format"] = false;
308 }
309 return true;
310 }
311
312 } // namespace gtest_extras
313 } // namespace android
314