1
# Copyright (C) 2009, 2010, 2011, 2016 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25
from breezy.tests import script
28
class TestSyntax(tests.TestCase):
30
def test_comment_is_ignored(self):
31
self.assertEqual([], script._script_to_commands('#comment\n'))
33
def test_comment_multiple_lines(self):
35
(['bar'], None, None, None),
37
script._script_to_commands("""
38
# this comment is ignored
44
def test_trim_blank_lines(self):
45
"""Blank lines are respected, but trimmed at the start and end.
47
Python triple-quoted syntax is going to give stubby/empty blank lines
48
right at the start and the end. These are cut off so that callers don't
49
need special syntax to avoid them.
51
However we do want to be able to match commands that emit blank lines.
54
(['bar'], None, '\n', None),
56
script._script_to_commands("""
61
def test_simple_command(self):
62
self.assertEqual([(['cd', 'trunk'], None, None, None)],
63
script._script_to_commands('$ cd trunk'))
65
def test_command_with_single_quoted_param(self):
66
story = """$ brz commit -m 'two words'"""
67
self.assertEqual([(['brz', 'commit', '-m', "'two words'"],
69
script._script_to_commands(story))
71
def test_command_with_double_quoted_param(self):
72
story = """$ brz commit -m "two words" """
73
self.assertEqual([(['brz', 'commit', '-m', '"two words"'],
75
script._script_to_commands(story))
77
def test_command_with_input(self):
79
[(['cat', '>file'], 'content\n', None, None)],
80
script._script_to_commands('$ cat >file\n<content\n'))
82
def test_indented(self):
83
# scripts are commonly given indented within the test source code, and
84
# common indentation is stripped off
90
self.assertEqual([(['brz', 'add'], None,
91
'adding file\nadding file2\n', None)],
92
script._script_to_commands(story))
94
def test_command_with_output(self):
100
self.assertEqual([(['brz', 'add'], None,
101
'adding file\nadding file2\n', None)],
102
script._script_to_commands(story))
104
def test_command_with_error(self):
107
2>brz: ERROR: Not a branch: "foo"
109
self.assertEqual([(['brz', 'branch', 'foo'],
110
None, None, 'brz: ERROR: Not a branch: "foo"\n')],
111
script._script_to_commands(story))
113
def test_input_without_command(self):
114
self.assertRaises(SyntaxError, script._script_to_commands, '<input')
116
def test_output_without_command(self):
117
self.assertRaises(SyntaxError, script._script_to_commands, '>input')
119
def test_command_with_backquotes(self):
121
$ foo = `brz file-id toto`
123
self.assertEqual([(['foo', '=', '`brz file-id toto`'],
125
script._script_to_commands(story))
128
class TestRedirections(tests.TestCase):
130
def _check(self, in_name, out_name, out_mode, remaining, args):
131
self.assertEqual(script._scan_redirection_options(args),
132
(in_name, out_name, out_mode, remaining))
134
def test_no_redirection(self):
135
self._check(None, None, None, [], [])
136
self._check(None, None, None, ['foo', 'bar'], ['foo', 'bar'])
138
def test_input_redirection(self):
139
self._check('foo', None, None, [], ['<foo'])
140
self._check('foo', None, None, ['bar'], ['bar', '<foo'])
141
self._check('foo', None, None, ['bar'], ['bar', '<', 'foo'])
142
self._check('foo', None, None, ['bar'], ['<foo', 'bar'])
143
self._check('foo', None, None, ['bar', 'baz'], ['bar', '<foo', 'baz'])
145
def test_output_redirection(self):
146
self._check(None, 'foo', 'w+', [], ['>foo'])
147
self._check(None, 'foo', 'w+', ['bar'], ['bar', '>foo'])
148
self._check(None, 'foo', 'w+', ['bar'], ['bar', '>', 'foo'])
149
self._check(None, 'foo', 'a+', [], ['>>foo'])
150
self._check(None, 'foo', 'a+', ['bar'], ['bar', '>>foo'])
151
self._check(None, 'foo', 'a+', ['bar'], ['bar', '>>', 'foo'])
153
def test_redirection_syntax_errors(self):
154
self._check('', None, None, [], ['<'])
155
self._check(None, '', 'w+', [], ['>'])
156
self._check(None, '', 'a+', [], ['>>'])
157
self._check('>', '', 'a+', [], ['<', '>', '>>'])
160
class TestExecution(script.TestCaseWithTransportAndScript):
162
def test_unknown_command(self):
163
"""A clear error is reported for commands that aren't recognised
165
Testing the attributes of the SyntaxError instance is equivalent to
166
using traceback.format_exception_only and comparing with:
167
File "<string>", line 1
170
SyntaxError: Command not found "foo"
172
e = self.assertRaises(SyntaxError, self.run_script, "$ foo --frob")
173
self.assertContainsRe(e.msg, "not found.*foo")
174
self.assertEqual(e.text, "foo --frob")
176
def test_blank_output_mismatches_output(self):
177
"""If you give output, the output must actually be blank.
179
See <https://bugs.launchpad.net/bzr/+bug/637830>: previously blank
180
output was a wildcard. Now you must say ... if you want that.
182
self.assertRaises(AssertionError,
188
def test_null_output_matches_option(self):
189
"""If you want null output to be a wild card, you can pass
190
null_output_matches_anything to run_script"""
194
""", null_output_matches_anything=True)
196
def test_ellipsis_everything(self):
197
"""A simple ellipsis matches everything."""
203
def test_ellipsis_matches_empty(self):
209
def test_stops_on_unexpected_output(self):
213
The cd command ouputs nothing
215
self.assertRaises(AssertionError, self.run_script, story)
217
def test_stops_on_unexpected_error(self):
223
self.assertRaises(AssertionError, self.run_script, story)
225
def test_continue_on_expected_error(self):
230
self.run_script(story)
232
def test_continue_on_error_output(self):
233
# The status matters, not the output
241
$ brz commit -m 'adding file'
244
self.run_script(story)
246
def test_ellipsis_output(self):
256
self.run_script(story)
261
self.run_script(story)
264
$ brz branch not-a-branch
265
2>brz: ERROR: Not a branch...not-a-branch/".
267
self.run_script(story)
270
class TestArgumentProcessing(script.TestCaseWithTransportAndScript):
272
def test_globing(self):
281
def test_quoted_globbing(self):
285
2>*: No such file or directory
288
def test_quotes_removal(self):
290
$ echo 'cat' "dog" '"chicken"' "'dragon'"
291
cat dog "chicken" 'dragon'
294
def test_verbosity_isolated(self):
295
"""Global verbosity is isolated from commands run in scripts.
297
# see also 656694; we should get rid of global verbosity
301
self.assertEqual(trace.is_quiet(), False)
304
class TestCat(script.TestCaseWithTransportAndScript):
306
def test_cat_usage(self):
307
self.assertRaises(SyntaxError, self.run_script, 'cat foo <bar')
309
def test_cat_input_to_output(self):
310
retcode, out, err = self.run_command(['cat'],
311
'content\n', 'content\n', None)
312
self.assertEqual('content\n', out)
313
self.assertEqual(None, err)
315
def test_cat_file_to_output(self):
316
self.build_tree_contents([('file', b'content\n')])
317
retcode, out, err = self.run_command(['cat', 'file'],
318
None, 'content\n', None)
319
self.assertEqual('content\n', out)
320
self.assertEqual(None, err)
322
def test_cat_input_to_file(self):
323
retcode, out, err = self.run_command(['cat', '>file'],
324
'content\n', None, None)
325
self.assertFileEqual('content\n', 'file')
326
self.assertEqual(None, out)
327
self.assertEqual(None, err)
328
retcode, out, err = self.run_command(['cat', '>>file'],
329
'more\n', None, None)
330
self.assertFileEqual('content\nmore\n', 'file')
331
self.assertEqual(None, out)
332
self.assertEqual(None, err)
334
def test_cat_file_to_file(self):
335
self.build_tree_contents([('file', b'content\n')])
336
retcode, out, err = self.run_command(['cat', 'file', '>file2'],
338
self.assertFileEqual(b'content\n', 'file2')
340
def test_cat_files_to_file(self):
341
self.build_tree_contents([('cat', b'cat\n')])
342
self.build_tree_contents([('dog', b'dog\n')])
343
retcode, out, err = self.run_command(['cat', 'cat', 'dog', '>file'],
345
self.assertFileEqual(b'cat\ndog\n', 'file')
347
def test_cat_bogus_input_file(self):
350
2>file: No such file or directory
353
def test_cat_bogus_output_file(self):
356
2>: No such file or directory
359
def test_echo_bogus_output_file(self):
360
# We need a backing file sysytem for that test so it can't be in
364
2>: No such file or directory
368
class TestMkdir(script.TestCaseWithTransportAndScript):
370
def test_mkdir_usage(self):
371
self.assertRaises(SyntaxError, self.run_script, '$ mkdir')
372
self.assertRaises(SyntaxError, self.run_script, '$ mkdir foo bar')
374
def test_mkdir_jailed(self):
375
self.assertRaises(ValueError, self.run_script, '$ mkdir /out-of-jail')
376
self.assertRaises(ValueError, self.run_script,
377
'$ mkdir ../out-of-jail')
379
def test_mkdir_in_jail(self):
386
self.assertPathExists('dir')
387
self.assertPathExists('dir2')
390
class TestCd(script.TestCaseWithTransportAndScript):
392
def test_cd_usage(self):
393
self.assertRaises(SyntaxError, self.run_script, '$ cd foo bar')
395
def test_cd_out_of_jail(self):
396
self.assertRaises(ValueError, self.run_script, '$ cd /out-of-jail')
397
self.assertRaises(ValueError, self.run_script, '$ cd ..')
399
def test_cd_dir_and_back_home(self):
400
self.assertEqual(self.test_dir, osutils.getcwd())
405
self.assertEqual(osutils.pathjoin(self.test_dir, 'dir'),
408
self.run_script('$ cd')
409
self.assertEqual(self.test_dir, osutils.getcwd())
412
class TestBrz(script.TestCaseWithTransportAndScript):
414
def test_brz_smoke(self):
417
Created a standalone tree (format: ...)
419
self.assertPathExists('branch')
422
class TestEcho(script.TestCaseWithMemoryTransportAndScript):
424
def test_echo_usage(self):
429
self.assertRaises(SyntaxError, self.run_script, story)
431
def test_echo_input(self):
432
self.assertRaises(SyntaxError, self.run_script, """
436
def test_echo_to_output(self):
437
retcode, out, err = self.run_command(['echo'], None, '\n', None)
438
self.assertEqual('\n', out)
439
self.assertEqual(None, err)
441
def test_echo_some_to_output(self):
442
retcode, out, err = self.run_command(['echo', 'hello'],
443
None, 'hello\n', None)
444
self.assertEqual('hello\n', out)
445
self.assertEqual(None, err)
447
def test_echo_more_output(self):
448
retcode, out, err = self.run_command(
449
['echo', 'hello', 'happy', 'world'],
450
None, 'hello happy world\n', None)
451
self.assertEqual('hello happy world\n', out)
452
self.assertEqual(None, err)
454
def test_echo_appended(self):
455
retcode, out, err = self.run_command(['echo', 'hello', '>file'],
457
self.assertEqual(None, out)
458
self.assertEqual(None, err)
459
self.assertFileEqual(b'hello\n', 'file')
460
retcode, out, err = self.run_command(['echo', 'happy', '>>file'],
462
self.assertEqual(None, out)
463
self.assertEqual(None, err)
464
self.assertFileEqual(b'hello\nhappy\n', 'file')
466
def test_empty_line_in_output_is_respected(self):
475
class TestRm(script.TestCaseWithTransportAndScript):
477
def test_rm_usage(self):
478
self.assertRaises(SyntaxError, self.run_script, '$ rm')
479
self.assertRaises(SyntaxError, self.run_script, '$ rm -ff foo')
481
def test_rm_file(self):
482
self.run_script('$ echo content >file')
483
self.assertPathExists('file')
484
self.run_script('$ rm file')
485
self.assertPathDoesNotExist('file')
487
def test_rm_file_force(self):
488
self.assertPathDoesNotExist('file')
489
self.run_script('$ rm -f file')
490
self.assertPathDoesNotExist('file')
492
def test_rm_files(self):
495
$ echo content >file2
497
self.assertPathExists('file2')
498
self.run_script('$ rm file file2')
499
self.assertPathDoesNotExist('file2')
501
def test_rm_dir(self):
502
self.run_script('$ mkdir dir')
503
self.assertPathExists('dir')
506
2>rm: cannot remove 'dir': Is a directory
508
self.assertPathExists('dir')
510
def test_rm_dir_recursive(self):
515
self.assertPathDoesNotExist('dir')
518
class TestMv(script.TestCaseWithTransportAndScript):
520
def test_usage(self):
521
self.assertRaises(SyntaxError, self.run_script, '$ mv')
522
self.assertRaises(SyntaxError, self.run_script, '$ mv f')
523
self.assertRaises(SyntaxError, self.run_script, '$ mv f1 f2 f3')
525
def test_move_file(self):
526
self.run_script('$ echo content >file')
527
self.assertPathExists('file')
528
self.run_script('$ mv file new_name')
529
self.assertPathDoesNotExist('file')
530
self.assertPathExists('new_name')
532
def test_move_unknown_file(self):
533
self.assertRaises(AssertionError,
534
self.run_script, '$ mv unknown does-not-exist')
536
def test_move_dir(self):
539
$ echo content >dir/file
541
self.run_script('$ mv dir new_name')
542
self.assertPathDoesNotExist('dir')
543
self.assertPathExists('new_name')
544
self.assertPathExists('new_name/file')
546
def test_move_file_into_dir(self):
549
$ echo content > file
551
self.run_script('$ mv file dir')
552
self.assertPathExists('dir')
553
self.assertPathDoesNotExist('file')
554
self.assertPathExists('dir/file')
557
class cmd_test_confirm(commands.Command):
560
if ui.ui_factory.get_boolean(
562
# 'breezy.tests.test_script.confirm',
565
self.outf.write('Do it!\n')
570
class TestUserInteraction(script.TestCaseWithMemoryTransportAndScript):
572
def test_confirm_action(self):
573
"""You can write tests that demonstrate user confirmation.
575
Specifically, ScriptRunner does't care if the output line for the
576
prompt isn't terminated by a newline from the program; it's implicitly
577
terminated by the input.
579
commands.builtin_command_registry.register(cmd_test_confirm)
581
commands.builtin_command_registry.remove, 'test-confirm')
584
2>Really do it? ([y]es, [n]o): yes
588
2>Really do it? ([y]es, [n]o): no
594
class TestShelve(script.TestCaseWithTransportAndScript):
597
super(TestShelve, self).setUp()
600
Created a standalone tree (format: 2a)
605
$ brz commit -m 'file added'
606
2>Committing to:...test/
608
2>Committed revision 1.
612
def test_shelve(self):
614
$ brz shelve -m 'shelve bar'
615
2>Shelve? ([y]es, [N]o, [f]inish, [q]uit): yes
619
2>Shelve 1 change(s)? ([y]es, [N]o, [f]inish, [q]uit): yes
621
2>Changes shelved with id "1".
623
null_output_matches_anything=True)
629
def test_dont_shelve(self):
630
# We intentionally provide no input here to test EOF
632
"$ brz shelve -m 'shelve bar'\n"
633
"2>Shelve? ([y]es, [N]o, [f]inish, [q]uit): \n"
634
"2>No changes to shelve.\n"
635
), null_output_matches_anything=True)