Rev 4687: Try to relax the test_case/script runner coupling. in file:///home/vila/src/bzr/experimental/shell-like-tests/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Fri Sep 18 13:54:01 BST 2009
At file:///home/vila/src/bzr/experimental/shell-like-tests/
------------------------------------------------------------
revno: 4687
revision-id: v.ladeuil+lp at free.fr-20090918125401-fb3gl822abfc8omt
parent: v.ladeuil+lp at free.fr-20090918084823-pul3e5pbvb4gvrff
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: shell-like-tests
timestamp: Fri 2009-09-18 14:54:01 +0200
message:
Try to relax the test_case/script runner coupling.
* bzrlib/tests/script.py:
(ScriptRunner.__init__): Don't keep the test_case as an attribute,
require the jail_root instead.
(ScriptRunner.run_script, ScriptRunner.run_command,
ScriptRunner._check_output): Add a test_case parameter to be able
to use fail() and assertEqualDiff().
(ScriptRunner.do_bzr): Add a test_case parameter to be able to use
_run_bzr_core().
(ScriptRunner.do_cat, ScriptRunner.do_echo, ScriptRunner,
ScriptRunner.do_mkdir, ScriptRunner.do_rm): Add a test_case
parameter to be able... to be ready for the memory based resources
only implementation :)
(TestCaseWithMemoryTransportAndScript,
TestCaseWithTransportAndScript): Add docstring describing the
intent.
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS 2009-09-07 03:35:06 +0000
+++ b/NEWS 2009-09-18 12:54:01 +0000
@@ -72,6 +72,8 @@
Documentation
*************
+* Describe the new shell-like test feature. (Vincent Ladeuil)
+
* Help on hooks no longer says 'Not deprecated' for hooks that are
currently supported. (Ian Clatworthy, #422415)
@@ -99,6 +101,10 @@
* Passing ``--lsprof-tests -v`` to bzr selftest will cause lsprof output to
be output for every test. Note that this is very verbose! (Robert Collins)
+* Shell-like tests can now be written. Code in ``bzrlib/tests/script.py`` ,
+ documentation in ``developers/testing.txt`` for details.
+ (Vincent Ladeuil)
+
* Test parameterisation now does a shallow copy, not a deep copy of the test
to be parameterised. This is not expected to break external use of test
parameterisation, and is substantially faster. (Robert Collins)
=== modified file 'bzrlib/tests/script.py'
--- a/bzrlib/tests/script.py 2009-09-18 08:48:23 +0000
+++ b/bzrlib/tests/script.py 2009-09-18 12:54:01 +0000
@@ -159,21 +159,42 @@
class ScriptRunner(object):
- def __init__(self, test_case):
- self.test_case = test_case
+ def __init__(self, jail_root='/'):
+ self.jail_root = jail_root
self.output_checker = doctest.OutputChecker()
self.check_options = doctest.ELLIPSIS
- def run_script(self, text):
+ def run_script(self, text, test_case):
for cmd, input, output, error in _script_to_commands(text):
- self.run_command(cmd, input, output, error)
-
- def _check_output(self, expected, actual):
+ self.run_command(cmd, input, output, error, test_case)
+
+ def run_command(self, cmd, input, output, error, test_case):
+ mname = 'do_' + cmd[0]
+ method = getattr(self, mname, None)
+ if method is None:
+ raise SyntaxError('Command not found "%s"' % (cmd[0],),
+ None, 1, ' '.join(cmd))
+ if input is None:
+ str_input = ''
+ else:
+ str_input = ''.join(input)
+ args = list(self._pre_process_args(cmd[1:]))
+ retcode, actual_output, actual_error = method(test_case,
+ str_input, args)
+
+ self._check_output(output, actual_output, test_case)
+ self._check_output(error, actual_error, test_case)
+ if retcode and not error and actual_error:
+ test_case.fail('In \n\t%s\nUnexpected error: %s'
+ % (' '.join(cmd), actual_error))
+ return retcode, actual_output, actual_error
+
+ def _check_output(self, expected, actual, test_case):
if expected is None:
# Specifying None means: any output is accepted
return
if actual is None:
- self.test_case.fail('Unexpected: %s' % actual)
+ test_case.fail('Unexpected: %s' % actual)
matching = self.output_checker.check_output(
expected, actual, self.check_options)
if not matching:
@@ -183,7 +204,7 @@
# 'expected' parameter. So we just fallback to our good old
# assertEqualDiff since we know there *are* differences and the
# output should be decently readable.
- self.test_case.assertEqualDiff(expected, actual)
+ test_case.assertEqualDiff(expected, actual)
def _pre_process_args(self, args):
new_args = []
@@ -205,26 +226,6 @@
else:
yield arg
- def run_command(self, cmd, input, output, error):
- mname = 'do_' + cmd[0]
- method = getattr(self, mname, None)
- if method is None:
- raise SyntaxError('Command not found "%s"' % (cmd[0],),
- None, 1, ' '.join(cmd))
- if input is None:
- str_input = ''
- else:
- str_input = ''.join(input)
- args = list(self._pre_process_args(cmd[1:]))
- retcode, actual_output, actual_error = method(str_input, args)
-
- self._check_output(output, actual_output)
- self._check_output(error, actual_error)
- if retcode and not error and actual_error:
- self.test_case.fail('In \n\t%s\nUnexpected error: %s'
- % (' '.join(cmd), actual_error))
- return retcode, actual_output, actual_error
-
def _read_input(self, input, in_name):
if in_name is not None:
infile = open(in_name, 'rb')
@@ -245,12 +246,12 @@
output = None
return output
- def do_bzr(self, input, args):
- retcode, out, err = self.test_case._run_bzr_core(
+ def do_bzr(self, test_case, input, args):
+ retcode, out, err = test_case._run_bzr_core(
args, retcode=None, encoding=None, stdin=input, working_dir=None)
return retcode, out, err
- def do_cat(self, input, args):
+ def do_cat(self, test_case, input, args):
(in_name, out_name, out_mode, args) = _scan_redirection_options(args)
if args and in_name is not None:
raise SyntaxError('Specify a file OR use redirection')
@@ -278,7 +279,7 @@
return 1, None, '%s: No such file or directory\n' % (out_name,)
return 0, output, None
- def do_echo(self, input, args):
+ def do_echo(self, test_case, input, args):
(in_name, out_name, out_mode, args) = _scan_redirection_options(args)
if input and args:
raise SyntaxError('Specify parameters OR use redirection')
@@ -302,22 +303,21 @@
return 0, output, None
def _ensure_in_jail(self, path):
- jail_root = self.test_case.get_jail_root()
- if not osutils.is_inside(jail_root, osutils.normalizepath(path)):
- raise ValueError('%s is not inside %s' % (path, jail_root))
+ if not osutils.is_inside(self.jail_root, osutils.normalizepath(path)):
+ raise ValueError('%s is not inside %s' % (path, self.jail_root))
- def do_cd(self, input, args):
+ def do_cd(self, test_case, input, args):
if len(args) > 1:
raise SyntaxError('Usage: cd [dir]')
if len(args) == 1:
d = args[0]
self._ensure_in_jail(d)
else:
- d = self.test_case.get_jail_root()
+ d = self.jail_root
os.chdir(d)
return 0, None, None
- def do_mkdir(self, input, args):
+ def do_mkdir(self, test_case, input, args):
if not args or len(args) != 1:
raise SyntaxError('Usage: mkdir dir')
d = args[0]
@@ -325,7 +325,7 @@
os.mkdir(d)
return 0, None, None
- def do_rm(self, input, args):
+ def do_rm(self, test_case, input, args):
err = None
def error(msg, path):
@@ -369,40 +369,50 @@
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
+ """Helper class to experiment shell-like test and memory fs.
+
+ This not intended to be used outside of experiments in implementing memoy
+ based file systems and evolving bzr so that test can use only memory based
+ resources.
+ """
def setUp(self):
super(TestCaseWithMemoryTransportAndScript, self).setUp()
- self.script_runner = ScriptRunner(self)
- # Break the circular dependency
- def break_dependency():
- self.script_runner = None
- self.addCleanup(break_dependency)
-
- def get_jail_root(self):
- raise NotImplementedError(self.get_jail_root)
+ # We don't have a jail_root to provide here. Yet.
+ self.script_runner = ScriptRunner()
def run_script(self, script):
- return self.script_runner.run_script(script)
+ return self.script_runner.run_script(script, self)
def run_command(self, cmd, input, output, error):
- return self.script_runner.run_command(cmd, input, output, error)
+ return self.script_runner.run_command(cmd, input, output, error, self)
class TestCaseWithTransportAndScript(tests.TestCaseWithTransport):
+ """Helper class to quickly define shell-like tests.
+
+ Can be used as:
+
+ from bzrlib.tests import script
+
+
+ class TestBug(script.TestCaseWithTransportAndScript):
+
+ def test_bug_nnnnn(self):
+ self.run_script('''
+ $ bzr init
+ $ bzr do-this
+ # Boom, error
+ ''')
+ """
def setUp(self):
super(TestCaseWithTransportAndScript, self).setUp()
- self.script_runner = ScriptRunner(self)
- # Break the circular dependency
- def break_dependency():
- self.script_runner = None
- self.addCleanup(break_dependency)
-
- def get_jail_root(self):
- return self.test_dir
+ self.script_runner = ScriptRunner(self.test_dir)
def run_script(self, script):
- return self.script_runner.run_script(script)
+ return self.script_runner.run_script(script, self)
def run_command(self, cmd, input, output, error):
- return self.script_runner.run_command(cmd, input, output, error)
+ return self.script_runner.run_command(cmd, input, output, error, self)
+
More information about the bazaar-commits
mailing list