blob: 9683ab717a8f96d83d69e0b87342da587799e389 [file] [log] [blame]
# 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