~cbehrens/nova/lp844160-build-works-with-zones

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/trial/test/test_warning.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2008-2009 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
"""
 
5
Tests for Trial's interaction with the Python warning system.
 
6
"""
 
7
 
 
8
import sys, warnings
 
9
from StringIO import StringIO
 
10
 
 
11
from twisted.python.filepath import FilePath
 
12
from twisted.trial.unittest import TestCase, _collectWarnings
 
13
from twisted.trial.reporter import TestResult
 
14
 
 
15
class Mask(object):
 
16
    """
 
17
    Hide a L{TestCase} definition from trial's automatic discovery mechanism.
 
18
    """
 
19
    class MockTests(TestCase):
 
20
        """
 
21
        A test case which is used by L{FlushWarningsTests} to verify behavior
 
22
        which cannot be verified by code inside a single test method.
 
23
        """
 
24
        message = "some warning text"
 
25
        category = UserWarning
 
26
 
 
27
        def test_unflushed(self):
 
28
            """
 
29
            Generate a warning and don't flush it.
 
30
            """
 
31
            warnings.warn(self.message, self.category)
 
32
 
 
33
 
 
34
        def test_flushed(self):
 
35
            """
 
36
            Generate a warning and flush it.
 
37
            """
 
38
            warnings.warn(self.message, self.category)
 
39
            self.assertEqual(len(self.flushWarnings()), 1)
 
40
 
 
41
 
 
42
 
 
43
class FlushWarningsTests(TestCase):
 
44
    """
 
45
    Tests for L{TestCase.flushWarnings}, an API for examining the warnings
 
46
    emitted so far in a test.
 
47
    """
 
48
 
 
49
    def assertDictSubset(self, set, subset):
 
50
        """
 
51
        Assert that all the keys present in C{subset} are also present in
 
52
        C{set} and that the corresponding values are equal.
 
53
        """
 
54
        for k, v in subset.iteritems():
 
55
            self.assertEqual(set[k], v)
 
56
 
 
57
 
 
58
    def assertDictSubsets(self, sets, subsets):
 
59
        """
 
60
        For each pair of corresponding elements in C{sets} and C{subsets},
 
61
        assert that the element from C{subsets} is a subset of the element from
 
62
        C{sets}.
 
63
        """
 
64
        self.assertEqual(len(sets), len(subsets))
 
65
        for a, b in zip(sets, subsets):
 
66
            self.assertDictSubset(a, b)
 
67
 
 
68
 
 
69
    def test_none(self):
 
70
        """
 
71
        If no warnings are emitted by a test, L{TestCase.flushWarnings} returns
 
72
        an empty list.
 
73
        """
 
74
        self.assertEqual(self.flushWarnings(), [])
 
75
 
 
76
 
 
77
    def test_several(self):
 
78
        """
 
79
        If several warnings are emitted by a test, L{TestCase.flushWarnings}
 
80
        returns a list containing all of them.
 
81
        """
 
82
        firstMessage = "first warning message"
 
83
        firstCategory = UserWarning
 
84
        warnings.warn(message=firstMessage, category=firstCategory)
 
85
 
 
86
        secondMessage = "second warning message"
 
87
        secondCategory = RuntimeWarning
 
88
        warnings.warn(message=secondMessage, category=secondCategory)
 
89
 
 
90
        self.assertDictSubsets(
 
91
            self.flushWarnings(),
 
92
            [{'category': firstCategory, 'message': firstMessage},
 
93
             {'category': secondCategory, 'message': secondMessage}])
 
94
 
 
95
 
 
96
    def test_repeated(self):
 
97
        """
 
98
        The same warning triggered twice from the same place is included twice
 
99
        in the list returned by L{TestCase.flushWarnings}.
 
100
        """
 
101
        message = "the message"
 
102
        category = RuntimeWarning
 
103
        for i in range(2):
 
104
            warnings.warn(message=message, category=category)
 
105
 
 
106
        self.assertDictSubsets(
 
107
            self.flushWarnings(),
 
108
            [{'category': category, 'message': message}] * 2)
 
109
 
 
110
 
 
111
    def test_cleared(self):
 
112
        """
 
113
        After a particular warning event has been returned by
 
114
        L{TestCase.flushWarnings}, it is not returned by subsequent calls.
 
115
        """
 
116
        message = "the message"
 
117
        category = RuntimeWarning
 
118
        warnings.warn(message=message, category=category)
 
