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
20
from textwrap import dedent
28
from ..sixish import (
37
class ExpectShelver(shelf_ui.Shelver):
38
"""A variant of Shelver that intercepts console activity, for testing."""
40
def __init__(self, work_tree, target_tree, diff_writer=None,
41
auto=False, auto_apply=False, file_list=None, message=None,
42
destroy=False, reporter=None):
43
shelf_ui.Shelver.__init__(self, work_tree, target_tree, diff_writer,
44
auto, auto_apply, file_list, message,
45
destroy, reporter=reporter)
47
self.diff_writer = BytesIO()
49
def expect(self, message, response):
50
self.expected.append((message, response))
52
def prompt(self, message, choices, default):
54
expected_message, response = self.expected.pop(0)
56
raise AssertionError('Unexpected prompt: %s' % message)
57
if message != expected_message:
58
raise AssertionError('Wrong prompt: %s' % message)
59
if choices != '&yes\n&No\n&finish\n&quit':
60
raise AssertionError('Wrong choices: %s' % choices)
64
LINES_AJ = 'a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n'
67
LINES_ZY = 'z\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
70
LINES_AY = 'a\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
73
class ShelfTestCase(tests.TestCaseWithTransport):
75
def create_shelvable_tree(self):
76
tree = self.make_branch_and_tree('tree')
77
self.build_tree_contents([('tree/foo', LINES_AJ)])
78
tree.add('foo', 'foo-id')
79
tree.commit('added foo')
80
self.build_tree_contents([('tree/foo', LINES_ZY)])
84
class TestShelver(ShelfTestCase):
86
def test_unexpected_prompt_failure(self):
87
tree = self.create_shelvable_tree()
88
tree.lock_tree_write()
89
self.addCleanup(tree.unlock)
90
shelver = ExpectShelver(tree, tree.basis_tree())
91
self.addCleanup(shelver.finalize)
92
e = self.assertRaises(AssertionError, shelver.run)
93
self.assertEqual('Unexpected prompt: Shelve?', str(e))
95
def test_wrong_prompt_failure(self):
96
tree = self.create_shelvable_tree()
97
tree.lock_tree_write()
98
self.addCleanup(tree.unlock)
99
shelver = ExpectShelver(tree, tree.basis_tree())
100
self.addCleanup(shelver.finalize)
101
shelver.expect('foo', 0)
102
e = self.assertRaises(AssertionError, shelver.run)
103
self.assertEqual('Wrong prompt: Shelve?', str(e))
105
def test_shelve_not_diff(self):
106
tree = self.create_shelvable_tree()
107
tree.lock_tree_write()
108
self.addCleanup(tree.unlock)
109
shelver = ExpectShelver(tree, tree.basis_tree())
110
self.addCleanup(shelver.finalize)
111
shelver.expect('Shelve?', 1)
112
shelver.expect('Shelve?', 1)
113
# No final shelving prompt because no changes were selected
115
self.assertFileEqual(LINES_ZY, 'tree/foo')
117
def test_shelve_diff_no(self):
118
tree = self.create_shelvable_tree()
119
tree.lock_tree_write()
120
self.addCleanup(tree.unlock)
121
shelver = ExpectShelver(tree, tree.basis_tree())
122
self.addCleanup(shelver.finalize)
123
shelver.expect('Shelve?', 0)
124
shelver.expect('Shelve?', 0)
125
shelver.expect('Shelve 2 change(s)?', 1)
127
self.assertFileEqual(LINES_ZY, 'tree/foo')
129
def test_shelve_diff(self):
130
tree = self.create_shelvable_tree()
131
tree.lock_tree_write()
132
self.addCleanup(tree.unlock)
133
shelver = ExpectShelver(tree, tree.basis_tree())
134
self.addCleanup(shelver.finalize)
135
shelver.expect('Shelve?', 0)
136
shelver.expect('Shelve?', 0)
137
shelver.expect('Shelve 2 change(s)?', 0)
139
self.assertFileEqual(LINES_AJ, 'tree/foo')
141
def test_shelve_one_diff(self):
142
tree = self.create_shelvable_tree()
143
tree.lock_tree_write()
144
self.addCleanup(tree.unlock)
145
shelver = ExpectShelver(tree, tree.basis_tree())
146
self.addCleanup(shelver.finalize)
147
shelver.expect('Shelve?', 0)
148
shelver.expect('Shelve?', 1)
149
shelver.expect('Shelve 1 change(s)?', 0)
151
self.assertFileEqual(LINES_AY, 'tree/foo')
153
def test_shelve_binary_change(self):
154
tree = self.create_shelvable_tree()
155
self.build_tree_contents([('tree/foo', '\x00')])
156
tree.lock_tree_write()
157
self.addCleanup(tree.unlock)
158
shelver = ExpectShelver(tree, tree.basis_tree())
159
self.addCleanup(shelver.finalize)
160
shelver.expect('Shelve binary changes?', 0)
161
shelver.expect('Shelve 1 change(s)?', 0)
163
self.assertFileEqual(LINES_AJ, 'tree/foo')
165
def test_shelve_rename(self):
166
tree = self.create_shelvable_tree()
167
tree.rename_one('foo', 'bar')
168
tree.lock_tree_write()
169
self.addCleanup(tree.unlock)
170
shelver = ExpectShelver(tree, tree.basis_tree())
171
self.addCleanup(shelver.finalize)
172
shelver.expect('Shelve renaming "foo" => "bar"?', 0)
173
shelver.expect('Shelve?', 0)
174
shelver.expect('Shelve?', 0)
175
shelver.expect('Shelve 3 change(s)?', 0)
177
self.assertFileEqual(LINES_AJ, 'tree/foo')
179
def test_shelve_deletion(self):
180
tree = self.create_shelvable_tree()
181
os.unlink('tree/foo')
182
tree.lock_tree_write()
183
self.addCleanup(tree.unlock)
184
shelver = ExpectShelver(tree, tree.basis_tree())
185
self.addCleanup(shelver.finalize)
186
shelver.expect('Shelve removing file "foo"?', 0)
187
shelver.expect('Shelve 1 change(s)?', 0)
189
self.assertFileEqual(LINES_AJ, 'tree/foo')
191
def test_shelve_creation(self):
192
tree = self.make_branch_and_tree('tree')
193
tree.commit('add tree root')
194
self.build_tree(['tree/foo'])
196
tree.lock_tree_write()
197
self.addCleanup(tree.unlock)
198
shelver = ExpectShelver(tree, tree.basis_tree())
199
self.addCleanup(shelver.finalize)
200
shelver.expect('Shelve adding file "foo"?', 0)
201
shelver.expect('Shelve 1 change(s)?', 0)
203
self.assertPathDoesNotExist('tree/foo')
205
def test_shelve_kind_change(self):
206
tree = self.create_shelvable_tree()
207
os.unlink('tree/foo')
209
tree.lock_tree_write()
210
self.addCleanup(tree.unlock)
211
shelver = ExpectShelver(tree, tree.basis_tree())
212
self.addCleanup(shelver.finalize)
213
shelver.expect('Shelve changing "foo" from file to directory?',
215
shelver.expect('Shelve 1 change(s)?', 0)
217
def test_shelve_modify_target(self):
218
self.requireFeature(features.SymlinkFeature)
219
tree = self.create_shelvable_tree()
220
os.symlink('bar', 'tree/baz')
221
tree.add('baz', 'baz-id')
222
tree.commit("Add symlink")
223
os.unlink('tree/baz')
224
os.symlink('vax', 'tree/baz')
225
tree.lock_tree_write()
226
self.addCleanup(tree.unlock)
227
shelver = ExpectShelver(tree, tree.basis_tree())
228
self.addCleanup(shelver.finalize)
229
shelver.expect('Shelve changing target of "baz" from "bar" to '
231
shelver.expect('Shelve 1 change(s)?', 0)
233
self.assertEqual('bar', os.readlink('tree/baz'))
235
def test_shelve_finish(self):
236
tree = self.create_shelvable_tree()
237
tree.lock_tree_write()
238
self.addCleanup(tree.unlock)
239
shelver = ExpectShelver(tree, tree.basis_tree())
240
self.addCleanup(shelver.finalize)
241
shelver.expect('Shelve?', 2)
242
shelver.expect('Shelve 2 change(s)?', 0)
244
self.assertFileEqual(LINES_AJ, 'tree/foo')
246
def test_shelve_quit(self):
247
tree = self.create_shelvable_tree()
248
tree.lock_tree_write()
249
self.addCleanup(tree.unlock)
250
shelver = ExpectShelver(tree, tree.basis_tree())
251
self.addCleanup(shelver.finalize)
252
shelver.expect('Shelve?', 3)
253
self.assertRaises(errors.UserAbort, shelver.run)
254
self.assertFileEqual(LINES_ZY, 'tree/foo')
256
def test_shelve_all(self):
257
tree = self.create_shelvable_tree()
258
shelver = ExpectShelver.from_args(sys.stdout, all=True,
264
self.assertFileEqual(LINES_AJ, 'tree/foo')
266
def test_shelve_filename(self):
267
tree = self.create_shelvable_tree()
268
self.build_tree(['tree/bar'])
270
tree.lock_tree_write()
271
self.addCleanup(tree.unlock)
272
shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])
273
self.addCleanup(shelver.finalize)
274
shelver.expect('Shelve adding file "bar"?', 0)
275
shelver.expect('Shelve 1 change(s)?', 0)
278
def test_shelve_destroy(self):
279
tree = self.create_shelvable_tree()
280
shelver = shelf_ui.Shelver.from_args(sys.stdout, all=True,
281
directory='tree', destroy=True)
282
self.addCleanup(shelver.finalize)
284
self.assertIs(None, tree.get_shelf_manager().last_shelf())
285
self.assertFileEqual(LINES_AJ, 'tree/foo')
288
def shelve_all(tree, target_revision_id):
291
target = tree.branch.repository.revision_tree(target_revision_id)
292
shelver = shelf_ui.Shelver(tree, target, auto=True,
301
def test_shelve_old_root_preserved(self):
302
tree1 = self.make_branch_and_tree('tree1')
303
tree1.commit('add root')
304
tree1_root_id = tree1.get_root_id()
305
tree2 = self.make_branch_and_tree('tree2')
306
rev2 = tree2.commit('add root')
307
self.assertNotEqual(tree1_root_id, tree2.get_root_id())
308
tree1.merge_from_branch(tree2.branch,
309
from_revision=revision.NULL_REVISION)
310
tree1.commit('merging in tree2')
311
self.assertEqual(tree1_root_id, tree1.get_root_id())
312
# This is essentially assertNotRaises(InconsistentDelta)
313
# With testtools 0.9.9, it can be rewritten as:
314
# with ExpectedException(AssertionError,
315
# 'InconsistentDelta not raised'):
316
# with ExpectedException(errors.InconsistentDelta, ''):
317
# self.shelve_all(tree1, rev2)
318
e = self.assertRaises(AssertionError, self.assertRaises,
319
errors.InconsistentDelta, self.shelve_all, tree1,
321
self.assertContainsRe('InconsistentDelta not raised', str(e))
323
def test_shelve_split(self):
324
outer_tree = self.make_branch_and_tree('outer')
325
outer_tree.commit('Add root')
326
inner_tree = self.make_branch_and_tree('outer/inner')
327
rev2 = inner_tree.commit('Add root')
328
outer_tree.subsume(inner_tree)
329
# This is essentially assertNotRaises(ValueError).
330
# The ValueError is 'None is not a valid file id'.
331
self.expectFailure('Cannot shelve a join back to the inner tree.',
332
self.assertRaises, AssertionError,
333
self.assertRaises, ValueError, self.shelve_all,
337
class TestApplyReporter(ShelfTestCase):
339
def test_shelve_not_diff(self):
340
tree = self.create_shelvable_tree()
341
tree.lock_tree_write()
342
self.addCleanup(tree.unlock)
343
shelver = ExpectShelver(tree, tree.basis_tree(),
344
reporter=shelf_ui.ApplyReporter())
345
self.addCleanup(shelver.finalize)
346
shelver.expect('Apply change?', 1)
347
shelver.expect('Apply change?', 1)
348
# No final shelving prompt because no changes were selected
350
self.assertFileEqual(LINES_ZY, 'tree/foo')
352
def test_shelve_diff_no(self):
353
tree = self.create_shelvable_tree()
354
tree.lock_tree_write()
355
self.addCleanup(tree.unlock)
356
shelver = ExpectShelver(tree, tree.basis_tree(),
357
reporter=shelf_ui.ApplyReporter())
358
self.addCleanup(shelver.finalize)
359
shelver.expect('Apply change?', 0)
360
shelver.expect('Apply change?', 0)
361
shelver.expect('Apply 2 change(s)?', 1)
363
self.assertFileEqual(LINES_ZY, 'tree/foo')
365
def test_shelve_diff(self):
366
tree = self.create_shelvable_tree()
367
tree.lock_tree_write()
368
self.addCleanup(tree.unlock)
369
shelver = ExpectShelver(tree, tree.basis_tree(),
370
reporter=shelf_ui.ApplyReporter())
371
self.addCleanup(shelver.finalize)
372
shelver.expect('Apply change?', 0)
373
shelver.expect('Apply change?', 0)
374
shelver.expect('Apply 2 change(s)?', 0)
376
self.assertFileEqual(LINES_AJ, 'tree/foo')
378
def test_shelve_binary_change(self):
379
tree = self.create_shelvable_tree()
380
self.build_tree_contents([('tree/foo', '\x00')])
381
tree.lock_tree_write()
382
self.addCleanup(tree.unlock)
383
shelver = ExpectShelver(tree, tree.basis_tree(),
384
reporter=shelf_ui.ApplyReporter())
385
self.addCleanup(shelver.finalize)
386
shelver.expect('Apply binary changes?', 0)
387
shelver.expect('Apply 1 change(s)?', 0)
389
self.assertFileEqual(LINES_AJ, 'tree/foo')
391
def test_shelve_rename(self):
392
tree = self.create_shelvable_tree()
393
tree.rename_one('foo', 'bar')
394
tree.lock_tree_write()
395
self.addCleanup(tree.unlock)
396
shelver = ExpectShelver(tree, tree.basis_tree(),
397
reporter=shelf_ui.ApplyReporter())
398
self.addCleanup(shelver.finalize)
399
shelver.expect('Rename "bar" => "foo"?', 0)
400
shelver.expect('Apply change?', 0)
401
shelver.expect('Apply change?', 0)
402
shelver.expect('Apply 3 change(s)?', 0)
404
self.assertFileEqual(LINES_AJ, 'tree/foo')
406
def test_shelve_deletion(self):
407
tree = self.create_shelvable_tree()
408
os.unlink('tree/foo')
409
tree.lock_tree_write()
410
self.addCleanup(tree.unlock)
411
shelver = ExpectShelver(tree, tree.basis_tree(),
412
reporter=shelf_ui.ApplyReporter())
413
self.addCleanup(shelver.finalize)
414
shelver.expect('Add file "foo"?', 0)
415
shelver.expect('Apply 1 change(s)?', 0)
417
self.assertFileEqual(LINES_AJ, 'tree/foo')
419
def test_shelve_creation(self):
420
tree = self.make_branch_and_tree('tree')
421
tree.commit('add tree root')
422
self.build_tree(['tree/foo'])
424
tree.lock_tree_write()
425
self.addCleanup(tree.unlock)
426
shelver = ExpectShelver(tree, tree.basis_tree(),
427
reporter=shelf_ui.ApplyReporter())
428
self.addCleanup(shelver.finalize)
429
shelver.expect('Delete file "foo"?', 0)
430
shelver.expect('Apply 1 change(s)?', 0)
432
self.assertPathDoesNotExist('tree/foo')
434
def test_shelve_kind_change(self):
435
tree = self.create_shelvable_tree()
436
os.unlink('tree/foo')
438
tree.lock_tree_write()
439
self.addCleanup(tree.unlock)
440
shelver = ExpectShelver(tree, tree.basis_tree(),
441
reporter=shelf_ui.ApplyReporter())
442
self.addCleanup(shelver.finalize)
443
shelver.expect('Change "foo" from directory to a file?', 0)
444
shelver.expect('Apply 1 change(s)?', 0)
446
def test_shelve_modify_target(self):
447
self.requireFeature(features.SymlinkFeature)
448
tree = self.create_shelvable_tree()
449
os.symlink('bar', 'tree/baz')
450
tree.add('baz', 'baz-id')
451
tree.commit("Add symlink")
452
os.unlink('tree/baz')
453
os.symlink('vax', 'tree/baz')
454
tree.lock_tree_write()
455
self.addCleanup(tree.unlock)
456
shelver = ExpectShelver(tree, tree.basis_tree(),
457
reporter=shelf_ui.ApplyReporter())
458
self.addCleanup(shelver.finalize)
459
shelver.expect('Change target of "baz" from "vax" to "bar"?',
461
shelver.expect('Apply 1 change(s)?', 0)
463
self.assertEqual('bar', os.readlink('tree/baz'))
466
class TestUnshelver(tests.TestCaseWithTransport):
468
def create_tree_with_shelf(self):
469
tree = self.make_branch_and_tree('tree')
472
self.build_tree_contents([('tree/foo', LINES_AJ)])
473
tree.add('foo', 'foo-id')
474
tree.commit('added foo')
475
self.build_tree_contents([('tree/foo', LINES_ZY)])
476
shelver = shelf_ui.Shelver(tree, tree.basis_tree(),
477
auto_apply=True, auto=True)
486
def test_unshelve(self):
487
tree = self.create_tree_with_shelf()
489
self.addCleanup(tree.unlock)
490
manager = tree.get_shelf_manager()
491
shelf_ui.Unshelver(tree, manager, 1, True, True, True).run()
492
self.assertFileEqual(LINES_ZY, 'tree/foo')
494
def test_unshelve_args(self):
495
tree = self.create_tree_with_shelf()
496
unshelver = shelf_ui.Unshelver.from_args(directory='tree')
500
unshelver.tree.unlock()
501
self.assertFileEqual(LINES_ZY, 'tree/foo')
502
self.assertIs(None, tree.get_shelf_manager().last_shelf())
504
def test_unshelve_args_dry_run(self):
505
tree = self.create_tree_with_shelf()
506
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
511
unshelver.tree.unlock()
512
self.assertFileEqual(LINES_AJ, 'tree/foo')
513
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
515
def test_unshelve_args_preview(self):
516
tree = self.create_tree_with_shelf()
517
write_diff_to = BytesIO()
518
unshelver = shelf_ui.Unshelver.from_args(
519
directory='tree', action='preview', write_diff_to=write_diff_to)
523
unshelver.tree.unlock()
524
# The changes were not unshelved.
525
self.assertFileEqual(LINES_AJ, 'tree/foo')
526
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
528
# But the diff was written to write_diff_to.
529
diff = write_diff_to.getvalue()
530
expected = dedent("""\
545
self.assertEqualDiff(expected, diff[-len(expected):])
547
def test_unshelve_args_delete_only(self):
548
tree = self.make_branch_and_tree('tree')
549
manager = tree.get_shelf_manager()
550
shelf_file = manager.new_shelf()[1]
552
shelf_file.write('garbage')
555
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
556
action='delete-only')
560
unshelver.tree.unlock()
561
self.assertIs(None, manager.last_shelf())
563
def test_unshelve_args_invalid_shelf_id(self):
564
tree = self.make_branch_and_tree('tree')
565
manager = tree.get_shelf_manager()
566
shelf_file = manager.new_shelf()[1]
568
shelf_file.write('garbage')
571
self.assertRaises(errors.InvalidShelfId,
572
shelf_ui.Unshelver.from_args, directory='tree',
573
action='delete-only', shelf_id='foo')
576
class TestUnshelveScripts(TestUnshelver,
577
script.TestCaseWithTransportAndScript):
579
def test_unshelve_messages_keep(self):
580
self.create_tree_with_shelf()
583
$ brz unshelve --keep
584
2>Using changes with id "1".
586
2>All changes applied successfully.
589
def test_unshelve_messages_delete(self):
590
self.create_tree_with_shelf()
593
$ brz unshelve --delete-only
594
2>Deleted changes with id "1".
597
def test_unshelve_messages_apply(self):
598
self.create_tree_with_shelf()
601
$ brz unshelve --apply
602
2>Using changes with id "1".
604
2>All changes applied successfully.
605
2>Deleted changes with id "1".
608
def test_unshelve_messages_dry_run(self):
609
self.create_tree_with_shelf()
612
$ brz unshelve --dry-run
613
2>Using changes with id "1".