1
# Copyright (C) 2008, 2009, 2010 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
18
from cStringIO import StringIO
21
from textwrap import dedent
29
from bzrlib.tests import script
30
from bzrlib.tests import (
35
class ExpectShelver(shelf_ui.Shelver):
36
"""A variant of Shelver that intercepts console activity, for testing."""
38
def __init__(self, work_tree, target_tree, diff_writer=None,
39
auto=False, auto_apply=False, file_list=None, message=None,
40
destroy=False, reporter=None):
41
shelf_ui.Shelver.__init__(self, work_tree, target_tree, diff_writer,
42
auto, auto_apply, file_list, message,
43
destroy, reporter=reporter)
45
self.diff_writer = StringIO()
47
def expect(self, message, response):
48
self.expected.append((message, response))
50
def prompt(self, message, choices, default):
52
expected_message, response = self.expected.pop(0)
54
raise AssertionError('Unexpected prompt: %s' % message)
55
if message != expected_message:
56
raise AssertionError('Wrong prompt: %s' % message)
57
if choices != '&yes\n&No\n&finish\n&quit':
58
raise AssertionError('Wrong choices: %s' % choices)
62
LINES_AJ = 'a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n'
65
LINES_ZY = 'z\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
68
LINES_AY = 'a\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
71
class ShelfTestCase(tests.TestCaseWithTransport):
73
def create_shelvable_tree(self):
74
tree = self.make_branch_and_tree('tree')
75
self.build_tree_contents([('tree/foo', LINES_AJ)])
76
tree.add('foo', 'foo-id')
77
tree.commit('added foo')
78
self.build_tree_contents([('tree/foo', LINES_ZY)])
82
class TestShelver(ShelfTestCase):
84
def test_unexpected_prompt_failure(self):
85
tree = self.create_shelvable_tree()
86
tree.lock_tree_write()
87
self.addCleanup(tree.unlock)
88
shelver = ExpectShelver(tree, tree.basis_tree())
89
self.addCleanup(shelver.finalize)
90
e = self.assertRaises(AssertionError, shelver.run)
91
self.assertEqual('Unexpected prompt: Shelve?', str(e))
93
def test_wrong_prompt_failure(self):
94
tree = self.create_shelvable_tree()
95
tree.lock_tree_write()
96
self.addCleanup(tree.unlock)
97
shelver = ExpectShelver(tree, tree.basis_tree())
98
self.addCleanup(shelver.finalize)
99
shelver.expect('foo', 0)
100
e = self.assertRaises(AssertionError, shelver.run)
101
self.assertEqual('Wrong prompt: Shelve?', str(e))
103
def test_shelve_not_diff(self):
104
tree = self.create_shelvable_tree()
105
tree.lock_tree_write()
106
self.addCleanup(tree.unlock)
107
shelver = ExpectShelver(tree, tree.basis_tree())
108
self.addCleanup(shelver.finalize)
109
shelver.expect('Shelve?', 1)
110
shelver.expect('Shelve?', 1)
111
# No final shelving prompt because no changes were selected
113
self.assertFileEqual(LINES_ZY, 'tree/foo')
115
def test_shelve_diff_no(self):
116
tree = self.create_shelvable_tree()
117
tree.lock_tree_write()
118
self.addCleanup(tree.unlock)
119
shelver = ExpectShelver(tree, tree.basis_tree())
120
self.addCleanup(shelver.finalize)
121
shelver.expect('Shelve?', 0)
122
shelver.expect('Shelve?', 0)
123
shelver.expect('Shelve 2 change(s)?', 1)
125
self.assertFileEqual(LINES_ZY, 'tree/foo')
127
def test_shelve_diff(self):
128
tree = self.create_shelvable_tree()
129
tree.lock_tree_write()
130
self.addCleanup(tree.unlock)
131
shelver = ExpectShelver(tree, tree.basis_tree())
132
self.addCleanup(shelver.finalize)
133
shelver.expect('Shelve?', 0)
134
shelver.expect('Shelve?', 0)
135
shelver.expect('Shelve 2 change(s)?', 0)
137
self.assertFileEqual(LINES_AJ, 'tree/foo')
139
def test_shelve_one_diff(self):
140
tree = self.create_shelvable_tree()
141
tree.lock_tree_write()
142
self.addCleanup(tree.unlock)
143
shelver = ExpectShelver(tree, tree.basis_tree())
144
self.addCleanup(shelver.finalize)
145
shelver.expect('Shelve?', 0)
146
shelver.expect('Shelve?', 1)
147
shelver.expect('Shelve 1 change(s)?', 0)
149
self.assertFileEqual(LINES_AY, 'tree/foo')
151
def test_shelve_binary_change(self):
152
tree = self.create_shelvable_tree()
153
self.build_tree_contents([('tree/foo', '\x00')])
154
tree.lock_tree_write()
155
self.addCleanup(tree.unlock)
156
shelver = ExpectShelver(tree, tree.basis_tree())
157
self.addCleanup(shelver.finalize)
158
shelver.expect('Shelve binary changes?', 0)
159
shelver.expect('Shelve 1 change(s)?', 0)
161
self.assertFileEqual(LINES_AJ, 'tree/foo')
163
def test_shelve_rename(self):
164
tree = self.create_shelvable_tree()
165
tree.rename_one('foo', 'bar')
166
tree.lock_tree_write()
167
self.addCleanup(tree.unlock)
168
shelver = ExpectShelver(tree, tree.basis_tree())
169
self.addCleanup(shelver.finalize)
170
shelver.expect('Shelve renaming "foo" => "bar"?', 0)
171
shelver.expect('Shelve?', 0)
172
shelver.expect('Shelve?', 0)
173
shelver.expect('Shelve 3 change(s)?', 0)
175
self.assertFileEqual(LINES_AJ, 'tree/foo')
177
def test_shelve_deletion(self):
178
tree = self.create_shelvable_tree()
179
os.unlink('tree/foo')
180
tree.lock_tree_write()
181
self.addCleanup(tree.unlock)
182
shelver = ExpectShelver(tree, tree.basis_tree())
183
self.addCleanup(shelver.finalize)
184
shelver.expect('Shelve removing file "foo"?', 0)
185
shelver.expect('Shelve 1 change(s)?', 0)
187
self.assertFileEqual(LINES_AJ, 'tree/foo')
189
def test_shelve_creation(self):
190
tree = self.make_branch_and_tree('tree')
191
tree.commit('add tree root')
192
self.build_tree(['tree/foo'])
194
tree.lock_tree_write()
195
self.addCleanup(tree.unlock)
196
shelver = ExpectShelver(tree, tree.basis_tree())
197
self.addCleanup(shelver.finalize)
198
shelver.expect('Shelve adding file "foo"?', 0)
199
shelver.expect('Shelve 1 change(s)?', 0)
201
self.assertPathDoesNotExist('tree/foo')
203
def test_shelve_kind_change(self):
204
tree = self.create_shelvable_tree()
205
os.unlink('tree/foo')
207
tree.lock_tree_write()
208
self.addCleanup(tree.unlock)
209
shelver = ExpectShelver(tree, tree.basis_tree())
210
self.addCleanup(shelver.finalize)
211
shelver.expect('Shelve changing "foo" from file to directory?',
213
shelver.expect('Shelve 1 change(s)?', 0)
215
def test_shelve_modify_target(self):
216
self.requireFeature(features.SymlinkFeature)
217
tree = self.create_shelvable_tree()
218
os.symlink('bar', 'tree/baz')
219
tree.add('baz', 'baz-id')
220
tree.commit("Add symlink")
221
os.unlink('tree/baz')
222
os.symlink('vax', 'tree/baz')
223
tree.lock_tree_write()
224
self.addCleanup(tree.unlock)
225
shelver = ExpectShelver(tree, tree.basis_tree())
226
self.addCleanup(shelver.finalize)
227
shelver.expect('Shelve changing target of "baz" from "bar" to '
229
shelver.expect('Shelve 1 change(s)?', 0)
231
self.assertEqual('bar', os.readlink('tree/baz'))
233
def test_shelve_finish(self):
234
tree = self.create_shelvable_tree()
235
tree.lock_tree_write()
236
self.addCleanup(tree.unlock)
237
shelver = ExpectShelver(tree, tree.basis_tree())
238
self.addCleanup(shelver.finalize)
239
shelver.expect('Shelve?', 2)
240
shelver.expect('Shelve 2 change(s)?', 0)
242
self.assertFileEqual(LINES_AJ, 'tree/foo')
244
def test_shelve_quit(self):
245
tree = self.create_shelvable_tree()
246
tree.lock_tree_write()
247
self.addCleanup(tree.unlock)
248
shelver = ExpectShelver(tree, tree.basis_tree())
249
self.addCleanup(shelver.finalize)
250
shelver.expect('Shelve?', 3)
251
self.assertRaises(errors.UserAbort, shelver.run)
252
self.assertFileEqual(LINES_ZY, 'tree/foo')
254
def test_shelve_all(self):
255
tree = self.create_shelvable_tree()
256
shelver = ExpectShelver.from_args(sys.stdout, all=True,
262
self.assertFileEqual(LINES_AJ, 'tree/foo')
264
def test_shelve_filename(self):
265
tree = self.create_shelvable_tree()
266
self.build_tree(['tree/bar'])
268
tree.lock_tree_write()
269
self.addCleanup(tree.unlock)
270
shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])
271
self.addCleanup(shelver.finalize)
272
shelver.expect('Shelve adding file "bar"?', 0)
273
shelver.expect('Shelve 1 change(s)?', 0)
276
def test_shelve_destroy(self):
277
tree = self.create_shelvable_tree()
278
shelver = shelf_ui.Shelver.from_args(sys.stdout, all=True,
279
directory='tree', destroy=True)
280
self.addCleanup(shelver.finalize)
282
self.assertIs(None, tree.get_shelf_manager().last_shelf())
283
self.assertFileEqual(LINES_AJ, 'tree/foo')
286
def shelve_all(tree, target_revision_id):
289
target = tree.branch.repository.revision_tree(target_revision_id)
290
shelver = shelf_ui.Shelver(tree, target, auto=True,
299
def test_shelve_old_root_preserved(self):
300
tree1 = self.make_branch_and_tree('tree1')
301
tree1.commit('add root')
302
tree1_root_id = tree1.get_root_id()
303
tree2 = self.make_branch_and_tree('tree2')
304
rev2 = tree2.commit('add root')
305
self.assertNotEquals(tree1_root_id, tree2.get_root_id())
306
tree1.merge_from_branch(tree2.branch,
307
from_revision=revision.NULL_REVISION)
308
tree1.commit('merging in tree2')
309
self.assertEquals(tree1_root_id, tree1.get_root_id())
310
# This is essentially assertNotRaises(InconsistentDelta)
311
# With testtools 0.9.9, it can be rewritten as:
312
# with ExpectedException(AssertionError,
313
# 'InconsistentDelta not raised'):
314
# with ExpectedException(errors.InconsistentDelta, ''):
315
# self.shelve_all(tree1, rev2)
316
e = self.assertRaises(AssertionError, self.assertRaises,
317
errors.InconsistentDelta, self.shelve_all, tree1,
319
self.assertContainsRe('InconsistentDelta not raised', str(e))
321
def test_shelve_split(self):
322
outer_tree = self.make_branch_and_tree('outer')
323
outer_tree.commit('Add root')
324
inner_tree = self.make_branch_and_tree('outer/inner')
325
rev2 = inner_tree.commit('Add root')
326
outer_tree.subsume(inner_tree)
327
# This is essentially assertNotRaises(ValueError).
328
# The ValueError is 'None is not a valid file id'.
329
self.expectFailure('Cannot shelve a join back to the inner tree.',
330
self.assertRaises, AssertionError,
331
self.assertRaises, ValueError, self.shelve_all,
335
class TestApplyReporter(ShelfTestCase):
337
def test_shelve_not_diff(self):
338
tree = self.create_shelvable_tree()
339
tree.lock_tree_write()
340
self.addCleanup(tree.unlock)
341
shelver = ExpectShelver(tree, tree.basis_tree(),
342
reporter=shelf_ui.ApplyReporter())
343
self.addCleanup(shelver.finalize)
344
shelver.expect('Apply change?', 1)
345
shelver.expect('Apply change?', 1)
346
# No final shelving prompt because no changes were selected
348
self.assertFileEqual(LINES_ZY, 'tree/foo')
350
def test_shelve_diff_no(self):
351
tree = self.create_shelvable_tree()
352
tree.lock_tree_write()
353
self.addCleanup(tree.unlock)
354
shelver = ExpectShelver(tree, tree.basis_tree(),
355
reporter=shelf_ui.ApplyReporter())
356
self.addCleanup(shelver.finalize)
357
shelver.expect('Apply change?', 0)
358
shelver.expect('Apply change?', 0)
359
shelver.expect('Apply 2 change(s)?', 1)
361
self.assertFileEqual(LINES_ZY, 'tree/foo')
363
def test_shelve_diff(self):
364
tree = self.create_shelvable_tree()
365
tree.lock_tree_write()
366
self.addCleanup(tree.unlock)
367
shelver = ExpectShelver(tree, tree.basis_tree(),
368
reporter=shelf_ui.ApplyReporter())
369
self.addCleanup(shelver.finalize)
370
shelver.expect('Apply change?', 0)
371
shelver.expect('Apply change?', 0)
372
shelver.expect('Apply 2 change(s)?', 0)
374
self.assertFileEqual(LINES_AJ, 'tree/foo')
376
def test_shelve_binary_change(self):
377
tree = self.create_shelvable_tree()
378
self.build_tree_contents([('tree/foo', '\x00')])
379
tree.lock_tree_write()
380
self.addCleanup(tree.unlock)
381
shelver = ExpectShelver(tree, tree.basis_tree(),
382
reporter=shelf_ui.ApplyReporter())
383
self.addCleanup(shelver.finalize)
384
shelver.expect('Apply binary changes?', 0)
385
shelver.expect('Apply 1 change(s)?', 0)
387
self.assertFileEqual(LINES_AJ, 'tree/foo')
389
def test_shelve_rename(self):
390
tree = self.create_shelvable_tree()
391
tree.rename_one('foo', 'bar')
392
tree.lock_tree_write()
393
self.addCleanup(tree.unlock)
394
shelver = ExpectShelver(tree, tree.basis_tree(),
395
reporter=shelf_ui.ApplyReporter())
396
self.addCleanup(shelver.finalize)
397
shelver.expect('Rename "bar" => "foo"?', 0)
398
shelver.expect('Apply change?', 0)
399
shelver.expect('Apply change?', 0)
400
shelver.expect('Apply 3 change(s)?', 0)
402
self.assertFileEqual(LINES_AJ, 'tree/foo')
404
def test_shelve_deletion(self):
405
tree = self.create_shelvable_tree()
406
os.unlink('tree/foo')
407
tree.lock_tree_write()
408
self.addCleanup(tree.unlock)
409
shelver = ExpectShelver(tree, tree.basis_tree(),
410
reporter=shelf_ui.ApplyReporter())
411
self.addCleanup(shelver.finalize)
412
shelver.expect('Add file "foo"?', 0)
413
shelver.expect('Apply 1 change(s)?', 0)
415
self.assertFileEqual(LINES_AJ, 'tree/foo')
417
def test_shelve_creation(self):
418
tree = self.make_branch_and_tree('tree')
419
tree.commit('add tree root')
420
self.build_tree(['tree/foo'])
422
tree.lock_tree_write()
423
self.addCleanup(tree.unlock)
424
shelver = ExpectShelver(tree, tree.basis_tree(),
425
reporter=shelf_ui.ApplyReporter())
426
self.addCleanup(shelver.finalize)
427
shelver.expect('Delete file "foo"?', 0)
428
shelver.expect('Apply 1 change(s)?', 0)
430
self.assertPathDoesNotExist('tree/foo')
432
def test_shelve_kind_change(self):
433
tree = self.create_shelvable_tree()
434
os.unlink('tree/foo')
436
tree.lock_tree_write()
437
self.addCleanup(tree.unlock)
438
shelver = ExpectShelver(tree, tree.basis_tree(),
439
reporter=shelf_ui.ApplyReporter())
440
self.addCleanup(shelver.finalize)
441
shelver.expect('Change "foo" from directory to a file?', 0)
442
shelver.expect('Apply 1 change(s)?', 0)
444
def test_shelve_modify_target(self):
445
self.requireFeature(features.SymlinkFeature)
446
tree = self.create_shelvable_tree()
447
os.symlink('bar', 'tree/baz')
448
tree.add('baz', 'baz-id')
449
tree.commit("Add symlink")
450
os.unlink('tree/baz')
451
os.symlink('vax', 'tree/baz')
452
tree.lock_tree_write()
453
self.addCleanup(tree.unlock)
454
shelver = ExpectShelver(tree, tree.basis_tree(),
455
reporter=shelf_ui.ApplyReporter())
456
self.addCleanup(shelver.finalize)
457
shelver.expect('Change target of "baz" from "vax" to "bar"?',
459
shelver.expect('Apply 1 change(s)?', 0)
461
self.assertEqual('bar', os.readlink('tree/baz'))
464
class TestUnshelver(tests.TestCaseWithTransport):
466
def create_tree_with_shelf(self):
467
tree = self.make_branch_and_tree('tree')
470
self.build_tree_contents([('tree/foo', LINES_AJ)])
471
tree.add('foo', 'foo-id')
472
tree.commit('added foo')
473
self.build_tree_contents([('tree/foo', LINES_ZY)])
474
shelver = shelf_ui.Shelver(tree, tree.basis_tree(),
475
auto_apply=True, auto=True)
484
def test_unshelve(self):
485
tree = self.create_tree_with_shelf()
487
self.addCleanup(tree.unlock)
488
manager = tree.get_shelf_manager()
489
shelf_ui.Unshelver(tree, manager, 1, True, True, True).run()
490
self.assertFileEqual(LINES_ZY, 'tree/foo')
492
def test_unshelve_args(self):
493
tree = self.create_tree_with_shelf()
494
unshelver = shelf_ui.Unshelver.from_args(directory='tree')
498
unshelver.tree.unlock()
499
self.assertFileEqual(LINES_ZY, 'tree/foo')
500
self.assertIs(None, tree.get_shelf_manager().last_shelf())
502
def test_unshelve_args_dry_run(self):
503
tree = self.create_tree_with_shelf()
504
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
509
unshelver.tree.unlock()
510
self.assertFileEqual(LINES_AJ, 'tree/foo')
511
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
513
def test_unshelve_args_preview(self):
514
tree = self.create_tree_with_shelf()
515
write_diff_to = StringIO()
516
unshelver = shelf_ui.Unshelver.from_args(
517
directory='tree', action='preview', write_diff_to=write_diff_to)
521
unshelver.tree.unlock()
522
# The changes were not unshelved.
523
self.assertFileEqual(LINES_AJ, 'tree/foo')
524
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
526
# But the diff was written to write_diff_to.
527
diff = write_diff_to.getvalue()
528
expected = dedent("""\
543
self.assertEqualDiff(expected, diff[-len(expected):])
545
def test_unshelve_args_delete_only(self):
546
tree = self.make_branch_and_tree('tree')
547
manager = tree.get_shelf_manager()
548
shelf_file = manager.new_shelf()[1]
550
shelf_file.write('garbage')
553
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
554
action='delete-only')
558
unshelver.tree.unlock()
559
self.assertIs(None, manager.last_shelf())
561
def test_unshelve_args_invalid_shelf_id(self):
562
tree = self.make_branch_and_tree('tree')
563
manager = tree.get_shelf_manager()
564
shelf_file = manager.new_shelf()[1]
566
shelf_file.write('garbage')
569
self.assertRaises(errors.InvalidShelfId,
570
shelf_ui.Unshelver.from_args, directory='tree',
571
action='delete-only', shelf_id='foo')
574
class TestUnshelveScripts(TestUnshelver,
575
script.TestCaseWithTransportAndScript):
577
def test_unshelve_messages_keep(self):
578
self.create_tree_with_shelf()
581
$ bzr unshelve --keep
582
2>Using changes with id "1".
584
2>All changes applied successfully.
587
def test_unshelve_messages_delete(self):
588
self.create_tree_with_shelf()
591
$ bzr unshelve --delete-only
592
2>Deleted changes with id "1".
595
def test_unshelve_messages_apply(self):
596
self.create_tree_with_shelf()
599
$ bzr unshelve --apply
600
2>Using changes with id "1".
602
2>All changes applied successfully.
603
2>Deleted changes with id "1".
606
def test_unshelve_messages_dry_run(self):
607
self.create_tree_with_shelf()
610
$ bzr unshelve --dry-run
611
2>Using changes with id "1".