119
        self.assertDictSubsets(
 
120
            self.flushWarnings(),
 
121
            [{'category': category, 'message': message}])
 
122
        self.assertEqual(self.flushWarnings(), [])
 
123
 
 
124
 
 
125
    def test_unflushed(self):
 
126
        """
 
127
        Any warnings emitted by a test which are not flushed are emitted to the
 
128
        Python warning system.
 
129
        """
 
130
        result = TestResult()
 
131
        case = Mask.MockTests('test_unflushed')
 
132
        case.run(result)
 
133
        warningsShown = self.flushWarnings([Mask.MockTests.test_unflushed])
 
134
        self.assertEqual(warningsShown[0]['message'], 'some warning text')
 
135
        self.assertIdentical(warningsShown[0]['category'], UserWarning)
 
136
 
 
137
        where = case.test_unflushed.im_func.func_code
 
138
        filename = where.co_filename
 
139
        # If someone edits MockTests.test_unflushed, the value added to
 
140
        # firstlineno might need to change.
 
141
        lineno = where.co_firstlineno + 4
 
142
 
 
143
        self.assertEqual(warningsShown[0]['filename'], filename)
 
144
        self.assertEqual(warningsShown[0]['lineno'], lineno)
 
145
 
 
146
        self.assertEqual(len(warningsShown), 1)
 
147
 
 
148
 
 
149
    def test_flushed(self):
 
150
        """
 
151
        Any warnings emitted by a test which are flushed are not emitted to the
 
152
        Python warning system.
 
153
        """
 
154
        result = TestResult()
 
155
        case = Mask.MockTests('test_flushed')
 
156
        output = StringIO()
 
157
        monkey = self.patch(sys, 'stdout', output)
 
158
        case.run(result)
 
159
        monkey.restore()
 
160
        self.assertEqual(output.getvalue(), "")
 
161
 
 
162
 
 
163
    def test_warningsConfiguredAsErrors(self):
 
164
        """
 
165
        If a warnings filter has been installed which turns warnings into
 
166
        exceptions, tests have an error added to the reporter for them for each
 
167
        unflushed warning.
 
168
        """
 
169
        class CustomWarning(Warning):
 
170
            pass
 
171
 
 
172
        result = TestResult()
 
173
        case = Mask.MockTests('test_unflushed')
 
174
        case.category = CustomWarning
 
175
 
 
176
        originalWarnings = warnings.filters[:]
 
177
        try:
 
178
            warnings.simplefilter('error')
 
179
            case.run(result)
 
180
            self.assertEqual(len(result.errors), 1)
 
181
            self.assertIdentical(result.errors[0][0], case)
 
182
            result.errors[0][1].trap(CustomWarning)
 
183
        finally:
 
184
            warnings.filters[:] = originalWarnings
 
185
 
 
186
 
 
187
    def test_flushedWarningsConfiguredAsErrors(self):
 
188
        """
 
189
        If a warnings filter has been installed which turns warnings into
 
190
        exceptions, tests which emit those warnings but flush them do not have
 
191
        an error added to the reporter.
 
192
        """
 
193
        class CustomWarning(Warning):
 
194
            pass
 
195
 
 
196
        result = TestResult()
 
197
        case = Mask.MockTests('test_flushed')
 
198
        case.category = CustomWarning
 
199
 
 
200
        originalWarnings = warnings.filters[:]
 
201
        try:
 
202
            warnings.simplefilter('error')
 
203
            case.run(result)
 
204
            self.assertEqual(result.errors, [])
 
205
        finally:
 
206
            warnings.filters[:] = originalWarnings
 
207
 
 
208
 
 
209
    def test_multipleFlushes(self):
 
210
        """
 
211
        Any warnings emitted after a call to L{TestCase.flushWarnings} can be
 
212
        flushed by another call to L{TestCase.flushWarnings}.
 
213
        """
 
214
        warnings.warn("first message")
 
215
        self.assertEqual(len(self.flushWarnings()), 1)
 
216
        warnings.warn("second message")
 
217
        self.assertEqual(len(self.flushWarnings()), 1)
 
218
 
 
219
 
 
220
    def test_filterOnOffendingFunction(self):
 
221
        """
 
222
        The list returned by L{TestCase.flushWarnings} includes only those
 
223
        warnings which refer to the source of the function passed as the value
 
224
        for C{offendingFunction}, if a value is passed for that parameter.
 
225
        """
 
