Merge lp:~doxxx/bzr/quiet-serve into lp:bzr

Proposed by Gordon Tyler
Status: Merged
Merged at revision: not available
Proposed branch: lp:~doxxx/bzr/quiet-serve
Merge into: lp:bzr
Diff against target: 158 lines (+68/-43)
2 files modified
bzrlib/tests/__init__.py (+16/-4)
bzrlib/tests/blackbox/test_serve.py (+52/-39)
To merge this branch: bzr merge lp:~doxxx/bzr/quiet-serve
Reviewer Review Type Date Requested Status
Vincent Ladeuil Needs Fixing
Robert Collins (community) Approve
Review via email: mp+15112@code.launchpad.net

This proposal supersedes a proposal from 2009-11-20.

To post a comment you must log in.
Revision history for this message
Gordon Tyler (doxxx) wrote : Posted in a previous version of this proposal

Adds a testcase for bug #252834. The actual bug seems to have been fixed some time ago, but I figured a testcase couldn't hurt.

Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

Thanks for submitting this. We really prefer to test things close to the
thing under question.

In this particular case, why is a subprocess needed?

 review needsfixing

review: Needs Fixing
Revision history for this message
Gordon Tyler (doxxx) wrote : Posted in a previous version of this proposal

I'm not sure, I just followed the example of the other tests in that class. Perhaps it's because if you started it in-process there would be no way to interrupt it and it would hang the tests?

Revision history for this message
John A Meinel (jameinel) wrote : Posted in a previous version of this proposal

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Gordon Tyler wrote:
> I'm not sure, I just followed the example of the other tests in that class. Perhaps it's because if you started it in-process there would be no way to interrupt it and it would hang the tests?

Also note that if you are using a subprocess that you are going to kill,
then you need to skip the test on windows so it doesn't hang. (At least
until a patch comes along that can use TerminateProcess :)

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAksGsZIACgkQJdeBCYSNAAMGlgCdF0kYZpWNZ3DN1H/q6Xp4ZiSS
f9sAoLFJNgWPdpKRaZuCHQoex7al6UMH
=kvJ+
-----END PGP SIGNATURE-----

Revision history for this message
Gordon Tyler (doxxx) wrote : Posted in a previous version of this proposal

> Also note that if you are using a subprocess that you are going to kill,
> then you need to skip the test on windows so it doesn't hang. (At least
> until a patch comes along that can use TerminateProcess :)

Note that it says "skip_if_plan_to_signal=True", which means skip on win32 until my other patch lands. ;)

Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

On Fri, 2009-11-20 at 13:02 +0000, Gordon Tyler wrote:
> I'm not sure, I just followed the example of the other tests in that
> class. Perhaps it's because if you started it in-process there would
> be no way to interrupt it and it would hang the tests?

So, you cargo culted, which is fine :). However this test doesn't need a
subprocess. The other ones did for a few reasons, but I don't see why
this one would.

subprocess tests are very expensive, its an exceptional thing to do to
add one.

-Rob

Revision history for this message
Gordon Tyler (doxxx) wrote : Posted in a previous version of this proposal

> So, you cargo culted, which is fine :). However this test doesn't need a
> subprocess. The other ones did for a few reasons, but I don't see why
> this one would.

Can you explain to me why this test doesn't require one? I cannot see a difference between this and the other tests. If the test invokes 'bzr serve' in-process it will block and the tests will hang.

Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

A couple of ways to prevent that
 - use a server startup hook to trigger shutdown.
 - use a thread

-Rob

Revision history for this message
Gordon Tyler (doxxx) wrote : Posted in a previous version of this proposal

> A couple of ways to prevent that
> - use a server startup hook to trigger shutdown.
> - use a thread

Well, silly me, I didn't see the utility functions in TestCmdServeChrooting which do all that. I'll adapt the new testcase to use that.

On a related note, why do the other tests in TestBzrServe launch a subprocess and not do the same thing as TestCmdServeChrooting?

Revision history for this message
Gordon Tyler (doxxx) wrote : Posted in a previous version of this proposal

I've been able to adapt the testcase to run the command in-process. I had to get inventive with the KeyboardInterrupt handling so that the testcase could still get the stdout and stderr.

Revision history for this message
Gordon Tyler (doxxx) wrote :

Adds a testcase for bug #252834. The actual bug seems to have been fixed some time ago, but I figured a testcase couldn't hurt.

