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