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