Rev 4603: Merge shell-like tests branch in file:///home/vila/src/bzr/experimental/conflict-manager/

Vincent Ladeuil v.ladeuil+lp at free.fr
Tue Sep 1 17:16:59 BST 2009


At file:///home/vila/src/bzr/experimental/conflict-manager/

------------------------------------------------------------
revno: 4603 [merge]
revision-id: v.ladeuil+lp at free.fr-20090901161659-vbwarv3g436lfbft
parent: v.ladeuil+lp at free.fr-20090831141203-lh84j2cgxepxs4wa
parent: v.ladeuil+lp at free.fr-20090901154552-bixxt5tircyhds3a
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: shell-like-tests
timestamp: Tue 2009-09-01 18:16:59 +0200
message:
  Merge shell-like tests branch
added:
  bzrlib/tests/script.py         script.py-20090901081155-yk3tiy1nunxg16ne-1
  bzrlib/tests/test_script.py    test_script.py-20090901081156-y90z4w2t62fv7e7b-1
modified:
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
-------------- next part --------------
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-08-28 21:05:31 +0000
+++ b/bzrlib/tests/__init__.py	2009-09-01 08:24:44 +0000
@@ -3618,6 +3618,7 @@
         'bzrlib.tests.test_rio',
         'bzrlib.tests.test_rules',
         'bzrlib.tests.test_sampler',
+        'bzrlib.tests.test_script',
         'bzrlib.tests.test_selftest',
         'bzrlib.tests.test_serializer',
         'bzrlib.tests.test_setup',

