Merge lp:~jimbaker/pyjuju/standardize-log-testing into lp:pyjuju

Proposed by Jim Baker
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 258
Merged at revision: 258
Proposed branch: lp:~jimbaker/pyjuju/standardize-log-testing
Merge into: lp:pyjuju
Diff against target: 557 lines (+62/-166)
7 files modified
ensemble/control/tests/test_status.py (+5/-5)
ensemble/hooks/invoker.py (+1/-1)
ensemble/hooks/tests/test_arguments.py (+2/-5)
ensemble/hooks/tests/test_cli.py (+7/-12)
ensemble/hooks/tests/test_communications.py (+12/-13)
ensemble/hooks/tests/test_invoker.py (+35/-49)
ensemble/lib/testing.py (+0/-81)
To merge this branch: bzr merge lp:~jimbaker/pyjuju/standardize-log-testing
Reviewer Review Type Date Requested Status
Kapil Thangavelu (community) Approve
Gustavo Niemeyer Approve
Review via email: mp+64568@code.launchpad.net

Description of the change

This branch standardizes the log testing as seen in hook tests and
test_status and makes it more robust as follows:

 * Removed usage and definition of save_logging, reset_logging, and
   assertInDefaultLog from codebase in favor of the standard usage of
   capture_logging and assertIn, including the use of custom
   formatters as appropriate. It seems like that the use of
   save_logging and related functions was to ensure that any changes
   in formatting or log levels for a log would not interfere with
   other tests. However, capture_logging fully ensures the
   independence of such operations from test to test.

 * Refactored test_status to use our cooperative inheritance
   convention.

 * HookProtocol now uses processEnded to ensure that output streams
   are ready for reading. This was the problem that prompted the
   investigation to begin with: why are some hook tests not able to
   loop properly. This change fixes this problem.

 * Removed unnecessary test timeout override in client-server
   communications tests. It's not clear why a smaller timeout here vs
   in any other place is useful.

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

This looks like a nice clean up, thanks Jim.

One question:

[1]

8 - yield ServiceStateManagerTestBase.setUp(self)
9 - yield ControlToolTest.setUp(self)
10 - self.save_logging()
11 + yield super(StatusTestBase, self).setUp()
12 + self.log = self.capture_logging()

Have you checked that _all_ of the base classes conform to this protocol, yielding on
every call to setUp? If one does not, we'll have issues.

review: Needs Information
Revision history for this message
Jim Baker (jimbaker) wrote :

On Tue, Jun 14, 2011 at 1:44 PM, Gustavo Niemeyer <email address hidden>wrote:

> Review: Needs Information
> This looks like a nice clean up, thanks Jim.
>
> One question:
>
> [1]
>
> 8 - yield ServiceStateManagerTestBase.setUp(self)
> 9 - yield ControlToolTest.setUp(self)
> 10 - self.save_logging()
> 11 + yield super(StatusTestBase, self).setUp()
> 12 + self.log = self.capture_logging()
>
> Have you checked that _all_ of the base classes conform to this protocol,
> yielding on
> every call to setUp? If one does not, we'll have issues.
>

I have rechecked that, and yes, they all conform to the cooperative
inheritance protocol. This was addressed in r120 in trunk for all ancestor
classes here. We may want to scan other test classes to see if there's been
other slippage since then, of course with any possible fixes in a different
branch.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

LGTM then, +1!

review: Approve
Revision history for this message
Jim Baker (jimbaker) wrote :

Some things to add, especially with respect to our new weekly report:

 * Removing the non-standardized log testing initially created more
   timing issues around test teardown - certain tests would now
   routinely fail. Further investigation revealed that this was
   because of the use of processExit instead of processEnded. However,
   I didn't look into detail as to why the removed log testing code
   was obscuring this problem; perhaps, it was due to the extra
   synchronization around the logging lock or some other aspect seen
   in saving and restoring settings.

 * Ensuring the output was ready before reading actually fixed a
   user-visible bug. In the previous version of the code, it was
   possible that output from hook invocation would potentially be
   lost.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Putting comments in a merge proposal is not enough for Ahmed to note it in the
weekly report. The technical lingo also has to be tamed for it to make sense
for the report. I barely understand what you mean, let alone a user who's
interested in managing formulas.

