~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/TestUtil.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#       Author: Robert Collins <robert.collins@canonical.com>
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
13
13
#
14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
#
18
18
 
19
19
import sys
20
20
import logging
21
21
import unittest
 
22
import weakref
 
23
 
 
24
from bzrlib import pyutils
22
25
 
23
26
# Mark this python module as being part of the implementation
24
27
# of unittest: this gives us better tracebacks where the last
27
30
 
28
31
 
29
32
class LogCollector(logging.Handler):
 
33
 
30
34
    def __init__(self):
31
35
        logging.Handler.__init__(self)
32
36
        self.records=[]
 
37
 
33
38
    def emit(self, record):
34
39
        self.records.append(record.getMessage())
35
40
 
47
52
def visitTests(suite, visitor):
48
53
    """A foreign method for visiting the tests in a test suite."""
49
54
    for test in suite._tests:
50
 
        #Abusing types to avoid monkey patching unittest.TestCase. 
 
55
        #Abusing types to avoid monkey patching unittest.TestCase.
51
56
        # Maybe that would be better?
52
57
        try:
53
58
            test.visit(visitor)
58
63
                visitor.visitSuite(test)
59
64
                visitTests(test, visitor)
60
65
            else:
61
 
                print "unvisitable non-unittest.TestCase element %r (%r)" % (test, test.__class__)
62
 
    
 
66
                print "unvisitable non-unittest.TestCase element %r (%r)" % (
 
67
                    test, test.__class__)
 
68
 
 
69
 
 
70
class FailedCollectionCase(unittest.TestCase):
 
71
    """Pseudo-test to run and report failure if given case was uncollected"""
 
72
 
 
73
    def __init__(self, case):
 
74
        super(FailedCollectionCase, self).__init__("fail_uncollected")
 
75
        # GZ 2011-09-16: Maybe catch errors from id() method as cases may be
 
76
        #                in a bit of a funny state by now.
 
77
        self._problem_case_id = case.id()
 
78
 
 
79
    def id(self):
 
80
        if self._problem_case_id[-1:] == ")":
 
81
            return self._problem_case_id[:-1] + ",uncollected)"
 
82
        return self._problem_case_id + "(uncollected)"
 
83
 
 
84
    def fail_uncollected(self):
 
85
        self.fail("Uncollected test case: " + self._problem_case_id)
 
86
 
63
87
 
64
88
class TestSuite(unittest.TestSuite):
65
89
    """I am an extended TestSuite with a visitor interface.
72
96
        visitor.visitSuite(self)
73
97
        visitTests(self, visitor)
74
98
 
 
99
    def run(self, result):
 
100
        """Run the tests in the suite, discarding references after running."""
 
101
        tests = list(self)
 
102
        tests.reverse()
 
103
        self._tests = []
 
104
        stored_count = 0
 
105
        count_stored_tests = getattr(result, "_count_stored_tests", int)
 
106
        from bzrlib.tests import selftest_debug_flags
 
107
        notify = "uncollected_cases" in selftest_debug_flags
 
108
        while tests:
 
109
            if result.shouldStop:
 
110
                self._tests = reversed(tests)
 
111
                break
 
112
            case = _run_and_collect_case(tests.pop(), result)()
 
113
            new_stored_count = count_stored_tests()
 
114
            if case is not None and isinstance(case, unittest.TestCase):
 
115
                if stored_count == new_stored_count and notify:
 
116
                    # Testcase didn't fail, but somehow is still alive
 
117
                    FailedCollectionCase(case).run(result)
 
118
                    # Adding a new failure so need to reupdate the count
 
119
                    new_stored_count = count_stored_tests()
 
120
                # GZ 2011-09-16: Previously zombied the case at this point by
 
121
                #                clearing the dict as fallback, skip for now.
 
122
            stored_count = new_stored_count
 
123
        return result
 
124
 
 
125
 
 
126
def _run_and_collect_case(case, res):
 
127
    """Run test case against result and use weakref to drop the refcount"""
 
128
    case.run(res)
 
129
    return weakref.ref(case)
 
130
 
75
131
 
76
132
class TestLoader(unittest.TestLoader):
77
133
    """Custom TestLoader to extend the stock python one."""
83
139
    def loadTestsFromModuleNames(self, names):
84
140
        """use a custom means to load tests from modules.
85
141
 
86
 
        There is an undesirable glitch in the python TestLoader where a 
87
 
        import error is ignore. We think this can be solved by ensuring the 
 
142
        There is an undesirable glitch in the python TestLoader where a
 
143
        import error is ignore. We think this can be solved by ensuring the
88
144
        requested name is resolvable, if its not raising the original error.
89
145
        """
90
146
        result = self.suiteClass()
94
150
 
95
151
    def loadTestsFromModuleName(self, name):
96
152
        result = self.suiteClass()
97
 
        module = _load_module_by_name(name)
 
153
        module = pyutils.get_named_object(name)
98
154
 
99
155
        result.addTests(self.loadTestsFromModule(module))
100
156
        return result
107
163
        regular python loadTestsFromModule.
108
164
 
109
165
        If a load_tests attribute is found, it is called and the result is
110
 
        returned. 
 
166
        returned.
111
167
 
112
168
        load_tests should be defined like so:
113
169
        >>> def load_tests(standard_tests, module, loader):
123
179
        >>>         result.addTests([test, test])
124
180
        >>>     return result
125
181
        """
126
 
        basic_tests = super(TestLoader, self).loadTestsFromModule(module)
 
182
        if sys.version_info < (2, 7):
 
183
            basic_tests = super(TestLoader, self).loadTestsFromModule(module)
 
184
        else:
 
185
            # GZ 2010-07-19: Python 2.7 unittest also uses load_tests but with
 
186
            #                a different and incompatible signature
 
187
            basic_tests = super(TestLoader, self).loadTestsFromModule(module,
 
188
                use_load_tests=False)
127
189
        load_tests = getattr(module, "load_tests", None)
128
190
        if load_tests is not None:
129
191
            return load_tests(basic_tests, module, self)
161
223
            return self.suiteClass()
162
224
 
163
225
 
164
 
def _load_module_by_name(mod_name):
165
 
    parts = mod_name.split('.')
166
 
    module = __import__(mod_name)
167
 
    del parts[0]
168
 
    # for historical reasons python returns the top-level module even though
169
 
    # it loads the submodule; we need to walk down to get the one we want.
170
 
    while parts:
171
 
        module = getattr(module, parts.pop(0))
172
 
    return module
173
 
 
174
 
 
175
226
class TestVisitor(object):
176
227
    """A visitor for Tests"""
 
228
 
177
229
    def visitSuite(self, aTestSuite):
178
230
        pass
 
231
 
179
232
    def visitCase(self, aTestCase):
180
233
        pass