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