I've been able to adapt the testcase since the previous merge proposal to run the command in-process. I had to get inventive with the KeyboardInterrupt handling so that the testcase could still get the stdout and stderr.

Revision history for this message
Robert Collins (lifeless) wrote :

The change to setUp is wrong - has to be fixed. This can be done as its merged.

review: Approve
Revision history for this message
Gordon Tyler (doxxx) wrote :

Sorry, still not used to Python's super call. Fixed and pushed.

Revision history for this message
Robert Collins (lifeless) wrote :

I'll shepard this through. At this point we still need a second review.

Revision history for this message
Vincent Ladeuil (vila) wrote :

Really tweaks:

+ class _TestBzrServeBase(TestCaseWithTransport):

We never use leading '_' for test classes, even if that may sound reasonable here.
I.e. I understand the intent, but let's not surprise future readers by starting
a new coding rule without good reason.

+ try:
+ self.run_bzr(['serve'] + list(serve_args))
+ except KeyboardInterrupt as e:
+ return e.args

1) I doubt python2.4 will accept 'as'.

2) While it's understandable in this patch context that e.args is really stdout, stderr, it will quickly become obscure. A comment will address that.

3) If the exception is raised, None will be returned instead of the (stdout, stderr) tuple. Again, today, the only that cares will pass, but if someone try to reuse the run_bzr_serve_then_func method, he will have a hard time. Please return (None, None) at least.

review: Needs Fixing
Revision history for this message
Gordon Tyler (doxxx) wrote :

Thanks for the suggestions. You're quite right about the stdout, stderr tuple. I've pushed an update which should fix both the clarity and the consistency of the return arguments, as well as the class name and Python 2.4 compatibility.

Revision history for this message
Vincent Ladeuil (vila) wrote :