Revision history for this message
Jim Baker (jimbaker) wrote :

Oh sure, I don't expect Ahmed to pick it up from this merge proposal
discussion, or that it is even in a form that is suitable for such a report
for formula writers. However, the bug was initiated in part to do some sort
of reporting back to a mailing list. So if this is of interest, I can shape
the writing of these conclusions into an appropriate form for such a report.
This can also help address the theme that this development phase is, in
part, working on addressing technical debt.

On the other hand, this might be considered too obscure for a general
audience (and not even that interesting, except to a narrow group of
Twisted-loving developers). Ordinarily that would be my thought on a set of
bug fixes like this.

Please tell me what you think works best!

On Tue, Jun 14, 2011 at 3:18 PM, Gustavo Niemeyer <email address hidden>wrote:

> Putting comments in a merge proposal is not enough for Ahmed to note it in
> the
> weekly report. The technical lingo also has to be tamed for it to make
> sense
> for the report. I barely understand what you mean, let alone a user who's
> interested in managing formulas.
> --
>
> https://code.launchpad.net/~jimbaker/ensemble/standardize-log-testing/+merge/64568<https://code.launchpad.net/%7Ejimbaker/ensemble/standardize-log-testing/+merge/64568>
> You are the owner of lp:~jimbaker/ensemble/standardize-log-testing.
>

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

This looks good to me, thanks for looking into this, one comment though.

+ yield self.sleep(1)

looks suspicious. does it really need to sleep for a whole second?

review: Approve
Revision history for this message
Jim Baker (jimbaker) wrote :

Yikes, that was from my debugging the timing issues. About to be removed!

On Wed, Jun 15, 2011 at 4:26 PM, Kapil Thangavelu <
<email address hidden>> wrote:

> Review: Approve
> This looks good to me, thanks for looking into this, one comment though.
>
> + yield self.sleep(1)
>
> looks suspicious. does it really need to sleep for a whole second?
> --
>
> https://code.launchpad.net/~jimbaker/ensemble/standardize-log-testing/+merge/64568<https://code.launchpad.net/%7Ejimbaker/ensemble/standardize-log-testing/+merge/64568>
> You are the owner of lp:~jimbaker/ensemble/standardize-log-testing.
>

259. By Jim Baker