226
        firstMessage = "first warning text"
 
227
        firstCategory = UserWarning
 
228
        def one():
 
229
            warnings.warn(firstMessage, firstCategory, stacklevel=1)
 
230
 
 
231
        secondMessage = "some text"
 
232
        secondCategory = RuntimeWarning
 
233
        def two():
 
234
            warnings.warn(secondMessage, secondCategory, stacklevel=1)
 
235
 
 
236
        one()
 
237
        two()
 
238
 
 
239
        self.assertDictSubsets(
 
240
            self.flushWarnings(offendingFunctions=[one]),
 
241
            [{'category': firstCategory, 'message': firstMessage}])
 
242
        self.assertDictSubsets(
 
243
            self.flushWarnings(offendingFunctions=[two]),
 
244
            [{'category': secondCategory, 'message': secondMessage}])
 
245
 
 
246
 
 
247
    def test_functionBoundaries(self):
 
248
        """
 
249
        Verify that warnings emitted at the very edges of a function are still
 
250
        determined to be emitted from that function.
 
251
        """
 
252
        def warner():
 
253
            warnings.warn("first line warning")
 
254
            warnings.warn("internal line warning")
 
255
            warnings.warn("last line warning")
 
256
 
 
257
        warner()
 
258
        self.assertEqual(
 
259
            len(self.flushWarnings(offendingFunctions=[warner])), 3)
 
260
 
 
261
 
 
262
    def test_invalidFilter(self):
 
263
        """
 
264
        If an object which is neither a function nor a method is included in
 
265
        the C{offendingFunctions} list, L{TestCase.flushWarnings} raises
 
266
        L{ValueError}.  Such a call flushes no warnings.
 
267
        """
 
268
        warnings.warn("oh no")
 
269
        self.assertRaises(ValueError, self.flushWarnings, [None])
 
270
        self.assertEqual(len(self.flushWarnings()), 1)
 
271
 
 
272
 
 
273
    def test_missingSource(self):
 
274
        """
 
275
        Warnings emitted by a function the source code of which is not
 
276
        available can still be flushed.
 
277
        """
 
278
        package = FilePath(self.mktemp()).child('twisted_private_helper')
 
279
        package.makedirs()
 
280
        package.child('__init__.py').setContent('')
 
281
        package.child('missingsourcefile.py').setContent('''
 
282
import warnings
 
283
def foo():
 
284
    warnings.warn("oh no")
 
285
''')
 
286
        sys.path.insert(0, package.parent().path)
 
287
        self.addCleanup(sys.path.remove, package.parent().path)
 
288
        from twisted_private_helper import missingsourcefile
 
289
        self.addCleanup(sys.modules.pop, 'twisted_private_helper')
 
290
        self.addCleanup(sys.modules.pop, missingsourcefile.__name__)
 
291
        package.child('missingsourcefile.py').remove()
 
292
 
 
293
        missingsourcefile.foo()
 
294
        self.assertEqual(len(self.flushWarnings([missingsourcefile.foo])), 1)
 
295
 
 
296
 
 
297
    def test_renamedSource(self):
 
298
        """
 
299
        Warnings emitted by a function defined in a file which has been renamed
 
300
        since it was initially compiled can still be flushed.
 
301
 
 
302
        This is testing the code which specifically supports working around the
 
303
        unfortunate behavior of CPython to write a .py source file name into
 
304
        the .pyc files it generates and then trust that it is correct in
 
305
        various places.  If source files are renamed, .pyc files may not be
 
306
        regenerated, but they will contain incorrect filenames.
 
307
        """
 
308
        package = FilePath(self.mktemp()).child('twisted_private_helper')
 
309
        package.makedirs()
 
310
        package.child('__init__.py').setContent('')
 
311
        package.child('module.py').setContent('''
 
312
import warnings
 
313
def foo():
 
314
    warnings.warn("oh no")
 
315
''')
 
316
        sys.path.insert(0, package.parent().path)
 
317
        self.addCleanup(sys.path.remove, package.parent().path)
 
318
 
 
319
        # Import it to cause pycs to be generated
 
320
        from twisted_private_helper import module
 
321
 
 
322
        # Clean up the state resulting from that import; we're not going to use
 
323
        # this module, so it should go away.
 
324
        del sys.modules['twisted_private_helper']
 
325
        del sys.modules[module.__name__]
 
326
 
 
327
        # Rename the source directory
 
