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
3# found in the LICENSE file.
4
5"""Utilities for dealing with the python unittest module."""
6
7import fnmatch
8import sys
9import unittest
10
11
12class _TextTestResult(unittest._TextTestResult):
13  """A test result class that can print formatted text results to a stream.
14
15  Results printed in conformance with gtest output format, like:
16  [ RUN        ] autofill.AutofillTest.testAutofillInvalid: "test desc."
17  [         OK ] autofill.AutofillTest.testAutofillInvalid
18  [ RUN        ] autofill.AutofillTest.testFillProfile: "test desc."
19  [         OK ] autofill.AutofillTest.testFillProfile
20  [ RUN        ] autofill.AutofillTest.testFillProfileCrazyCharacters: "Test."
21  [         OK ] autofill.AutofillTest.testFillProfileCrazyCharacters
22  """
23  def __init__(self, stream, descriptions, verbosity):
24    unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
25    self._fails = set()
26
27  def _GetTestURI(self, test):
28    return '%s.%s.%s' % (test.__class__.__module__,
29                         test.__class__.__name__,
30                         test._testMethodName)
31
32  def getDescription(self, test):
33    return '%s: "%s"' % (self._GetTestURI(test), test.shortDescription())
34
35  def startTest(self, test):
36    unittest.TestResult.startTest(self, test)
37    self.stream.writeln('[ RUN        ] %s' % self.getDescription(test))
38
39  def addSuccess(self, test):
40    unittest.TestResult.addSuccess(self, test)
41    self.stream.writeln('[         OK ] %s' % self._GetTestURI(test))
42
43  def addError(self, test, err):
44    unittest.TestResult.addError(self, test, err)
45    self.stream.writeln('[      ERROR ] %s' % self._GetTestURI(test))
46    self._fails.add(self._GetTestURI(test))
47
48  def addFailure(self, test, err):
49    unittest.TestResult.addFailure(self, test, err)
50    self.stream.writeln('[     FAILED ] %s' % self._GetTestURI(test))
51    self._fails.add(self._GetTestURI(test))
52
53  def getRetestFilter(self):
54    return ':'.join(self._fails)
55
56
57class TextTestRunner(unittest.TextTestRunner):
58  """Test Runner for displaying test results in textual format.
59
60  Results are displayed in conformance with google test output.
61  """
62
63  def __init__(self, verbosity=1):
64    unittest.TextTestRunner.__init__(self, stream=sys.stderr,
65                                     verbosity=verbosity)
66
67  def _makeResult(self):
68    return _TextTestResult(self.stream, self.descriptions, self.verbosity)
69
70
71def GetTestsFromSuite(suite):
72  """Returns all the tests from a given test suite."""
73  tests = []
74  for x in suite:
75    if isinstance(x, unittest.TestSuite):
76      tests += GetTestsFromSuite(x)
77    else:
78      tests += [x]
79  return tests
80
81
82def GetTestNamesFromSuite(suite):
83  """Returns a list of every test name in the given suite."""
84  return map(lambda x: GetTestName(x), GetTestsFromSuite(suite))
85
86
87def GetTestName(test):
88  """Gets the test name of the given unittest test."""
89  return '.'.join([test.__class__.__module__,
90                   test.__class__.__name__,
91                   test._testMethodName])
92
93
94def FilterTestSuite(suite, gtest_filter):
95  """Returns a new filtered tests suite based on the given gtest filter.
96
97  See https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md
98  for gtest_filter specification.
99  """
100  return unittest.TestSuite(FilterTests(GetTestsFromSuite(suite), gtest_filter))
101
102
103def FilterTests(all_tests, gtest_filter):
104  """Filter a list of tests based on the given gtest filter.
105
106  Args:
107    all_tests: List of tests (unittest.TestSuite)
108    gtest_filter: Filter to apply.
109
110  Returns:
111    Filtered subset of the given list of tests.
112  """
113  test_names = [GetTestName(test) for test in all_tests]
114  filtered_names = FilterTestNames(test_names, gtest_filter)
115  return [test for test in all_tests if GetTestName(test) in filtered_names]
116
117
118def FilterTestNames(all_tests, gtest_filter):
119  """Filter a list of test names based on the given gtest filter.
120
121  See https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md
122  for gtest_filter specification.
123
124  Args:
125    all_tests: List of test names.
126    gtest_filter: Filter to apply.
127
128  Returns:
129    Filtered subset of the given list of test names.
130  """
131  pattern_groups = gtest_filter.split('-')
132  positive_patterns = ['*']
133  if pattern_groups[0]:
134    positive_patterns = pattern_groups[0].split(':')
135  negative_patterns = []
136  if len(pattern_groups) > 1:
137    negative_patterns = pattern_groups[1].split(':')
138
139  tests = []
140  test_set = set()
141  for pattern in positive_patterns:
142    pattern_tests = [
143        test for test in all_tests
144        if (fnmatch.fnmatch(test, pattern)
145            and not any(fnmatch.fnmatch(test, p) for p in negative_patterns)
146            and test not in test_set)]
147    tests.extend(pattern_tests)
148    test_set.update(pattern_tests)
149  return tests
150