Removed spurious sleep (shouldn't have been part of push)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ensemble/control/tests/test_status.py'
2--- ensemble/control/tests/test_status.py 2011-04-18 17:06:34 +0000
3+++ ensemble/control/tests/test_status.py 2011-06-15 22:43:24 +0000
4@@ -24,9 +24,8 @@
5
6 @inlineCallbacks
7 def setUp(self):
8- yield ServiceStateManagerTestBase.setUp(self)
9- yield ControlToolTest.setUp(self)
10- self.save_logging()
11+ yield super(StatusTestBase, self).setUp()
12+ self.log = self.capture_logging()
13
14 config = {
15 "ensemble": "environments",
16@@ -385,8 +384,9 @@
17
18 yield status.collect(
19 None, self.provider, self.client, logging.getLogger())
20- self.assertInDefaultLog(
21- "Machine provider information missing: machine 7")
22+ self.assertIn(
23+ "Machine provider information missing: machine 7",
24+ self.log.getvalue())
25
26 @inlineCallbacks
27 def test_render_yaml(self):
28
29=== modified file 'ensemble/hooks/invoker.py'
30--- ensemble/hooks/invoker.py 2011-04-04 15:04:50 +0000
31+++ ensemble/hooks/invoker.py 2011-06-15 22:43:24 +0000
32@@ -28,7 +28,7 @@
33 def errReceived(self, data):
34 self._log.error(data)
35
36- def processExited(self, reason):
37+ def processEnded(self, reason):
38 exit_code = reason.value.exitCode
39 if exit_code in (0, None):
40 self.deferred.callback(exit_code)
41
42=== modified file 'ensemble/hooks/tests/test_arguments.py'
43--- ensemble/hooks/tests/test_arguments.py 2011-06-01 17:43:29 +0000
44+++ ensemble/hooks/tests/test_arguments.py 2011-06-15 22:43:24 +0000
45@@ -34,9 +34,6 @@
46 verify that the socket argument is accepted from a command
47 line flag, or the environment or raises an error.
48 """
49- self.save_logging()
50- self.reset_logging()
51-
52 self.setup_environment()
53 os.environ.pop("ENSEMBLE_AGENT_SOCKET", None)
54
55@@ -57,8 +54,8 @@
56 error = self.failUnlessRaises(SystemExit, cli.parse_args)
57 self.assertEquals("No ENSEMBLE_AGENT_SOCKET/-s option found",
58 str(error))
59- self.assertInDefaultLog("No ENSEMBLE_AGENT_SOCKET/-s option found",
60- err)
61+ self.assertIn("No ENSEMBLE_AGENT_SOCKET/-s option found",
62+ err.getvalue())
63
64 def test_single_keyvalue(self):
65 """
66
67=== modified file 'ensemble/hooks/tests/test_cli.py'
68--- ensemble/hooks/tests/test_cli.py 2011-06-09 17:19:26 +0000
69+++ ensemble/hooks/tests/test_cli.py 2011-06-15 22:43:24 +0000
70@@ -234,7 +234,7 @@
71 error = self.failUnlessRaises(SystemExit, cli,
72 "test_unit foobar".split())
73 self.assertEquals(error.code, 2)
74- self.assertInDefaultLog("No ENSEMBLE_AGENT_SOCKET", error_log)
75+ self.assertIn("No ENSEMBLE_AGENT_SOCKET", error_log.getvalue())
76
77 def test_cli_no_client_id(self):
78 # don't set up the environment with a socket
79@@ -252,13 +252,11 @@
80 error = self.failUnlessRaises(SystemExit, cli,
81 "test_unit foobar".split())
82 self.assertEquals(error.code, 2)
83- self.assertInDefaultLog("No ENSEMBLE_CLIENT_ID", error_log)
84+ self.assertIn("No ENSEMBLE_CLIENT_ID", error_log.getvalue())
85
86 def test_log_level(self):
87 self.setup_environment()
88 self.change_args(__file__)
89- self.save_logging()
90- self.reset_logging()
91
92 cli = GetCli()
93 cli.manage_connection = False
94@@ -266,9 +264,10 @@
95 self.mocker.replay()
96
97 # bad log level
98+ log = self.capture_logging()
99 cli.setup_parser()
100 cli.parse_args("--log-level XYZZY test_unit".split())
101- self.assertInDefaultLog("Invalid log level")
102+ self.assertIn("Invalid log level", log.getvalue())
103 # still get a default
104 self.assertEqual(cli.options.log_level, logging.INFO)
105
106@@ -283,8 +282,6 @@
107 def test_log_format(self):
108 self.setup_environment()
109 self.change_args(__file__)
110- self.save_logging()
111- self.reset_logging()
112
113 cli = NoopCli()
114
115@@ -301,14 +298,12 @@
116 self.setup_exit(0)
117 self.mocker.replay()
118 cli("--format missing".split())
119- self.assertInDefaultLog("missing", err)
120- self.assertInDefaultLog("Namespace", out)
121+ self.assertIn("missing", err.getvalue())
122+ self.assertIn("Namespace", out.getvalue())
123
124 def test_render_error(self):
125 self.setup_environment()
126 self.change_args(__file__)
127- self.save_logging()
128- self.reset_logging()
129
130 cli = ErrorCli()
131 # bad log level
132@@ -318,7 +313,7 @@
133 self.mocker.replay()
134 cli("")
135 # make sure we got a traceback on stderr
136- self.assertInDefaultLog("Checking render error", err)
137+ self.assertIn("Checking render error", err.getvalue())
138
139 def test_parse_log_level(self):
140 self.assertEquals(parse_log_level("INFO"), logging.INFO)
141
142=== modified file 'ensemble/hooks/tests/test_communications.py'
143--- ensemble/hooks/tests/test_communications.py 2011-06-04 03:24:31 +0000
144+++ ensemble/hooks/tests/test_communications.py 2011-06-15 22:43:24 +0000
145@@ -191,9 +191,13 @@
146 """
147 client_protocol = UnitAgentClient
148 server_protocol = UnitAgentServer
149- # short timeout, these are not long running requests when
150- # correct
151- timeout = 1.0
152+
153+ @defer.inlineCallbacks
154+ def setUp(self):
155+ yield super(TestCommunications, self).setUp()
156+ self.log = self.capture_logging(
157+ level=logging.DEBUG,
158+ formatter=logging.Formatter("%(levelname)s %(message)s"))
159
160 @defer.inlineCallbacks
161 def test_relation_get_command(self):
162@@ -252,18 +256,14 @@
163
164 @defer.inlineCallbacks
165 def test_log_command(self):
166- self.save_logging()
167 # This is the default calling convention from clients
168 yield self.client.log(logging.WARNING, ["This", "is", "a", "WARNING"])
169 yield self.client.log(logging.INFO, "This is INFO")
170 yield self.client.log(logging.CRITICAL, ["This is CRITICAL"])
171
172- self.assertInDefaultLog("WARNING This is a WARNING",
173- self.server_factory._agent_io)
174- self.assertInDefaultLog("INFO This is INFO",
175- self.server_factory._agent_io)
176- self.assertInDefaultLog("CRITICAL This is CRITICAL",
177- self.server_factory._agent_io)
178+ self.assertIn("WARNING This is a WARNING", self.log.getvalue())
179+ self.assertIn("INFO This is INFO", self.log.getvalue())
180+ self.assertIn("CRITICAL This is CRITICAL", self.log.getvalue())
181
182 @defer.inlineCallbacks
183 def test_config_get_command(self):
184@@ -299,7 +299,6 @@
185
186 @defer.inlineCallbacks
187 def test_port_commands(self):
188- self.save_logging()
189 mock_service_unit_state = MockServiceState().get_unit_state("mock/0")
190 yield self.client.open_port("client-id", 80, "tcp")
191 self.assertEqual(mock_service_unit_state.ports, set([(80, "tcp")]))
192@@ -308,9 +307,9 @@
193 self.assertEqual(mock_service_unit_state.ports, set([(53, "udp")]))
194 yield self.client.close_port("client-id", 53, "udp")
195 self.assertEqual(mock_service_unit_state.ports, set())
196- self.assertInDefaultLog(
197+ self.assertIn(
198 "DEBUG opened 80/tcp\n"
199 "DEBUG opened 53/udp\n"
200 "DEBUG closed 80/tcp\n"
201 "DEBUG closed 53/udp\n",
202- self.server_factory._agent_io)
203+ self.log.getvalue())
204
205=== modified file 'ensemble/hooks/tests/test_invoker.py'
206--- ensemble/hooks/tests/test_invoker.py 2011-06-04 03:24:31 +0000
207+++ ensemble/hooks/tests/test_invoker.py 2011-06-15 22:43:24 +0000
208@@ -8,6 +8,7 @@
209 import yaml
210
211 from twisted.internet import defer
212+from twisted.python.log import PythonLoggingObserver
213
214 import ensemble
215 from ensemble import errors
216@@ -318,6 +319,10 @@
217 self.update_invoker_env("mysql/0", "wordpress/0")
218 self.socket_path = self.makeFile()
219 self.ua = MockUnitAgent(self.client, self.socket_path)
220+ self.log = self.capture_logging(
221+ formatter=logging.Formatter(
222+ "%(name)s:%(levelname)s:: %(message)s"),
223+ level=logging.DEBUG)
224
225 @defer.inlineCallbacks
226 def tearDown(self):
227@@ -358,14 +363,10 @@
228 self.assertEqual(env["ENSEMBLE_UNIT_NAME"], "mysql/0")
229
230 def test_missing_hook(self):
231- self.save_logging()
232-
233 exe = self.ua.get_invoker("database", "add", "mysql/0", self.relation)
234 self.failUnlessRaises(errors.FileNotFound, exe, "hook-missing")
235
236 def test_noexec_hook(self):
237- self.save_logging()
238-
239 exe = self.ua.get_invoker("database", "add", "mysql/0",
240 self.relation)
241 hook = self.get_test_hook("noexec-hook")
242@@ -377,29 +378,25 @@
243 @defer.inlineCallbacks
244 def test_spawn_success(self):
245 """Validate hook with success from exit."""
246- self.save_logging()
247- self.reset_logging()
248 exe = self.ua.get_invoker("database", "add", "mysql/0", self.relation)
249 result = yield exe(self.get_test_hook("success-hook"))
250 self.assertEqual(result, 0)
251- self.assertInDefaultLog("WIN")
252- self.assertInDefaultLog("exit code 0")
253+ self.assertIn("WIN", self.log.getvalue())
254+ self.assertIn("exit code 0", self.log.getvalue())
255
256 @defer.inlineCallbacks
257 def test_spawn_fail(self):
258 """Validate hook with fail from exit."""
259- self.save_logging()
260- self.reset_logging()
261 exe = self.ua.get_invoker("database", "add", "mysql/0", self.relation)
262 d = exe(self.get_test_hook("fail-hook"))
263 result = yield self.assertFailure(d, errors.FormulaInvocationError)
264 self.assertEqual(result.exit_code, 1)
265 # ERROR indicate the level name, we are checking that the
266 # proper level was logged here
267- self.assertInDefaultLog("ERROR")
268+ self.assertIn("ERROR", self.log.getvalue())
269 # and the message
270- self.assertInDefaultLog("FAIL")
271- self.assertInDefaultLog("exit code 1")
272+ self.assertIn("FAIL", self.log.getvalue())
273+ self.assertIn("exit code 1", self.log.getvalue())
274
275 def test_path_setup(self):
276 """Validate that the path allows finding the executable."""
277@@ -460,8 +457,6 @@
278
279 def test_get_remote_unit_arg(self):
280 """Simple test around remote arg parsing."""
281- self.save_logging()
282-
283 self.change_environment(ENSEMBLE_UNIT_NAME="mysql/0",
284 ENSEMBLE_CLIENT_ID="client_id",
285 ENSEMBLE_AGENT_SOCKET=self.socket_path)
286@@ -516,8 +511,6 @@
287
288 @defer.inlineCallbacks
289 def test_logging(self):
290- self.save_logging()
291- self.reset_logging()
292 exe = self.ua.get_invoker("database", "add", "mysql/0", self.relation)
293
294 # The echo hook will echo out the value
295@@ -533,21 +526,19 @@
296 result = yield exe(self.get_test_hook("echo-hook"))
297 self.assertEqual(result, 0)
298
299- self.assertInDefaultLog(message)
300+ self.assertIn(message, self.log.getvalue())
301 # The 'error' was sent via ensemble-log
302 # to the UA. Our test UA has a fake log stream
303 # which we can check now
304 output = self.ua.log_file.getvalue()
305- self.assertInDefaultLog("ERROR:: " + error)
306- self.assertInDefaultLog("INFO:: " + default)
307+ self.assertIn("ERROR:: " + error, self.log.getvalue())
308+ self.assertIn("INFO:: " + default, self.log.getvalue())
309
310 assert message not in output, """Log includes unintended messages"""
311
312 @defer.inlineCallbacks
313 def test_spawn_cli_list_hook_smart(self):
314 """Validate the get hook can transmit values to the hook."""
315- self.save_logging()
316- self.reset_logging()
317 exe = self.ua.get_invoker("database", "add", "mysql/0",
318 self.relation,
319 client_id="client_id")
320@@ -568,13 +559,11 @@
321 "--format=smart"))
322
323 self.assertEqual(result, 0)
324- self.assertInDefaultLog("alpha/0\nbeta/0\n")
325+ self.assertIn("alpha/0\nbeta/0\n", self.log.getvalue())
326
327 @defer.inlineCallbacks
328 def test_spawn_cli_list_hook_eval(self):
329 """Validate the get hook can transmit values to the hook."""
330- self.save_logging()
331- self.reset_logging()
332 exe = self.ua.get_invoker("database", "add", "mysql/0",
333 self.relation,
334 client_id="client_id")
335@@ -594,13 +583,11 @@
336 "--format=eval"))
337
338 self.assertEqual(result, 0)
339- self.assertInDefaultLog("alpha/0 beta/0")
340+ self.assertIn("alpha/0 beta/0", self.log.getvalue())
341
342 @defer.inlineCallbacks
343 def test_spawn_cli_list_hook_json(self):
344 """Validate the get hook can transmit values to the hook."""
345- self.save_logging()
346- self.reset_logging()
347 exe = self.ua.get_invoker("database", "add", "mysql/0",
348 self.relation,
349 client_id="client_id")
350@@ -619,14 +606,14 @@
351 result = yield exe(self.create_hook("relation-list", "--format json"))
352
353 self.assertEqual(result, 0)
354- self.assertInDefaultLog('["alpha/0", "beta/0"]')
355+ self.assertIn('["alpha/0", "beta/0"]', self.log.getvalue())
356
357
358 class PortCommandsTest(RelationInvokerTestBase):
359
360 def assertLogLines(self, expected):
361 """Asserts that the lines of `expected` exist in order in the log."""
362- logged = self.log_stream.getvalue().split("\n")
363+ logged = self.log.getvalue().split("\n")
364 expected = iter(expected)
365 for line in logged:
366 expected, peekat = itertools.tee(expected)
367@@ -651,9 +638,6 @@
368 @defer.inlineCallbacks
369 def test_open_and_close_ports(self):
370 """Verify that port hook commands run and changes are immediate."""
371- self.save_logging()
372- self.reset_logging()
373-
374 unit_state = self.mysql_states["unit"]
375 self.assertEqual((yield unit_state.get_open_ports()), [])
376
377@@ -705,8 +689,6 @@
378 @defer.inlineCallbacks
379 def test_open_port_args(self):
380 """Verify that open-port properly reports arg parse errors."""
381- self.save_logging()
382- self.reset_logging()
383 unit_state = self.mysql_states["unit"]
384 exe = self.ua.get_invoker("database", "add", "mysql/0", self.relation)
385
386@@ -714,31 +696,32 @@
387 exe(self.create_hook("open-port", "80/invalid-protocol")),
388 errors.FormulaInvocationError)
389 self.assertEqual(result.exit_code, 2)
390- self.assertInDefaultLog(
391+ self.assertIn(
392 "open-port: error: argument PORT[/PROTOCOL]: "
393- "Invalid protocol, must be 'tcp' or 'udp', got 'invalid-protocol'")
394+ "Invalid protocol, must be 'tcp' or 'udp', got 'invalid-protocol'",
395+ self.log.getvalue())
396
397 result = yield self.assertFailure(
398 exe(self.create_hook("open-port", "0/tcp")),
399 errors.FormulaInvocationError)
400 self.assertEqual(result.exit_code, 2)
401- self.assertInDefaultLog(
402+ self.assertIn(
403 "open-port: error: argument PORT[/PROTOCOL]: "
404- "Invalid port, must be from 1 to 65535, got 0")
405+ "Invalid port, must be from 1 to 65535, got 0",
406+ self.log.getvalue())
407
408 result = yield self.assertFailure(
409 exe(self.create_hook("open-port", "80/udp/extra-info")),
410 errors.FormulaInvocationError)
411 self.assertEqual(result.exit_code, 2)
412- self.assertInDefaultLog(
413+ self.assertIn(
414 "open-port: error: argument PORT[/PROTOCOL]: "
415- "Invalid format for port/protocol, got '80/udp/extra-info")
416+ "Invalid format for port/protocol, got '80/udp/extra-info",
417+ self.log.getvalue())
418
419 @defer.inlineCallbacks
420 def test_close_port_args(self):
421 """Verify that close-port properly reports arg parse errors."""
422- self.save_logging()
423- self.reset_logging()
424 unit_state = self.mysql_states["unit"]
425 exe = self.ua.get_invoker("database", "add", "mysql/0", self.relation)
426
427@@ -746,22 +729,25 @@
428 exe(self.create_hook("close-port", "80/invalid-protocol")),
429 errors.FormulaInvocationError)
430 self.assertEqual(result.exit_code, 2)
431- self.assertInDefaultLog(
432+ self.assertIn(
433 "close-port: error: argument PORT[/PROTOCOL]: "
434- "Invalid protocol, must be 'tcp' or 'udp', got 'invalid-protocol'")
435+ "Invalid protocol, must be 'tcp' or 'udp', got 'invalid-protocol'",
436+ self.log.getvalue())
437
438 result = yield self.assertFailure(
439 exe(self.create_hook("close-port", "0/tcp")),
440 errors.FormulaInvocationError)
441 self.assertEqual(result.exit_code, 2)
442- self.assertInDefaultLog(
443+ self.assertIn(
444 "close-port: error: argument PORT[/PROTOCOL]: "
445- "Invalid port, must be from 1 to 65535, got 0")
446+ "Invalid port, must be from 1 to 65535, got 0",
447+ self.log.getvalue())
448
449 result = yield self.assertFailure(
450 exe(self.create_hook("close-port", "80/udp/extra-info")),
451 errors.FormulaInvocationError)
452 self.assertEqual(result.exit_code, 2)
453- self.assertInDefaultLog(
454+ self.assertIn(
455 "close-port: error: argument PORT[/PROTOCOL]: "
456- "Invalid format for port/protocol, got '80/udp/extra-info")
457+ "Invalid format for port/protocol, got '80/udp/extra-info",
458+ self.log.getvalue())
459
460=== modified file 'ensemble/lib/testing.py'
461--- ensemble/lib/testing.py 2011-03-18 21:18:49 +0000
462+++ ensemble/lib/testing.py 2011-06-15 22:43:24 +0000
463@@ -6,7 +6,6 @@
464 from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
465 from twisted.internet import reactor
466 from twisted.trial.unittest import TestCase as TrialTestCase
467-from twisted.python.log import PythonLoggingObserver
468
469 from txzookeeper import ZookeeperClient
470
471@@ -55,86 +54,6 @@
472
473 return log_file
474
475- log_format = "%(name)s:%(levelname)s:: %(message)s"
476-
477- def save_logging(self):
478- """
479- Save stdlib's logging settings (which are kept in globals)
480- and restore them after a test run.
481- """
482- logger_dict = logging.getLogger().manager.loggerDict
483- logging._acquireLock()
484- try:
485- self.saved_handlers = logging._handlers.copy()
486- self.saved_handler_list = logging._handlerList[:]
487- self.saved_loggers = logger_dict.copy()
488- self.saved_level_names = logging._levelNames.copy()
489- finally:
490- logging._releaseLock()
491-
492- self.root_logger = logging.getLogger("")
493- self.original_logging_level = self.root_logger.getEffectiveLevel()
494-
495- self.log_stream = StringIO.StringIO()
496- self.root_logger.setLevel(logging.DEBUG)
497- self.root_hdlr = logging.StreamHandler(self.log_stream)
498- self.root_formatter = logging.Formatter(self.log_format)
499- self.root_hdlr.setFormatter(self.root_formatter)
500- self.root_logger.addHandler(self.root_hdlr)
501- self.log_observer = PythonLoggingObserver()
502- self.log_observer.start()
503-
504- @self.addCleanup
505- def restore_logging():
506- self.log_stream.close()
507- self.root_logger.removeHandler(self.root_hdlr)
508- while self.root_logger.handlers:
509- h = self.root_logger.handlers[0]
510- self.root_logger.removeHandler(h)
511- h.close()
512- self.root_logger.setLevel(self.original_logging_level)
513-
514- logging._acquireLock()
515- try:
516- logging._levelNames.clear()
517- logging._levelNames.update(self.saved_level_names)
518- logging._handlers.clear()
519- logging._handlers.update(self.saved_handlers)
520- logging._handlerList[:] = self.saved_handler_list
521- loggerDict = logging.getLogger().manager.loggerDict
522- loggerDict.clear()
523- loggerDict.update(self.saved_loggers)
524- finally:
525- logging._releaseLock()
526- self.log_observer.stop()
527-
528- def reset_logging(self):
529- """
530- reset all logging to a normalized state.
531- """
532-
533- logger_dict = logging.getLogger().manager.loggerDict
534- logging._acquireLock()
535- try:
536- logger_dict.clear()
537- logging._handlers.clear()
538- del logging._handlerList[:]
539- logging.getLogger().manager.loggerDict = {}
540- finally:
541- logging._releaseLock()
542-
543- def assertInDefaultLog(self, expected_value, iobuffer=None):
544- """
545- assertInDefaultLog(value, [iobuffer])
546-
547- used in conjunction with self.save_logging() this will test
548- for expected values in the default logging stream. An
549- alternative StringIO buffer may be passed as a second value.
550- """
551- stream = iobuffer or self.log_stream
552- content = stream.getvalue()
553- return self.assertIn(expected_value, content)
554-
555 _missing_attr = object()
556
557 def patch(self, object, attr, value):

Subscribers

People subscribed via source and target branches

to status/vote changes: