1
# Copyright (C) 2008 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
30
class ExpectShelver(shelf_ui.Shelver):
31
"""A variant of Shelver that intercepts console activity, for testing."""
33
def __init__(self, work_tree, target_tree, diff_writer=None,
34
auto=False, auto_apply=False, file_list=None, message=None,
35
destroy=False, reporter=None):
36
shelf_ui.Shelver.__init__(self, work_tree, target_tree, diff_writer,
37
auto, auto_apply, file_list, message,
38
destroy, reporter=reporter)
40
self.diff_writer = StringIO()
42
def expect(self, prompt, response):
43
self.expected.append((prompt, response))
45
def prompt(self, message):
47
prompt, response = self.expected.pop(0)
49
raise AssertionError('Unexpected prompt: %s' % message)
51
raise AssertionError('Wrong prompt: %s' % message)
55
LINES_AJ = 'a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n'
58
LINES_ZY = 'z\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
61
LINES_AY = 'a\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
64
class TestShelver(tests.TestCaseWithTransport):
66
def create_shelvable_tree(self):
67
tree = self.make_branch_and_tree('tree')
68
self.build_tree_contents([('tree/foo', LINES_AJ)])
69
tree.add('foo', 'foo-id')
70
tree.commit('added foo')
71
self.build_tree_contents([('tree/foo', LINES_ZY)])
74
def test_unexpected_prompt_failure(self):
75
tree = self.create_shelvable_tree()
76
tree.lock_tree_write()
77
self.addCleanup(tree.unlock)
78
shelver = ExpectShelver(tree, tree.basis_tree())
79
self.addCleanup(shelver.finalize)
80
e = self.assertRaises(AssertionError, shelver.run)
81
self.assertEqual('Unexpected prompt: Shelve? [yNfq?]', str(e))
83
def test_wrong_prompt_failure(self):
84
tree = self.create_shelvable_tree()
85
tree.lock_tree_write()
86
self.addCleanup(tree.unlock)
87
shelver = ExpectShelver(tree, tree.basis_tree())
88
self.addCleanup(shelver.finalize)
89
shelver.expect('foo', 'y')
90
e = self.assertRaises(AssertionError, shelver.run)
91
self.assertEqual('Wrong prompt: Shelve? [yNfq?]', str(e))
93
def test_shelve_not_diff(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('Shelve? [yNfq?]', 'n')
100
shelver.expect('Shelve? [yNfq?]', 'n')
101
# No final shelving prompt because no changes were selected
103
self.assertFileEqual(LINES_ZY, 'tree/foo')
105
def test_shelve_diff_no(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? [yNfq?]', 'y')
112
shelver.expect('Shelve? [yNfq?]', 'y')
113
shelver.expect('Shelve 2 change(s)? [yNfq?]', 'n')
115
self.assertFileEqual(LINES_ZY, 'tree/foo')
117
def test_shelve_diff(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? [yNfq?]', 'y')
124
shelver.expect('Shelve? [yNfq?]', 'y')
125
shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
127
self.assertFileEqual(LINES_AJ, 'tree/foo')
129
def test_shelve_one_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? [yNfq?]', 'y')
136
shelver.expect('Shelve? [yNfq?]', 'n')
137
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
139
self.assertFileEqual(LINES_AY, 'tree/foo')
141
def test_shelve_binary_change(self):
142
tree = self.create_shelvable_tree()
143
self.build_tree_contents([('tree/foo', '\x00')])
144
tree.lock_tree_write()
145
self.addCleanup(tree.unlock)
146
shelver = ExpectShelver(tree, tree.basis_tree())
147
self.addCleanup(shelver.finalize)
148
shelver.expect('Shelve binary changes? [yNfq?]', 'y')
149
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
151
self.assertFileEqual(LINES_AJ, 'tree/foo')
153
def test_shelve_rename(self):
154
tree = self.create_shelvable_tree()
155
tree.rename_one('foo', 'bar')
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 renaming "foo" => "bar"? [yNfq?]', 'y')
161
shelver.expect('Shelve? [yNfq?]', 'y')
162
shelver.expect('Shelve? [yNfq?]', 'y')
163
shelver.expect('Shelve 3 change(s)? [yNfq?]', 'y')
165
self.assertFileEqual(LINES_AJ, 'tree/foo')
167
def test_shelve_deletion(self):
168
tree = self.create_shelvable_tree()
169
os.unlink('tree/foo')
170
tree.lock_tree_write()
171
self.addCleanup(tree.unlock)
172
shelver = ExpectShelver(tree, tree.basis_tree())
173
self.addCleanup(shelver.finalize)
174
shelver.expect('Shelve removing file "foo"? [yNfq?]', 'y')
175
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
177
self.assertFileEqual(LINES_AJ, 'tree/foo')
179
def test_shelve_creation(self):
180
tree = self.make_branch_and_tree('tree')
181
tree.commit('add tree root')
182
self.build_tree(['tree/foo'])
184
tree.lock_tree_write()
185
self.addCleanup(tree.unlock)
186
shelver = ExpectShelver(tree, tree.basis_tree())
187
self.addCleanup(shelver.finalize)
188
shelver.expect('Shelve adding file "foo"? [yNfq?]', 'y')
189
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
191
self.failIfExists('tree/foo')
193
def test_shelve_kind_change(self):
194
tree = self.create_shelvable_tree()
195
os.unlink('tree/foo')
197
tree.lock_tree_write()
198
self.addCleanup(tree.unlock)
199
shelver = ExpectShelver(tree, tree.basis_tree())
200
self.addCleanup(shelver.finalize)
201
shelver.expect('Shelve changing "foo" from file to directory? [yNfq?]',
203
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
205
def test_shelve_modify_target(self):
206
self.requireFeature(tests.SymlinkFeature)
207
tree = self.create_shelvable_tree()
208
os.symlink('bar', 'tree/baz')
209
tree.add('baz', 'baz-id')
210
tree.commit("Add symlink")
211
os.unlink('tree/baz')
212
os.symlink('vax', 'tree/baz')
213
tree.lock_tree_write()
214
self.addCleanup(tree.unlock)
215
shelver = ExpectShelver(tree, tree.basis_tree())
216
self.addCleanup(shelver.finalize)
217
shelver.expect('Shelve changing target of "baz" from "bar" to '
218
'"vax"? [yNfq?]', 'y')
219
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
221
self.assertEqual('bar', os.readlink('tree/baz'))
223
def test_shelve_finish(self):
224
tree = self.create_shelvable_tree()
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? [yNfq?]', 'f')
230
shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
232
self.assertFileEqual(LINES_AJ, 'tree/foo')
234
def test_shelve_quit(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? [yNfq?]', 'q')
241
self.assertRaises(errors.UserAbort, shelver.run)
242
self.assertFileEqual(LINES_ZY, 'tree/foo')
244
def test_shelve_all(self):
245
tree = self.create_shelvable_tree()
246
shelver = ExpectShelver.from_args(sys.stdout, all=True,
252
self.assertFileEqual(LINES_AJ, 'tree/foo')
254
def test_shelve_filename(self):
255
tree = self.create_shelvable_tree()
256
self.build_tree(['tree/bar'])
258
tree.lock_tree_write()
259
self.addCleanup(tree.unlock)
260
shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])
261
self.addCleanup(shelver.finalize)
262
shelver.expect('Shelve adding file "bar"? [yNfq?]', 'y')
263
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
266
def test_shelve_help(self):
267
tree = self.create_shelvable_tree()
268
tree.lock_tree_write()
269
self.addCleanup(tree.unlock)
270
shelver = ExpectShelver(tree, tree.basis_tree())
271
self.addCleanup(shelver.finalize)
272
shelver.expect('Shelve? [yNfq?]', '?')
273
shelver.expect('Shelve? [(y)es, (N)o, (f)inish, or (q)uit]', 'f')
274
shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
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_deleted(self):
301
tree1 = self.make_branch_and_tree('tree1')
302
tree1.commit('add root')
303
tree2 = self.make_branch_and_tree('tree2')
304
rev2 = tree2.commit('add root')
305
tree1.merge_from_branch(tree2.branch,
306
from_revision=revision.NULL_REVISION)
307
tree1.commit('Replaced root entry')
308
# This is essentially assertNotRaises(InconsistentDelta)
309
self.expectFailure('Cannot shelve replacing a root entry',
310
self.assertRaises, AssertionError,
311
self.assertRaises, errors.InconsistentDelta,
312
self.shelve_all, tree1, rev2)
314
def test_shelve_split(self):
315
outer_tree = self.make_branch_and_tree('outer')
316
outer_tree.commit('Add root')
317
inner_tree = self.make_branch_and_tree('outer/inner')
318
rev2 = inner_tree.commit('Add root')
319
outer_tree.subsume(inner_tree)
320
# This is essentially assertNotRaises(ValueError).
321
# The ValueError is 'None is not a valid file id'.
322
self.expectFailure('Cannot shelve a join back to the inner tree.',
323
self.assertRaises, AssertionError,
324
self.assertRaises, ValueError, self.shelve_all,
328
class TestApplyReporter(TestShelver):
330
def test_shelve_not_diff(self):
331
tree = self.create_shelvable_tree()
332
tree.lock_tree_write()
333
self.addCleanup(tree.unlock)
334
shelver = ExpectShelver(tree, tree.basis_tree(),
335
reporter=shelf_ui.ApplyReporter())
336
self.addCleanup(shelver.finalize)
337
shelver.expect('Apply change? [yNfq?]', 'n')
338
shelver.expect('Apply change? [yNfq?]', 'n')
339
# No final shelving prompt because no changes were selected
341
self.assertFileEqual(LINES_ZY, 'tree/foo')
343
def test_shelve_diff_no(self):
344
tree = self.create_shelvable_tree()
345
tree.lock_tree_write()
346
self.addCleanup(tree.unlock)
347
shelver = ExpectShelver(tree, tree.basis_tree(),
348
reporter=shelf_ui.ApplyReporter())
349
self.addCleanup(shelver.finalize)
350
shelver.expect('Apply change? [yNfq?]', 'y')
351
shelver.expect('Apply change? [yNfq?]', 'y')
352
shelver.expect('Apply 2 change(s)? [yNfq?]', 'n')
354
self.assertFileEqual(LINES_ZY, 'tree/foo')
356
def test_shelve_diff(self):
357
tree = self.create_shelvable_tree()
358
tree.lock_tree_write()
359
self.addCleanup(tree.unlock)
360
shelver = ExpectShelver(tree, tree.basis_tree(),
361
reporter=shelf_ui.ApplyReporter())
362
self.addCleanup(shelver.finalize)
363
shelver.expect('Apply change? [yNfq?]', 'y')
364
shelver.expect('Apply change? [yNfq?]', 'y')
365
shelver.expect('Apply 2 change(s)? [yNfq?]', 'y')
367
self.assertFileEqual(LINES_AJ, 'tree/foo')
369
def test_shelve_binary_change(self):
370
tree = self.create_shelvable_tree()
371
self.build_tree_contents([('tree/foo', '\x00')])
372
tree.lock_tree_write()
373
self.addCleanup(tree.unlock)
374
shelver = ExpectShelver(tree, tree.basis_tree(),
375
reporter=shelf_ui.ApplyReporter())
376
self.addCleanup(shelver.finalize)
377
shelver.expect('Apply binary changes? [yNfq?]', 'y')
378
shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
380
self.assertFileEqual(LINES_AJ, 'tree/foo')
382
def test_shelve_rename(self):
383
tree = self.create_shelvable_tree()
384
tree.rename_one('foo', 'bar')
385
tree.lock_tree_write()
386
self.addCleanup(tree.unlock)
387
shelver = ExpectShelver(tree, tree.basis_tree(),
388
reporter=shelf_ui.ApplyReporter())
389
self.addCleanup(shelver.finalize)
390
shelver.expect('Rename "bar" => "foo"? [yNfq?]', 'y')
391
shelver.expect('Apply change? [yNfq?]', 'y')
392
shelver.expect('Apply change? [yNfq?]', 'y')
393
shelver.expect('Apply 3 change(s)? [yNfq?]', 'y')
395
self.assertFileEqual(LINES_AJ, 'tree/foo')
397
def test_shelve_deletion(self):
398
tree = self.create_shelvable_tree()
399
os.unlink('tree/foo')
400
tree.lock_tree_write()
401
self.addCleanup(tree.unlock)
402
shelver = ExpectShelver(tree, tree.basis_tree(),
403
reporter=shelf_ui.ApplyReporter())
404
self.addCleanup(shelver.finalize)
405
shelver.expect('Add file "foo"? [yNfq?]', 'y')
406
shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
408
self.assertFileEqual(LINES_AJ, 'tree/foo')
410
def test_shelve_creation(self):
411
tree = self.make_branch_and_tree('tree')
412
tree.commit('add tree root')
413
self.build_tree(['tree/foo'])
415
tree.lock_tree_write()
416
self.addCleanup(tree.unlock)
417
shelver = ExpectShelver(tree, tree.basis_tree(),
418
reporter=shelf_ui.ApplyReporter())
419
self.addCleanup(shelver.finalize)
420
shelver.expect('Delete file "foo"? [yNfq?]', 'y')
421
shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
423
self.failIfExists('tree/foo')
425
def test_shelve_kind_change(self):
426
tree = self.create_shelvable_tree()
427
os.unlink('tree/foo')
429
tree.lock_tree_write()
430
self.addCleanup(tree.unlock)
431
shelver = ExpectShelver(tree, tree.basis_tree(),
432
reporter=shelf_ui.ApplyReporter())
433
self.addCleanup(shelver.finalize)
434
shelver.expect('Change "foo" from directory to a file? [yNfq?]', 'y')
435
shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
437
def test_shelve_modify_target(self):
438
self.requireFeature(tests.SymlinkFeature)
439
tree = self.create_shelvable_tree()
440
os.symlink('bar', 'tree/baz')
441
tree.add('baz', 'baz-id')
442
tree.commit("Add symlink")
443
os.unlink('tree/baz')
444
os.symlink('vax', 'tree/baz')
445
tree.lock_tree_write()
446
self.addCleanup(tree.unlock)
447
shelver = ExpectShelver(tree, tree.basis_tree(),
448
reporter=shelf_ui.ApplyReporter())
449
self.addCleanup(shelver.finalize)
450
shelver.expect('Change target of "baz" from "vax" to "bar"? [yNfq?]',
452
shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
454
self.assertEqual('bar', os.readlink('tree/baz'))
457
class TestUnshelver(tests.TestCaseWithTransport):
459
def create_tree_with_shelf(self):
460
tree = self.make_branch_and_tree('tree')
463
self.build_tree_contents([('tree/foo', LINES_AJ)])
464
tree.add('foo', 'foo-id')
465
tree.commit('added foo')
466
self.build_tree_contents([('tree/foo', LINES_ZY)])
467
shelver = shelf_ui.Shelver(tree, tree.basis_tree(),
468
auto_apply=True, auto=True)
477
def test_unshelve(self):
478
tree = self.create_tree_with_shelf()
480
self.addCleanup(tree.unlock)
481
manager = tree.get_shelf_manager()
482
shelf_ui.Unshelver(tree, manager, 1, True, True, True).run()
483
self.assertFileEqual(LINES_ZY, 'tree/foo')
485
def test_unshelve_args(self):
486
tree = self.create_tree_with_shelf()
487
unshelver = shelf_ui.Unshelver.from_args(directory='tree')
491
unshelver.tree.unlock()
492
self.assertFileEqual(LINES_ZY, 'tree/foo')
493
self.assertIs(None, tree.get_shelf_manager().last_shelf())
495
def test_unshelve_args_dry_run(self):
496
tree = self.create_tree_with_shelf()
497
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
502
unshelver.tree.unlock()
503
self.assertFileEqual(LINES_AJ, 'tree/foo')
504
self.assertEqual(1, tree.get_shelf_manager().last_shelf())
506
def test_unshelve_args_delete_only(self):
507
tree = self.make_branch_and_tree('tree')
508
manager = tree.get_shelf_manager()
509
shelf_file = manager.new_shelf()[1]
511
shelf_file.write('garbage')
514
unshelver = shelf_ui.Unshelver.from_args(directory='tree',
515
action='delete-only')
519
unshelver.tree.unlock()
520
self.assertIs(None, manager.last_shelf())
522
def test_unshelve_args_invalid_shelf_id(self):
523
tree = self.make_branch_and_tree('tree')
524
manager = tree.get_shelf_manager()
525
shelf_file = manager.new_shelf()[1]
527
shelf_file.write('garbage')
530
self.assertRaises(errors.InvalidShelfId,
531
shelf_ui.Unshelver.from_args, directory='tree',
532
action='delete-only', shelf_id='foo')