|  | # Copyright 2013 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Utilities for dealing with the python unittest module.""" | 
|  |  | 
|  | import fnmatch | 
|  | import re | 
|  | import sys | 
|  | import unittest | 
|  |  | 
|  |  | 
|  | class _TextTestResult(unittest._TextTestResult): | 
|  | """A test result class that can print formatted text results to a stream. | 
|  |  | 
|  | Results printed in conformance with gtest output format, like: | 
|  | [ RUN        ] autofill.AutofillTest.testAutofillInvalid: "test desc." | 
|  | [         OK ] autofill.AutofillTest.testAutofillInvalid | 
|  | [ RUN        ] autofill.AutofillTest.testFillProfile: "test desc." | 
|  | [         OK ] autofill.AutofillTest.testFillProfile | 
|  | [ RUN        ] autofill.AutofillTest.testFillProfileCrazyCharacters: "Test." | 
|  | [         OK ] autofill.AutofillTest.testFillProfileCrazyCharacters | 
|  | """ | 
|  | def __init__(self, stream, descriptions, verbosity): | 
|  | unittest._TextTestResult.__init__(self, stream, descriptions, verbosity) | 
|  | self._fails = set() | 
|  |  | 
|  | def _GetTestURI(self, test): | 
|  | return '%s.%s.%s' % (test.__class__.__module__, | 
|  | test.__class__.__name__, | 
|  | test._testMethodName) | 
|  |  | 
|  | def getDescription(self, test): | 
|  | return '%s: "%s"' % (self._GetTestURI(test), test.shortDescription()) | 
|  |  | 
|  | def startTest(self, test): | 
|  | unittest.TestResult.startTest(self, test) | 
|  | self.stream.writeln('[ RUN        ] %s' % self.getDescription(test)) | 
|  |  | 
|  | def addSuccess(self, test): | 
|  | unittest.TestResult.addSuccess(self, test) | 
|  | self.stream.writeln('[         OK ] %s' % self._GetTestURI(test)) | 
|  |  | 
|  | def addError(self, test, err): | 
|  | unittest.TestResult.addError(self, test, err) | 
|  | self.stream.writeln('[      ERROR ] %s' % self._GetTestURI(test)) | 
|  | self._fails.add(self._GetTestURI(test)) | 
|  |  | 
|  | def addFailure(self, test, err): | 
|  | unittest.TestResult.addFailure(self, test, err) | 
|  | self.stream.writeln('[     FAILED ] %s' % self._GetTestURI(test)) | 
|  | self._fails.add(self._GetTestURI(test)) | 
|  |  | 
|  | def getRetestFilter(self): | 
|  | return ':'.join(self._fails) | 
|  |  | 
|  |  | 
|  | class TextTestRunner(unittest.TextTestRunner): | 
|  | """Test Runner for displaying test results in textual format. | 
|  |  | 
|  | Results are displayed in conformance with google test output. | 
|  | """ | 
|  |  | 
|  | def __init__(self, verbosity=1): | 
|  | unittest.TextTestRunner.__init__(self, stream=sys.stderr, | 
|  | verbosity=verbosity) | 
|  |  | 
|  | def _makeResult(self): | 
|  | return _TextTestResult(self.stream, self.descriptions, self.verbosity) | 
|  |  | 
|  |  | 
|  | def GetTestsFromSuite(suite): | 
|  | """Returns all the tests from a given test suite.""" | 
|  | tests = [] | 
|  | for x in suite: | 
|  | if isinstance(x, unittest.TestSuite): | 
|  | tests += GetTestsFromSuite(x) | 
|  | else: | 
|  | tests += [x] | 
|  | return tests | 
|  |  | 
|  |  | 
|  | def GetTestNamesFromSuite(suite): | 
|  | """Returns a list of every test name in the given suite.""" | 
|  | return map(lambda x: GetTestName(x), GetTestsFromSuite(suite)) | 
|  |  | 
|  |  | 
|  | def GetTestName(test): | 
|  | """Gets the test name of the given unittest test.""" | 
|  | return '.'.join([test.__class__.__module__, | 
|  | test.__class__.__name__, | 
|  | test._testMethodName]) | 
|  |  | 
|  |  | 
|  | def FilterTestSuite(suite, gtest_filter): | 
|  | """Returns a new filtered tests suite based on the given gtest filter. | 
|  |  | 
|  | See https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md | 
|  | for gtest_filter specification. | 
|  | """ | 
|  | return unittest.TestSuite(FilterTests(GetTestsFromSuite(suite), gtest_filter)) | 
|  |  | 
|  |  | 
|  | def FilterTests(all_tests, gtest_filter): | 
|  | """Filter a list of tests based on the given gtest filter. | 
|  |  | 
|  | Args: | 
|  | all_tests: List of tests (unittest.TestSuite) | 
|  | gtest_filter: Filter to apply. | 
|  |  | 
|  | Returns: | 
|  | Filtered subset of the given list of tests. | 
|  | """ | 
|  | test_names = [GetTestName(test) for test in all_tests] | 
|  | filtered_names = FilterTestNames(test_names, gtest_filter) | 
|  | return [test for test in all_tests if GetTestName(test) in filtered_names] | 
|  |  | 
|  |  | 
|  | def FilterTestNames(all_tests, gtest_filter): | 
|  | """Filter a list of test names based on the given gtest filter. | 
|  |  | 
|  | See https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md | 
|  | for gtest_filter specification. | 
|  |  | 
|  | Args: | 
|  | all_tests: List of test names. | 
|  | gtest_filter: Filter to apply. | 
|  |  | 
|  | Returns: | 
|  | Filtered subset of the given list of test names. | 
|  | """ | 
|  | pattern_groups = gtest_filter.split('-') | 
|  | positive_patterns = ['*'] | 
|  | if pattern_groups[0]: | 
|  | positive_patterns = pattern_groups[0].split(':') | 
|  | negative_patterns = [] | 
|  | if len(pattern_groups) > 1: | 
|  | negative_patterns = pattern_groups[1].split(':') | 
|  |  | 
|  | neg_pats = None | 
|  | if negative_patterns: | 
|  | neg_pats = re.compile('|'.join(fnmatch.translate(p) for p in | 
|  | negative_patterns)) | 
|  |  | 
|  | tests = [] | 
|  | test_set = set() | 
|  | for pattern in positive_patterns: | 
|  | pattern_tests = [ | 
|  | test for test in all_tests | 
|  | if (fnmatch.fnmatch(test, pattern) | 
|  | and not (neg_pats and neg_pats.match(test)) | 
|  | and test not in test_set)] | 
|  | tests.extend(pattern_tests) | 
|  | test_set.update(pattern_tests) | 
|  | return tests |