146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)# Copyright 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import unittest
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from telemetry import decorators
9116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom telemetry.core import browser_finder
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import browser_options
11116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom telemetry.core import command_line
1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)from telemetry.core import discover
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.unittest import json_results
146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)from telemetry.unittest import progress_reporter
15116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
17116680a4aac90f2aa7413d9095a592090648e557Ben Murdochclass Config(object):
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  def __init__(self, top_level_dir, test_dirs, progress_reporters):
19116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._top_level_dir = top_level_dir
20116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._test_dirs = tuple(test_dirs)
216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._progress_reporters = tuple(progress_reporters)
22116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
23116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def top_level_dir(self):
25116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return self._top_level_dir
26116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
28116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def test_dirs(self):
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return self._test_dirs
30116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  def progress_reporters(self):
336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return self._progress_reporters
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def Discover(start_dir, top_level_dir=None, pattern='test*.py'):
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loader = unittest.defaultTestLoader
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  loader.suiteClass = progress_reporter.TestSuite
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
40116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  test_suites = []
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  modules = discover.DiscoverModules(start_dir, top_level_dir, pattern)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for module in modules:
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if hasattr(module, 'suite'):
44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      suite = module.suite()
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
46116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      suite = loader.loadTestsFromModule(module)
47116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if suite.countTestCases():
48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      test_suites.append(suite)
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return test_suites
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def FilterSuite(suite, predicate):
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  new_suite = suite.__class__()
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for test in suite:
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if isinstance(test, unittest.TestSuite):
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      subsuite = FilterSuite(test, predicate)
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if subsuite.countTestCases():
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        new_suite.addTest(subsuite)
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    else:
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      assert isinstance(test, unittest.TestCase)
61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if predicate(test):
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        new_suite.addTest(test)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new_suite
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
67116680a4aac90f2aa7413d9095a592090648e557Ben Murdochdef DiscoverTests(search_dirs, top_level_dir, possible_browser,
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                  selected_tests=None, selected_tests_are_exact=False,
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                  run_disabled_tests=False):
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def IsTestSelected(test):
71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if selected_tests:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      found = False
73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for name in selected_tests:
746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        if selected_tests_are_exact:
756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          if name == test.id():
766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            found = True
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        else:
786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          if name in test.id():
796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            found = True
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not found:
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return False
82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if run_disabled_tests:
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return True
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # pylint: disable=W0212
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not hasattr(test, '_testMethodName'):
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return True
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    method = getattr(test, test._testMethodName)
88116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return decorators.IsEnabled(method, possible_browser)
89116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  wrapper_suite = progress_reporter.TestSuite()
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for search_dir in search_dirs:
92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    wrapper_suite.addTests(Discover(search_dir, top_level_dir, '*_unittest.py'))
93116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return FilterSuite(wrapper_suite, IsTestSelected)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)def RestoreLoggingLevel(func):
97d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def _LoggingRestoreWrapper(*args, **kwargs):
98d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # Cache the current logging level, this needs to be done before calling
99d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # parser.parse_args, which changes logging level based on verbosity
100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # setting.
101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    logging_level = logging.getLogger().getEffectiveLevel()
102d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    try:
103d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return func(*args, **kwargs)
104d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    finally:
105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      # Restore logging level, which may be changed in parser.parse_args.
106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      logging.getLogger().setLevel(logging_level)
107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return _LoggingRestoreWrapper
109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
111116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconfig = None
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
114116680a4aac90f2aa7413d9095a592090648e557Ben Murdochclass RunTestsCommand(command_line.OptparseCommand):
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  """Run unit tests"""
116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  usage = '[test_name ...] [<options>]'
118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
119116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @classmethod
120116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def CreateParser(cls):
121116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    options = browser_options.BrowserFinderOptions()
122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    options.browser_type = 'any'
123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    parser = options.CreateParser('%%prog %s' % cls.usage)
124116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return parser
125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
126116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @classmethod
127116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def AddCommandLineArgs(cls, parser):
128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    parser.add_option('--repeat-count', type='int', default=1,
129116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      help='Repeats each a provided number of times.')
130116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    parser.add_option('-d', '--also-run-disabled-tests',
131116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      dest='run_disabled_tests',
132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      action='store_true', default=False,
133116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      help='Ignore @Disabled and @Enabled restrictions.')
1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    parser.add_option('--retry-limit', type='int',
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                      help='Retry each failure up to N times'
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                           ' to de-flake things.')
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    parser.add_option('--exact-test-filter', action='store_true', default=False,
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                      help='Treat test filter as exact matches (default is '
1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                           'substring matches).')
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    json_results.AddOptions(parser)
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @classmethod
143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def ProcessCommandLineArgs(cls, parser, args):
144116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if args.verbosity == 0:
145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      logging.getLogger().setLevel(logging.WARN)
146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    # We retry failures by default unless we're running a list of tests
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    # explicitly.
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if args.retry_limit is None and not args.positional_args:
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      args.retry_limit = 3
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    try:
153116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      possible_browser = browser_finder.FindBrowser(args)
154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    except browser_finder.BrowserFinderException, ex:
155116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      parser.error(ex)
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if not possible_browser:
158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      parser.error('No browser found of type %s. Cannot run tests.\n'
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                   'Re-run with --browser=list to see '
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                   'available browser types.' % args.browser_type)
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    json_results.ValidateArgs(parser, args)
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def Run(self, args):
165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    possible_browser = browser_finder.FindBrowser(args)
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    test_suite, result = self.RunOneSuite(possible_browser, args)
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    results = [result]
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    failed_tests = json_results.FailedTestNames(test_suite, result)
1726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    retry_limit = args.retry_limit
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    while retry_limit and failed_tests:
1756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      args.positional_args = failed_tests
1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      args.exact_test_filter = True
1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      _, result = self.RunOneSuite(possible_browser, args)
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      results.append(result)
1806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      failed_tests = json_results.FailedTestNames(test_suite, result)
1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      retry_limit -= 1
1836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    full_results = json_results.FullResults(args, test_suite, results)
1856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    json_results.WriteFullResultsIfNecessary(args, full_results)
1866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    err_occurred, err_str = json_results.UploadFullResultsIfNecessary(
1886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        args, full_results)
1896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if err_occurred:
1906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      for line in err_str.splitlines():
1916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        logging.error(line)
1926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return 1
1936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return json_results.ExitCodeFromFullResults(full_results)
1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  def RunOneSuite(self, possible_browser, args):
1976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    test_suite = DiscoverTests(config.test_dirs, config.top_level_dir,
1986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                               possible_browser, args.positional_args,
1996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                               args.exact_test_filter, args.run_disabled_tests)
2006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    runner = progress_reporter.TestRunner()
2016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    result = runner.run(test_suite, config.progress_reporters,
2026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                        args.repeat_count, args)
2036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return test_suite, result
204116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
205116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @classmethod
206116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @RestoreLoggingLevel
207116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def main(cls, args=None):
208116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return super(RunTestsCommand, cls).main(args)
209