Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 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 | |
| 7 | import fnmatch |
| 8 | import sys |
| 9 | import unittest |
| 10 | |
| 11 | |
| 12 | class _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 | |
| 57 | class 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 | |
| 71 | def 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 | |
| 82 | def 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 | |
| 87 | def 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 | |
| 94 | def 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 | |
| 103 | def 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 | |
| 118 | def 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 |