Rev 4714: Last tweaks and NEWS enrty for shell-like tests in http://bazaar.launchpad.net/~vila/bzr/integration
Vincent Ladeuil
v.ladeuil+lp at free.fr
Thu Sep 24 07:54:00 BST 2009
At http://bazaar.launchpad.net/~vila/bzr/integration
------------------------------------------------------------
revno: 4714 [merge]
revision-id: v.ladeuil+lp at free.fr-20090924065343-nszjtbyg3x5swu0k
parent: pqm at pqm.ubuntu.com-20090924064418-h9ke4tfywi57otzi
parent: v.ladeuil+lp at free.fr-20090918150344-ncxyjrv2vlwnfesn
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: integration
timestamp: Thu 2009-09-24 08:53:43 +0200
message:
Last tweaks and NEWS enrty for shell-like tests
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/tests/script.py script.py-20090901081155-yk3tiy1nunxg16ne-1
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS 2009-09-23 06:54:14 +0000
+++ b/NEWS 2009-09-24 06:53:43 +0000
@@ -118,6 +118,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)
@@ -154,6 +156,10 @@
* Setting ``BZR_TEST_PDB=1`` when running selftest will cause a pdb
post_mortem to be triggered when a test failure occurs. (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-19 16:14:10 +0000
+++ b/bzrlib/tests/script.py 2009-09-24 06:53:43 +0000
@@ -158,22 +158,66 @@
class ScriptRunner(object):
-
- def __init__(self, test_case):
- self.test_case = test_case
+ """Run a shell-like script from a test.
+
+ Can be used as:
+
+ from bzrlib.tests import script
+
+ ...
+
+ def test_bug_nnnnn(self):
+ sr = script.ScriptRunner()
+ sr.run_script(self, '''
+ $ bzr init
+ $ bzr do-this
+ # Boom, error
+ ''')
+ """
+
+ def __init__(self):
self.output_checker = doctest.OutputChecker()
self.check_options = doctest.ELLIPSIS
- def run_script(self, text):
+ def run_script(self, test_case, text):
+ """Run a shell-like script as a test.
+
+ :param test_case: A TestCase instance that should provide the fail(),
+ assertEqualDiff and _run_bzr_core() methods as well as a 'test_dir'
+ attribute used as a jail root.
+
+ :param text: A shell-like script (see _script_to_commands for syntax).
+ """
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(test_case, cmd, input, output, error)
+
+ def run_command(self, test_case, 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(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 +227,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 +249,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 +269,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 +302,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')
@@ -301,31 +325,35 @@
return 1, None, '%s: No such file or directory\n' % (out_name,)
return 0, output, None
- def _ensure_in_jail(self, path):
- jail_root = self.test_case.get_jail_root()
+ def _get_jail_root(self, test_case):
+ return test_case.test_dir
+
+ def _ensure_in_jail(self, test_case, path):
+ jail_root = self._get_jail_root(test_case)
if not osutils.is_inside(jail_root, osutils.normalizepath(path)):
raise ValueError('%s is not inside %s' % (path, 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)
+ self._ensure_in_jail(test_case, d)
else:
- d = self.test_case.get_jail_root()
+ # The test "home" directory is the root of its jail
+ d = self._get_jail_root(test_case)
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]
- self._ensure_in_jail(d)
+ self._ensure_in_jail(test_case, d)
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):
@@ -344,7 +372,7 @@
if not args or opts:
raise SyntaxError('Usage: rm [-fr] path+')
for p in args:
- self._ensure_in_jail(p)
+ self._ensure_in_jail(test_case, p)
# FIXME: Should we put that in osutils ?
try:
os.remove(p)
@@ -371,40 +399,49 @@
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)
+ self.script_runner = ScriptRunner()
def run_script(self, script):
- return self.script_runner.run_script(script)
+ return self.script_runner.run_script(self, script)
def run_command(self, cmd, input, output, error):
- return self.script_runner.run_command(cmd, input, output, error)
+ return self.script_runner.run_command(self, cmd, input, output, error)
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()
def run_script(self, script):
- return self.script_runner.run_script(script)
+ return self.script_runner.run_script(self, script)
def run_command(self, cmd, input, output, error):
- return self.script_runner.run_command(cmd, input, output, error)
+ return self.script_runner.run_command(self, cmd, input, output, error)
+
More information about the bazaar-commits
mailing list