OLD | NEW |
---|---|
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Parses the command line, discovers the appropriate tests, and runs them. | 5 """Parses the command line, discovers the appropriate tests, and runs them. |
6 | 6 |
7 Handles test configuration, but all the logic for | 7 Handles test configuration, but all the logic for |
8 actually running the test is in Test and PageRunner.""" | 8 actually running the test is in Test and PageRunner.""" |
9 | 9 |
10 import hashlib | 10 import hashlib |
11 import inspect | 11 import inspect |
12 import json | 12 import json |
13 import os | 13 import os |
14 import sys | 14 import sys |
15 | 15 |
16 from telemetry import benchmark | 16 from telemetry import benchmark |
17 from telemetry import decorators | 17 from telemetry import decorators |
18 from telemetry.core import browser_finder | 18 from telemetry.core import browser_finder |
19 from telemetry.core import browser_options | 19 from telemetry.core import browser_options |
20 from telemetry.core import command_line | 20 from telemetry.core import command_line |
21 from telemetry.core import discover | 21 from telemetry.core import discover |
22 from telemetry.core import environment | |
23 from telemetry.core import util | |
24 from telemetry.page import page_set | 22 from telemetry.page import page_set |
25 from telemetry.page import page_test | 23 from telemetry.page import page_test |
26 from telemetry.page import profile_creator | |
27 from telemetry.util import find_dependencies | 24 from telemetry.util import find_dependencies |
28 | 25 |
29 | 26 |
27 test_class_types = [benchmark.Benchmark, page_test.PageTest] | |
nednguyen
2014/09/04 03:31:37
May better be explicit about this: _BENCHMARK_AND_
| |
28 | |
29 | |
30 class Deps(find_dependencies.FindDependenciesCommand): | 30 class Deps(find_dependencies.FindDependenciesCommand): |
31 """Prints all dependencies""" | 31 """Prints all dependencies""" |
32 | 32 |
33 def Run(self, args): | 33 def Run(self, args): |
34 main_module = sys.modules['__main__'] | 34 main_module = sys.modules['__main__'] |
35 args.positional_args.append(os.path.realpath(main_module.__file__)) | 35 args.positional_args.append(os.path.realpath(main_module.__file__)) |
36 return super(Deps, self).Run(args) | 36 return super(Deps, self).Run(args) |
37 | 37 |
38 | 38 |
39 class Help(command_line.OptparseCommand): | 39 class Help(command_line.OptparseCommand): |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
73 return parser | 73 return parser |
74 | 74 |
75 @classmethod | 75 @classmethod |
76 def AddCommandLineArgs(cls, parser): | 76 def AddCommandLineArgs(cls, parser): |
77 parser.add_option('-j', '--json-output-file', type='string') | 77 parser.add_option('-j', '--json-output-file', type='string') |
78 parser.add_option('-n', '--num-shards', type='int', default=1) | 78 parser.add_option('-n', '--num-shards', type='int', default=1) |
79 | 79 |
80 @classmethod | 80 @classmethod |
81 def ProcessCommandLineArgs(cls, parser, args): | 81 def ProcessCommandLineArgs(cls, parser, args): |
82 if not args.positional_args: | 82 if not args.positional_args: |
83 args.tests = _Tests() | 83 args.tests = _GetAllTests() |
84 elif len(args.positional_args) == 1: | 84 elif len(args.positional_args) == 1: |
85 args.tests = _MatchTestName(args.positional_args[0], exact_matches=False) | 85 args.tests = _MatchTestName(args.positional_args[0], exact_matches=False) |
86 else: | 86 else: |
87 parser.error('Must provide at most one test name.') | 87 parser.error('Must provide at most one test name.') |
88 | 88 |
89 def Run(self, args): | 89 def Run(self, args): |
90 if args.json_output_file: | 90 if args.json_output_file: |
91 possible_browser = browser_finder.FindBrowser(args) | 91 possible_browser = browser_finder.FindBrowser(args) |
92 with open(args.json_output_file, 'w') as f: | 92 with open(args.json_output_file, 'w') as f: |
93 f.write(_GetJsonTestList(possible_browser, args.tests, args.num_shards)) | 93 f.write(_GetJsonTestList(possible_browser, args.tests, args.num_shards)) |
(...skipping 26 matching lines...) Expand all Loading... | |
120 # TODO(dtu): After move to argparse, add command-line args for all tests | 120 # TODO(dtu): After move to argparse, add command-line args for all tests |
121 # to subparser. Using subparsers will avoid duplicate arguments. | 121 # to subparser. Using subparsers will avoid duplicate arguments. |
122 matching_test = matching_tests.pop() | 122 matching_test = matching_tests.pop() |
123 matching_test.AddCommandLineArgs(parser) | 123 matching_test.AddCommandLineArgs(parser) |
124 # The test's options override the defaults! | 124 # The test's options override the defaults! |
125 matching_test.SetArgumentDefaults(parser) | 125 matching_test.SetArgumentDefaults(parser) |
126 | 126 |
127 @classmethod | 127 @classmethod |
128 def ProcessCommandLineArgs(cls, parser, args): | 128 def ProcessCommandLineArgs(cls, parser, args): |
129 if not args.positional_args: | 129 if not args.positional_args: |
130 _PrintTestList(_Tests()) | 130 _PrintTestList(output_stream=sys.stderr) |
131 sys.exit(-1) | 131 sys.exit(-1) |
132 | 132 |
133 input_test_name = args.positional_args[0] | 133 input_test_name = args.positional_args[0] |
134 matching_tests = _MatchTestName(input_test_name) | 134 matching_tests = _MatchTestName(input_test_name) |
135 if not matching_tests: | 135 if not matching_tests: |
136 print >> sys.stderr, 'No test named "%s".' % input_test_name | 136 print >> sys.stderr, 'No test named "%s".' % input_test_name |
137 print >> sys.stderr | 137 print >> sys.stderr |
138 _PrintTestList(_Tests()) | 138 _PrintTestList(output_stream=sys.stderr) |
139 sys.exit(-1) | 139 sys.exit(-1) |
140 | 140 |
141 if len(matching_tests) > 1: | 141 if len(matching_tests) > 1: |
142 print >> sys.stderr, 'Multiple tests named "%s".' % input_test_name | 142 print >> sys.stderr, 'Multiple tests named "%s".' % input_test_name |
143 print >> sys.stderr, 'Did you mean one of these?' | 143 print >> sys.stderr, 'Did you mean one of these?' |
144 print >> sys.stderr | 144 print >> sys.stderr |
145 _PrintTestList(matching_tests) | 145 _PrintTestList(matching_tests, output_stream=sys.stderr) |
146 sys.exit(-1) | 146 sys.exit(-1) |
147 | 147 |
148 test_class = matching_tests.pop() | 148 test_class = matching_tests.pop() |
149 if issubclass(test_class, page_test.PageTest): | 149 if issubclass(test_class, page_test.PageTest): |
150 if len(args.positional_args) < 2: | 150 if len(args.positional_args) < 2: |
151 parser.error('Need to specify a page set for "%s".' % test_class.Name()) | 151 parser.error('Need to specify a page set for "%s".' % test_class.Name()) |
152 if len(args.positional_args) > 2: | 152 if len(args.positional_args) > 2: |
153 parser.error('Too many arguments.') | 153 parser.error('Too many arguments.') |
154 page_set_path = args.positional_args[1] | 154 page_set_path = args.positional_args[1] |
155 if not os.path.exists(page_set_path): | 155 if not os.path.exists(page_set_path): |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 | 188 |
189 def _Commands(): | 189 def _Commands(): |
190 """Generates a list of all classes in this file that subclass Command.""" | 190 """Generates a list of all classes in this file that subclass Command.""" |
191 for _, cls in inspect.getmembers(sys.modules[__name__]): | 191 for _, cls in inspect.getmembers(sys.modules[__name__]): |
192 if not inspect.isclass(cls): | 192 if not inspect.isclass(cls): |
193 continue | 193 continue |
194 if not issubclass(cls, command_line.Command): | 194 if not issubclass(cls, command_line.Command): |
195 continue | 195 continue |
196 yield cls | 196 yield cls |
197 | 197 |
198 | |
198 def _MatchingCommands(string): | 199 def _MatchingCommands(string): |
199 return [command for command in _Commands() | 200 return [command for command in _Commands() |
200 if command.Name().startswith(string)] | 201 if command.Name().startswith(string)] |
201 | 202 |
202 @decorators.Cache | 203 |
203 def _Tests(): | 204 def _GetAllTests(): |
204 tests = [] | 205 return discover.GetValidSubclassesOfClasses(test_class_types) |
205 for base_dir in config.base_paths: | |
206 tests += discover.DiscoverClasses(base_dir, base_dir, benchmark.Benchmark, | |
207 index_by_class_name=True).values() | |
208 page_tests = discover.DiscoverClasses(base_dir, base_dir, | |
209 page_test.PageTest, | |
210 index_by_class_name=True).values() | |
211 tests += [test_class for test_class in page_tests | |
212 if not issubclass(test_class, profile_creator.ProfileCreator)] | |
213 return tests | |
214 | 206 |
215 | 207 |
216 def _MatchTestName(input_test_name, exact_matches=True): | 208 def _MatchTestName(input_name, exact_matches=True): |
217 def _Matches(input_string, search_string): | 209 return discover.MatchName(test_class_types, input_name, exact_matches) |
218 if search_string.startswith(input_string): | |
219 return True | |
220 for part in search_string.split('.'): | |
221 if part.startswith(input_string): | |
222 return True | |
223 return False | |
224 | 210 |
225 # Exact matching. | |
226 if exact_matches: | |
227 # Don't add aliases to search dict, only allow exact matching for them. | |
228 if input_test_name in config.test_aliases: | |
229 exact_match = config.test_aliases[input_test_name] | |
230 else: | |
231 exact_match = input_test_name | |
232 | 211 |
233 for test_class in _Tests(): | 212 def _PrintTestList(class_list=None, output_stream=sys.stdout): |
234 if exact_match == test_class.Name(): | 213 discover.PrintAvailableClasses(test_class_types, class_list, output_stream) |
235 return [test_class] | |
236 | |
237 # Fuzzy matching. | |
238 return [test_class for test_class in _Tests() | |
239 if _Matches(input_test_name, test_class.Name())] | |
240 | 214 |
241 | 215 |
242 def _GetJsonTestList(possible_browser, test_classes, num_shards): | 216 def _GetJsonTestList(possible_browser, test_classes, num_shards): |
243 """Returns a list of all enabled tests in a JSON format expected by buildbots. | 217 """Returns a list of all enabled tests in a JSON format expected by buildbots. |
244 | 218 |
245 JSON format (see build/android/pylib/perf/test_runner.py): | 219 JSON format (see build/android/pylib/perf/test_runner.py): |
246 { "version": int, | 220 { "version": int, |
247 "steps": { | 221 "steps": { |
248 "foo": { | 222 "foo": { |
249 "device_affinity": int, | 223 "device_affinity": int, |
(...skipping 24 matching lines...) Expand all Loading... | |
274 # TODO(tonyg): Currently we set the device affinity to a stable hash of | 248 # TODO(tonyg): Currently we set the device affinity to a stable hash of |
275 # the test name. This somewhat evenly distributes benchmarks among the | 249 # the test name. This somewhat evenly distributes benchmarks among the |
276 # requested number of shards. However, it is far from optimal in terms of | 250 # requested number of shards. However, it is far from optimal in terms of |
277 # cycle time. We should add a test size decorator (e.g. small, medium, | 251 # cycle time. We should add a test size decorator (e.g. small, medium, |
278 # large) and let that inform sharding. | 252 # large) and let that inform sharding. |
279 'device_affinity': int(hashlib.sha1(name).hexdigest(), 16) % num_shards | 253 'device_affinity': int(hashlib.sha1(name).hexdigest(), 16) % num_shards |
280 } | 254 } |
281 return json.dumps(output, indent=2, sort_keys=True) | 255 return json.dumps(output, indent=2, sort_keys=True) |
282 | 256 |
283 | 257 |
284 def _PrintTestList(tests): | |
285 if not tests: | |
286 print >> sys.stderr, 'No tests found!' | |
287 return | |
288 | |
289 # Align the test names to the longest one. | |
290 format_string = ' %%-%ds %%s' % max(len(t.Name()) for t in tests) | |
291 | |
292 filtered_tests = [test_class for test_class in tests | |
293 if issubclass(test_class, benchmark.Benchmark)] | |
294 if filtered_tests: | |
295 print >> sys.stderr, 'Available tests are:' | |
296 for test_class in sorted(filtered_tests, key=lambda t: t.Name()): | |
297 print >> sys.stderr, format_string % ( | |
298 test_class.Name(), test_class.Description()) | |
299 print >> sys.stderr | |
300 | |
301 filtered_tests = [test_class for test_class in tests | |
302 if issubclass(test_class, page_test.PageTest)] | |
303 if filtered_tests: | |
304 print >> sys.stderr, 'Available page tests are:' | |
305 for test_class in sorted(filtered_tests, key=lambda t: t.Name()): | |
306 print >> sys.stderr, format_string % ( | |
307 test_class.Name(), test_class.Description()) | |
308 print >> sys.stderr | |
309 | |
310 | |
311 config = environment.Environment([util.GetBaseDir()]) | |
312 | |
313 | |
314 def main(): | 258 def main(): |
315 # Get the command name from the command line. | 259 # Get the command name from the command line. |
316 if len(sys.argv) > 1 and sys.argv[1] == '--help': | 260 if len(sys.argv) > 1 and sys.argv[1] == '--help': |
317 sys.argv[1] = 'help' | 261 sys.argv[1] = 'help' |
318 | 262 |
319 command_name = 'run' | 263 command_name = 'run' |
320 for arg in sys.argv[1:]: | 264 for arg in sys.argv[1:]: |
321 if not arg.startswith('-'): | 265 if not arg.startswith('-'): |
322 command_name = arg | 266 command_name = arg |
323 break | 267 break |
(...skipping 14 matching lines...) Expand all Loading... | |
338 | 282 |
339 # Parse and run the command. | 283 # Parse and run the command. |
340 parser = command.CreateParser() | 284 parser = command.CreateParser() |
341 command.AddCommandLineArgs(parser) | 285 command.AddCommandLineArgs(parser) |
342 options, args = parser.parse_args() | 286 options, args = parser.parse_args() |
343 if commands: | 287 if commands: |
344 args = args[1:] | 288 args = args[1:] |
345 options.positional_args = args | 289 options.positional_args = args |
346 command.ProcessCommandLineArgs(parser, options) | 290 command.ProcessCommandLineArgs(parser, options) |
347 return command().Run(options) | 291 return command().Run(options) |
OLD | NEW |