Merge lp:~lifeless/bzr/test-speed into lp:~bzr/bzr/trunk-old

Proposed by Robert Collins
Status: Merged
Merge reported by: Robert Collins
Merged at revision: not available
Proposed branch: lp:~lifeless/bzr/test-speed
Merge into: lp:~bzr/bzr/trunk-old
Diff against target: 1320 lines
To merge this branch: bzr merge lp:~lifeless/bzr/test-speed
Reviewer Review Type Date Requested Status
Vincent Ladeuil Approve
Review via email: mp+10590@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

This finishes the thread I started tugging on this morning.

It does two basic things:
 - moves tests that are not for the bzr selftest command line out of
   bzrlib.tests.blackbox.test_selftest
 - where possiblestops invoking child processes to test how we call such
   processes, instead extending the existing idioms for intercepting
   such calls to test how the calls are setup.

This saves another 50% of test time in 'test_selftest' tests over my
patch earlier today.

-Rob

--

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

Nice work and a particular kudo for the clarification comments.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/tests/__init__.py'
2--- bzrlib/tests/__init__.py 2009-08-20 05:05:59 +0000
3+++ bzrlib/tests/__init__.py 2009-08-24 06:35:12 +0000
4@@ -3207,6 +3207,7 @@
5 starting_with=None,
6 runner_class=None,
7 suite_decorators=None,
8+ stream=None,
9 ):
10 """Run the whole test suite under the enhanced runner"""
11 # XXX: Very ugly way to do this...
12@@ -3230,9 +3231,14 @@
13 else:
14 keep_only = load_test_id_list(load_list)
15 if test_suite_factory is None:
16+ # Reduce loading time by loading modules based on the starting_with
17+ # patterns.
18 suite = test_suite(keep_only, starting_with)
19 else:
20 suite = test_suite_factory()
21+ if starting_with:
22+ # But always filter as requested.
23+ suite = filter_suite_by_id_startswith(suite, starting_with)
24 return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
25 stop_on_failure=stop_on_failure,
26 transport=transport,
27@@ -3245,6 +3251,7 @@
28 strict=strict,
29 runner_class=runner_class,
30 suite_decorators=suite_decorators,
31+ stream=stream,
32 )
33 finally:
34 default_transport = old_transport
35@@ -3683,9 +3690,6 @@
36 reload(sys)
37 sys.setdefaultencoding(default_encoding)
38
39- if starting_with:
40- suite = filter_suite_by_id_startswith(suite, starting_with)
41-
42 if keep_only is not None:
43 # Now that the referred modules have loaded their tests, keep only the
44 # requested ones.
45
46=== modified file 'bzrlib/tests/blackbox/test_selftest.py'
47--- bzrlib/tests/blackbox/test_selftest.py 2009-07-10 07:14:02 +0000
48+++ bzrlib/tests/blackbox/test_selftest.py 2009-08-24 06:35:13 +0000
49@@ -16,474 +16,111 @@
50
51 """UI tests for the test framework."""
52
53-from cStringIO import StringIO
54-import os
55-import re
56-import signal
57-import sys
58-import unittest
59-
60-import bzrlib
61+import bzrlib.transport
62 from bzrlib import (
63- osutils,
64+ benchmarks,
65+ tests,
66 )
67 from bzrlib.errors import ParamikoNotPresent
68 from bzrlib.tests import (
69 SubUnitFeature,
70 TestCase,
71 TestCaseInTempDir,
72- TestCaseWithMemoryTransport,
73- TestCaseWithTransport,
74- TestUIFactory,
75 TestSkipped,
76 )
77-from bzrlib.tests.blackbox import ExternalBase
78-
79-
80-class TestOptions(TestCase):
81-
82- current_test = None
83+
84+
85+class SelfTestPatch:
86+
87+ def get_params_passed_to_core(self, cmdline):
88+ params = []
89+ def selftest(*args, **kwargs):
90+ """Capture the arguments selftest was run with."""
91+ params.append((args, kwargs))
92+ return True
93+ # Yes this prevents using threads to run the test suite in parallel,
94+ # however we don't have a clean dependency injector for commands,
95+ # and even if we did - we'd still be testing that the glue is wired
96+ # up correctly. XXX: TODO: Solve this testing problem.
97+ original_selftest = tests.selftest
98+ tests.selftest = selftest
99+ try:
100+ self.run_bzr(cmdline)
101+ return params[0]
102+ finally:
103+ tests.selftest = original_selftest
104+
105+
106+class TestOptionsWritingToDisk(TestCaseInTempDir, SelfTestPatch):
107+
108+ def test_benchmark_runs_benchmark_tests(self):
109+ """selftest --benchmark should change the suite factory."""
110+ params = self.get_params_passed_to_core('selftest --benchmark')
111+ self.assertEqual(benchmarks.test_suite,
112+ params[1]['test_suite_factory'])
113+ self.assertNotEqual(None, params[1]['bench_history'])
114+ benchfile = open(".perf_history", "rt")
115+ try:
116+ lines = benchfile.readlines()
117+ finally:
118+ benchfile.close()
119+ # Because we don't run the actual test code no output is made to the
120+ # file.
121+ self.assertEqual(0, len(lines))
122+
123+
124+class TestOptions(TestCase, SelfTestPatch):
125+
126+ def test_load_list(self):
127+ params = self.get_params_passed_to_core('selftest --load-list foo')
128+ self.assertEqual('foo', params[1]['load_list'])
129
130 def test_transport_set_to_sftp(self):
131- # test the --transport option has taken effect from within the
132- # test_transport test
133+ # Test that we can pass a transport to the selftest core - sftp
134+ # version.
135 try:
136 import bzrlib.transport.sftp
137 except ParamikoNotPresent:
138 raise TestSkipped("Paramiko not present")
139- if TestOptions.current_test != "test_transport_set_to_sftp":
140- return
141+ params = self.get_params_passed_to_core('selftest --transport=sftp')
142 self.assertEqual(bzrlib.transport.sftp.SFTPAbsoluteServer,
143- bzrlib.tests.default_transport)
144+ params[1]["transport"])
145
146 def test_transport_set_to_memory(self):
147- # test the --transport option has taken effect from within the
148- # test_transport test
149+ # Test that we can pass a transport to the selftest core - memory
150+ # version.
151 import bzrlib.transport.memory
152- if TestOptions.current_test != "test_transport_set_to_memory":
153- return
154+ params = self.get_params_passed_to_core('selftest --transport=memory')
155 self.assertEqual(bzrlib.transport.memory.MemoryServer,
156- bzrlib.tests.default_transport)
157-
158- def test_transport(self):
159- # test that --transport=sftp works
160- try:
161- import bzrlib.transport.sftp
162- except ParamikoNotPresent:
163- raise TestSkipped("Paramiko not present")
164- old_transport = bzrlib.tests.default_transport
165- old_root = TestCaseWithMemoryTransport.TEST_ROOT
166- TestCaseWithMemoryTransport.TEST_ROOT = None
167- try:
168- TestOptions.current_test = "test_transport_set_to_sftp"
169- stdout = self.run_bzr(
170- 'selftest --transport=sftp test_transport_set_to_sftp')[0]
171- self.assertContainsRe(stdout, 'Ran 1 test')
172- self.assertEqual(old_transport, bzrlib.tests.default_transport)
173-
174- TestOptions.current_test = "test_transport_set_to_memory"
175- stdout = self.run_bzr(
176- 'selftest --transport=memory test_transport_set_to_memory')[0]
177- self.assertContainsRe(stdout, 'Ran 1 test')
178- self.assertEqual(old_transport, bzrlib.tests.default_transport)
179- finally:
180- bzrlib.tests.default_transport = old_transport
181- TestOptions.current_test = None
182- TestCaseWithMemoryTransport.TEST_ROOT = old_root
183+ params[1]["transport"])
184+
185+ def test_parameters_passed_to_core(self):
186+ params = self.get_params_passed_to_core('selftest --list-only')
187+ self.assertTrue("list_only" in params[1])
188+ params = self.get_params_passed_to_core('selftest --list-only selftest')
189+ self.assertTrue("list_only" in params[1])
190+ params = self.get_params_passed_to_core(['selftest', '--list-only',
191+ '--exclude', 'selftest'])
192+ self.assertTrue("list_only" in params[1])
193+ params = self.get_params_passed_to_core(['selftest', '--list-only',
194+ 'selftest', '--randomize', 'now'])
195+ self.assertSubset(["list_only", "random_seed"], params[1])
196+
197+ def test_starting_with(self):
198+ params = self.get_params_passed_to_core('selftest --starting-with foo')
199+ self.assertEqual(['foo'], params[1]['starting_with'])
200+
201+ def test_starting_with_multiple_argument(self):
202+ params = self.get_params_passed_to_core(
203+ 'selftest --starting-with foo --starting-with bar')
204+ self.assertEqual(['foo', 'bar'], params[1]['starting_with'])
205
206 def test_subunit(self):
207- """Passing --subunit results in subunit output."""
208 self.requireFeature(SubUnitFeature)
209- from subunit import ProtocolTestCase
210- stdout = self.run_bzr(
211- 'selftest --subunit --no-plugins '
212- 'tests.test_selftest.SelftestTests.test_import_tests')[0]
213- stream = StringIO(str(stdout))
214- test = ProtocolTestCase(stream)
215- result = unittest.TestResult()
216- test.run(result)
217- self.assertEqual(1, result.testsRun)
218-
219-
220-class TestRunBzr(ExternalBase):
221-
222- def _run_bzr_core(self, argv, retcode=0, encoding=None, stdin=None,
223- working_dir=None):
224- """Override _run_bzr_core to test how it is invoked by run_bzr.
225-
226- Attempts to run bzr from inside this class don't actually run it.
227-
228- We test how run_bzr actually invokes bzr in another location.
229- Here we only need to test that it is run_bzr passes the right
230- parameters to run_bzr.
231- """
232- self.argv = list(argv)
233- self.retcode = retcode
234- self.encoding = encoding
235- self.stdin = stdin
236- self.working_dir = working_dir
237- return '', ''
238-
239- def test_encoding(self):
240- """Test that run_bzr passes encoding to _run_bzr_core"""
241- self.run_bzr('foo bar')
242- self.assertEqual(None, self.encoding)
243- self.assertEqual(['foo', 'bar'], self.argv)
244-
245- self.run_bzr('foo bar', encoding='baz')
246- self.assertEqual('baz', self.encoding)
247- self.assertEqual(['foo', 'bar'], self.argv)
248-
249- def test_retcode(self):
250- """Test that run_bzr passes retcode to _run_bzr_core"""
251- # Default is retcode == 0
252- self.run_bzr('foo bar')
253- self.assertEqual(0, self.retcode)
254- self.assertEqual(['foo', 'bar'], self.argv)
255-
256- self.run_bzr('foo bar', retcode=1)
257- self.assertEqual(1, self.retcode)
258- self.assertEqual(['foo', 'bar'], self.argv)
259-
260- self.run_bzr('foo bar', retcode=None)
261- self.assertEqual(None, self.retcode)
262- self.assertEqual(['foo', 'bar'], self.argv)
263-
264- self.run_bzr(['foo', 'bar'], retcode=3)
265- self.assertEqual(3, self.retcode)
266- self.assertEqual(['foo', 'bar'], self.argv)
267-
268- def test_stdin(self):
269- # test that the stdin keyword to run_bzr is passed through to
270- # _run_bzr_core as-is. We do this by overriding
271- # _run_bzr_core in this class, and then calling run_bzr,
272- # which is a convenience function for _run_bzr_core, so
273- # should invoke it.
274- self.run_bzr('foo bar', stdin='gam')
275- self.assertEqual('gam', self.stdin)
276- self.assertEqual(['foo', 'bar'], self.argv)
277-
278- self.run_bzr('foo bar', stdin='zippy')
279- self.assertEqual('zippy', self.stdin)
280- self.assertEqual(['foo', 'bar'], self.argv)
281-
282- def test_working_dir(self):
283- """Test that run_bzr passes working_dir to _run_bzr_core"""
284- self.run_bzr('foo bar')
285- self.assertEqual(None, self.working_dir)
286- self.assertEqual(['foo', 'bar'], self.argv)
287-
288- self.run_bzr('foo bar', working_dir='baz')
289- self.assertEqual('baz', self.working_dir)
290- self.assertEqual(['foo', 'bar'], self.argv)
291-
292- def test_reject_extra_keyword_arguments(self):
293- self.assertRaises(TypeError, self.run_bzr, "foo bar",
294- error_regex=['error message'])
295-
296-
297-class TestBenchmarkTests(TestCaseWithTransport):
298-
299- def test_benchmark_runs_benchmark_tests(self):
300- """bzr selftest --benchmark should not run the default test suite."""
301- # We test this by passing a regression test name to --benchmark, which
302- # should result in 0 tests run.
303- old_root = TestCaseWithMemoryTransport.TEST_ROOT
304- try:
305- TestCaseWithMemoryTransport.TEST_ROOT = None
306- out, err = self.run_bzr('selftest --benchmark'
307- ' per_workingtree')
308- finally:
309- TestCaseWithMemoryTransport.TEST_ROOT = old_root
310- self.assertContainsRe(out, 'Ran 0 tests.*\n\nOK')
311- self.assertContainsRe(out, 'tests passed\n')
312- benchfile = open(".perf_history", "rt")
313- try:
314- lines = benchfile.readlines()
315- finally:
316- benchfile.close()
317- self.assertEqual(1, len(lines))
318- self.assertContainsRe(lines[0], "--date [0-9.]+")
319-
320-
321-class TestRunBzrCaptured(ExternalBase):
322-
323- def apply_redirected(self, stdin=None, stdout=None, stderr=None,
324- a_callable=None, *args, **kwargs):
325- self.stdin = stdin
326- self.factory_stdin = getattr(bzrlib.ui.ui_factory, "stdin", None)
327- self.factory = bzrlib.ui.ui_factory
328- self.working_dir = osutils.getcwd()
329- stdout.write('foo\n')
330- stderr.write('bar\n')
331- return 0
332-
333- def test_stdin(self):
334- # test that the stdin keyword to _run_bzr_core is passed through to
335- # apply_redirected as a StringIO. We do this by overriding
336- # apply_redirected in this class, and then calling _run_bzr_core,
337- # which calls apply_redirected.
338- self.run_bzr(['foo', 'bar'], stdin='gam')
339- self.assertEqual('gam', self.stdin.read())
340- self.assertTrue(self.stdin is self.factory_stdin)
341- self.run_bzr(['foo', 'bar'], stdin='zippy')
342- self.assertEqual('zippy', self.stdin.read())
343- self.assertTrue(self.stdin is self.factory_stdin)
344-
345- def test_ui_factory(self):
346- # each invocation of self.run_bzr should get its
347- # own UI factory, which is an instance of TestUIFactory,
348- # with stdin, stdout and stderr attached to the stdin,
349- # stdout and stderr of the invoked run_bzr
350- current_factory = bzrlib.ui.ui_factory
351- self.run_bzr(['foo'])
352- self.failIf(current_factory is self.factory)
353- self.assertNotEqual(sys.stdout, self.factory.stdout)
354- self.assertNotEqual(sys.stderr, self.factory.stderr)
355- self.assertEqual('foo\n', self.factory.stdout.getvalue())
356- self.assertEqual('bar\n', self.factory.stderr.getvalue())
357- self.assertIsInstance(self.factory, TestUIFactory)
358-
359- def test_working_dir(self):
360- self.build_tree(['one/', 'two/'])
361- cwd = osutils.getcwd()
362-
363- # Default is to work in the current directory
364- self.run_bzr(['foo', 'bar'])
365- self.assertEqual(cwd, self.working_dir)
366-
367- self.run_bzr(['foo', 'bar'], working_dir=None)
368- self.assertEqual(cwd, self.working_dir)
369-
370- # The function should be run in the alternative directory
371- # but afterwards the current working dir shouldn't be changed
372- self.run_bzr(['foo', 'bar'], working_dir='one')
373- self.assertNotEqual(cwd, self.working_dir)
374- self.assertEndsWith(self.working_dir, 'one')
375- self.assertEqual(cwd, osutils.getcwd())
376-
377- self.run_bzr(['foo', 'bar'], working_dir='two')
378- self.assertNotEqual(cwd, self.working_dir)
379- self.assertEndsWith(self.working_dir, 'two')
380- self.assertEqual(cwd, osutils.getcwd())
381-
382-
383-class TestRunBzrSubprocess(TestCaseWithTransport):
384-
385- def test_run_bzr_subprocess(self):
386- """The run_bzr_helper_external command behaves nicely."""
387- result = self.run_bzr_subprocess('--version')
388- result = self.run_bzr_subprocess(['--version'])
389- result = self.run_bzr_subprocess('--version', retcode=None)
390- self.assertContainsRe(result[0], 'is free software')
391- self.assertRaises(AssertionError, self.run_bzr_subprocess,
392- '--versionn')
393- result = self.run_bzr_subprocess('--versionn', retcode=3)
394- result = self.run_bzr_subprocess('--versionn', retcode=None)
395- self.assertContainsRe(result[1], 'unknown command')
396- err = self.run_bzr_subprocess(['merge', '--merge-type',
397- 'magic merge'], retcode=3)[1]
398- self.assertContainsRe(err, 'Bad value "magic merge" for option'
399- ' "merge-type"')
400-
401- def test_run_bzr_subprocess_env(self):
402- """run_bzr_subprocess can set environment variables in the child only.
403-
404- These changes should not change the running process, only the child.
405- """
406- # The test suite should unset this variable
407- self.assertEqual(None, os.environ.get('BZR_EMAIL'))
408- out, err = self.run_bzr_subprocess('whoami', env_changes={
409- 'BZR_EMAIL':'Joe Foo <joe@foo.com>'
410- }, universal_newlines=True)
411- self.assertEqual('', err)
412- self.assertEqual('Joe Foo <joe@foo.com>\n', out)
413- # And it should not be modified
414- self.assertEqual(None, os.environ.get('BZR_EMAIL'))
415-
416- # Do it again with a different address, just to make sure
417- # it is actually changing
418- out, err = self.run_bzr_subprocess('whoami', env_changes={
419- 'BZR_EMAIL':'Barry <bar@foo.com>'
420- }, universal_newlines=True)
421- self.assertEqual('', err)
422- self.assertEqual('Barry <bar@foo.com>\n', out)
423- self.assertEqual(None, os.environ.get('BZR_EMAIL'))
424-
425- def test_run_bzr_subprocess_env_del(self):
426- """run_bzr_subprocess can remove environment variables too."""
427- # Create a random email, so we are sure this won't collide
428- rand_bzr_email = 'John Doe <jdoe@%s.com>' % (osutils.rand_chars(20),)
429- rand_email = 'Jane Doe <jdoe@%s.com>' % (osutils.rand_chars(20),)
430- os.environ['BZR_EMAIL'] = rand_bzr_email
431- os.environ['EMAIL'] = rand_email
432- try:
433- # By default, the child will inherit the current env setting
434- out, err = self.run_bzr_subprocess('whoami', universal_newlines=True)
435- self.assertEqual('', err)
436- self.assertEqual(rand_bzr_email + '\n', out)
437-
438- # Now that BZR_EMAIL is not set, it should fall back to EMAIL
439- out, err = self.run_bzr_subprocess('whoami',
440- env_changes={'BZR_EMAIL':None},
441- universal_newlines=True)
442- self.assertEqual('', err)
443- self.assertEqual(rand_email + '\n', out)
444-
445- # This switches back to the default email guessing logic
446- # Which shouldn't match either of the above addresses
447- out, err = self.run_bzr_subprocess('whoami',
448- env_changes={'BZR_EMAIL':None, 'EMAIL':None},
449- universal_newlines=True)
450-
451- self.assertEqual('', err)
452- self.assertNotEqual(rand_bzr_email + '\n', out)
453- self.assertNotEqual(rand_email + '\n', out)
454- finally:
455- # TestCase cleans up BZR_EMAIL, and EMAIL at startup
456- del os.environ['BZR_EMAIL']
457- del os.environ['EMAIL']
458-
459- def test_run_bzr_subprocess_env_del_missing(self):
460- """run_bzr_subprocess won't fail if deleting a nonexistant env var"""
461- self.failIf('NON_EXISTANT_ENV_VAR' in os.environ)
462- out, err = self.run_bzr_subprocess('rocks',
463- env_changes={'NON_EXISTANT_ENV_VAR':None},
464- universal_newlines=True)
465- self.assertEqual('It sure does!\n', out)
466- self.assertEqual('', err)
467-
468- def test_run_bzr_subprocess_working_dir(self):
469- """Test that we can specify the working dir for the child"""
470- cwd = osutils.getcwd()
471-
472- self.make_branch_and_tree('.')
473- self.make_branch_and_tree('one')
474- self.make_branch_and_tree('two')
475-
476- def get_root(**kwargs):
477- """Spawn a process to get the 'root' of the tree.
478-
479- You can pass in arbitrary new arguments. This just makes
480- sure that the returned path doesn't have trailing whitespace.
481- """
482- return self.run_bzr_subprocess('root', **kwargs)[0].rstrip()
483-
484- self.assertEqual(cwd, get_root())
485- self.assertEqual(cwd, get_root(working_dir=None))
486- # Has our path changed?
487- self.assertEqual(cwd, osutils.getcwd())
488-
489- dir1 = get_root(working_dir='one')
490- self.assertEndsWith(dir1, 'one')
491- self.assertEqual(cwd, osutils.getcwd())
492-
493- dir2 = get_root(working_dir='two')
494- self.assertEndsWith(dir2, 'two')
495- self.assertEqual(cwd, osutils.getcwd())
496-
497-
498-class _DontSpawnProcess(Exception):
499- """A simple exception which just allows us to skip unnecessary steps"""
500-
501-
502-class TestRunBzrSubprocessCommands(TestCaseWithTransport):
503-
504- def _popen(self, *args, **kwargs):
505- """Record the command that is run, so that we can ensure it is correct"""
506- self._popen_args = args
507- self._popen_kwargs = kwargs
508- raise _DontSpawnProcess()
509-
510- def test_run_bzr_subprocess_no_plugins(self):
511- self.assertRaises(_DontSpawnProcess, self.run_bzr_subprocess, '')
512- command = self._popen_args[0]
513- self.assertEqual(sys.executable, command[0])
514- self.assertEqual(self.get_bzr_path(), command[1])
515- self.assertEqual(['--no-plugins'], command[2:])
516-
517- def test_allow_plugins(self):
518- self.assertRaises(_DontSpawnProcess,
519- self.run_bzr_subprocess, '', allow_plugins=True)
520- command = self._popen_args[0]
521- self.assertEqual([], command[2:])
522-
523-
524-class TestBzrSubprocess(TestCaseWithTransport):
525-
526- def test_start_and_stop_bzr_subprocess(self):
527- """We can start and perform other test actions while that process is
528- still alive.
529- """
530- process = self.start_bzr_subprocess(['--version'])
531- result = self.finish_bzr_subprocess(process)
532- self.assertContainsRe(result[0], 'is free software')
533- self.assertEqual('', result[1])
534-
535- def test_start_and_stop_bzr_subprocess_with_error(self):
536- """finish_bzr_subprocess allows specification of the desired exit code.
537- """
538- process = self.start_bzr_subprocess(['--versionn'])
539- result = self.finish_bzr_subprocess(process, retcode=3)
540- self.assertEqual('', result[0])
541- self.assertContainsRe(result[1], 'unknown command')
542-
543- def test_start_and_stop_bzr_subprocess_ignoring_retcode(self):
544- """finish_bzr_subprocess allows the exit code to be ignored."""
545- process = self.start_bzr_subprocess(['--versionn'])
546- result = self.finish_bzr_subprocess(process, retcode=None)
547- self.assertEqual('', result[0])
548- self.assertContainsRe(result[1], 'unknown command')
549-
550- def test_start_and_stop_bzr_subprocess_with_unexpected_retcode(self):
551- """finish_bzr_subprocess raises self.failureException if the retcode is
552- not the expected one.
553- """
554- process = self.start_bzr_subprocess(['--versionn'])
555- self.assertRaises(self.failureException, self.finish_bzr_subprocess,
556- process)
557-
558- def test_start_and_stop_bzr_subprocess_send_signal(self):
559- """finish_bzr_subprocess raises self.failureException if the retcode is
560- not the expected one.
561- """
562- process = self.start_bzr_subprocess(['wait-until-signalled'],
563- skip_if_plan_to_signal=True)
564- self.assertEqual('running\n', process.stdout.readline())
565- result = self.finish_bzr_subprocess(process, send_signal=signal.SIGINT,
566- retcode=3)
567- self.assertEqual('', result[0])
568- self.assertEqual('bzr: interrupted\n', result[1])
569-
570- def test_start_and_stop_working_dir(self):
571- cwd = osutils.getcwd()
572-
573- self.make_branch_and_tree('one')
574-
575- process = self.start_bzr_subprocess(['root'], working_dir='one')
576- result = self.finish_bzr_subprocess(process, universal_newlines=True)
577- self.assertEndsWith(result[0], 'one\n')
578- self.assertEqual('', result[1])
579-
580-
581-class TestRunBzrError(ExternalBase):
582-
583- def test_run_bzr_error(self):
584- # retcode=0 is specially needed here because run_bzr_error expects
585- # an error (oddly enough) but we want to test the case of not
586- # actually getting one
587- out, err = self.run_bzr_error(['^$'], ['rocks'], retcode=0)
588- self.assertEqual(out, 'It sure does!\n')
589- # now test actually getting an error
590- out, err = self.run_bzr_error(
591- ["bzr: ERROR: foobarbaz is not versioned"],
592- ['file-id', 'foobarbaz'])
593-
594-
595-class TestSelftestListOnly(TestCase):
596-
597- @staticmethod
598- def _parse_test_list(lines, newlines_in_header=0):
599+ params = self.get_params_passed_to_core('selftest --subunit')
600+ self.assertEqual(tests.SubUnitBzrRunner, params[1]['runner_class'])
601+
602+ def _parse_test_list(self, lines, newlines_in_header=0):
603 "Parse a list of lines into a tuple of 3 lists (header,body,footer)."
604 in_header = newlines_in_header != 0
605 in_footer = False
606@@ -512,92 +149,26 @@
607 return (header,body,footer)
608
609 def test_list_only(self):
610- # check that bzr selftest --list-only works correctly
611- out,err = self.run_bzr('selftest selftest --list-only')
612- (header,body,footer) = self._parse_test_list(out.splitlines())
613- num_tests = len(body)
614- self.assertLength(0, header)
615- self.assertLength(0, footer)
616- self.assertEqual('', err)
617-
618- def test_list_only_filtered(self):
619- # check that a filtered --list-only works, both include and exclude
620- out_all,err_all = self.run_bzr('selftest --list-only')
621- tests_all = self._parse_test_list(out_all.splitlines())[1]
622- out_incl,err_incl = self.run_bzr('selftest --list-only selftest')
623- tests_incl = self._parse_test_list(out_incl.splitlines())[1]
624- self.assertSubset(tests_incl, tests_all)
625- out_excl,err_excl = self.run_bzr(['selftest', '--list-only',
626- '--exclude', 'selftest'])
627- tests_excl = self._parse_test_list(out_excl.splitlines())[1]
628- self.assertSubset(tests_excl, tests_all)
629- set_incl = set(tests_incl)
630- set_excl = set(tests_excl)
631- intersection = set_incl.intersection(set_excl)
632- self.assertEquals(0, len(intersection))
633- self.assertEquals(len(tests_all), len(tests_incl) + len(tests_excl))
634-
635- def test_list_only_random(self):
636- # check that --randomize works correctly
637- out_all,err_all = self.run_bzr('selftest --list-only selftest')
638- tests_all = self._parse_test_list(out_all.splitlines())[1]
639- # XXX: It looks like there are some orders for generating tests that
640- # fail as of 20070504 - maybe because of import order dependencies.
641- # So unfortunately this will rarely intermittently fail at the moment.
642- # -- mbp 20070504
643- out_rand,err_rand = self.run_bzr(['selftest', '--list-only',
644- 'selftest', '--randomize', 'now'])
645- (header_rand,tests_rand,dummy) = self._parse_test_list(
646- out_rand.splitlines(), 1)
647- # XXX: The following line asserts that the randomized order is not the
648- # same as the default order. It is just possible that they'll get
649- # randomized into the same order and this will falsely fail, but
650- # that's very unlikely in practice because there are thousands of
651- # tests.
652- self.assertNotEqual(tests_all, tests_rand)
653- self.assertEqual(sorted(tests_all), sorted(tests_rand))
654- # Check that the seed can be reused to get the exact same order
655- seed_re = re.compile('Randomizing test order using seed (\w+)')
656- match_obj = seed_re.search(header_rand[-1])
657- seed = match_obj.group(1)
658- out_rand2,err_rand2 = self.run_bzr(['selftest', '--list-only',
659- 'selftest', '--randomize', seed])
660- (header_rand2,tests_rand2,dummy) = self._parse_test_list(
661- out_rand2.splitlines(), 1)
662- self.assertEqual(tests_rand, tests_rand2)
663-
664-
665-class TestSelftestWithIdList(TestCaseInTempDir):
666-
667- def test_load_list(self):
668- # We don't want to call selftest for the whole suite, so we start with
669- # a reduced list.
670- test_list_fname = 'test.list'
671- fl = open(test_list_fname, 'wt')
672- fl.write('%s\n' % self.id())
673- fl.close()
674- out, err = self.run_bzr(
675- ['selftest', '--load-list', test_list_fname, '--list'])
676- self.assertContainsRe(out, "TestSelftestWithIdList")
677- self.assertLength(1, out.splitlines())
678-
679- def test_load_unknown(self):
680- out, err = self.run_bzr('selftest --load-list I_do_not_exist ',
681- retcode=3)
682-
683-
684-class TestSelftestStartingWith(TestCase):
685-
686- def test_starting_with_single_argument(self):
687- out, err = self.run_bzr(
688- ['selftest', '--starting-with', self.id(), '--list'])
689- self.assertContainsRe(out, self.id())
690-
691- def test_starting_with_multiple_argument(self):
692- out, err = self.run_bzr(
693- ['selftest',
694- '--starting-with', self.id(),
695- '--starting-with', 'bzrlib.tests.test_sampler',
696- '--list'])
697- self.assertContainsRe(out, self.id())
698- self.assertContainsRe(out, 'bzrlib.tests.test_sampler')
699+ # check that bzr selftest --list-only outputs no ui noise
700+ def selftest(*args, **kwargs):
701+ """Capture the arguments selftest was run with."""
702+ return True
703+ def outputs_nothing(cmdline):
704+ out,err = self.run_bzr(cmdline)
705+ (header,body,footer) = self._parse_test_list(out.splitlines())
706+ num_tests = len(body)
707+ self.assertLength(0, header)
708+ self.assertLength(0, footer)
709+ self.assertEqual('', err)
710+ # Yes this prevents using threads to run the test suite in parallel,
711+ # however we don't have a clean dependency injector for commands,
712+ # and even if we did - we'd still be testing that the glue is wired
713+ # up correctly. XXX: TODO: Solve this testing problem.
714+ original_selftest = tests.selftest
715+ tests.selftest = selftest
716+ try:
717+ outputs_nothing('selftest --list-only')
718+ outputs_nothing('selftest --list-only selftest')
719+ outputs_nothing(['selftest', '--list-only', '--exclude', 'selftest'])
720+ finally:
721+ tests.selftest = original_selftest
722
723=== modified file 'bzrlib/tests/test_selftest.py'
724--- bzrlib/tests/test_selftest.py 2009-08-17 03:47:03 +0000
725+++ bzrlib/tests/test_selftest.py 2009-08-24 06:35:12 +0000
726@@ -18,6 +18,7 @@
727
728 from cStringIO import StringIO
729 import os
730+import signal
731 import sys
732 import time
733 import unittest
734@@ -50,6 +51,7 @@
735 deprecated_method,
736 )
737 from bzrlib.tests import (
738+ SubUnitFeature,
739 test_lsprof,
740 test_sftp_transport,
741 TestUtil,
742@@ -1745,7 +1747,7 @@
743
744 def test_make_tree_for_sftp_branch(self):
745 """Transports backed by local directories create local trees."""
746-
747+ # NB: This is arguably a bug in the definition of make_branch_and_tree.
748 tree = self.make_branch_and_tree('t1')
749 base = tree.bzrdir.root_transport.base
750 self.failIf(base.startswith('sftp'),
751@@ -1756,7 +1758,24 @@
752 tree.branch.repository.bzrdir.root_transport)
753
754
755-class TestSelftest(tests.TestCase):
756+class SelfTestHelper:
757+
758+ def run_selftest(self, **kwargs):
759+ """Run selftest returning its output."""
760+ output = StringIO()
761+ old_transport = bzrlib.tests.default_transport
762+ old_root = tests.TestCaseWithMemoryTransport.TEST_ROOT
763+ tests.TestCaseWithMemoryTransport.TEST_ROOT = None
764+ try:
765+ self.assertEqual(True, tests.selftest(stream=output, **kwargs))
766+ finally:
767+ bzrlib.tests.default_transport = old_transport
768+ tests.TestCaseWithMemoryTransport.TEST_ROOT = old_root
769+ output.seek(0)
770+ return output
771+
772+
773+class TestSelftest(tests.TestCase, SelfTestHelper):
774 """Tests of bzrlib.tests.selftest."""
775
776 def test_selftest_benchmark_parameter_invokes_test_suite__benchmark__(self):
777@@ -1770,6 +1789,511 @@
778 test_suite_factory=factory)
779 self.assertEqual([True], factory_called)
780
781+ def factory(self):
782+ """A test suite factory."""
783+ class Test(tests.TestCase):
784+ def a(self):
785+ pass
786+ def b(self):
787+ pass
788+ def c(self):
789+ pass
790+ return TestUtil.TestSuite([Test("a"), Test("b"), Test("c")])
791+
792+ def test_list_only(self):
793+ output = self.run_selftest(test_suite_factory=self.factory,
794+ list_only=True)
795+ self.assertEqual(3, len(output.readlines()))
796+
797+ def test_list_only_filtered(self):
798+ output = self.run_selftest(test_suite_factory=self.factory,
799+ list_only=True, pattern="Test.b")
800+ self.assertEndsWith(output.getvalue(), "Test.b\n")
801+ self.assertLength(1, output.readlines())
802+
803+ def test_list_only_excludes(self):
804+ output = self.run_selftest(test_suite_factory=self.factory,
805+ list_only=True, exclude_pattern="Test.b")
806+ self.assertNotContainsRe("Test.b", output.getvalue())
807+ self.assertLength(2, output.readlines())
808+
809+ def test_random(self):
810+ # test randomising by listing a number of tests.
811+ output_123 = self.run_selftest(test_suite_factory=self.factory,
812+ list_only=True, random_seed="123")
813+ output_234 = self.run_selftest(test_suite_factory=self.factory,
814+ list_only=True, random_seed="234")
815+ self.assertNotEqual(output_123, output_234)
816+ # "Randominzing test order..\n\n
817+ self.assertLength(5, output_123.readlines())
818+ self.assertLength(5, output_234.readlines())
819+
820+ def test_random_reuse_is_same_order(self):
821+ # test randomising by listing a number of tests.
822+ expected = self.run_selftest(test_suite_factory=self.factory,
823+ list_only=True, random_seed="123")
824+ repeated = self.run_selftest(test_suite_factory=self.factory,
825+ list_only=True, random_seed="123")
826+ self.assertEqual(expected.getvalue(), repeated.getvalue())
827+
828+ def test_runner_class(self):
829+ self.requireFeature(SubUnitFeature)
830+ from subunit import ProtocolTestCase
831+ stream = self.run_selftest(runner_class=tests.SubUnitBzrRunner,
832+ test_suite_factory=self.factory)
833+ test = ProtocolTestCase(stream)
834+ result = unittest.TestResult()
835+ test.run(result)
836+ self.assertEqual(3, result.testsRun)
837+
838+ def test_starting_with_single_argument(self):
839+ output = self.run_selftest(test_suite_factory=self.factory,
840+ starting_with=['bzrlib.tests.test_selftest.Test.a'],
841+ list_only=True)
842+ self.assertEqual('bzrlib.tests.test_selftest.Test.a\n',
843+ output.getvalue())
844+
845+ def test_starting_with_multiple_argument(self):
846+ output = self.run_selftest(test_suite_factory=self.factory,
847+ starting_with=['bzrlib.tests.test_selftest.Test.a',
848+ 'bzrlib.tests.test_selftest.Test.b'],
849+ list_only=True)
850+ self.assertEqual('bzrlib.tests.test_selftest.Test.a\n'
851+ 'bzrlib.tests.test_selftest.Test.b\n',
852+ output.getvalue())
853+
854+ def check_transport_set(self, transport_server):
855+ captured_transport = []
856+ def seen_transport(a_transport):
857+ captured_transport.append(a_transport)
858+ class Capture(tests.TestCase):
859+ def a(self):
860+ seen_transport(bzrlib.tests.default_transport)
861+ def factory():
862+ return TestUtil.TestSuite([Capture("a")])
863+ self.run_selftest(transport=transport_server, test_suite_factory=factory)
864+ self.assertEqual(transport_server, captured_transport[0])
865+
866+ def test_transport_sftp(self):
867+ try:
868+ import bzrlib.transport.sftp
869+ except ParamikoNotPresent:
870+ raise TestSkipped("Paramiko not present")
871+ self.check_transport_set(bzrlib.transport.sftp.SFTPAbsoluteServer)
872+
873+ def test_transport_memory(self):
874+ self.check_transport_set(bzrlib.transport.memory.MemoryServer)
875+
876+
877+class TestSelftestWithIdList(tests.TestCaseInTempDir, SelfTestHelper):
878+ # Does IO: reads test.list
879+
880+ def test_load_list(self):
881+ # Provide a list with one test - this test.
882+ test_id_line = '%s\n' % self.id()
883+ self.build_tree_contents([('test.list', test_id_line)])
884+ # And generate a list of the tests in the suite.
885+ stream = self.run_selftest(load_list='test.list', list_only=True)
886+ self.assertEqual(test_id_line, stream.getvalue())
887+
888+ def test_load_unknown(self):
889+ # Provide a list with one test - this test.
890+ # And generate a list of the tests in the suite.
891+ err = self.assertRaises(errors.NoSuchFile, self.run_selftest,
892+ load_list='missing file name', list_only=True)
893+
894+
895+class TestRunBzr(tests.TestCase):
896+
897+ out = ''
898+ err = ''
899+
900+ def _run_bzr_core(self, argv, retcode=0, encoding=None, stdin=None,
901+ working_dir=None):
902+ """Override _run_bzr_core to test how it is invoked by run_bzr.
903+
904+ Attempts to run bzr from inside this class don't actually run it.
905+
906+ We test how run_bzr actually invokes bzr in another location.
907+ Here we only need to test that it is run_bzr passes the right
908+ parameters to run_bzr.
909+ """
910+ self.argv = list(argv)
911+ self.retcode = retcode
912+ self.encoding = encoding
913+ self.stdin = stdin
914+ self.working_dir = working_dir
915+ return self.out, self.err
916+
917+ def test_run_bzr_error(self):
918+ self.out = "It sure does!\n"
919+ out, err = self.run_bzr_error(['^$'], ['rocks'], retcode=34)
920+ self.assertEqual(['rocks'], self.argv)
921+ self.assertEqual(34, self.retcode)
922+ self.assertEqual(out, 'It sure does!\n')
923+
924+ def test_run_bzr_error_regexes(self):
925+ self.out = ''
926+ self.err = "bzr: ERROR: foobarbaz is not versioned"
927+ out, err = self.run_bzr_error(
928+ ["bzr: ERROR: foobarbaz is not versioned"],
929+ ['file-id', 'foobarbaz'])
930+
931+ def test_encoding(self):
932+ """Test that run_bzr passes encoding to _run_bzr_core"""
933+ self.run_bzr('foo bar')
934+ self.assertEqual(None, self.encoding)
935+ self.assertEqual(['foo', 'bar'], self.argv)
936+
937+ self.run_bzr('foo bar', encoding='baz')
938+ self.assertEqual('baz', self.encoding)
939+ self.assertEqual(['foo', 'bar'], self.argv)
940+
941+ def test_retcode(self):
942+ """Test that run_bzr passes retcode to _run_bzr_core"""
943+ # Default is retcode == 0
944+ self.run_bzr('foo bar')
945+ self.assertEqual(0, self.retcode)
946+ self.assertEqual(['foo', 'bar'], self.argv)
947+
948+ self.run_bzr('foo bar', retcode=1)
949+ self.assertEqual(1, self.retcode)
950+ self.assertEqual(['foo', 'bar'], self.argv)
951+
952+ self.run_bzr('foo bar', retcode=None)
953+ self.assertEqual(None, self.retcode)
954+ self.assertEqual(['foo', 'bar'], self.argv)
955+
956+ self.run_bzr(['foo', 'bar'], retcode=3)
957+ self.assertEqual(3, self.retcode)
958+ self.assertEqual(['foo', 'bar'], self.argv)
959+
960+ def test_stdin(self):
961+ # test that the stdin keyword to run_bzr is passed through to
962+ # _run_bzr_core as-is. We do this by overriding
963+ # _run_bzr_core in this class, and then calling run_bzr,
964+ # which is a convenience function for _run_bzr_core, so
965+ # should invoke it.
966+ self.run_bzr('foo bar', stdin='gam')
967+ self.assertEqual('gam', self.stdin)
968+ self.assertEqual(['foo', 'bar'], self.argv)
969+
970+ self.run_bzr('foo bar', stdin='zippy')
971+ self.assertEqual('zippy', self.stdin)
972+ self.assertEqual(['foo', 'bar'], self.argv)
973+
974+ def test_working_dir(self):
975+ """Test that run_bzr passes working_dir to _run_bzr_core"""
976+ self.run_bzr('foo bar')
977+ self.assertEqual(None, self.working_dir)
978+ self.assertEqual(['foo', 'bar'], self.argv)
979+
980+ self.run_bzr('foo bar', working_dir='baz')
981+ self.assertEqual('baz', self.working_dir)
982+ self.assertEqual(['foo', 'bar'], self.argv)
983+
984+ def test_reject_extra_keyword_arguments(self):
985+ self.assertRaises(TypeError, self.run_bzr, "foo bar",
986+ error_regex=['error message'])
987+
988+
989+class TestRunBzrCaptured(tests.TestCaseWithTransport):
990+ # Does IO when testing the working_dir parameter.
991+
992+ def apply_redirected(self, stdin=None, stdout=None, stderr=None,
993+ a_callable=None, *args, **kwargs):
994+ self.stdin = stdin
995+ self.factory_stdin = getattr(bzrlib.ui.ui_factory, "stdin", None)
996+ self.factory = bzrlib.ui.ui_factory
997+ self.working_dir = osutils.getcwd()
998+ stdout.write('foo\n')
999+ stderr.write('bar\n')
1000+ return 0
1001+
1002+ def test_stdin(self):
1003+ # test that the stdin keyword to _run_bzr_core is passed through to
1004+ # apply_redirected as a StringIO. We do this by overriding
1005+ # apply_redirected in this class, and then calling _run_bzr_core,
1006+ # which calls apply_redirected.
1007+ self.run_bzr(['foo', 'bar'], stdin='gam')
1008+ self.assertEqual('gam', self.stdin.read())
1009+ self.assertTrue(self.stdin is self.factory_stdin)
1010+ self.run_bzr(['foo', 'bar'], stdin='zippy')
1011+ self.assertEqual('zippy', self.stdin.read())
1012+ self.assertTrue(self.stdin is self.factory_stdin)
1013+
1014+ def test_ui_factory(self):
1015+ # each invocation of self.run_bzr should get its
1016+ # own UI factory, which is an instance of TestUIFactory,
1017+ # with stdin, stdout and stderr attached to the stdin,
1018+ # stdout and stderr of the invoked run_bzr
1019+ current_factory = bzrlib.ui.ui_factory
1020+ self.run_bzr(['foo'])
1021+ self.failIf(current_factory is self.factory)
1022+ self.assertNotEqual(sys.stdout, self.factory.stdout)
1023+ self.assertNotEqual(sys.stderr, self.factory.stderr)
1024+ self.assertEqual('foo\n', self.factory.stdout.getvalue())
1025+ self.assertEqual('bar\n', self.factory.stderr.getvalue())
1026+ self.assertIsInstance(self.factory, tests.TestUIFactory)
1027+
1028+ def test_working_dir(self):
1029+ self.build_tree(['one/', 'two/'])
1030+ cwd = osutils.getcwd()
1031+
1032+ # Default is to work in the current directory
1033+ self.run_bzr(['foo', 'bar'])
1034+ self.assertEqual(cwd, self.working_dir)
1035+
1036+ self.run_bzr(['foo', 'bar'], working_dir=None)
1037+ self.assertEqual(cwd, self.working_dir)
1038+
1039+ # The function should be run in the alternative directory
1040+ # but afterwards the current working dir shouldn't be changed
1041+ self.run_bzr(['foo', 'bar'], working_dir='one')
1042+ self.assertNotEqual(cwd, self.working_dir)
1043+ self.assertEndsWith(self.working_dir, 'one')
1044+ self.assertEqual(cwd, osutils.getcwd())
1045+
1046+ self.run_bzr(['foo', 'bar'], working_dir='two')
1047+ self.assertNotEqual(cwd, self.working_dir)
1048+ self.assertEndsWith(self.working_dir, 'two')
1049+ self.assertEqual(cwd, osutils.getcwd())
1050+
1051+
1052+class StubProcess(object):
1053+ """A stub process for testing run_bzr_subprocess."""
1054+
1055+ def __init__(self, out="", err="", retcode=0):
1056+ self.out = out
1057+ self.err = err
1058+ self.returncode = retcode
1059+
1060+ def communicate(self):
1061+ return self.out, self.err
1062+
1063+
1064+class TestRunBzrSubprocess(tests.TestCaseWithTransport):
1065+
1066+ def setUp(self):
1067+ tests.TestCaseWithTransport.setUp(self)
1068+ self.subprocess_calls = []
1069+
1070+ def start_bzr_subprocess(self, process_args, env_changes=None,
1071+ skip_if_plan_to_signal=False,
1072+ working_dir=None,
1073+ allow_plugins=False):
1074+ """capture what run_bzr_subprocess tries to do."""
1075+ self.subprocess_calls.append({'process_args':process_args,
1076+ 'env_changes':env_changes,
1077+ 'skip_if_plan_to_signal':skip_if_plan_to_signal,
1078+ 'working_dir':working_dir, 'allow_plugins':allow_plugins})
1079+ return self.next_subprocess
1080+
1081+ def assertRunBzrSubprocess(self, expected_args, process, *args, **kwargs):
1082+ """Run run_bzr_subprocess with args and kwargs using a stubbed process.
1083+
1084+ Inside TestRunBzrSubprocessCommands we use a stub start_bzr_subprocess
1085+ that will return static results. This assertion method populates those
1086+ results and also checks the arguments run_bzr_subprocess generates.
1087+ """
1088+ self.next_subprocess = process
1089+ try:
1090+ result = self.run_bzr_subprocess(*args, **kwargs)
1091+ except:
1092+ self.next_subprocess = None
1093+ for key, expected in expected_args.iteritems():
1094+ self.assertEqual(expected, self.subprocess_calls[-1][key])
1095+ raise
1096+ else:
1097+ self.next_subprocess = None
1098+ for key, expected in expected_args.iteritems():
1099+ self.assertEqual(expected, self.subprocess_calls[-1][key])
1100+ return result
1101+
1102+ def test_run_bzr_subprocess(self):
1103+ """The run_bzr_helper_external command behaves nicely."""
1104+ self.assertRunBzrSubprocess({'process_args':['--version']},
1105+ StubProcess(), '--version')
1106+ self.assertRunBzrSubprocess({'process_args':['--version']},
1107+ StubProcess(), ['--version'])
1108+ # retcode=None disables retcode checking
1109+ result = self.assertRunBzrSubprocess({},
1110+ StubProcess(retcode=3), '--version', retcode=None)
1111+ result = self.assertRunBzrSubprocess({},
1112+ StubProcess(out="is free software"), '--version')
1113+ self.assertContainsRe(result[0], 'is free software')
1114+ # Running a subcommand that is missing errors
1115+ self.assertRaises(AssertionError, self.assertRunBzrSubprocess,
1116+ {'process_args':['--versionn']}, StubProcess(retcode=3),
1117+ '--versionn')
1118+ # Unless it is told to expect the error from the subprocess
1119+ result = self.assertRunBzrSubprocess({},
1120+ StubProcess(retcode=3), '--versionn', retcode=3)
1121+ # Or to ignore retcode checking
1122+ result = self.assertRunBzrSubprocess({},
1123+ StubProcess(err="unknown command", retcode=3), '--versionn',
1124+ retcode=None)
1125+ self.assertContainsRe(result[1], 'unknown command')
1126+
1127+ def test_env_change_passes_through(self):
1128+ self.assertRunBzrSubprocess(
1129+ {'env_changes':{'new':'value', 'changed':'newvalue', 'deleted':None}},
1130+ StubProcess(), '',
1131+ env_changes={'new':'value', 'changed':'newvalue', 'deleted':None})
1132+
1133+ def test_no_working_dir_passed_as_None(self):
1134+ self.assertRunBzrSubprocess({'working_dir': None}, StubProcess(), '')
1135+
1136+ def test_no_working_dir_passed_through(self):
1137+ self.assertRunBzrSubprocess({'working_dir': 'dir'}, StubProcess(), '',
1138+ working_dir='dir')
1139+
1140+ def test_run_bzr_subprocess_no_plugins(self):
1141+ self.assertRunBzrSubprocess({'allow_plugins': False},
1142+ StubProcess(), '')
1143+
1144+ def test_allow_plugins(self):
1145+ self.assertRunBzrSubprocess({'allow_plugins': True},
1146+ StubProcess(), '', allow_plugins=True)
1147+
1148+
1149+class _DontSpawnProcess(Exception):
1150+ """A simple exception which just allows us to skip unnecessary steps"""
1151+
1152+
1153+class TestStartBzrSubProcess(tests.TestCase):
1154+
1155+ def check_popen_state(self):
1156+ """Replace to make assertions when popen is called."""
1157+
1158+ def _popen(self, *args, **kwargs):
1159+ """Record the command that is run, so that we can ensure it is correct"""
1160+ self.check_popen_state()
1161+ self._popen_args = args
1162+ self._popen_kwargs = kwargs
1163+ raise _DontSpawnProcess()
1164+
1165+ def test_run_bzr_subprocess_no_plugins(self):
1166+ self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [])
1167+ command = self._popen_args[0]
1168+ self.assertEqual(sys.executable, command[0])
1169+ self.assertEqual(self.get_bzr_path(), command[1])
1170+ self.assertEqual(['--no-plugins'], command[2:])
1171+
1172+ def test_allow_plugins(self):
1173+ self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
1174+ allow_plugins=True)
1175+ command = self._popen_args[0]
1176+ self.assertEqual([], command[2:])
1177+
1178+ def test_set_env(self):
1179+ self.failIf('EXISTANT_ENV_VAR' in os.environ)
1180+ # set in the child
1181+ def check_environment():
1182+ self.assertEqual('set variable', os.environ['EXISTANT_ENV_VAR'])
1183+ self.check_popen_state = check_environment
1184+ self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
1185+ env_changes={'EXISTANT_ENV_VAR':'set variable'})
1186+ # not set in theparent
1187+ self.assertFalse('EXISTANT_ENV_VAR' in os.environ)
1188+
1189+ def test_run_bzr_subprocess_env_del(self):
1190+ """run_bzr_subprocess can remove environment variables too."""
1191+ self.failIf('EXISTANT_ENV_VAR' in os.environ)
1192+ def check_environment():
1193+ self.assertFalse('EXISTANT_ENV_VAR' in os.environ)
1194+ os.environ['EXISTANT_ENV_VAR'] = 'set variable'
1195+ self.check_popen_state = check_environment
1196+ self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
1197+ env_changes={'EXISTANT_ENV_VAR':None})
1198+ # Still set in parent
1199+ self.assertEqual('set variable', os.environ['EXISTANT_ENV_VAR'])
1200+ del os.environ['EXISTANT_ENV_VAR']
1201+
1202+ def test_env_del_missing(self):
1203+ self.failIf('NON_EXISTANT_ENV_VAR' in os.environ)
1204+ def check_environment():
1205+ self.assertFalse('NON_EXISTANT_ENV_VAR' in os.environ)
1206+ self.check_popen_state = check_environment
1207+ self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
1208+ env_changes={'NON_EXISTANT_ENV_VAR':None})
1209+
1210+ def test_working_dir(self):
1211+ """Test that we can specify the working dir for the child"""
1212+ orig_getcwd = osutils.getcwd
1213+ orig_chdir = os.chdir
1214+ chdirs = []
1215+ def chdir(path):
1216+ chdirs.append(path)
1217+ os.chdir = chdir
1218+ try:
1219+ def getcwd():
1220+ return 'current'
1221+ osutils.getcwd = getcwd
1222+ try:
1223+ self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
1224+ working_dir='foo')
1225+ finally:
1226+ osutils.getcwd = orig_getcwd
1227+ finally:
1228+ os.chdir = orig_chdir
1229+ self.assertEqual(['foo', 'current'], chdirs)
1230+
1231+
1232+class TestBzrSubprocess(tests.TestCaseWithTransport):
1233+
1234+ def test_start_and_stop_bzr_subprocess(self):
1235+ """We can start and perform other test actions while that process is
1236+ still alive.
1237+ """
1238+ process = self.start_bzr_subprocess(['--version'])
1239+ result = self.finish_bzr_subprocess(process)
1240+ self.assertContainsRe(result[0], 'is free software')
1241+ self.assertEqual('', result[1])
1242+
1243+ def test_start_and_stop_bzr_subprocess_with_error(self):
1244+ """finish_bzr_subprocess allows specification of the desired exit code.
1245+ """
1246+ process = self.start_bzr_subprocess(['--versionn'])
1247+ result = self.finish_bzr_subprocess(process, retcode=3)
1248+ self.assertEqual('', result[0])
1249+ self.assertContainsRe(result[1], 'unknown command')
1250+
1251+ def test_start_and_stop_bzr_subprocess_ignoring_retcode(self):
1252+ """finish_bzr_subprocess allows the exit code to be ignored."""
1253+ process = self.start_bzr_subprocess(['--versionn'])
1254+ result = self.finish_bzr_subprocess(process, retcode=None)
1255+ self.assertEqual('', result[0])
1256+ self.assertContainsRe(result[1], 'unknown command')
1257+
1258+ def test_start_and_stop_bzr_subprocess_with_unexpected_retcode(self):
1259+ """finish_bzr_subprocess raises self.failureException if the retcode is
1260+ not the expected one.
1261+ """
1262+ process = self.start_bzr_subprocess(['--versionn'])
1263+ self.assertRaises(self.failureException, self.finish_bzr_subprocess,
1264+ process)
1265+
1266+ def test_start_and_stop_bzr_subprocess_send_signal(self):
1267+ """finish_bzr_subprocess raises self.failureException if the retcode is
1268+ not the expected one.
1269+ """
1270+ process = self.start_bzr_subprocess(['wait-until-signalled'],
1271+ skip_if_plan_to_signal=True)
1272+ self.assertEqual('running\n', process.stdout.readline())
1273+ result = self.finish_bzr_subprocess(process, send_signal=signal.SIGINT,
1274+ retcode=3)
1275+ self.assertEqual('', result[0])
1276+ self.assertEqual('bzr: interrupted\n', result[1])
1277+
1278+ def test_start_and_stop_working_dir(self):
1279+ cwd = osutils.getcwd()
1280+ self.make_branch_and_tree('one')
1281+ process = self.start_bzr_subprocess(['root'], working_dir='one')
1282+ result = self.finish_bzr_subprocess(process, universal_newlines=True)
1283+ self.assertEndsWith(result[0], 'one\n')
1284+ self.assertEqual('', result[1])
1285+
1286
1287 class TestKnownFailure(tests.TestCase):
1288
1289@@ -1840,8 +2364,8 @@
1290 tests.TestCase.setUp(self)
1291 self.suite = TestUtil.TestSuite()
1292 self.loader = TestUtil.TestLoader()
1293- self.suite.addTest(self.loader.loadTestsFromModuleNames([
1294- 'bzrlib.tests.test_selftest']))
1295+ self.suite.addTest(self.loader.loadTestsFromModule(
1296+ sys.modules['bzrlib.tests.test_selftest']))
1297 self.all_names = _test_ids(self.suite)
1298
1299 def test_condition_id_re(self):
1300@@ -2158,8 +2682,8 @@
1301 class TestTestSuite(tests.TestCase):
1302
1303 def test_test_suite(self):
1304- # This test is slow, so we do a single test with one test in each
1305- # category
1306+ # This test is slow - it loads the entire test suite to operate, so we
1307+ # do a single test with one test in each category
1308 test_list = [
1309 # testmod_names
1310 'bzrlib.tests.blackbox.test_branch.TestBranch.test_branch',
1311@@ -2175,6 +2699,9 @@
1312 self.assertEquals(test_list, _test_ids(suite))
1313
1314 def test_test_suite_list_and_start(self):
1315+ # We cannot test this at the same time as the main load, because we want
1316+ # to know that starting_with == None works. So a second full load is
1317+ # incurred.
1318 test_list = ['bzrlib.tests.test_selftest.TestTestSuite.test_test_suite']
1319 suite = tests.test_suite(test_list,
1320 ['bzrlib.tests.test_selftest.TestTestSuite'])