1# Copyright 2013 The Chromium Authors 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/main/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/main/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