• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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