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