| OLD | NEW |
| 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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 copy | 10 import copy |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 def name(self): | 27 def name(self): |
| 28 return self.__class__.__name__.lower() | 28 return self.__class__.__name__.lower() |
| 29 | 29 |
| 30 @property | 30 @property |
| 31 def description(self): | 31 def description(self): |
| 32 return self.__doc__ | 32 return self.__doc__ |
| 33 | 33 |
| 34 def CreateParser(self): | 34 def CreateParser(self): |
| 35 return optparse.OptionParser('%%prog %s %s' % (self.name, self.usage)) | 35 return optparse.OptionParser('%%prog %s %s' % (self.name, self.usage)) |
| 36 | 36 |
| 37 def AddParserOptions(self, parser): | 37 def AddCommandLineOptions(self, parser): |
| 38 pass | 38 pass |
| 39 | 39 |
| 40 def ValidateCommandLine(self, parser, options, args): | 40 def ProcessCommandLine(self, parser, options, args): |
| 41 pass | 41 pass |
| 42 | 42 |
| 43 def Run(self, options, args): | 43 def Run(self, options, args): |
| 44 raise NotImplementedError() | 44 raise NotImplementedError() |
| 45 | 45 |
| 46 | 46 |
| 47 class Help(Command): | 47 class Help(Command): |
| 48 """Display help information""" | 48 """Display help information""" |
| 49 | 49 |
| 50 def Run(self, options, args): | 50 def Run(self, options, args): |
| 51 print ('usage: %s <command> [<args>]' % _GetScriptName()) | 51 print >> sys.stderr, ('usage: %s <command> [<options>]' % _GetScriptName()) |
| 52 print 'Available commands are:' | 52 print >> sys.stderr, 'Available commands are:' |
| 53 for command in COMMANDS: | 53 for command in COMMANDS: |
| 54 print ' %-10s %s' % (command.name, command.description) | 54 print >> sys.stderr, ' %-10s %s' % (command.name, command.description) |
| 55 return 0 | 55 return 0 |
| 56 | 56 |
| 57 | 57 |
| 58 class List(Command): | 58 class List(Command): |
| 59 """Lists the available tests""" | 59 """Lists the available tests""" |
| 60 | 60 |
| 61 def AddParserOptions(self, parser): | 61 usage = '[test_name] [<options>]' |
| 62 |
| 63 def __init__(self): |
| 64 super(List, self).__init__() |
| 65 self._tests = None |
| 66 |
| 67 def AddCommandLineOptions(self, parser): |
| 62 parser.add_option('-j', '--json', action='store_true') | 68 parser.add_option('-j', '--json', action='store_true') |
| 63 | 69 |
| 70 def ProcessCommandLine(self, parser, options, args): |
| 71 if not args: |
| 72 self._tests = _GetTests() |
| 73 elif len(args) == 1: |
| 74 self._tests = _MatchTestName(args[0]) |
| 75 else: |
| 76 parser.error('Must provide at most one test name.') |
| 77 |
| 64 def Run(self, options, args): | 78 def Run(self, options, args): |
| 65 if options.json: | 79 if options.json: |
| 66 test_list = [] | 80 test_list = [] |
| 67 for test_name, test_class in sorted(_GetTests().items()): | 81 for test_name, test_class in sorted(self._tests.items()): |
| 68 test_list.append({ | 82 test_list.append({ |
| 69 'name': test_name, | 83 'name': test_name, |
| 70 'description': test_class.__doc__, | 84 'description': test_class.__doc__, |
| 71 'enabled': test_class.enabled, | 85 'enabled': test_class.enabled, |
| 72 'options': test_class.options, | 86 'options': test_class.options, |
| 73 }) | 87 }) |
| 74 print json.dumps(test_list) | 88 print json.dumps(test_list) |
| 75 else: | 89 else: |
| 76 print 'Available tests are:' | 90 print >> sys.stderr, 'Available tests are:' |
| 77 for test_name, test_class in sorted(_GetTests().items()): | 91 _PrintTestList(self._tests) |
| 78 if test_class.__doc__: | |
| 79 print ' %-20s %s' % (test_name, | |
| 80 test_class.__doc__.splitlines()[0]) | |
| 81 else: | |
| 82 print ' %-20s' % test_name | |
| 83 return 0 | 92 return 0 |
| 84 | 93 |
| 85 | 94 |
| 86 class Run(Command): | 95 class Run(Command): |
| 87 """Run one or more tests""" | 96 """Run one or more tests""" |
| 88 | 97 |
| 89 usage = 'test_name [...] [<args>]' | 98 usage = 'test_name [<options>]' |
| 99 |
| 100 def __init__(self): |
| 101 super(Run, self).__init__() |
| 102 self._test = None |
| 90 | 103 |
| 91 def CreateParser(self): | 104 def CreateParser(self): |
| 92 options = browser_options.BrowserFinderOptions() | 105 options = browser_options.BrowserFinderOptions() |
| 93 parser = options.CreateParser('%%prog %s %s' % (self.name, self.usage)) | 106 parser = options.CreateParser('%%prog %s %s' % (self.name, self.usage)) |
| 94 return parser | 107 return parser |
| 95 | 108 |
| 96 def AddParserOptions(self, parser): | 109 def AddCommandLineOptions(self, parser): |
| 97 test.Test.AddCommandLineOptions(parser) | 110 test.Test.AddCommandLineOptions(parser) |
| 98 | 111 |
| 99 # Allow tests to add their own command line options | 112 # Allow tests to add their own command line options. |
| 113 matching_tests = {} |
| 100 for arg in sys.argv[1:]: | 114 for arg in sys.argv[1:]: |
| 101 if arg in _GetTests(): | 115 matching_tests.update(_MatchTestName(arg)) |
| 102 _GetTests()[arg].AddTestCommandLineOptions(parser) | 116 for test_class in matching_tests.itervalues(): |
| 117 test_class.AddTestCommandLineOptions(parser) |
| 103 | 118 |
| 104 def ValidateCommandLine(self, parser, options, args): | 119 def ProcessCommandLine(self, parser, options, args): |
| 105 if not args: | 120 if len(args) != 1: |
| 106 parser.error('Must provide at least one test name') | 121 parser.error('Must provide one test name.') |
| 107 for test_name in args: | 122 |
| 108 if test_name not in _GetTests(): | 123 input_test_name = args[0] |
| 109 parser.error('No test named "%s"' % test_name) | 124 matching_tests = _MatchTestName(input_test_name) |
| 125 if not matching_tests: |
| 126 print >> sys.stderr, 'No test named "%s".' % input_test_name |
| 127 print >> sys.stderr |
| 128 print >> sys.stderr, 'Available tests:' |
| 129 _PrintTestList(_GetTests()) |
| 130 sys.exit(1) |
| 131 if len(matching_tests) > 1: |
| 132 print >> sys.stderr, 'Multiple tests named "%s".' % input_test_name |
| 133 print >> sys.stderr |
| 134 print >> sys.stderr, 'Did you mean one of these?' |
| 135 _PrintTestList(matching_tests) |
| 136 sys.exit(1) |
| 137 |
| 138 self._test = matching_tests.popitem()[1] |
| 110 | 139 |
| 111 def Run(self, options, args): | 140 def Run(self, options, args): |
| 112 total_failures = 0 | 141 return min(255, self._test().Run(copy.copy(options))) |
| 113 for test_name in args: | |
| 114 test_failures = _GetTests()[test_name]().Run(copy.copy(options)) | |
| 115 total_failures += test_failures | |
| 116 | |
| 117 return min(255, total_failures) | |
| 118 | 142 |
| 119 | 143 |
| 120 COMMANDS = [cls() for _, cls in inspect.getmembers(sys.modules[__name__]) | 144 COMMANDS = [cls() for _, cls in inspect.getmembers(sys.modules[__name__]) |
| 121 if inspect.isclass(cls) | 145 if inspect.isclass(cls) |
| 122 and cls is not Command and issubclass(cls, Command)] | 146 and cls is not Command and issubclass(cls, Command)] |
| 123 | 147 |
| 124 | 148 |
| 125 def _GetScriptName(): | 149 def _GetScriptName(): |
| 126 return os.path.basename(sys.argv[0]) | 150 return os.path.basename(sys.argv[0]) |
| 127 | 151 |
| 128 | 152 |
| 129 def _GetTests(): | 153 def _GetTests(): |
| 130 # Lazy load and cache results. | 154 # Lazy load and cache results. |
| 131 if not hasattr(_GetTests, 'tests'): | 155 if not hasattr(_GetTests, 'tests'): |
| 132 base_dir = util.GetBaseDir() | 156 base_dir = util.GetBaseDir() |
| 133 _GetTests.tests = discover.DiscoverClasses(base_dir, base_dir, test.Test, | 157 tests = discover.DiscoverClasses(base_dir, base_dir, test.Test, |
| 134 index_by_class_name=True) | 158 index_by_class_name=True) |
| 159 tests = dict((test.GetName(), test) for test in tests.itervalues()) |
| 160 _GetTests.tests = tests |
| 135 return _GetTests.tests | 161 return _GetTests.tests |
| 136 | 162 |
| 137 | 163 |
| 164 def _MatchTestName(input_test_name): |
| 165 def _Matches(input_string, search_string): |
| 166 for part in search_string.split('.'): |
| 167 if part.startswith(input_string): |
| 168 return True |
| 169 return False |
| 170 |
| 171 return dict((test_name, test_class) |
| 172 for test_name, test_class in _GetTests().iteritems() |
| 173 if _Matches(input_test_name, test_name)) |
| 174 |
| 175 |
| 176 def _PrintTestList(tests): |
| 177 for test_name, test_class in sorted(tests.items()): |
| 178 if test_class.__doc__: |
| 179 description = test_class.__doc__.splitlines()[0] |
| 180 # Align the test names to the longest one. |
| 181 format_string = ' %%-%ds %%s' % max(map(len, tests.iterkeys())) |
| 182 print >> sys.stderr, format_string % (test_name, description) |
| 183 else: |
| 184 print >> sys.stderr, ' %s' % test_name |
| 185 |
| 186 |
| 138 def Main(): | 187 def Main(): |
| 139 # Get the command name from the command line. | 188 # Get the command name from the command line. |
| 140 if len(sys.argv) > 1 and sys.argv[1] == '--help': | 189 if len(sys.argv) > 1 and sys.argv[1] == '--help': |
| 141 sys.argv[1] = 'help' | 190 sys.argv[1] = 'help' |
| 142 | 191 |
| 143 command_name = 'run' | 192 command_name = 'run' |
| 144 for arg in sys.argv[1:]: | 193 for arg in sys.argv[1:]: |
| 145 if not arg.startswith('-'): | 194 if not arg.startswith('-'): |
| 146 command_name = arg | 195 command_name = arg |
| 147 break | 196 break |
| 148 | 197 |
| 149 # Validate and interpret the command name. | 198 # Validate and interpret the command name. |
| 150 commands = [command for command in COMMANDS | 199 commands = [command for command in COMMANDS |
| 151 if command.name.startswith(command_name)] | 200 if command.name.startswith(command_name)] |
| 152 if len(commands) > 1: | 201 if len(commands) > 1: |
| 153 print >> sys.stderr, ('"%s" is not a %s command. Did you mean one of these?' | 202 print >> sys.stderr, ('"%s" is not a %s command. Did you mean one of these?' |
| 154 % (command_name, _GetScriptName())) | 203 % (command_name, _GetScriptName())) |
| 155 for command in commands: | 204 for command in commands: |
| 156 print >> sys.stderr, ' %-10s %s' % (command.name, command.description) | 205 print >> sys.stderr, ' %-10s %s' % (command.name, command.description) |
| 157 return 1 | 206 return 1 |
| 158 if commands: | 207 if commands: |
| 159 command = commands[0] | 208 command = commands[0] |
| 160 else: | 209 else: |
| 161 command = Run() | 210 command = Run() |
| 162 | 211 |
| 163 # Parse and run the command. | 212 # Parse and run the command. |
| 164 parser = command.CreateParser() | 213 parser = command.CreateParser() |
| 165 command.AddParserOptions(parser) | 214 command.AddCommandLineOptions(parser) |
| 166 options, args = parser.parse_args() | 215 options, args = parser.parse_args() |
| 167 if commands: | 216 if commands: |
| 168 args = args[1:] | 217 args = args[1:] |
| 169 command.ValidateCommandLine(parser, options, args) | 218 command.ProcessCommandLine(parser, options, args) |
| 170 return command.Run(options, args) | 219 return command.Run(options, args) |
| OLD | NEW |