logxpy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logxpy/__init__.py +126 -0
- logxpy/_action.py +958 -0
- logxpy/_async.py +186 -0
- logxpy/_base.py +80 -0
- logxpy/_compat.py +71 -0
- logxpy/_config.py +45 -0
- logxpy/_dest.py +88 -0
- logxpy/_errors.py +58 -0
- logxpy/_fmt.py +68 -0
- logxpy/_generators.py +136 -0
- logxpy/_mask.py +23 -0
- logxpy/_message.py +195 -0
- logxpy/_output.py +517 -0
- logxpy/_pool.py +93 -0
- logxpy/_traceback.py +126 -0
- logxpy/_types.py +71 -0
- logxpy/_util.py +56 -0
- logxpy/_validation.py +486 -0
- logxpy/_version.py +21 -0
- logxpy/cli.py +61 -0
- logxpy/dask.py +172 -0
- logxpy/decorators.py +268 -0
- logxpy/filter.py +124 -0
- logxpy/journald.py +88 -0
- logxpy/json.py +149 -0
- logxpy/loggerx.py +253 -0
- logxpy/logwriter.py +84 -0
- logxpy/parse.py +191 -0
- logxpy/prettyprint.py +173 -0
- logxpy/serializers.py +36 -0
- logxpy/stdlib.py +23 -0
- logxpy/tai64n.py +45 -0
- logxpy/testing.py +472 -0
- logxpy/tests/__init__.py +9 -0
- logxpy/tests/common.py +36 -0
- logxpy/tests/strategies.py +231 -0
- logxpy/tests/test_action.py +1751 -0
- logxpy/tests/test_api.py +86 -0
- logxpy/tests/test_async.py +67 -0
- logxpy/tests/test_compat.py +13 -0
- logxpy/tests/test_config.py +21 -0
- logxpy/tests/test_coroutines.py +105 -0
- logxpy/tests/test_dask.py +211 -0
- logxpy/tests/test_decorators.py +54 -0
- logxpy/tests/test_filter.py +122 -0
- logxpy/tests/test_fmt.py +42 -0
- logxpy/tests/test_generators.py +292 -0
- logxpy/tests/test_journald.py +246 -0
- logxpy/tests/test_json.py +208 -0
- logxpy/tests/test_loggerx.py +44 -0
- logxpy/tests/test_logwriter.py +262 -0
- logxpy/tests/test_message.py +334 -0
- logxpy/tests/test_output.py +921 -0
- logxpy/tests/test_parse.py +309 -0
- logxpy/tests/test_pool.py +55 -0
- logxpy/tests/test_prettyprint.py +303 -0
- logxpy/tests/test_pyinstaller.py +35 -0
- logxpy/tests/test_serializers.py +36 -0
- logxpy/tests/test_stdlib.py +73 -0
- logxpy/tests/test_tai64n.py +66 -0
- logxpy/tests/test_testing.py +1051 -0
- logxpy/tests/test_traceback.py +251 -0
- logxpy/tests/test_twisted.py +814 -0
- logxpy/tests/test_util.py +45 -0
- logxpy/tests/test_validation.py +989 -0
- logxpy/twisted.py +265 -0
- logxpy-0.1.0.dist-info/METADATA +100 -0
- logxpy-0.1.0.dist-info/RECORD +72 -0
- logxpy-0.1.0.dist-info/WHEEL +5 -0
- logxpy-0.1.0.dist-info/entry_points.txt +2 -0
- logxpy-0.1.0.dist-info/licenses/LICENSE +201 -0
- logxpy-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for L{eliot.testing}.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from unittest import SkipTest, TestResult, TestCase, skipUnless
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import numpy as np
|
|
9
|
+
except ImportError:
|
|
10
|
+
np = None
|
|
11
|
+
|
|
12
|
+
from ..testing import (
|
|
13
|
+
issuperset,
|
|
14
|
+
assertContainsFields,
|
|
15
|
+
LoggedAction,
|
|
16
|
+
LoggedMessage,
|
|
17
|
+
validateLogging,
|
|
18
|
+
UnflushedTracebacks,
|
|
19
|
+
assertHasMessage,
|
|
20
|
+
assertHasAction,
|
|
21
|
+
validate_logging,
|
|
22
|
+
capture_logging,
|
|
23
|
+
swap_logger,
|
|
24
|
+
check_for_errors,
|
|
25
|
+
)
|
|
26
|
+
from .._output import MemoryLogger
|
|
27
|
+
from .._action import start_action
|
|
28
|
+
from .._message import Message
|
|
29
|
+
from .._validation import ActionType, MessageType, ValidationError, Field
|
|
30
|
+
from .._traceback import write_traceback
|
|
31
|
+
from .. import add_destination, remove_destination, _output, log_message
|
|
32
|
+
from .common import CustomObject, CustomJSONEncoder
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class IsSuperSetTests(TestCase):
|
|
36
|
+
"""
|
|
37
|
+
Tests for L{issuperset}.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def test_equal(self):
|
|
41
|
+
"""
|
|
42
|
+
Equal dictionaries are supersets of each other.
|
|
43
|
+
"""
|
|
44
|
+
a = {"a": 1}
|
|
45
|
+
b = a.copy()
|
|
46
|
+
self.assertTrue(issuperset(a, b))
|
|
47
|
+
|
|
48
|
+
def test_additionalIsSuperSet(self):
|
|
49
|
+
"""
|
|
50
|
+
If C{A} is C{B} plus some extra entries, C{A} is superset of C{B}.
|
|
51
|
+
"""
|
|
52
|
+
a = {"a": 1, "b": 2, "c": 3}
|
|
53
|
+
b = {"a": 1, "c": 3}
|
|
54
|
+
self.assertTrue(issuperset(a, b))
|
|
55
|
+
|
|
56
|
+
def test_missingIsNotSuperSet(self):
|
|
57
|
+
"""
|
|
58
|
+
If C{A} is C{B} minus some entries, C{A} is not a superset of C{B}.
|
|
59
|
+
"""
|
|
60
|
+
a = {"a": 1, "c": 3}
|
|
61
|
+
b = {"a": 1, "b": 2, "c": 3}
|
|
62
|
+
self.assertFalse(issuperset(a, b))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class LoggedActionTests(TestCase):
|
|
66
|
+
"""
|
|
67
|
+
Tests for L{LoggedAction}.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def test_values(self):
|
|
71
|
+
"""
|
|
72
|
+
The values given to the L{LoggedAction} constructor are stored on it.
|
|
73
|
+
"""
|
|
74
|
+
d1 = {"x": 1}
|
|
75
|
+
d2 = {"y": 2}
|
|
76
|
+
root = LoggedAction(d1, d2, [])
|
|
77
|
+
self.assertEqual((root.startMessage, root.endMessage), (d1, d2))
|
|
78
|
+
|
|
79
|
+
def fromMessagesIndex(self, messages, index):
|
|
80
|
+
"""
|
|
81
|
+
Call L{LoggedAction.fromMessages} using action specified by index in
|
|
82
|
+
a list of message dictionaries.
|
|
83
|
+
|
|
84
|
+
@param messages: A C{list} of message dictionaries.
|
|
85
|
+
|
|
86
|
+
@param index: Index to the logger's messages.
|
|
87
|
+
|
|
88
|
+
@return: Result of L{LoggedAction.fromMessages}.
|
|
89
|
+
"""
|
|
90
|
+
uuid = messages[index]["task_uuid"]
|
|
91
|
+
level = messages[index]["task_level"]
|
|
92
|
+
return LoggedAction.fromMessages(uuid, level, messages)
|
|
93
|
+
|
|
94
|
+
def test_fromMessagesCreatesLoggedAction(self):
|
|
95
|
+
"""
|
|
96
|
+
L{LoggedAction.fromMessages} returns a L{LoggedAction}.
|
|
97
|
+
"""
|
|
98
|
+
logger = MemoryLogger()
|
|
99
|
+
with start_action(logger, "test"):
|
|
100
|
+
pass
|
|
101
|
+
logged = self.fromMessagesIndex(logger.messages, 0)
|
|
102
|
+
self.assertIsInstance(logged, LoggedAction)
|
|
103
|
+
|
|
104
|
+
def test_fromMessagesStartAndSuccessfulFinish(self):
|
|
105
|
+
"""
|
|
106
|
+
L{LoggedAction.fromMessages} finds the start and successful finish
|
|
107
|
+
messages of an action and stores them in the result.
|
|
108
|
+
"""
|
|
109
|
+
logger = MemoryLogger()
|
|
110
|
+
Message.new(x=1).write(logger)
|
|
111
|
+
with start_action(logger, "test"):
|
|
112
|
+
Message.new(x=1).write(logger)
|
|
113
|
+
# Now we should have x message, start action message, another x message
|
|
114
|
+
# and finally finish message.
|
|
115
|
+
logged = self.fromMessagesIndex(logger.messages, 1)
|
|
116
|
+
self.assertEqual(
|
|
117
|
+
(logged.startMessage, logged.endMessage),
|
|
118
|
+
(logger.messages[1], logger.messages[3]),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def test_fromMessagesStartAndErrorFinish(self):
|
|
122
|
+
"""
|
|
123
|
+
L{LoggedAction.fromMessages} finds the start and successful finish
|
|
124
|
+
messages of an action and stores them in the result.
|
|
125
|
+
"""
|
|
126
|
+
logger = MemoryLogger()
|
|
127
|
+
try:
|
|
128
|
+
with start_action(logger, "test"):
|
|
129
|
+
raise KeyError()
|
|
130
|
+
except KeyError:
|
|
131
|
+
pass
|
|
132
|
+
logged = self.fromMessagesIndex(logger.messages, 0)
|
|
133
|
+
self.assertEqual(
|
|
134
|
+
(logged.startMessage, logged.endMessage),
|
|
135
|
+
(logger.messages[0], logger.messages[1]),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def test_fromMessagesStartNotFound(self):
|
|
139
|
+
"""
|
|
140
|
+
L{LoggedAction.fromMessages} raises a L{ValueError} if a start message
|
|
141
|
+
is not found.
|
|
142
|
+
"""
|
|
143
|
+
logger = MemoryLogger()
|
|
144
|
+
with start_action(logger, action_type="test"):
|
|
145
|
+
pass
|
|
146
|
+
self.assertRaises(ValueError, self.fromMessagesIndex, logger.messages[1:], 0)
|
|
147
|
+
|
|
148
|
+
def test_fromMessagesFinishNotFound(self):
|
|
149
|
+
"""
|
|
150
|
+
L{LoggedAction.fromMessages} raises a L{ValueError} if a finish message
|
|
151
|
+
is not found.
|
|
152
|
+
"""
|
|
153
|
+
logger = MemoryLogger()
|
|
154
|
+
with start_action(logger, action_type="test"):
|
|
155
|
+
pass
|
|
156
|
+
with self.assertRaises(ValueError) as cm:
|
|
157
|
+
self.fromMessagesIndex(logger.messages[:1], 0)
|
|
158
|
+
self.assertEqual(cm.exception.args[0], "Missing end message of type test")
|
|
159
|
+
|
|
160
|
+
def test_fromMessagesAddsChildMessages(self):
|
|
161
|
+
"""
|
|
162
|
+
L{LoggedAction.fromMessages} adds direct child messages to the
|
|
163
|
+
constructed L{LoggedAction}.
|
|
164
|
+
"""
|
|
165
|
+
logger = MemoryLogger()
|
|
166
|
+
# index 0:
|
|
167
|
+
Message.new(x=1).write(logger)
|
|
168
|
+
# index 1 - start action
|
|
169
|
+
with start_action(logger, "test"):
|
|
170
|
+
# index 2
|
|
171
|
+
Message.new(x=2).write(logger)
|
|
172
|
+
# index 3
|
|
173
|
+
Message.new(x=3).write(logger)
|
|
174
|
+
# index 4 - end action
|
|
175
|
+
# index 5
|
|
176
|
+
Message.new(x=4).write(logger)
|
|
177
|
+
logged = self.fromMessagesIndex(logger.messages, 1)
|
|
178
|
+
|
|
179
|
+
expectedChildren = [
|
|
180
|
+
LoggedMessage(logger.messages[2]),
|
|
181
|
+
LoggedMessage(logger.messages[3]),
|
|
182
|
+
]
|
|
183
|
+
self.assertEqual(logged.children, expectedChildren)
|
|
184
|
+
|
|
185
|
+
def test_fromMessagesAddsChildActions(self):
|
|
186
|
+
"""
|
|
187
|
+
L{LoggedAction.fromMessages} recursively adds direct child actions to
|
|
188
|
+
the constructed L{LoggedAction}.
|
|
189
|
+
"""
|
|
190
|
+
logger = MemoryLogger()
|
|
191
|
+
# index 0
|
|
192
|
+
with start_action(logger, "test"):
|
|
193
|
+
# index 1:
|
|
194
|
+
with start_action(logger, "test2"):
|
|
195
|
+
# index 2
|
|
196
|
+
Message.new(message_type="end", x=2).write(logger)
|
|
197
|
+
# index 3 - end action
|
|
198
|
+
with start_action(logger, "test3"):
|
|
199
|
+
# index 4
|
|
200
|
+
pass
|
|
201
|
+
# index 5 - end action
|
|
202
|
+
# index 6 - end action
|
|
203
|
+
logged = self.fromMessagesIndex(logger.messages, 0)
|
|
204
|
+
|
|
205
|
+
self.assertEqual(logged.children[0], self.fromMessagesIndex(logger.messages, 1))
|
|
206
|
+
self.assertEqual(
|
|
207
|
+
logged.type_tree(), {"test": [{"test2": ["end"]}, {"test3": []}]}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def test_ofType(self):
|
|
211
|
+
"""
|
|
212
|
+
L{LoggedAction.ofType} returns a list of L{LoggedAction} created by the
|
|
213
|
+
specified L{ActionType}.
|
|
214
|
+
"""
|
|
215
|
+
ACTION = ActionType("myaction", [], [], "An action!")
|
|
216
|
+
logger = MemoryLogger()
|
|
217
|
+
# index 0
|
|
218
|
+
with start_action(logger, "test"):
|
|
219
|
+
# index 1:
|
|
220
|
+
with ACTION(logger):
|
|
221
|
+
# index 2
|
|
222
|
+
Message.new(x=2).write(logger)
|
|
223
|
+
# index 3 - end action
|
|
224
|
+
# index 4 - end action
|
|
225
|
+
# index 5
|
|
226
|
+
with ACTION(logger):
|
|
227
|
+
pass
|
|
228
|
+
# index 6 - end action
|
|
229
|
+
logged = LoggedAction.ofType(logger.messages, ACTION)
|
|
230
|
+
self.assertEqual(
|
|
231
|
+
logged,
|
|
232
|
+
[
|
|
233
|
+
self.fromMessagesIndex(logger.messages, 1),
|
|
234
|
+
self.fromMessagesIndex(logger.messages, 5),
|
|
235
|
+
],
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# String-variant of ofType:
|
|
239
|
+
logged2 = LoggedAction.ofType(logger.messages, "myaction")
|
|
240
|
+
self.assertEqual(logged, logged2)
|
|
241
|
+
|
|
242
|
+
def test_ofTypeNotFound(self):
|
|
243
|
+
"""
|
|
244
|
+
L{LoggedAction.ofType} returns an empty list if actions of the given
|
|
245
|
+
type cannot be found.
|
|
246
|
+
"""
|
|
247
|
+
ACTION = ActionType("myaction", [], [], "An action!")
|
|
248
|
+
logger = MemoryLogger()
|
|
249
|
+
self.assertEqual(LoggedAction.ofType(logger.messages, ACTION), [])
|
|
250
|
+
|
|
251
|
+
def test_descendants(self):
|
|
252
|
+
"""
|
|
253
|
+
L{LoggedAction.descendants} returns all descendants of the
|
|
254
|
+
L{LoggedAction}.
|
|
255
|
+
"""
|
|
256
|
+
ACTION = ActionType("myaction", [], [], "An action!")
|
|
257
|
+
logger = MemoryLogger()
|
|
258
|
+
# index 0
|
|
259
|
+
with ACTION(logger):
|
|
260
|
+
# index 1:
|
|
261
|
+
with start_action(logger, "test"):
|
|
262
|
+
# index 2
|
|
263
|
+
Message.new(x=2).write(logger)
|
|
264
|
+
# index 3 - end action
|
|
265
|
+
# index 4
|
|
266
|
+
Message.new(x=2).write(logger)
|
|
267
|
+
# index 5 - end action
|
|
268
|
+
|
|
269
|
+
loggedAction = LoggedAction.ofType(logger.messages, ACTION)[0]
|
|
270
|
+
self.assertEqual(
|
|
271
|
+
list(loggedAction.descendants()),
|
|
272
|
+
[
|
|
273
|
+
self.fromMessagesIndex(logger.messages, 1),
|
|
274
|
+
LoggedMessage(logger.messages[2]),
|
|
275
|
+
LoggedMessage(logger.messages[4]),
|
|
276
|
+
],
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def test_succeeded(self):
|
|
280
|
+
"""
|
|
281
|
+
If the action succeeded, L{LoggedAction.succeeded} will be true.
|
|
282
|
+
"""
|
|
283
|
+
logger = MemoryLogger()
|
|
284
|
+
with start_action(logger, "test"):
|
|
285
|
+
pass
|
|
286
|
+
logged = self.fromMessagesIndex(logger.messages, 0)
|
|
287
|
+
self.assertTrue(logged.succeeded)
|
|
288
|
+
|
|
289
|
+
def test_notSucceeded(self):
|
|
290
|
+
"""
|
|
291
|
+
If the action failed, L{LoggedAction.succeeded} will be false.
|
|
292
|
+
"""
|
|
293
|
+
logger = MemoryLogger()
|
|
294
|
+
try:
|
|
295
|
+
with start_action(logger, "test"):
|
|
296
|
+
raise KeyError()
|
|
297
|
+
except KeyError:
|
|
298
|
+
pass
|
|
299
|
+
logged = self.fromMessagesIndex(logger.messages, 0)
|
|
300
|
+
self.assertFalse(logged.succeeded)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class LoggedMessageTest(TestCase):
|
|
304
|
+
"""
|
|
305
|
+
Tests for L{LoggedMessage}.
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
def test_values(self):
|
|
309
|
+
"""
|
|
310
|
+
The values given to the L{LoggedMessage} constructor are stored on it.
|
|
311
|
+
"""
|
|
312
|
+
message = {"x": 1}
|
|
313
|
+
logged = LoggedMessage(message)
|
|
314
|
+
self.assertEqual(logged.message, message)
|
|
315
|
+
|
|
316
|
+
def test_ofType(self):
|
|
317
|
+
"""
|
|
318
|
+
L{LoggedMessage.ofType} returns a list of L{LoggedMessage} created by the
|
|
319
|
+
specified L{MessageType}.
|
|
320
|
+
"""
|
|
321
|
+
MESSAGE = MessageType("mymessage", [], "A message!")
|
|
322
|
+
logger = MemoryLogger()
|
|
323
|
+
# index 0
|
|
324
|
+
MESSAGE().write(logger)
|
|
325
|
+
# index 1
|
|
326
|
+
Message.new(x=2).write(logger)
|
|
327
|
+
# index 2
|
|
328
|
+
MESSAGE().write(logger)
|
|
329
|
+
logged = LoggedMessage.ofType(logger.messages, MESSAGE)
|
|
330
|
+
self.assertEqual(
|
|
331
|
+
logged,
|
|
332
|
+
[LoggedMessage(logger.messages[0]), LoggedMessage(logger.messages[2])],
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Lookup by string type:
|
|
336
|
+
logged2 = LoggedMessage.ofType(logger.messages, "mymessage")
|
|
337
|
+
self.assertEqual(logged, logged2)
|
|
338
|
+
|
|
339
|
+
def test_ofTypeNotFound(self):
|
|
340
|
+
"""
|
|
341
|
+
L{LoggedMessage.ofType} returns an empty list if messages of the given
|
|
342
|
+
type cannot be found.
|
|
343
|
+
"""
|
|
344
|
+
MESSAGE = MessageType("mymessage", [], "A message!")
|
|
345
|
+
logger = MemoryLogger()
|
|
346
|
+
self.assertEqual(LoggedMessage.ofType(logger.messages, MESSAGE), [])
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class AssertContainsFields(TestCase):
|
|
350
|
+
"""
|
|
351
|
+
Tests for L{assertContainsFields}.
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
class ContainsTest(TestCase):
|
|
355
|
+
"""
|
|
356
|
+
A test case that uses L{assertContainsFields}.
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
def __init__(self, message, expectedFields):
|
|
360
|
+
TestCase.__init__(self)
|
|
361
|
+
self.message = message
|
|
362
|
+
self.expectedFields = expectedFields
|
|
363
|
+
|
|
364
|
+
def runTest(self):
|
|
365
|
+
assertContainsFields(self, self.message, self.expectedFields)
|
|
366
|
+
|
|
367
|
+
def test_equal(self):
|
|
368
|
+
"""
|
|
369
|
+
Equal dictionaries contain each other.
|
|
370
|
+
"""
|
|
371
|
+
message = {"a": 1}
|
|
372
|
+
expected = message.copy()
|
|
373
|
+
test = self.ContainsTest(message, expected)
|
|
374
|
+
# No exception raised:
|
|
375
|
+
test.debug()
|
|
376
|
+
|
|
377
|
+
def test_additionalIsSuperSet(self):
|
|
378
|
+
"""
|
|
379
|
+
If C{A} is C{B} plus some extra entries, C{A} contains the fields in
|
|
380
|
+
C{B}.
|
|
381
|
+
"""
|
|
382
|
+
message = {"a": 1, "b": 2, "c": 3}
|
|
383
|
+
expected = {"a": 1, "c": 3}
|
|
384
|
+
test = self.ContainsTest(message, expected)
|
|
385
|
+
# No exception raised:
|
|
386
|
+
test.debug()
|
|
387
|
+
|
|
388
|
+
def test_missingFields(self):
|
|
389
|
+
"""
|
|
390
|
+
If C{A} is C{B} minus some entries, C{A} does not contain the fields in
|
|
391
|
+
C{B}.
|
|
392
|
+
"""
|
|
393
|
+
message = {"a": 1, "c": 3}
|
|
394
|
+
expected = {"a": 1, "b": 2, "c": 3}
|
|
395
|
+
test = self.ContainsTest(message, expected)
|
|
396
|
+
self.assertRaises(AssertionError, test.debug)
|
|
397
|
+
|
|
398
|
+
def test_differentValues(self):
|
|
399
|
+
"""
|
|
400
|
+
If C{A} has a different value for a specific field than C{B}, C{A} does
|
|
401
|
+
not contain the fields in C{B}.
|
|
402
|
+
"""
|
|
403
|
+
message = {"a": 1, "c": 3}
|
|
404
|
+
expected = {"a": 1, "c": 2}
|
|
405
|
+
test = self.ContainsTest(message, expected)
|
|
406
|
+
self.assertRaises(AssertionError, test.debug)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class ValidateLoggingTestsMixin(object):
|
|
410
|
+
"""
|
|
411
|
+
Tests for L{validateLogging} and L{capture_logging}.
|
|
412
|
+
"""
|
|
413
|
+
|
|
414
|
+
validate = None
|
|
415
|
+
|
|
416
|
+
def test_decoratedFunctionCalledWithMemoryLogger(self):
|
|
417
|
+
"""
|
|
418
|
+
The underlying function decorated with L{validateLogging} is called with
|
|
419
|
+
a L{MemoryLogger} instance.
|
|
420
|
+
"""
|
|
421
|
+
result = []
|
|
422
|
+
|
|
423
|
+
class MyTest(TestCase):
|
|
424
|
+
@self.validate(None)
|
|
425
|
+
def test_foo(this, logger):
|
|
426
|
+
result.append((this, logger.__class__))
|
|
427
|
+
|
|
428
|
+
theTest = MyTest("test_foo")
|
|
429
|
+
theTest.run()
|
|
430
|
+
self.assertEqual(result, [(theTest, MemoryLogger)])
|
|
431
|
+
|
|
432
|
+
def test_decorated_function_passthrough(self):
|
|
433
|
+
"""
|
|
434
|
+
Additional arguments are passed to the underlying function.
|
|
435
|
+
"""
|
|
436
|
+
result = []
|
|
437
|
+
|
|
438
|
+
def another_wrapper(f):
|
|
439
|
+
def g(this):
|
|
440
|
+
f(this, 1, 2, c=3)
|
|
441
|
+
|
|
442
|
+
return g
|
|
443
|
+
|
|
444
|
+
class MyTest(TestCase):
|
|
445
|
+
@another_wrapper
|
|
446
|
+
@self.validate(None)
|
|
447
|
+
def test_foo(this, a, b, logger, c=None):
|
|
448
|
+
result.append((a, b, c))
|
|
449
|
+
|
|
450
|
+
theTest = MyTest("test_foo")
|
|
451
|
+
theTest.debug()
|
|
452
|
+
self.assertEqual(result, [(1, 2, 3)])
|
|
453
|
+
|
|
454
|
+
def test_newMemoryLogger(self):
|
|
455
|
+
"""
|
|
456
|
+
The underlying function decorated with L{validateLogging} is called with
|
|
457
|
+
a new L{MemoryLogger} every time the wrapper is called.
|
|
458
|
+
"""
|
|
459
|
+
result = []
|
|
460
|
+
|
|
461
|
+
class MyTest(TestCase):
|
|
462
|
+
@self.validate(None)
|
|
463
|
+
def test_foo(this, logger):
|
|
464
|
+
result.append(logger)
|
|
465
|
+
|
|
466
|
+
theTest = MyTest("test_foo")
|
|
467
|
+
theTest.run()
|
|
468
|
+
theTest.run()
|
|
469
|
+
self.assertIsNot(result[0], result[1])
|
|
470
|
+
|
|
471
|
+
def test_returns(self):
|
|
472
|
+
"""
|
|
473
|
+
The result of the underlying function is returned by wrapper when called.
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
class MyTest(TestCase):
|
|
477
|
+
@self.validate(None)
|
|
478
|
+
def test_foo(self, logger):
|
|
479
|
+
return 123
|
|
480
|
+
|
|
481
|
+
self.assertEqual(MyTest("test_foo").test_foo(), 123)
|
|
482
|
+
|
|
483
|
+
def test_raises(self):
|
|
484
|
+
"""
|
|
485
|
+
The exception raised by the underlying function is passed through by the
|
|
486
|
+
wrapper when called.
|
|
487
|
+
"""
|
|
488
|
+
exc = Exception()
|
|
489
|
+
|
|
490
|
+
class MyTest(TestCase):
|
|
491
|
+
@self.validate(None)
|
|
492
|
+
def test_foo(self, logger):
|
|
493
|
+
raise exc
|
|
494
|
+
|
|
495
|
+
raised = None
|
|
496
|
+
try:
|
|
497
|
+
MyTest("test_foo").debug()
|
|
498
|
+
except Exception as e:
|
|
499
|
+
raised = e
|
|
500
|
+
self.assertIs(exc, raised)
|
|
501
|
+
|
|
502
|
+
def test_name(self):
|
|
503
|
+
"""
|
|
504
|
+
The wrapper has the same name as the wrapped function.
|
|
505
|
+
"""
|
|
506
|
+
|
|
507
|
+
class MyTest(TestCase):
|
|
508
|
+
@self.validate(None)
|
|
509
|
+
def test_foo(self, logger):
|
|
510
|
+
pass
|
|
511
|
+
|
|
512
|
+
self.assertEqual(MyTest.test_foo.__name__, "test_foo")
|
|
513
|
+
|
|
514
|
+
def test_addCleanupValidate(self):
|
|
515
|
+
"""
|
|
516
|
+
When a test method is decorated with L{validateLogging} it has
|
|
517
|
+
L{MemoryLogger.validate} registered as a test cleanup.
|
|
518
|
+
"""
|
|
519
|
+
MESSAGE = MessageType("mymessage", [], "A message")
|
|
520
|
+
|
|
521
|
+
class MyTest(TestCase):
|
|
522
|
+
@self.validate(None)
|
|
523
|
+
def runTest(self, logger):
|
|
524
|
+
self.logger = logger
|
|
525
|
+
logger.write({"message_type": "wrongmessage"}, MESSAGE._serializer)
|
|
526
|
+
|
|
527
|
+
test = MyTest()
|
|
528
|
+
with self.assertRaises(ValidationError) as context:
|
|
529
|
+
test.debug()
|
|
530
|
+
# Some reference to the reason:
|
|
531
|
+
self.assertIn("wrongmessage", str(context.exception))
|
|
532
|
+
# Some reference to which file caused the problem:
|
|
533
|
+
self.assertIn("test_testing.py", str(context.exception))
|
|
534
|
+
|
|
535
|
+
def test_addCleanupTracebacks(self):
|
|
536
|
+
"""
|
|
537
|
+
When a test method is decorated with L{validateLogging} it has has a
|
|
538
|
+
check unflushed tracebacks in the L{MemoryLogger} registered as a
|
|
539
|
+
test cleanup.
|
|
540
|
+
"""
|
|
541
|
+
|
|
542
|
+
class MyTest(TestCase):
|
|
543
|
+
@self.validate(None)
|
|
544
|
+
def runTest(self, logger):
|
|
545
|
+
try:
|
|
546
|
+
1 / 0
|
|
547
|
+
except ZeroDivisionError:
|
|
548
|
+
write_traceback(logger)
|
|
549
|
+
|
|
550
|
+
test = MyTest()
|
|
551
|
+
self.assertRaises(UnflushedTracebacks, test.debug)
|
|
552
|
+
|
|
553
|
+
def test_assertion(self):
|
|
554
|
+
"""
|
|
555
|
+
If a callable is passed to L{validateLogging}, it is called with the
|
|
556
|
+
L{TestCase} instance and the L{MemoryLogger} passed to the test
|
|
557
|
+
method.
|
|
558
|
+
"""
|
|
559
|
+
result = []
|
|
560
|
+
|
|
561
|
+
class MyTest(TestCase):
|
|
562
|
+
def assertLogging(self, logger):
|
|
563
|
+
result.append((self, logger))
|
|
564
|
+
|
|
565
|
+
@self.validate(assertLogging)
|
|
566
|
+
def runTest(self, logger):
|
|
567
|
+
self.logger = logger
|
|
568
|
+
|
|
569
|
+
test = MyTest()
|
|
570
|
+
test.run()
|
|
571
|
+
self.assertEqual(result, [(test, test.logger)])
|
|
572
|
+
|
|
573
|
+
def test_assertionArguments(self):
|
|
574
|
+
"""
|
|
575
|
+
If a callable together with additional arguments and keyword arguments are
|
|
576
|
+
passed to L{validateLogging}, the callable is called with the additional
|
|
577
|
+
args and kwargs.
|
|
578
|
+
"""
|
|
579
|
+
result = []
|
|
580
|
+
|
|
581
|
+
class MyTest(TestCase):
|
|
582
|
+
def assertLogging(self, logger, x, y):
|
|
583
|
+
result.append((self, logger, x, y))
|
|
584
|
+
|
|
585
|
+
@self.validate(assertLogging, 1, y=2)
|
|
586
|
+
def runTest(self, logger):
|
|
587
|
+
self.logger = logger
|
|
588
|
+
|
|
589
|
+
test = MyTest()
|
|
590
|
+
test.run()
|
|
591
|
+
self.assertEqual(result, [(test, test.logger, 1, 2)])
|
|
592
|
+
|
|
593
|
+
def test_assertionAfterTest(self):
|
|
594
|
+
"""
|
|
595
|
+
If a callable is passed to L{validateLogging}, it is called with the
|
|
596
|
+
after the main test code has run, allowing it to make assertions
|
|
597
|
+
about log messages from the test.
|
|
598
|
+
"""
|
|
599
|
+
|
|
600
|
+
class MyTest(TestCase):
|
|
601
|
+
def assertLogging(self, logger):
|
|
602
|
+
self.result.append(2)
|
|
603
|
+
|
|
604
|
+
@self.validate(assertLogging)
|
|
605
|
+
def runTest(self, logger):
|
|
606
|
+
self.result = [1]
|
|
607
|
+
|
|
608
|
+
test = MyTest()
|
|
609
|
+
test.run()
|
|
610
|
+
self.assertEqual(test.result, [1, 2])
|
|
611
|
+
|
|
612
|
+
def test_assertionBeforeTracebackCleanup(self):
|
|
613
|
+
"""
|
|
614
|
+
If a callable is passed to L{validateLogging}, it is called with the
|
|
615
|
+
before the check for unflushed tracebacks, allowing it to flush
|
|
616
|
+
traceback log messages.
|
|
617
|
+
"""
|
|
618
|
+
|
|
619
|
+
class MyTest(TestCase):
|
|
620
|
+
def assertLogging(self, logger):
|
|
621
|
+
logger.flushTracebacks(ZeroDivisionError)
|
|
622
|
+
self.flushed = True
|
|
623
|
+
|
|
624
|
+
@self.validate(assertLogging)
|
|
625
|
+
def runTest(self, logger):
|
|
626
|
+
self.flushed = False
|
|
627
|
+
try:
|
|
628
|
+
1 / 0
|
|
629
|
+
except ZeroDivisionError:
|
|
630
|
+
write_traceback(logger)
|
|
631
|
+
|
|
632
|
+
test = MyTest()
|
|
633
|
+
test.run()
|
|
634
|
+
self.assertTrue(test.flushed)
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
class ValidateLoggingTests(ValidateLoggingTestsMixin, TestCase):
|
|
638
|
+
"""
|
|
639
|
+
Tests for L{validate_logging}.
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
validate = staticmethod(validate_logging)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
class CaptureLoggingTests(ValidateLoggingTestsMixin, TestCase):
|
|
646
|
+
"""
|
|
647
|
+
Tests for L{capture_logging}.
|
|
648
|
+
"""
|
|
649
|
+
|
|
650
|
+
validate = staticmethod(capture_logging)
|
|
651
|
+
|
|
652
|
+
def setUp(self):
|
|
653
|
+
# Since we're not always calling the test method via the TestCase
|
|
654
|
+
# infrastructure, sometimes cleanup methods are not called. This
|
|
655
|
+
# means the original default logger is not restored. So we do so
|
|
656
|
+
# manually. If the issue is a bug in capture_logging itself the
|
|
657
|
+
# tests below will catch that.
|
|
658
|
+
original_logger = _output._DEFAULT_LOGGER
|
|
659
|
+
|
|
660
|
+
def cleanup():
|
|
661
|
+
_output._DEFAULT_LOGGER = original_logger
|
|
662
|
+
|
|
663
|
+
self.addCleanup(cleanup)
|
|
664
|
+
|
|
665
|
+
def test_default_logger(self):
|
|
666
|
+
"""
|
|
667
|
+
L{capture_logging} captures messages from logging that
|
|
668
|
+
doesn't specify a L{Logger}.
|
|
669
|
+
"""
|
|
670
|
+
|
|
671
|
+
class MyTest(TestCase):
|
|
672
|
+
@capture_logging(None)
|
|
673
|
+
def runTest(self, logger):
|
|
674
|
+
Message.log(some_key=1234)
|
|
675
|
+
self.logger = logger
|
|
676
|
+
|
|
677
|
+
test = MyTest()
|
|
678
|
+
test.run()
|
|
679
|
+
self.assertEqual(test.logger.messages[0]["some_key"], 1234)
|
|
680
|
+
|
|
681
|
+
def test_global_cleanup(self):
|
|
682
|
+
"""
|
|
683
|
+
After the function wrapped with L{capture_logging} finishes,
|
|
684
|
+
logging that doesn't specify a logger is logged normally.
|
|
685
|
+
"""
|
|
686
|
+
|
|
687
|
+
class MyTest(TestCase):
|
|
688
|
+
@capture_logging(None)
|
|
689
|
+
def runTest(self, logger):
|
|
690
|
+
pass
|
|
691
|
+
|
|
692
|
+
test = MyTest()
|
|
693
|
+
test.run()
|
|
694
|
+
messages = []
|
|
695
|
+
add_destination(messages.append)
|
|
696
|
+
self.addCleanup(remove_destination, messages.append)
|
|
697
|
+
Message.log(some_key=1234)
|
|
698
|
+
self.assertEqual(messages[0]["some_key"], 1234)
|
|
699
|
+
|
|
700
|
+
def test_global_cleanup_exception(self):
|
|
701
|
+
"""
|
|
702
|
+
If the function wrapped with L{capture_logging} throws an exception,
|
|
703
|
+
logging that doesn't specify a logger is logged normally.
|
|
704
|
+
"""
|
|
705
|
+
|
|
706
|
+
class MyTest(TestCase):
|
|
707
|
+
@capture_logging(None)
|
|
708
|
+
def runTest(self, logger):
|
|
709
|
+
raise RuntimeError()
|
|
710
|
+
|
|
711
|
+
test = MyTest()
|
|
712
|
+
test.run()
|
|
713
|
+
messages = []
|
|
714
|
+
add_destination(messages.append)
|
|
715
|
+
self.addCleanup(remove_destination, messages.append)
|
|
716
|
+
Message.log(some_key=1234)
|
|
717
|
+
self.assertEqual(messages[0]["some_key"], 1234)
|
|
718
|
+
|
|
719
|
+
def test_validationNotRunForSkip(self):
|
|
720
|
+
"""
|
|
721
|
+
If the decorated test raises L{SkipTest} then the logging validation is
|
|
722
|
+
also skipped.
|
|
723
|
+
"""
|
|
724
|
+
|
|
725
|
+
class MyTest(TestCase):
|
|
726
|
+
recorded = False
|
|
727
|
+
|
|
728
|
+
def record(self, logger):
|
|
729
|
+
self.recorded = True
|
|
730
|
+
|
|
731
|
+
@validateLogging(record)
|
|
732
|
+
def runTest(self, logger):
|
|
733
|
+
raise SkipTest("Do not run this test.")
|
|
734
|
+
|
|
735
|
+
test = MyTest()
|
|
736
|
+
result = TestResult()
|
|
737
|
+
test.run(result)
|
|
738
|
+
|
|
739
|
+
# Verify that the validation function did not run and that the test was
|
|
740
|
+
# nevertheless marked as a skip with the correct reason.
|
|
741
|
+
self.assertEqual(
|
|
742
|
+
(test.recorded, result.skipped, result.errors, result.failures),
|
|
743
|
+
(False, [(test, "Do not run this test.")], [], []),
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
class JSONEncodingTests(TestCase):
|
|
748
|
+
"""Tests for L{capture_logging} JSON encoder support."""
|
|
749
|
+
|
|
750
|
+
@skipUnless(np, "NumPy is not installed.")
|
|
751
|
+
@capture_logging(None)
|
|
752
|
+
def test_default_JSON_encoder(self, logger):
|
|
753
|
+
"""
|
|
754
|
+
L{capture_logging} validates using L{EliotJSONEncoder} by default.
|
|
755
|
+
"""
|
|
756
|
+
# Default JSON encoder can't handle NumPy:
|
|
757
|
+
log_message(message_type="hello", number=np.uint32(12))
|
|
758
|
+
|
|
759
|
+
@capture_logging(None, encoder_=CustomJSONEncoder)
|
|
760
|
+
def test_custom_JSON_encoder(self, logger):
|
|
761
|
+
"""
|
|
762
|
+
L{capture_logging} can be called with a custom JSON encoder, which is then
|
|
763
|
+
used for validation.
|
|
764
|
+
"""
|
|
765
|
+
# Default JSON encoder can't handle this custom object:
|
|
766
|
+
log_message(message_type="hello", object=CustomObject())
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
MESSAGE1 = MessageType(
|
|
770
|
+
"message1", [Field.forTypes("x", [int], "A number")], "A message for testing."
|
|
771
|
+
)
|
|
772
|
+
MESSAGE2 = MessageType("message2", [], "A message for testing.")
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
class AssertHasMessageTests(TestCase):
|
|
776
|
+
"""
|
|
777
|
+
Tests for L{assertHasMessage}.
|
|
778
|
+
"""
|
|
779
|
+
|
|
780
|
+
class UnitTest(TestCase):
|
|
781
|
+
"""
|
|
782
|
+
Test case that can be instantiated.
|
|
783
|
+
"""
|
|
784
|
+
|
|
785
|
+
def runTest(self):
|
|
786
|
+
pass
|
|
787
|
+
|
|
788
|
+
def test_failIfNoMessagesOfType(self):
|
|
789
|
+
"""
|
|
790
|
+
L{assertHasMessage} raises L{AssertionError} if the given L{MemoryLogger}
|
|
791
|
+
has no messages of the given L{MessageType}.
|
|
792
|
+
"""
|
|
793
|
+
test = self.UnitTest()
|
|
794
|
+
logger = MemoryLogger()
|
|
795
|
+
MESSAGE1(x=123).write(logger)
|
|
796
|
+
self.assertRaises(AssertionError, assertHasMessage, test, logger, MESSAGE2)
|
|
797
|
+
|
|
798
|
+
def test_returnsIfMessagesOfType(self):
|
|
799
|
+
"""
|
|
800
|
+
L{assertHasMessage} returns the first message of the given L{MessageType}.
|
|
801
|
+
"""
|
|
802
|
+
test = self.UnitTest()
|
|
803
|
+
logger = MemoryLogger()
|
|
804
|
+
MESSAGE1(x=123).write(logger)
|
|
805
|
+
self.assertEqual(
|
|
806
|
+
assertHasMessage(test, logger, MESSAGE1),
|
|
807
|
+
LoggedMessage.ofType(logger.messages, MESSAGE1)[0],
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
def test_failIfNotSubset(self):
|
|
811
|
+
"""
|
|
812
|
+
L{assertHasMessage} raises L{AssertionError} if the found message doesn't
|
|
813
|
+
contain the given fields.
|
|
814
|
+
"""
|
|
815
|
+
test = self.UnitTest()
|
|
816
|
+
logger = MemoryLogger()
|
|
817
|
+
MESSAGE1(x=123).write(logger)
|
|
818
|
+
self.assertRaises(
|
|
819
|
+
AssertionError, assertHasMessage, test, logger, MESSAGE1, {"x": 24}
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
def test_returnsIfSubset(self):
|
|
823
|
+
"""
|
|
824
|
+
L{assertHasMessage} returns the first message of the given L{MessageType} if
|
|
825
|
+
it contains the given fields.
|
|
826
|
+
"""
|
|
827
|
+
test = self.UnitTest()
|
|
828
|
+
logger = MemoryLogger()
|
|
829
|
+
MESSAGE1(x=123).write(logger)
|
|
830
|
+
self.assertEqual(
|
|
831
|
+
assertHasMessage(test, logger, MESSAGE1, {"x": 123}),
|
|
832
|
+
LoggedMessage.ofType(logger.messages, MESSAGE1)[0],
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
ACTION1 = ActionType(
|
|
837
|
+
"action1",
|
|
838
|
+
[Field.forTypes("x", [int], "A number")],
|
|
839
|
+
[Field.forTypes("result", [int], "A number")],
|
|
840
|
+
"A action for testing.",
|
|
841
|
+
)
|
|
842
|
+
ACTION2 = ActionType("action2", [], [], "A action for testing.")
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
class AssertHasActionTests(TestCase):
|
|
846
|
+
"""
|
|
847
|
+
Tests for L{assertHasAction}.
|
|
848
|
+
"""
|
|
849
|
+
|
|
850
|
+
class UnitTest(TestCase):
|
|
851
|
+
"""
|
|
852
|
+
Test case that can be instantiated.
|
|
853
|
+
"""
|
|
854
|
+
|
|
855
|
+
def runTest(self):
|
|
856
|
+
pass
|
|
857
|
+
|
|
858
|
+
def test_failIfNoActionsOfType(self):
|
|
859
|
+
"""
|
|
860
|
+
L{assertHasAction} raises L{AssertionError} if the given L{MemoryLogger}
|
|
861
|
+
has no actions of the given L{ActionType}.
|
|
862
|
+
"""
|
|
863
|
+
test = self.UnitTest()
|
|
864
|
+
logger = MemoryLogger()
|
|
865
|
+
with ACTION1(logger, x=123):
|
|
866
|
+
pass
|
|
867
|
+
self.assertRaises(AssertionError, assertHasAction, test, logger, ACTION2, True)
|
|
868
|
+
|
|
869
|
+
def test_failIfWrongSuccessStatus(self):
|
|
870
|
+
"""
|
|
871
|
+
L{assertHasAction} raises L{AssertionError} if the given success status does
|
|
872
|
+
not match that of the found actions.
|
|
873
|
+
"""
|
|
874
|
+
test = self.UnitTest()
|
|
875
|
+
logger = MemoryLogger()
|
|
876
|
+
with ACTION1(logger, x=123):
|
|
877
|
+
pass
|
|
878
|
+
try:
|
|
879
|
+
with ACTION2(logger):
|
|
880
|
+
1 / 0
|
|
881
|
+
except ZeroDivisionError:
|
|
882
|
+
pass
|
|
883
|
+
self.assertRaises(AssertionError, assertHasAction, test, logger, ACTION1, False)
|
|
884
|
+
self.assertRaises(AssertionError, assertHasAction, test, logger, ACTION2, True)
|
|
885
|
+
|
|
886
|
+
def test_returnsIfMessagesOfType(self):
|
|
887
|
+
"""
|
|
888
|
+
A successful L{assertHasAction} returns the first message of the given
|
|
889
|
+
L{ActionType}.
|
|
890
|
+
"""
|
|
891
|
+
test = self.UnitTest()
|
|
892
|
+
logger = MemoryLogger()
|
|
893
|
+
with ACTION1(logger, x=123):
|
|
894
|
+
pass
|
|
895
|
+
self.assertEqual(
|
|
896
|
+
assertHasAction(test, logger, ACTION1, True),
|
|
897
|
+
LoggedAction.ofType(logger.messages, ACTION1)[0],
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
def test_failIfNotStartSubset(self):
|
|
901
|
+
"""
|
|
902
|
+
L{assertHasAction} raises L{AssertionError} if the found action doesn't
|
|
903
|
+
contain the given start fields.
|
|
904
|
+
"""
|
|
905
|
+
test = self.UnitTest()
|
|
906
|
+
logger = MemoryLogger()
|
|
907
|
+
with ACTION1(logger, x=123):
|
|
908
|
+
pass
|
|
909
|
+
self.assertRaises(
|
|
910
|
+
AssertionError, assertHasAction, test, logger, ACTION1, True, {"x": 24}
|
|
911
|
+
)
|
|
912
|
+
|
|
913
|
+
def test_failIfNotEndSubset(self):
|
|
914
|
+
"""
|
|
915
|
+
L{assertHasAction} raises L{AssertionError} if the found action doesn't
|
|
916
|
+
contain the given end fields.
|
|
917
|
+
"""
|
|
918
|
+
test = self.UnitTest()
|
|
919
|
+
logger = MemoryLogger()
|
|
920
|
+
with ACTION1(logger, x=123) as act:
|
|
921
|
+
act.addSuccessFields(result=5)
|
|
922
|
+
self.assertRaises(
|
|
923
|
+
AssertionError,
|
|
924
|
+
assertHasAction,
|
|
925
|
+
test,
|
|
926
|
+
logger,
|
|
927
|
+
ACTION1,
|
|
928
|
+
True,
|
|
929
|
+
startFields={"x": 123},
|
|
930
|
+
endFields={"result": 24},
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
def test_returns(self):
|
|
934
|
+
"""
|
|
935
|
+
A successful L{assertHasAction} returns the first message of the given
|
|
936
|
+
L{ActionType} after doing all validation.
|
|
937
|
+
"""
|
|
938
|
+
test = self.UnitTest()
|
|
939
|
+
logger = MemoryLogger()
|
|
940
|
+
with ACTION1(logger, x=123) as act:
|
|
941
|
+
act.addSuccessFields(result=5)
|
|
942
|
+
self.assertEqual(
|
|
943
|
+
assertHasAction(test, logger, ACTION1, True, {"x": 123}, {"result": 5}),
|
|
944
|
+
LoggedAction.ofType(logger.messages, ACTION1)[0],
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
class PEP8Tests(TestCase):
|
|
949
|
+
"""
|
|
950
|
+
Tests for PEP 8 method compatibility.
|
|
951
|
+
"""
|
|
952
|
+
|
|
953
|
+
def test_LoggedAction_from_messages(self):
|
|
954
|
+
"""
|
|
955
|
+
L{LoggedAction.from_messages} is the same as
|
|
956
|
+
L{LoggedAction.fromMessages}.
|
|
957
|
+
"""
|
|
958
|
+
self.assertEqual(LoggedAction.from_messages, LoggedAction.fromMessages)
|
|
959
|
+
|
|
960
|
+
def test_LoggedAction_of_type(self):
|
|
961
|
+
"""
|
|
962
|
+
L{LoggedAction.of_type} is the same as
|
|
963
|
+
L{LoggedAction.ofType}.
|
|
964
|
+
"""
|
|
965
|
+
self.assertEqual(LoggedAction.of_type, LoggedAction.ofType)
|
|
966
|
+
|
|
967
|
+
def test_LoggedAction_end_message(self):
|
|
968
|
+
"""
|
|
969
|
+
L{LoggedAction.end_message} is the same as L{LoggedAction.endMessage}.
|
|
970
|
+
"""
|
|
971
|
+
action = LoggedAction({1: 2}, {3: 4}, [])
|
|
972
|
+
self.assertEqual(action.end_message, action.endMessage)
|
|
973
|
+
|
|
974
|
+
def test_LoggedAction_start_message(self):
|
|
975
|
+
"""
|
|
976
|
+
L{LoggedAction.start_message} is the same as
|
|
977
|
+
L{LoggedAction.startMessage}.
|
|
978
|
+
"""
|
|
979
|
+
action = LoggedAction({1: 2}, {3: 4}, [])
|
|
980
|
+
self.assertEqual(action.start_message, action.startMessage)
|
|
981
|
+
|
|
982
|
+
def test_LoggedMessage_of_type(self):
|
|
983
|
+
"""
|
|
984
|
+
L{LoggedMessage.of_type} is the same as
|
|
985
|
+
L{LoggedMessage.ofType}.
|
|
986
|
+
"""
|
|
987
|
+
self.assertEqual(LoggedMessage.of_type, LoggedMessage.ofType)
|
|
988
|
+
|
|
989
|
+
def test_validate_logging(self):
|
|
990
|
+
"""
|
|
991
|
+
L{validate_logging} is the same as L{validateLogging}.
|
|
992
|
+
"""
|
|
993
|
+
self.assertEqual(validate_logging, validateLogging)
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
class LowLevelTestingHooks(TestCase):
|
|
997
|
+
"""Tests for lower-level APIs for setting up MemoryLogger."""
|
|
998
|
+
|
|
999
|
+
@capture_logging(None)
|
|
1000
|
+
def test_swap_logger(self, logger):
|
|
1001
|
+
"""C{swap_logger} swaps out the current logger."""
|
|
1002
|
+
new_logger = MemoryLogger()
|
|
1003
|
+
old_logger = swap_logger(new_logger)
|
|
1004
|
+
Message.log(message_type="hello")
|
|
1005
|
+
|
|
1006
|
+
# We swapped out old logger for new:
|
|
1007
|
+
self.assertIs(old_logger, logger)
|
|
1008
|
+
self.assertEqual(new_logger.messages[0]["message_type"], "hello")
|
|
1009
|
+
|
|
1010
|
+
# Now restore old logger:
|
|
1011
|
+
intermediate_logger = swap_logger(old_logger)
|
|
1012
|
+
Message.log(message_type="goodbye")
|
|
1013
|
+
self.assertIs(intermediate_logger, new_logger)
|
|
1014
|
+
self.assertEqual(logger.messages[0]["message_type"], "goodbye")
|
|
1015
|
+
|
|
1016
|
+
def test_check_for_errors_unflushed_tracebacks(self):
|
|
1017
|
+
"""C{check_for_errors} raises on unflushed tracebacks."""
|
|
1018
|
+
logger = MemoryLogger()
|
|
1019
|
+
|
|
1020
|
+
# No errors initially:
|
|
1021
|
+
check_for_errors(logger)
|
|
1022
|
+
|
|
1023
|
+
try:
|
|
1024
|
+
1 / 0
|
|
1025
|
+
except ZeroDivisionError:
|
|
1026
|
+
write_traceback(logger)
|
|
1027
|
+
logger.flush_tracebacks(ZeroDivisionError)
|
|
1028
|
+
|
|
1029
|
+
# Flushed tracebacks don't count:
|
|
1030
|
+
check_for_errors(logger)
|
|
1031
|
+
|
|
1032
|
+
# But unflushed tracebacks do:
|
|
1033
|
+
try:
|
|
1034
|
+
raise RuntimeError
|
|
1035
|
+
except RuntimeError:
|
|
1036
|
+
write_traceback(logger)
|
|
1037
|
+
with self.assertRaises(UnflushedTracebacks):
|
|
1038
|
+
check_for_errors(logger)
|
|
1039
|
+
|
|
1040
|
+
def test_check_for_errors_validation(self):
|
|
1041
|
+
"""C{check_for_errors} raises on validation errors."""
|
|
1042
|
+
logger = MemoryLogger()
|
|
1043
|
+
logger.write({"x": 1, "message_type": "mem"})
|
|
1044
|
+
|
|
1045
|
+
# No errors:
|
|
1046
|
+
check_for_errors(logger)
|
|
1047
|
+
|
|
1048
|
+
# Now long something unserializable to JSON:
|
|
1049
|
+
logger.write({"message_type": object()})
|
|
1050
|
+
with self.assertRaises(TypeError):
|
|
1051
|
+
check_for_errors(logger)
|