1 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Parses the command line, discovers the appropriate tests, and runs them.
7 Handles test configuration, but all the logic for
8 actually running the test is in Test and PageRunner."""
15 from telemetry import decorators
16 from telemetry import test
17 from telemetry.core import browser_options
18 from telemetry.core import command_line
19 from telemetry.core import discover
20 from telemetry.core import environment
21 from telemetry.core import util
22 from telemetry.page import page_set
23 from telemetry.page import page_test
24 from telemetry.page import profile_creator
25 from telemetry.util import find_dependencies
28 class Deps(find_dependencies.FindDependenciesCommand):
29 """Prints all dependencies"""
32 main_module = sys.modules['__main__']
33 args.positional_args.append(os.path.realpath(main_module.__file__))
34 return super(Deps, self).Run(args)
37 class Help(command_line.OptparseCommand):
38 """Display help information"""
41 print >> sys.stderr, ('usage: %s <command> [<options>]' % _ScriptName())
42 print >> sys.stderr, 'Available commands are:'
43 for command in _Commands():
44 print >> sys.stderr, ' %-10s %s' % (
45 command.Name(), command.Description())
49 class List(command_line.OptparseCommand):
50 """Lists the available tests"""
52 usage = '[test_name] [<options>]'
55 def AddCommandLineArgs(cls, parser):
56 parser.add_option('-j', '--json', action='store_true')
59 def ProcessCommandLineArgs(cls, parser, args):
60 if not args.positional_args:
62 elif len(args.positional_args) == 1:
63 args.tests = _MatchTestName(args.positional_args[0], exact_matches=False)
65 parser.error('Must provide at most one test name.')
70 for test_class in sorted(args.tests, key=lambda t: t.Name()):
72 'name': test_class.Name(),
73 'description': test_class.Description(),
74 'options': test_class.options,
76 print json.dumps(test_list)
78 _PrintTestList(args.tests)
82 class Run(command_line.OptparseCommand):
83 """Run one or more tests"""
85 usage = 'test_name [page_set] [<options>]'
88 def CreateParser(cls):
89 options = browser_options.BrowserFinderOptions()
90 parser = options.CreateParser('%%prog %s %s' % (cls.Name(), cls.usage))
94 def AddCommandLineArgs(cls, parser):
95 test.AddCommandLineArgs(parser)
97 # Allow tests to add their own command line options.
99 for arg in sys.argv[1:]:
100 matching_tests += _MatchTestName(arg)
103 # TODO(dtu): After move to argparse, add command-line args for all tests
104 # to subparser. Using subparsers will avoid duplicate arguments.
105 matching_test = matching_tests.pop()
106 matching_test.AddCommandLineArgs(parser)
107 # The test's options override the defaults!
108 matching_test.SetArgumentDefaults(parser)
111 def ProcessCommandLineArgs(cls, parser, args):
112 if not args.positional_args:
113 _PrintTestList(_Tests())
116 input_test_name = args.positional_args[0]
117 matching_tests = _MatchTestName(input_test_name)
118 if not matching_tests:
119 print >> sys.stderr, 'No test named "%s".' % input_test_name
121 _PrintTestList(_Tests())
124 if len(matching_tests) > 1:
125 print >> sys.stderr, 'Multiple tests named "%s".' % input_test_name
126 print >> sys.stderr, 'Did you mean one of these?'
128 _PrintTestList(matching_tests)
131 test_class = matching_tests.pop()
132 if issubclass(test_class, page_test.PageTest):
133 if len(args.positional_args) < 2:
134 parser.error('Need to specify a page set for "%s".' % test_class.Name())
135 if len(args.positional_args) > 2:
136 parser.error('Too many arguments.')
137 page_set_path = args.positional_args[1]
138 if not os.path.exists(page_set_path):
139 parser.error('Page set not found.')
140 if not (os.path.isfile(page_set_path) and
141 discover.IsPageSetFile(page_set_path)):
142 parser.error('Unsupported page set file format.')
144 class TestWrapper(test.Test):
148 def CreatePageSet(cls, options):
149 return page_set.PageSet.FromFile(page_set_path)
151 test_class = TestWrapper
153 if len(args.positional_args) > 1:
154 parser.error('Too many arguments.')
156 assert issubclass(test_class, test.Test), 'Trying to run a non-Test?!'
158 test_class.ProcessCommandLineArgs(parser, args)
159 test.ProcessCommandLineArgs(parser, args)
161 cls._test = test_class
164 return min(255, self._test().Run(args))
168 return os.path.basename(sys.argv[0])
172 """Generates a list of all classes in this file that subclass Command."""
173 for _, cls in inspect.getmembers(sys.modules[__name__]):
174 if not inspect.isclass(cls):
176 if not issubclass(cls, command_line.Command):
184 for base_dir in config.base_paths:
185 tests += discover.DiscoverClasses(base_dir, base_dir, test.Test,
186 index_by_class_name=True).values()
187 page_tests = discover.DiscoverClasses(base_dir, base_dir,
189 index_by_class_name=True).values()
190 tests += [test_class for test_class in page_tests
191 if not issubclass(test_class, profile_creator.ProfileCreator)]
195 def _MatchTestName(input_test_name, exact_matches=True):
196 def _Matches(input_string, search_string):
197 if search_string.startswith(input_string):
199 for part in search_string.split('.'):
200 if part.startswith(input_string):
206 # Don't add aliases to search dict, only allow exact matching for them.
207 if input_test_name in config.test_aliases:
208 exact_match = config.test_aliases[input_test_name]
210 exact_match = input_test_name
212 for test_class in _Tests():
213 if exact_match == test_class.Name():
217 return [test_class for test_class in _Tests()
218 if _Matches(input_test_name, test_class.Name())]
221 def _PrintTestList(tests):
223 print >> sys.stderr, 'No tests found!'
226 # Align the test names to the longest one.
227 format_string = ' %%-%ds %%s' % max(len(t.Name()) for t in tests)
229 filtered_tests = [test_class for test_class in tests
230 if issubclass(test_class, test.Test)]
232 print >> sys.stderr, 'Available tests are:'
233 for test_class in sorted(filtered_tests, key=lambda t: t.Name()):
234 print >> sys.stderr, format_string % (
235 test_class.Name(), test_class.Description())
238 filtered_tests = [test_class for test_class in tests
239 if issubclass(test_class, page_test.PageTest)]
241 print >> sys.stderr, 'Available page tests are:'
242 for test_class in sorted(filtered_tests, key=lambda t: t.Name()):
243 print >> sys.stderr, format_string % (
244 test_class.Name(), test_class.Description())
248 config = environment.Environment([util.GetBaseDir()])
252 # Get the command name from the command line.
253 if len(sys.argv) > 1 and sys.argv[1] == '--help':
257 for arg in sys.argv[1:]:
258 if not arg.startswith('-'):
262 # Validate and interpret the command name.
263 commands = [command for command in _Commands()
264 if command.Name().startswith(command_name)]
265 if len(commands) > 1:
266 print >> sys.stderr, ('"%s" is not a %s command. Did you mean one of these?'
267 % (command_name, _ScriptName()))
268 for command in commands:
269 print >> sys.stderr, ' %-10s %s' % (
270 command.Name(), command.Description())
273 command = commands[0]
277 # Parse and run the command.
278 parser = command.CreateParser()
279 command.AddCommandLineArgs(parser)
280 options, args = parser.parse_args()
283 options.positional_args = args
284 command.ProcessCommandLineArgs(parser, options)
285 return command().Run(options)