328
        package.moveTo(package.sibling('twisted_renamed_helper'))
 
329
 
 
330
        # Import the newly renamed version
 
331
        from twisted_renamed_helper import module
 
332
        self.addCleanup(sys.modules.pop, 'twisted_renamed_helper')
 
333
        self.addCleanup(sys.modules.pop, module.__name__)
 
334
 
 
335
        # Generate the warning
 
336
        module.foo()
 
337
 
 
338
        # Flush it
 
339
        self.assertEqual(len(self.flushWarnings([module.foo])), 1)
 
340
 
 
341
 
 
342
 
 
343
class FakeWarning(Warning):
 
344
    pass
 
345
 
 
346
 
 
347
 
 
348
class CollectWarningsTests(TestCase):
 
349
    """
 
350
    Tests for L{_collectWarnings}.
 
351
    """
 
352
    def test_callsObserver(self):
 
353
        """
 
354
        L{_collectWarnings} calls the observer with each emitted warning.
 
355
        """
 
356
        firstMessage = "dummy calls observer warning"
 
357
        secondMessage = firstMessage[::-1]
 
358
        events = []
 
359
        def f():
 
360
            events.append('call')
 
361
            warnings.warn(firstMessage)
 
362
            warnings.warn(secondMessage)
 
363
            events.append('returning')
 
364
 
 
365
        _collectWarnings(events.append, f)
 
366
 
 
367
        self.assertEqual(events[0], 'call')
 
368
        self.assertEqual(events[1].message, firstMessage)
 
369
        self.assertEqual(events[2].message, secondMessage)
 
370
        self.assertEqual(events[3], 'returning')
 
371
        self.assertEqual(len(events), 4)
 
372
 
 
373
 
 
374
    def test_suppresses(self):
 
375
        """
 
376
        Any warnings emitted by a call to a function passed to
 
377
        L{_collectWarnings} are not actually emitted to the warning system.
 
378
        """
 
379
        output = StringIO()
 
380
        self.patch(sys, 'stdout', output)
 
381
        _collectWarnings(lambda x: None, warnings.warn, "text")
 
382
        self.assertEqual(output.getvalue(), "")
 
383
 
 
384
 
 
385
    def test_callsFunction(self):
 
386
        """
 
387
        L{_collectWarnings} returns the result of calling the callable passed to
 
388
        it with the parameters given.
 
389
        """
 
390
        arguments = []
 
391
        value = object()
 
392
 
 
393
        def f(*args, **kwargs):
 
394
            arguments.append((args, kwargs))
 
395
            return value
 
396
 
 
397
        result = _collectWarnings(lambda x: None, f, 1, 'a', b=2, c='d')
 
398
        self.assertEqual(arguments, [((1, 'a'), {'b': 2, 'c': 'd'})])
 
399
        self.assertIdentical(result, value)
 
400
 
 
401
 
 
402
    def test_duplicateWarningCollected(self):
 
403
        """
 
404
        Subsequent emissions of a warning from a particular source site can be
 
405
        collected by L{_collectWarnings}.  In particular, the per-module
 
406
        emitted-warning cache should be bypassed (I{__warningregistry__}).
 
407
        """
 
408
        # Make sure the worst case is tested: if __warningregistry__ isn't in a
 
409
        # module's globals, then the warning system will add it and start using
 
410
        # it to avoid emitting duplicate warnings.  Delete __warningregistry__
 
411
        # to ensure that even modules which are first imported as a test is
 
412
        # running still interact properly with the warning system.
 
413
        global __warningregistry__
 
414
        del __warningregistry__
 
415
 
 
416
        def f():
 
417
            warnings.warn("foo")
 
418
        warnings.simplefilter('default')
 
419
        f()
 
420
        events = []
 
421
        _collectWarnings(events.append, f)
 
422
        self.assertEqual(len(events), 1)
 
423
        self.assertEqual(events[0].message, "foo")
 
424
        self.assertEqual(len(self.flushWarnings()), 1)
 
425
 
 
426
 
 
427
    def test_immutableObject(self):
 
428
        """
 
429
        L{_collectWarnings}'s behavior is not altered by the presence of an
 
430
        object which cannot have attributes set on it as a value in
 
431
        C{sys.modules}.
 
432
        """
 
433
        key = object()
 
434
        sys.modules[key] = key
 
435
        self.addCleanup(sys.modules.pop, key)
 
436
        self.test_duplicateWarningCollected()