Rev 4607: Merge shell-like-tests into description in file:///home/vila/src/bzr/experimental/conflict-manager/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Wed Sep 9 13:11:52 BST 2009
At file:///home/vila/src/bzr/experimental/conflict-manager/
------------------------------------------------------------
revno: 4607 [merge]
revision-id: v.ladeuil+lp at free.fr-20090909121151-cdj33eyvs7m3f2bf
parent: v.ladeuil+lp at free.fr-20090909120924-j9sucms358rz4o5w
parent: v.ladeuil+lp at free.fr-20090909121141-8efjrvlz3hgen3kc
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: description
timestamp: Wed 2009-09-09 14:11:51 +0200
message:
Merge shell-like-tests into description
modified:
bzrlib/tests/script.py script.py-20090901081155-yk3tiy1nunxg16ne-1
bzrlib/tests/test_script.py test_script.py-20090901081156-y90z4w2t62fv7e7b-1
-------------- next part --------------
=== modified file 'bzrlib/tests/script.py'
--- a/bzrlib/tests/script.py 2009-09-01 15:45:52 +0000
+++ b/bzrlib/tests/script.py 2009-09-04 14:55:59 +0000
@@ -13,10 +13,89 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-from cStringIO import StringIO
+"""Shell-like test scripts.
+
+This allows users to write tests in a syntax very close to a shell session,
+using a restricted and limited set of commands that should be enough to mimic
+most of the behaviours.
+
+A script is a set of commands, each command is composed of:
+- one mandatory command line,
+- one optional set of input lines to feed the command,
+- one optional set of output expected lines,
+- one optional set of error expected lines.
+
+The optional lines starts with a special string (mnemonic: shell redirection):
+- '<' for input,
+- '>' for output,
+- '2>' for errors,
+
+The execution stops as soon as an expected output or an expected error is not
+matched.
+
+When no output is specified, any ouput from the command is accepted
+and let the execution continue.
+
+If an error occurs and no expected error is specified, the execution stops.
+
+The matching is done on a full string comparison basis unless '...' is used, in
+which case expected output/errors can be lees precise.
+
+Examples:
+
+The following will succeeds only if 'bzr add' outputs 'adding file'.
+
+ bzr add file
+ >adding file
+
+If you want the command to succeed for any output, just use:
+
+ bzr add file
+
+The following will stop with an error:
+
+ bzr not-a-command
+
+If you want it to succeed, use:
+
+ bzr not-a-command
+ 2> bzr: ERROR: unknown command "not-a-command"
+
+You can use ellipsis (...) to replace any piece of text you don't want to be
+matched exactly:
+
+ bzr branch not-a-branch
+ 2>bzr: ERROR: Not a branch...not-a-branch/".
+
+
+This can be used to ignore entire lines too:
+
+cat
+<first line
+<second line
+<third line
+<fourth line
+<last line
+>first line
+>...
+>last line
+
+You can check the content of a file with cat:
+
+ cat <file
+ >expected content
+
+You can also check the existence of a file with cat, the following will fail if
+the file doesn't exist:
+
+ cat file
+
+"""
+
+import doctest
import os
import shlex
+from cStringIO import StringIO
from bzrlib import (
osutils,
@@ -144,21 +223,33 @@
return in_name, out_name, out_mode, remaining
-class TestCaseWithScript(tests.TestCaseWithTransport):
+class ScriptRunner(object):
- def setUp(self):
- super(TestCaseWithScript, self).setUp()
- self._vars = {}
+ def __init__(self, test_case):
+ self.test_case = test_case
+ self.output_checker = doctest.OutputChecker()
+ self.check_options = doctest.ELLIPSIS
def run_script(self, text):
for cmd, input, output, error in _script_to_commands(text):
- self.run_command(cmd, input, output, error)
+ out, err = self.run_command(cmd, input, output, error)
def _check_output(self, expected, actual):
if expected is None:
# Specifying None means: any output is accepted
return
- self.assertEquals(expected, actual)
+ if actual is None:
+ self.test_case.fail('Unexpected: %s' % actual)
+ matching = self.output_checker.check_output(
+ expected, actual, self.check_options)
+ if not matching:
+ # Note that we can't use output_checker.output_difference() here
+ # because... the API is boken (expected must be a doctest specific
+ # object of whicha 'want' attribute will be our 'expected'
+ # parameter. So we just fallbacl to our good old assertEqualDiff
+ # since we know there are differences and the output should be
+ # decently readable.
+ self.test_case.assertEqualDiff(expected, actual)
def run_command(self, cmd, input, output, error):
mname = 'do_' + cmd[0]
@@ -174,6 +265,8 @@
self._check_output(output, actual_output)
self._check_output(error, actual_error)
+ if not error and actual_error:
+ self.test_case.fail('Unexpected error: %s' % actual_error)
return actual_output, actual_error
def _read_input(self, input, in_name):
@@ -197,8 +290,8 @@
return output
def do_bzr(self, input, args):
- out, err = self._run_bzr_core(args, retcode=None, encoding=None,
- stdin=input, working_dir=None)
+ out, err = self.test_case._run_bzr_core(
+ args, retcode=None, encoding=None, stdin=input, working_dir=None)
return out, err
def do_cat(self, input, args):
@@ -232,8 +325,9 @@
return output, None
def _ensure_in_jail(self, path):
- if not osutils.is_inside(self.test_dir, osutils.normalizepath(path)):
- raise ValueError('%s is not inside %s' % (path, self.test_dir))
+ 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))
def do_cd(self, input, args):
if len(args) > 1:
@@ -242,7 +336,7 @@
d = args[0]
self._ensure_in_jail(d)
else:
- d = self.test_dir
+ d = self.test_case.get_jail_root()
os.chdir(d)
return None, None
@@ -254,3 +348,42 @@
os.mkdir(d)
return None, None
+
+class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
+
+ 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)
+
+ def run_script(self, script):
+ return self.script_runner.run_script(script)
+
+ def run_command(self, cmd, input, output, error):
+ return self.script_runner.run_command(cmd, input, output, error)
+
+
+class TestCaseWithTransportAndScript(tests.TestCaseWithTransport):
+
+ 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
+
+ def run_script(self, script):
+ return self.script_runner.run_script(script)
+
+ def run_command(self, cmd, input, output, error):
+ return self.script_runner.run_command(cmd, input, output, error)
=== modified file 'bzrlib/tests/test_script.py'
--- a/bzrlib/tests/test_script.py 2009-09-01 15:45:52 +0000
+++ b/bzrlib/tests/test_script.py 2009-09-04 14:55:59 +0000
@@ -83,12 +83,12 @@
script._script_to_commands(story))
-class TestScriptExecution(script.TestCaseWithScript):
+class TestScriptExecution(script.TestCaseWithTransportAndScript):
def test_unknown_command(self):
self.assertRaises(SyntaxError, self.run_script, 'foo')
- def test_unexpected_output(self):
+ def test_stops_on_unexpected_output(self):
story = """
mkdir dir
cd dir
@@ -97,7 +97,46 @@
self.assertRaises(AssertionError, self.run_script, story)
-class TestCat(script.TestCaseWithScript):
+ def test_stops_on_unexpected_error(self):
+ story = """
+cat
+<Hello
+bzr not-a-command
+"""
+ self.assertRaises(AssertionError, self.run_script, story)
+
+ def test_continue_on_expected_error(self):
+ story = """
+bzr not-a-command
+2>..."not-a-command"
+"""
+ self.run_script(story)
+
+ def test_ellipsis_output(self):
+ story = """
+cat
+<first line
+<second line
+<last line
+>first line
+>...
+>last line
+"""
+ self.run_script(story)
+ story = """
+bzr not-a-command
+2>..."not-a-command"
+"""
+ self.run_script(story)
+
+ story = """
+bzr branch not-a-branch
+2>bzr: ERROR: Not a branch...not-a-branch/".
+"""
+ self.run_script(story)
+
+
+class TestCat(script.TestCaseWithTransportAndScript):
def test_cat_usage(self):
self.assertRaises(SyntaxError, self.run_script, 'cat foo bar baz')
@@ -130,7 +169,7 @@
self.assertFileEqual('content\n', 'file2')
-class TestMkdir(script.TestCaseWithScript):
+class TestMkdir(script.TestCaseWithTransportAndScript):
def test_mkdir_usage(self):
self.assertRaises(SyntaxError, self.run_script, 'mkdir')
@@ -151,7 +190,7 @@
self.failUnlessExists('dir2')
-class TestCd(script.TestCaseWithScript):
+class TestCd(script.TestCaseWithTransportAndScript):
def test_cd_usage(self):
self.assertRaises(SyntaxError, self.run_script, 'cd foo bar')
@@ -173,14 +212,14 @@
self.assertEquals(self.test_dir, osutils.getcwd())
-class TestBzr(script.TestCaseWithScript):
+class TestBzr(script.TestCaseWithTransportAndScript):
def test_bzr_smoke(self):
self.run_script('bzr init branch')
self.failUnlessExists('branch')
-class TestEcho(script.TestCaseWithScript):
+class TestEcho(script.TestCaseWithMemoryTransportAndScript):
def test_echo_usage(self):
story = """
More information about the bazaar-commits
mailing list