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."""
17 from telemetry import test
18 from telemetry.core import browser_options
19 from telemetry.core import discover
20 from telemetry.core import util
23 class Command(object):
28 return self.__class__.__name__.lower()
31 def description(self):
34 def CreateParser(self):
35 return optparse.OptionParser('%%prog %s %s' % (self.name, self.usage))
37 def AddCommandLineOptions(self, parser):
40 def ProcessCommandLine(self, parser, options, args):
43 def Run(self, options, args):
44 raise NotImplementedError()
48 """Display help information"""
50 def Run(self, options, args):
51 print >> sys.stderr, ('usage: %s <command> [<options>]' % _GetScriptName())
52 print >> sys.stderr, 'Available commands are:'
53 for command in COMMANDS:
54 print >> sys.stderr, ' %-10s %s' % (command.name, command.description)
59 """Lists the available tests"""
61 usage = '[test_name] [<options>]'
64 super(List, self).__init__()
67 def AddCommandLineOptions(self, parser):
68 parser.add_option('-j', '--json', action='store_true')
70 def ProcessCommandLine(self, parser, options, args):
72 self._tests = _GetTests()
74 self._tests = _MatchTestName(args[0])
76 parser.error('Must provide at most one test name.')
78 def Run(self, options, args):
81 for test_name, test_class in sorted(self._tests.items()):
84 'description': test_class.__doc__,
85 'enabled': test_class.enabled,
86 'options': test_class.options,
88 print json.dumps(test_list)
90 print >> sys.stderr, 'Available tests are:'
91 _PrintTestList(self._tests)
96 """Run one or more tests"""
98 usage = 'test_name [<options>]'
101 super(Run, self).__init__()
104 def CreateParser(self):
105 options = browser_options.BrowserFinderOptions()
106 parser = options.CreateParser('%%prog %s %s' % (self.name, self.usage))
109 def AddCommandLineOptions(self, parser):
110 test.Test.AddCommandLineOptions(parser)
112 # Allow tests to add their own command line options.
114 for arg in sys.argv[1:]:
115 matching_tests.update(_MatchTestName(arg))
116 for test_class in matching_tests.itervalues():
117 test_class.AddTestCommandLineOptions(parser)
119 def ProcessCommandLine(self, parser, options, args):
121 parser.error('Must provide one test name.')
123 input_test_name = args[0]
124 matching_tests = _MatchTestName(input_test_name)
125 if not matching_tests:
126 print >> sys.stderr, 'No test named "%s".' % input_test_name
128 print >> sys.stderr, 'Available tests:'
129 _PrintTestList(_GetTests())
131 if len(matching_tests) > 1:
132 print >> sys.stderr, 'Multiple tests named "%s".' % input_test_name
134 print >> sys.stderr, 'Did you mean one of these?'
135 _PrintTestList(matching_tests)
138 self._test = matching_tests.popitem()[1]
140 def Run(self, options, args):
141 if not self._test.enabled:
142 print >> sys.stderr, 'TEST IS DISABLED. SKIPPING.'
144 return min(255, self._test().Run(copy.copy(options)))
147 COMMANDS = [cls() for _, cls in inspect.getmembers(sys.modules[__name__])
148 if inspect.isclass(cls)
149 and cls is not Command and issubclass(cls, Command)]
152 def _GetScriptName():
153 return os.path.basename(sys.argv[0])
157 # Lazy load and cache results.
158 if not hasattr(_GetTests, 'tests'):
159 base_dir = util.GetBaseDir()
160 tests = discover.DiscoverClasses(base_dir, base_dir, test.Test,
161 index_by_class_name=True)
162 tests = dict((test.GetName(), test) for test in tests.itervalues())
163 _GetTests.tests = tests
164 return _GetTests.tests
167 def _MatchTestName(input_test_name):
168 def _Matches(input_string, search_string):
169 if search_string.startswith(input_string):
171 for part in search_string.split('.'):
172 if part.startswith(input_string):
177 if input_test_name in test_aliases:
178 exact_match = test_aliases[input_test_name]
180 exact_match = input_test_name
181 if exact_match in _GetTests():
182 return {exact_match: _GetTests()[exact_match]}
185 return dict((test_name, test_class)
186 for test_name, test_class in _GetTests().iteritems()
187 if _Matches(input_test_name, test_name))
190 def _PrintTestList(tests):
191 for test_name, test_class in sorted(tests.items()):
192 if test_class.__doc__:
193 description = test_class.__doc__.splitlines()[0]
194 # Align the test names to the longest one.
195 format_string = ' %%-%ds %%s' % max(map(len, tests.iterkeys()))
196 print >> sys.stderr, format_string % (test_name, description)
198 print >> sys.stderr, ' %s' % test_name
205 # Get the command name from the command line.
206 if len(sys.argv) > 1 and sys.argv[1] == '--help':
210 for arg in sys.argv[1:]:
211 if not arg.startswith('-'):
215 # Validate and interpret the command name.
216 commands = [command for command in COMMANDS
217 if command.name.startswith(command_name)]
218 if len(commands) > 1:
219 print >> sys.stderr, ('"%s" is not a %s command. Did you mean one of these?'
220 % (command_name, _GetScriptName()))
221 for command in commands:
222 print >> sys.stderr, ' %-10s %s' % (command.name, command.description)
225 command = commands[0]
229 # Parse and run the command.
230 parser = command.CreateParser()
231 command.AddCommandLineOptions(parser)
232 options, args = parser.parse_args()
235 command.ProcessCommandLine(parser, options, args)
236 return command.Run(options, args)