=== added file 'bzrlib/tests/script.py'
--- a/bzrlib/tests/script.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/script.py	2009-09-01 15:45:52 +0000
@@ -0,0 +1,256 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# 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
+import os
+import shlex
+
+from bzrlib import (
+    osutils,
+    tests,
+    )
+
+
+def split(s):
+    """Split a command line respecting quotes."""
+    scanner = shlex.shlex(s)
+    scanner.quotes = '\'"`'
+    scanner.whitespace_split = True
+    for t in list(scanner):
+        # Strip the simple and double quotes since we don't care about them.
+        # We leave the backquotes in place though since they have a different
+        # semantic.
+        if t[0] in  ('"', "'") and t[0] == t[-1]:
+            yield t[1:-1]
+        else:
+            yield t
+
+
+def _script_to_commands(text, file_name=None):
+    """Turn a script into a list of commands with their associated IOs.
+
+    Each command appears on a line by itself. It can be associated with an
+    input that will feed it and an expected output.
+    Comments starts with '#' until the end of line.
+    Empty lines are ignored.
+    Input and output are full lines terminated by a '\n'.
+    Input lines start with '<'.
+    Output lines start with '>'.
+    Error lines start with '2>'.
+    """
+
+    commands = []
+
+    def add_command(cmd, input, output, error):
+        if cmd is not None:
+            if input is not None:
+                input = ''.join(input)
+            if output is not None:
+                output = ''.join(output)
+            if error is not None:
+                error = ''.join(error)
+            commands.append((cmd, input, output, error))
+
+    cmd_cur = None
+    cmd_line = 1
+    lineno = 0
+    input, output, error = None, None, None
+    for line in text.split('\n'):
+        lineno += 1
+        # Keep a copy for error reporting
+        orig = line
+        comment =  line.find('#')
+        if comment >= 0:
+            # Delete comments
+            line = line[0:comment]
+            line = line.rstrip()
+        if line == '':
+            # Ignore empty lines
+            continue
+        if line.startswith('<'):
+            if input is None:
+                if cmd_cur is None:
+                    raise SyntaxError('No command for that input',
+                                      (file_name, lineno, 1, orig))
+                input = []
+            input.append(line[1:] + '\n')
+            continue
+        elif line.startswith('>'):
+            if output is None:
+                if cmd_cur is None:
+                    raise SyntaxError('No command for that output',
+                                      (file_name, lineno, 1, orig))
+                output = []
+            output.append(line[1:] + '\n')
+            continue
+        elif line.startswith('2>'):
+            if error is None:
+                if cmd_cur is None:
+                    raise SyntaxError('No command for that error',
+                                      (file_name, lineno, 1, orig))
+                error = []
+            error.append(line[2:] + '\n')
+            continue
+        else:
+            # Time to output the current command
+            add_command(cmd_cur, input, output, error)
+            # And start a new one
+            cmd_cur = list(split(line))
+            cmd_line = lineno
+            input, output, error = None, None, None
+    # Add the last seen command
+    add_command(cmd_cur, input, output, error)
+    return commands
+
+
+def _scan_redirection_options(args):
+    """Recognize and process input and output redirections.
+
+    :param args: The command line arguments
+
+    :return: A tuple containing: 
+        - The file name redirected from or None
+        - The file name redirected to or None
+        - The mode to open the output file or None
+        - The reamining arguments
+    """
+    remaining = []
+    in_name = None
+    out_name, out_mode = None, None
+    for arg in  args:
+        if arg.startswith('<'):
+            in_name = arg[1:]
+        elif arg.startswith('>>'):
+            out_name = arg[2:]
+            out_mode = 'ab+'
+        elif arg.startswith('>'):
+            out_name = arg[1:]
+            out_mode = 'wb+'
+        else:
+            remaining.append(arg)
+    return in_name, out_name, out_mode, remaining
+
+
+class TestCaseWithScript(tests.TestCaseWithTransport):
+
+    def setUp(self):
+        super(TestCaseWithScript, self).setUp()
+        self._vars = {}
+
+    def run_script(self, text):
+        for cmd, input, output, error in _script_to_commands(text):
+            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)
+
+    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)
+        actual_output, actual_error = method(str_input, cmd[1:])
+
+        self._check_output(output, actual_output)
+        self._check_output(error, actual_error)
+        return actual_output, actual_error
+
+    def _read_input(self, input, in_name):
+        if in_name is not None:
+            infile = open(in_name, 'rb')
+            try:
+                # Command redirection takes precedence over provided input
+                input = infile.read()
+            finally:
+                infile.close()
+        return input
+
+    def _write_output(self, output, out_name, out_mode):
+        if out_name is not None:
+            outfile = open(out_name, out_mode)
+            try:
+                outfile.write(output)
+            finally:
+                outfile.close()
+            output = None
+        return output
+
+    def do_bzr(self, input, args):
+        out, err = self._run_bzr_core(args, retcode=None, encoding=None,
+                                      stdin=input, working_dir=None)
+        return out, err
+
+    def do_cat(self, input, args):
+        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
+        if len(args) > 1:
+            raise SyntaxError('Usage: cat [file1]')
+        if args:
+            if in_name is not None:
+                raise SyntaxError('Specify a file OR use redirection')
+            in_name = args[0]
+        input = self._read_input(input, in_name)
+        # Basically cat copy input to output
+        output = input
+        # Handle output redirections
+        output = self._write_output(output, out_name, out_mode)
+        return output, None
+
+    def do_echo(self, input, args):
+        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
+        if input and args:
+                raise SyntaxError('Specify parameters OR use redirection')
+        if args:
+            input = ''.join(args)
+        input = self._read_input(input, in_name)
+        # Always append a \n'
+        input += '\n'
+        # Process output
+        output = input
+        # Handle output redirections
+        output = self._write_output(output, out_name, out_mode)
+        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))
+
+    def do_cd(self, 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_dir
+        os.chdir(d)
+        return None, None
+
+    def do_mkdir(self, input, args):
+        if not args or len(args) != 1:
+            raise SyntaxError('Usage: mkdir dir')
+        d = args[0]
+        self._ensure_in_jail(d)
+        os.mkdir(d)
+        return None, None
+

=== added file 'bzrlib/tests/test_script.py'
--- a/bzrlib/tests/test_script.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/test_script.py	2009-09-01 15:45:52 +0000
@@ -0,0 +1,218 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# 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 bzrlib import (
+    osutils,
+    tests,
+    )
+from bzrlib.tests import script
+
+
+class TestScriptSyntax(tests.TestCase):
+
+    def test_comment_is_ignored(self):
+        self.assertEquals([], script._script_to_commands('#comment\n'))
+
+    def test_empty_line_is_ignored(self):
+        self.assertEquals([], script._script_to_commands('\n'))
+
+    def test_simple_command(self):
+        self.assertEquals([(['cd', 'trunk'], None, None, None)],
+                           script._script_to_commands('cd trunk'))
+
+    def test_command_with_single_quoted_param(self):
+        story = """bzr commit -m 'two words'"""
+        self.assertEquals([(['bzr', 'commit', '-m', 'two words'],
+                            None, None, None)],
+                           script._script_to_commands(story))
+
+    def test_command_with_double_quoted_param(self):
+        story = """bzr commit -m "two words" """
+        self.assertEquals([(['bzr', 'commit', '-m', 'two words'],
+                            None, None, None)],
+                           script._script_to_commands(story))
+
+    def test_command_with_input(self):
+        self.assertEquals([(['cat', '>file'], 'content\n', None, None)],
+                           script._script_to_commands('cat >file\n<content\n'))
+
+    def test_command_with_output(self):
+        story = """
+bzr add
+>adding file
+>adding file2
+"""
+        self.assertEquals([(['bzr', 'add'], None,
+                            'adding file\nadding file2\n', None)],
+                          script._script_to_commands(story))
+
+    def test_command_with_error(self):
+        story = """
+bzr branch foo
+2>bzr: ERROR: Not a branch: "foo"
+"""
+        self.assertEquals([(['bzr', 'branch', 'foo'],
+                            None, None, 'bzr: ERROR: Not a branch: "foo"\n')],
+                          script._script_to_commands(story))
+    def test_input_without_command(self):
+        self.assertRaises(SyntaxError, script._script_to_commands, '<input')
+
+    def test_output_without_command(self):
+        self.assertRaises(SyntaxError, script._script_to_commands, '>input')
+
+    def test_command_with_backquotes(self):
+        story = """
+foo = `bzr file-id toto`
+"""
+        self.assertEquals([(['foo', '=', '`bzr file-id toto`'],
+                            None, None, None)],
+                          script._script_to_commands(story))
+
+
+class TestScriptExecution(script.TestCaseWithScript):
+
+    def test_unknown_command(self):
+        self.assertRaises(SyntaxError, self.run_script, 'foo')
+
+    def test_unexpected_output(self):
+        story = """
+mkdir dir
+cd dir
+>Hello, I have just cd into dir !
+"""
+        self.assertRaises(AssertionError, self.run_script, story)
+
+
+class TestCat(script.TestCaseWithScript):
+
+    def test_cat_usage(self):
+        self.assertRaises(SyntaxError, self.run_script, 'cat foo bar baz')
+        self.assertRaises(SyntaxError, self.run_script, 'cat foo <bar')
+
+    def test_cat_input_to_output(self):
+        out, err = self.run_command(['cat'], 'content\n', 'content\n', None)
+        self.assertEquals('content\n', out)
+        self.assertEquals(None, err)
+
+    def test_cat_file_to_output(self):
+        self.build_tree_contents([('file', 'content\n')])
+        out, err = self.run_command(['cat', 'file'], None, 'content\n', None)
+        self.assertEquals('content\n', out)
+        self.assertEquals(None, err)
+
+    def test_cat_input_to_file(self):
+        out, err = self.run_command(['cat', '>file'], 'content\n', None, None)
+        self.assertFileEqual('content\n', 'file')
+        self.assertEquals(None, out)
+        self.assertEquals(None, err)
+        out, err = self.run_command(['cat', '>>file'], 'more\n', None, None)
+        self.assertFileEqual('content\nmore\n', 'file')
+        self.assertEquals(None, out)
+        self.assertEquals(None, err)
+
+    def test_cat_file_to_file(self):
+        self.build_tree_contents([('file', 'content\n')])
+        out, err = self.run_command(['cat', 'file', '>file2'], None, None, None)
+        self.assertFileEqual('content\n', 'file2')
+
+
+class TestMkdir(script.TestCaseWithScript):
+
+    def test_mkdir_usage(self):
+        self.assertRaises(SyntaxError, self.run_script, 'mkdir')
+        self.assertRaises(SyntaxError, self.run_script, 'mkdir foo bar')
+
+    def test_mkdir_jailed(self):
+        self.assertRaises(ValueError, self.run_script, 'mkdir /out-of-jail')
+        self.assertRaises(ValueError, self.run_script, 'mkdir ../out-of-jail')
+
+    def test_mkdir_in_jail(self):
+        self.run_script("""
+mkdir dir
+cd dir
+mkdir ../dir2
+cd ..
+""")
+        self.failUnlessExists('dir')
+        self.failUnlessExists('dir2')
+
+
+class TestCd(script.TestCaseWithScript):
+
+    def test_cd_usage(self):
+        self.assertRaises(SyntaxError, self.run_script, 'cd foo bar')
+
+    def test_cd_out_of_jail(self):
+        self.assertRaises(ValueError, self.run_script, 'cd /out-of-jail')
+        self.assertRaises(ValueError, self.run_script, 'cd ..')
+
+    def test_cd_dir_and_back_home(self):
+        self.assertEquals(self.test_dir, osutils.getcwd())
+        self.run_script("""
+mkdir dir
+cd dir
+""")
+        self.assertEquals(osutils.pathjoin(self.test_dir, 'dir'),
+                          osutils.getcwd())
+
+        self.run_script('cd')
+        self.assertEquals(self.test_dir, osutils.getcwd())
+
+
+class TestBzr(script.TestCaseWithScript):
+
+    def test_bzr_smoke(self):
+        self.run_script('bzr init branch')
+        self.failUnlessExists('branch')
+
+
+class TestEcho(script.TestCaseWithScript):
+
+    def test_echo_usage(self):
+        story = """
+echo foo
+<bar
+"""
+        self.assertRaises(SyntaxError, self.run_script, story)
+
+    def test_echo_to_output(self):
+        out, err = self.run_command(['echo'], None, '\n', None)
+        self.assertEquals('\n', out)
+        self.assertEquals(None, err)
+
+    def test_echo_some_to_output(self):
+        out, err = self.run_command(['echo', 'hello'], None, 'hello\n', None)
+        self.assertEquals('hello\n', out)
+        self.assertEquals(None, err)
+
+    def test_echo_more_output(self):
+        out, err = self.run_command(['echo', 'hello', 'happy', 'world'],
+                                    None, 'hellohappyworld\n', None)
+        self.assertEquals('hellohappyworld\n', out)
+        self.assertEquals(None, err)
+
+    def test_echo_appended(self):
+        out, err = self.run_command(['echo', 'hello', '>file'],
+                                    None, None, None)
+        self.assertEquals(None, out)
+        self.assertEquals(None, err)
+        self.assertFileEqual('hello\n', 'file')
+        out, err = self.run_command(['echo', 'happy', '>>file'],
+                                    None, None, None)
+        self.assertEquals(None, out)
+        self.assertEquals(None, err)
+        self.assertFileEqual('hello\nhappy\n', 'file')



More information about the bazaar-commits mailing list