1
# Copyright (C) 2008-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
18
from io import BytesIO
21
from textwrap import dedent
36
class ExpectShelver(shelf_ui.Shelver):
37
"""A variant of Shelver that intercepts console activity, for testing."""
39
def __init__(self, work_tree, target_tree, diff_writer=None,
40
auto=False, auto_apply=False, file_list=None, message=None,
41
destroy=False, reporter=None):
42
shelf_ui.Shelver.__init__(self, work_tree, target_tree, diff_writer,
43
auto, auto_apply, file_list, message,
44
destroy, reporter=reporter)
46
self.diff_writer = BytesIO()
48
def expect(self, message, response):
49
self.expected.append((message, response))
51
def prompt(self, message, choices, default):
53
expected_message, response = self.expected.pop(0)
55
raise AssertionError('Unexpected prompt: %s' % message)
56
if message != expected_message:
57
raise AssertionError('Wrong prompt: %s' % message)
58
if choices != '&yes\n&No\n&finish\n&quit':
59
raise AssertionError('Wrong choices: %s' % choices)
63
LINES_AJ = b'a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n'
66
LINES_ZY = b'z\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
69
LINES_AY = b'a\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
72
class ShelfTestCase(tests.TestCaseWithTransport):
74
def create_shelvable_tree(self):
75
tree = self.make_branch_and_tree('tree')
76
self.build_tree_contents([('tree/foo', LINES_AJ)])
77
tree.add('foo', b'foo-id')
78
tree.commit('added foo')
79
self.build_tree_contents([('tree/foo', LINES_ZY)])
83
class TestShelver(ShelfTestCase):
85
def test_unexpected_prompt_failure(self):
86
tree = self.create_shelvable_tree()
87
tree.lock_tree_write()
88
self.addCleanup(tree.unlock)
89
shelver = ExpectShelver(tree, tree.basis_tree())
90
self.addCleanup(shelver.finalize)
91
e = self.assertRaises(AssertionError, shelver.run)
92
self.assertEqual('Unexpected prompt: Shelve?', str(e))
94
def test_wrong_prompt_failure(self):
95
tree = self.create_shelvable_tree()
96
tree.lock_tree_write()
97
self.addCleanup(tree.unlock)
98
shelver = ExpectShelver(tree, tree.basis_tree())
99
self.addCleanup(shelver.finalize)
100
shelver.expect('foo', 0)
101
e = self.assertRaises(AssertionError, shelver.run)
102
self.assertEqual('Wrong prompt: Shelve?', str(e))
104
def test_shelve_not_diff(self):
105
tree = self.create_shelvable_tree()
106
tree.lock_tree_write()
107
self.addCleanup(tree.unlock)
108
shelver = ExpectShelver(tree, tree.basis_tree())
109
self.addCleanup(shelver.finalize)
110
shelver.expect('Shelve?', 1)
111
shelver.expect('Shelve?', 1)
112
# No final shelving prompt because no changes were selected
114
self.assertFileEqual(LINES_ZY, 'tree/foo')
116
def test_shelve_diff_no(self):
117
tree = self.create_shelvable_tree()
118
tree.lock_tree_write()
119
self.addCleanup(tree.unlock)
120
shelver = ExpectShelver(tree, tree.basis_tree())
121
self.addCleanup(shelver.finalize)
122
shelver.expect('Shelve?', 0)
123
shelver.expect('Shelve?', 0)
124
shelver.expect('Shelve 2 change(s)?', 1)
126
self.assertFileEqual(LINES_ZY, 'tree/foo')
128
def test_shelve_diff(self):
129
tree = self.create_shelvable_tree()
130
tree.lock_tree_write()
131
self.addCleanup(tree.unlock)
132
shelver = ExpectShelver(tree, tree.basis_tree())
133
self.addCleanup(shelver.finalize)
134
shelver.expect('Shelve?', 0)
135
shelver.expect('Shelve?', 0)
136
shelver.expect('Shelve 2 change(s)?', 0)
138
self.assertFileEqual(LINES_AJ, 'tree/foo')
140
def test_shelve_one_diff(self):
141
tree = self.create_shelvable_tree()
142
tree.lock_tree_write()
143
self.addCleanup(tree.unlock)
144
shelver = ExpectShelver(tree, tree.basis_tree())
145
self.addCleanup(shelver.finalize)
146
shelver.expect('Shelve?', 0)
147
shelver.expect('Shelve?', 1)
148
shelver.expect('Shelve 1 change(s)?', 0)
150
self.assertFileEqual(LINES_AY, 'tree/foo')
152
def test_shelve_binary_change(self):
153
tree = self.create_shelvable_tree()
154
self.build_tree_contents([('tree/foo', b'\x00')])
155
tree.lock_tree_write()
156
self.addCleanup(tree.unlock)
157
shelver = ExpectShelver(tree, tree.basis_tree())
158
self.addCleanup(shelver.finalize)
159
shelver.expect('Shelve binary changes?', 0)
160
shelver.expect('Shelve 1 change(s)?', 0)
162
self.assertFileEqual(LINES_AJ, 'tree/foo')
164
def test_shelve_rename(self):
165
tree = self.create_shelvable_tree()
166
tree.rename_one('foo', 'bar')
167
tree.lock_tree_write()
168
self.addCleanup(tree.unlock)
169
shelver = ExpectShelver(tree, tree.basis_tree())
170
self.addCleanup(shelver.finalize)
171
shelver.expect('Shelve renaming "foo" => "bar"?', 0)
172
shelver.expect('Shelve?', 0)
173
shelver.expect('Shelve?', 0)
174
shelver.expect('Shelve 3 change(s)?', 0)
176
self.assertFileEqual(LINES_AJ, 'tree/foo')
178
def test_shelve_deletion(self):
179
tree = self.create_shelvable_tree()
180
os.unlink('tree/foo')
181
tree.lock_tree_write()
182
self.addCleanup(tree.unlock)
183
shelver = ExpectShelver(tree, tree.basis_tree())
184
self.addCleanup(shelver.finalize)
185
shelver.expect('Shelve removing file "foo"?', 0)
186
shelver.expect('Shelve 1 change(s)?', 0)
188
self.assertFileEqual(LINES_AJ, 'tree/foo')
190
def test_shelve_creation(self):
191
tree = self.make_branch_and_tree('tree')
192
tree.commit('add tree root')
193
self.build_tree(['tree/foo'])
195
tree.lock_tree_write()
196
self.addCleanup(tree.unlock)
197
shelver = ExpectShelver(tree, tree.basis_tree())
198
self.addCleanup(shelver.finalize)
199
shelver.expect('Shelve adding file "foo"?', 0)
200
shelver.expect('Shelve 1 change(s)?', 0)
202
self.assertPathDoesNotExist('tree/foo')
204
def test_shelve_kind_change(self):
205
tree = self.create_shelvable_tree()
206
os.unlink('tree/foo')
208
tree.lock_tree_write()
209
self.addCleanup(tree.unlock)
210
shelver = ExpectShelver(tree, tree.basis_tree())
211
self.addCleanup(shelver.finalize)
212
shelver.expect('Shelve changing "foo" from file to directory?',
214
shelver.expect('Shelve 1 change(s)?', 0)
216
def test_shelve_modify_target(self):
217
self.requireFeature(features.SymlinkFeature)
218
tree = self.create_shelvable_tree()
219
os.symlink('bar', 'tree/baz')
220
tree.add('baz', b'baz-id')
221
tree.commit("Add symlink")
222
os.unlink('tree/baz')
223
os.symlink('vax', 'tree/baz')
224
tree.lock_tree_write()
225
self.addCleanup(tree.unlock)
226
shelver = ExpectShelver(tree, tree.basis_tree())
227
self.addCleanup(shelver.finalize)
228
shelver.expect('Shelve changing target of "baz" from "bar" to '
230
shelver.expect('Shelve 1 change(s)?', 0)
232
self.assertEqual('bar', os.readlink('tree/baz'))
234
def test_shelve_finish(self):
235
tree = self.create_shelvable_tree()
236
tree.lock_tree_write()
237
self.addCleanup(tree.unlock)
238
shelver = ExpectShelver(tree, tree.basis_tree())
239
self.addCleanup(shelver.finalize)
240
shelver.expect('Shelve?', 2)
241
shelver.expect('Shelve 2 change(s)?', 0)
243
self.assertFileEqual(LINES_AJ, 'tree/foo')
245
def test_shelve_quit(self):
246
tree = self.create_shelvable_tree()
247
tree.lock_tree_write()
248
self.addCleanup(tree.unlock)
249
shelver = ExpectShelver(tree, tree.basis_tree())
250
self.addCleanup(shelver.finalize)
251
shelver.expect('Shelve?', 3)
252
self.assertRaises(errors.UserAbort, shelver.run)
253
self.assertFileEqual(LINES_ZY, 'tree/foo')
255
def test_shelve_all(self):
256
self.create_shelvable_tree()
257
shelver = ExpectShelver.from_args(sys.stdout, all=True,
263
self.assertFileEqual(LINES_AJ, 'tree/foo')
265
def test_shelve_filename(self):
266
tree = self.create_shelvable_tree()
267
self.build_tree(['tree/bar'])
269
tree.lock_tree_write()
270
self.addCleanup(tree.unlock)
271
shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])
272
self.addCleanup(shelver.finalize)
273
shelver.expect('Shelve adding file "bar"?', 0)
274
shelver.expect('Shelve 1 change(s)?', 0)
277
def test_shelve_destroy(self):
278
tree = self.create_shelvable_tree()
279
shelver = shelf_ui.Shelver.from_args(sys.stdout, all=True,
280
directory='tree', destroy=True)
281
self.addCleanup(shelver.finalize)
283
self.assertIs(None, tree.get_shelf_manager().last_shelf())
284
self.assertFileEqual(LINES_AJ, 'tree/foo')
287
def shelve_all(tree, target_revision_id):
290
target = tree.branch.repository.revision_tree(target_revision_id)
291
shelver = shelf_ui.Shelver(tree, target, auto=True,
300
def test_shelve_old_root_preserved(self):
301
tree1 = self.make_branch_and_tree('tree1')
302
tree1.commit('add root')
303
tree1_root_id = tree1.path2id('')
304
tree2 = self.make_branch_and_tree('tree2')
305
rev2 = tree2.commit('add root')
306
self.assertNotEqual(tree1_root_id, tree2.path2id(''))
307
tree1.merge_from_branch(tree2.branch,
308
from_revision=revision.NULL_REVISION)
309
tree1.commit('merging in tree2')
310
self.assertEqual(tree1_root_id, tree1.path2id(''))
311
# This is essentially assertNotRaises(InconsistentDelta)
312
# With testtools 0.9.9, it can be rewritten as:
313
# with ExpectedException(AssertionError,
314
# 'InconsistentDelta not raised'):
315
# with ExpectedException(errors.InconsistentDelta, ''):
316
# self.shelve_all(tree1, rev2)
317
e = self.assertRaises(AssertionError, self.assertRaises,
318
errors.InconsistentDelta, self.shelve_all, tree1,
320
self.assertContainsRe('InconsistentDelta not raised', str(e))
322
def test_shelve_split(self):
323
outer_tree = self.make_branch_and_tree('outer')
324
outer_tree.commit('Add root')
325
inner_tree = self.make_branch_and_tree('outer/inner')
326
rev2 = inner_tree.commit('Add root')
327
outer_tree.subsume(inner_tree)
328
# This is essentially assertNotRaises(ValueError).
329
# The ValueError is 'None is not a valid file id'.
330
self.expectFailure('Cannot shelve a join back to the inner tree.',
331
self.assertRaises, AssertionError,
332
self.assertRaises, ValueError, self.shelve_all,
336
class TestApplyReporter(ShelfTestCase):
338
def test_shelve_not_diff(self):
339
tree = self.create_shelvable_tree()
340
tree.lock_tree_write()
341
self.addCleanup(tree.unlock)
342
shelver = ExpectShelver(tree, tree.basis_tree(),
343
reporter=shelf_ui.ApplyReporter())
344
self.addCleanup(shelver.finalize)
345
shelver.expect('Apply change?', 1)
346
shelver.expect('Apply change?', 1)
347
# No final shelving prompt because no changes were selected
349
self.assertFileEqual(LINES_ZY, 'tree/foo')
351
def test_shelve_diff_no(self):
352
tree = self.create_shelvable_tree()
353
tree.lock_tree_write()
354
self.addCleanup(tree.unlock)
355
shelver = ExpectShelver(tree, tree.basis_tree(),
356
reporter=shelf_ui.ApplyReporter())
357
self.addCleanup(shelver.finalize)
358
shelver.expect('Apply change?', 0)
359
shelver.expect('Apply change?', 0)
360
shelver.expect('Apply 2 change(s)?', 1)
362
self.assertFileEqual(LINES_ZY, 'tree/foo')
364
def test_shelve_diff(self):
365
tree = self.create_shelvable_tree()
366
tree.lock_tree_write()
367
self.addCleanup(tree.unlock)
368
shelver = ExpectShelver(tree, tree.basis_tree(),
369
reporter=shelf_ui.ApplyReporter())
370
self.addCleanup(shelver.finalize)
371
shelver.expect('Apply change?', 0)
372
shelver.expect('Apply change?', 0)
373
shelver.expect('Apply 2 change(s)?', 0)
375
self.assertFileEqual(LINES_AJ, 'tree/foo')
377
def test_shelve_binary_change(self):
378
tree = self.create_shelvable_tree()
379
self.build_tree_contents([('tree/foo', b'\x00')])
380
tree.lock_tree_write()
381
self.addCleanup(tree.unlock)
382
shelver = ExpectShelver(tree, tree.basis_tree(),
383
reporter=shelf_ui.ApplyReporter())
384
self.addCleanup(shelver.finalize)
385
shelver.expect('Apply binary changes?', 0)
386
shelver.expect('Apply 1 change(s)?', 0)
388
self.assertFileEqual(LINES_AJ, 'tree/foo')
390
def test_shelve_rename(self):
391
tree = self.create_shelvable_tree()
392
tree.rename_one('foo', 'bar')
393
tree.lock_tree_write()
394
self.addCleanup(tree.unlock)
395
shelver = ExpectShelver(tree, tree.basis_tree(),
396
reporter=shelf_ui.ApplyReporter())
397
self.addCleanup(shelver.finalize)
398
shelver.expect('Rename "bar" => "foo"?', 0)
399
shelver.expect('Apply change?', 0)
400
shelver.expect('Apply change?', 0)
401
shelver.expect('Apply 3 change(s)?', 0)
403
self.assertFileEqual(LINES_AJ, 'tree/foo')
405
def test_shelve_deletion(self):
406
tree = self.create_shelvable_tree()
407
os.unlink('tree/foo')
408
tree.lock_tree_write()
409
self.addCleanup(tree.unlock)
410
shelver = ExpectShelver(tree, tree.basis_tree(),
411
reporter=shelf_ui.ApplyReporter())
412
self.addCleanup(shelver.finalize)
413
shelver.expect('Add file "foo"?', 0)
414
shelver.expect('Apply 1 change(s)?', 0)
416
self.assertFileEqual(LINES_AJ, 'tree/foo')
418
def test_shelve_creation(self):
419
tree = self.make_branch_and_tree('tree')
420
tree.commit('add tree root')
421
self.build_tree(['tree/foo'])
423
tree.lock_tree_write()
424
self.addCleanup(tree.unlock)
425
shelver = ExpectShelver(tree, tree.basis_tree(),
426
reporter=shelf_ui.ApplyReporter())
427
self.addCleanup(shelver.finalize)
428
shelver.expect('Delete file "foo"?', 0)
429
shelver.expect('Apply 1 change(s)?', 0)
431
self.assertPathDoesNotExist('tree/foo')
433
def test_shelve_kind_change(self):
434
tree = self.create_shelvable_tree()
435
os.unlink('tree/foo')
437
tree.lock_tree_write()
438
self.addCleanup(tree.unlock)
439
shelver = ExpectShelver(tree, tree.basis_tree(),
440
reporter=shelf_ui.ApplyReporter())
441
self.addCleanup(shelver.finalize)
442
shelver.expect('Change "foo" from directory to a file?', 0)
443
shelver.expect('Apply 1 change(s)?', 0)
445
def test_shelve_modify_target(self):
446
self.requireFeature(features.SymlinkFeature)
447
tree = self.create_shelvable_tree()
448
os.symlink('bar', 'tree/baz')
449
tree.add('baz', b'baz-id')
450
tree.commit("Add symlink")
451
os.unlink('tree/baz')
452
os.symlink('vax', 'tree/baz')
453
tree.lock_tree_write()
454
self.addCleanup(tree.unlock)
455
shelver = ExpectShelver(tree, tree.basis_tree(),
456
reporter=shelf_ui.ApplyReporter())
457
self.addCleanup(shelver.finalize)
458
shelver.expect('Change target of "baz" from "vax" to "bar"?',
460
shelver.expect('Apply 1 change(s)?', 0)
462
self.assertEqual('bar', os.readlink('tree/baz'))
465
class TestUnshelver(tests.TestCaseWithTransport):
467
def create_tree_with_shelf(self):
468
tree = self.make_branch_and_tree('tree')
471
self.build_tree_contents([('tree/foo', LINES_AJ)])
472
tree.add('foo', b'foo-id')
473
tree.commit('added foo')
474
self.build_tree_contents([('tree/foo', LINES_ZY)])
475
shelver = shelf_ui.Shelver(tree, tree.basis_tree(),
476
auto_apply=True, auto=True)
485
def test_unshelve(self):
486
tree = self.create_tree_with_shelf()
488
self.addCleanup(tree.unlock)
489
manager = tree.get_shelf_manager()
490
shelf_ui.Unshelver(tree, manager, 1, True, True, True).run()
491
self.assertFileEqual(LINES_ZY, 'tree/foo')
493
def test_unshelve_args(self):
494
tree = self.create_tree_with_shelf()
495
unshelver = shelf_ui.Unshelver.from_args(directory='tree')
499
unshelver.tree.unlock()
500
self.assertFileEqual(LINES_ZY, 'tree/foo')
501
self.assertIs(None, tree.get_shelf_manager().last_shelf())
503
def test_unshelve_args_dry_run(self):
504
tree = self.create_tree_with_shelf()
505
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
510
unshelver.tree.unlock()
511
self.assertFileEqual(LINES_AJ, 'tree/foo')
512
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
514
def test_unshelve_args_preview(self):
515
tree = self.create_tree_with_shelf()
516
write_diff_to = BytesIO()
517
unshelver = shelf_ui.Unshelver.from_args(
518
directory='tree', action='preview', write_diff_to=write_diff_to)
522
unshelver.tree.unlock()
523
# The changes were not unshelved.
524
self.assertFileEqual(LINES_AJ, 'tree/foo')
525
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
527
# But the diff was written to write_diff_to.
528
diff = write_diff_to.getvalue()
529
expected = dedent("""\
544
self.assertEqualDiff(expected.encode('utf-8'), diff[-len(expected):])
546
def test_unshelve_args_delete_only(self):
547
tree = self.make_branch_and_tree('tree')
548
manager = tree.get_shelf_manager()
549
shelf_file = manager.new_shelf()[1]
551
shelf_file.write(b'garbage')
554
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
555
action='delete-only')
559
unshelver.tree.unlock()
560
self.assertIs(None, manager.last_shelf())
562
def test_unshelve_args_invalid_shelf_id(self):
563
tree = self.make_branch_and_tree('tree')
564
manager = tree.get_shelf_manager()
565
shelf_file = manager.new_shelf()[1]
567
shelf_file.write(b'garbage')
570
self.assertRaises(shelf.InvalidShelfId,
571
shelf_ui.Unshelver.from_args, directory='tree',
572
action='delete-only', shelf_id='foo')
575
class TestUnshelveScripts(TestUnshelver,
576
script.TestCaseWithTransportAndScript):
578
def test_unshelve_messages_keep(self):
579
self.create_tree_with_shelf()
582
$ brz unshelve --keep
583
2>Using changes with id "1".
585
2>All changes applied successfully.
588
def test_unshelve_messages_delete(self):
589
self.create_tree_with_shelf()
592
$ brz unshelve --delete-only
593
2>Deleted changes with id "1".
596
def test_unshelve_messages_apply(self):
597
self.create_tree_with_shelf()
600
$ brz unshelve --apply
601
2>Using changes with id "1".
603
2>All changes applied successfully.
604
2>Deleted changes with id "1".
607
def test_unshelve_messages_dry_run(self):
608
self.create_tree_with_shelf()
611
$ brz unshelve --dry-run
612
2>Using changes with id "1".