Excellent, thanks, I'll merge (just checked that all tests pass for python 2.[456]).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/tests/__init__.py'
--- bzrlib/tests/__init__.py 2009-11-24 08:28:41 +0000
+++ bzrlib/tests/__init__.py 2009-11-25 13:25:23 +0000
@@ -1836,10 +1836,22 @@
1836 os.chdir(working_dir)1836 os.chdir(working_dir)
18371837
1838 try:1838 try:
1839 result = self.apply_redirected(ui.ui_factory.stdin,1839 try:
1840 stdout, stderr,1840 result = self.apply_redirected(ui.ui_factory.stdin,
1841 bzrlib.commands.run_bzr_catch_user_errors,1841 stdout, stderr,
1842 args)1842 bzrlib.commands.run_bzr_catch_user_errors,
1843 args)
1844 except KeyboardInterrupt:
1845 # Reraise KeyboardInterrupt with contents of redirected stdout and
1846 # stderr as arguments, for tests which are interested in stdout and
1847 # stderr and are expecting the exception.
1848 out = stdout.getvalue()
1849 err = stderr.getvalue()
1850 if out:
1851 self.log('output:\n%r', out)
1852 if err:
1853 self.log('errors:\n%r', err)
1854 raise KeyboardInterrupt(out, err)
1843 finally:1855 finally:
1844 logger.removeHandler(handler)1856 logger.removeHandler(handler)
1845 ui.ui_factory = old_ui_factory1857 ui.ui_factory = old_ui_factory
18461858
=== modified file 'bzrlib/tests/blackbox/test_serve.py'
--- bzrlib/tests/blackbox/test_serve.py 2009-11-16 21:36:52 +0000
+++ bzrlib/tests/blackbox/test_serve.py 2009-11-25 13:25:23 +0000
@@ -45,8 +45,49 @@
45from bzrlib.trace import mutter45from bzrlib.trace import mutter
46from bzrlib.transport import get_transport, remote46from bzrlib.transport import get_transport, remote
4747
4848class TestBzrServeBase(TestCaseWithTransport):
49class TestBzrServe(TestCaseWithTransport):49
50 def run_bzr_serve_then_func(self, serve_args, retcode=0, func=None,
51 *func_args, **func_kwargs):
52 """Run 'bzr serve', and run the given func in a thread once the server
53 has started.
54
55 When 'func' terminates, the server will be terminated too.
56
57 Returns stdout and stderr.
58 """
59 # install hook
60 def on_server_start(backing_urls, tcp_server):
61 t = threading.Thread(
62 target=on_server_start_thread, args=(tcp_server,))
63 t.start()
64 def on_server_start_thread(tcp_server):
65 try:
66 # Run func if set
67 self.tcp_server = tcp_server
68 if not func is None:
69 try:
70 func(*func_args, **func_kwargs)
71 except Exception, e:
72 # Log errors to make some test failures a little less
73 # mysterious.
74 mutter('func broke: %r', e)
75 finally:
76 # Then stop the server
77 mutter('interrupting...')
78 thread.interrupt_main()
79 SmartTCPServer.hooks.install_named_hook(
80 'server_started_ex', on_server_start,
81 'run_bzr_serve_then_func hook')
82 # start a TCP server
83 try:
84 out, err = self.run_bzr(['serve'] + list(serve_args))
85 except KeyboardInterrupt, e:
86 out, err = e.args
87 return out, err
88
89
90class TestBzrServe(TestBzrServeBase):
5091
51 def setUp(self):92 def setUp(self):
52 super(TestBzrServe, self).setUp()93 super(TestBzrServe, self).setUp()
@@ -119,6 +160,13 @@
119 url = 'bzr://localhost:%d/' % port160 url = 'bzr://localhost:%d/' % port
120 self.permit_url(url)161 self.permit_url(url)
121 return process, url162 return process, url
163
164 def test_bzr_serve_quiet(self):
165 self.make_branch('.')
166 args = ['--port', 'localhost:0', '--quiet']
167 out, err = self.run_bzr_serve_then_func(args, retcode=3)
168 self.assertEqual('', out)
169 self.assertEqual('', err)
122170
123 def test_bzr_serve_inet_readonly(self):171 def test_bzr_serve_inet_readonly(self):
124 """bzr server should provide a read only filesystem by default."""172 """bzr server should provide a read only filesystem by default."""
@@ -168,7 +216,7 @@
168 self.assertServerFinishesCleanly(process)216 self.assertServerFinishesCleanly(process)
169217
170218
171class TestCmdServeChrooting(TestCaseWithTransport):219class TestCmdServeChrooting(TestBzrServeBase):
172220
173 def test_serve_tcp(self):221 def test_serve_tcp(self):
174 """'bzr serve' wraps the given --directory in a ChrootServer.222 """'bzr serve' wraps the given --directory in a ChrootServer.
@@ -183,47 +231,12 @@
183 ['--port', '127.0.0.1:0',231 ['--port', '127.0.0.1:0',
184 '--directory', t.local_abspath('server-root'),232 '--directory', t.local_abspath('server-root'),
185 '--allow-writes'],233 '--allow-writes'],
186 self.when_server_started)234 func=self.when_server_started)
187 # The when_server_started method issued a find_repositoryV3 that should235 # The when_server_started method issued a find_repositoryV3 that should
188 # fail with 'norepository' because there are no repositories inside the236 # fail with 'norepository' because there are no repositories inside the
189 # --directory.237 # --directory.
190 self.assertEqual(('norepository',), self.client_resp)238 self.assertEqual(('norepository',), self.client_resp)
191239
192 def run_bzr_serve_then_func(self, serve_args, func, *func_args,
193 **func_kwargs):
194 """Run 'bzr serve', and run the given func in a thread once the server
195 has started.
196
197 When 'func' terminates, the server will be terminated too.
198 """
199 # install hook
200 def on_server_start(backing_urls, tcp_server):
201 t = threading.Thread(
202 target=on_server_start_thread, args=(tcp_server,))
203 t.start()
204 def on_server_start_thread(tcp_server):
205 try:
206 # Run func
207 self.tcp_server = tcp_server
208 try:
209 func(*func_args, **func_kwargs)
210 except Exception, e:
211 # Log errors to make some test failures a little less
212 # mysterious.
213 mutter('func broke: %r', e)
214 finally:
215 # Then stop the server
216 mutter('interrupting...')
217 thread.interrupt_main()
218 SmartTCPServer.hooks.install_named_hook(
219 'server_started_ex', on_server_start,
220 'run_bzr_serve_then_func hook')
221 # start a TCP server
222 try:
223 self.run_bzr(['serve'] + list(serve_args))
224 except KeyboardInterrupt:
225 pass
226
227 def when_server_started(self):240 def when_server_started(self):
228 # Connect to the TCP server and issue some requests and see what comes241 # Connect to the TCP server and issue some requests and see what comes
229 # back.242 # back.