~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_shelf.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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
 
16
 
 
17
import os
 
18
 
 
19
from bzrlib import (
 
20
    errors,
 
21
    osutils,
 
22
    pack,
 
23
    shelf,
 
24
    tests,
 
25
    transform,
 
26
    workingtree,
 
27
    )
 
28
 
 
29
 
 
30
EMPTY_SHELF = ("Bazaar pack format 1 (introduced in 0.18)\n"
 
31
               "B23\n"
 
32
               "metadata\n\n"
 
33
               "d11:revision_id5:null:e"
 
34
               "B159\n"
 
35
               "attribs\n\n"
 
36
               "d10:_id_numberi0e18:_new_executabilityde7:_new_idde"
 
37
               "9:_new_namede11:_new_parentde16:_non_present_idsde"
 
38
               "17:_removed_contentsle11:_removed_idle14:_tree_path_idsdeeE")
 
39
 
 
40
 
 
41
class TestPrepareShelf(tests.TestCaseWithTransport):
 
42
 
 
43
    def prepare_shelve_rename(self):
 
44
        tree = self.make_branch_and_tree('.')
 
45
        self.build_tree(['foo'])
 
46
        tree.add(['foo'], ['foo-id'])
 
47
        tree.commit('foo')
 
48
        tree.rename_one('foo', 'bar')
 
49
        tree.lock_tree_write()
 
50
        self.addCleanup(tree.unlock)
 
51
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
52
        self.addCleanup(creator.finalize)
 
53
        self.assertEqual([('rename', 'foo-id', 'foo', 'bar')],
 
54
                          list(creator.iter_shelvable()))
 
55
        return creator
 
56
 
 
57
    def check_shelve_rename(self, creator):
 
58
        work_trans_id = creator.work_transform.trans_id_file_id('foo-id')
 
59
        self.assertEqual('foo', creator.work_transform.final_name(
 
60
                         work_trans_id))
 
61
        shelf_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
 
62
        self.assertEqual('bar', creator.shelf_transform.final_name(
 
63
                         shelf_trans_id))
 
64
 
 
65
    def test_shelve_rename(self):
 
66
        creator = self.prepare_shelve_rename()
 
67
        creator.shelve_rename('foo-id')
 
68
        self.check_shelve_rename(creator)
 
69
 
 
70
    def test_shelve_change_handles_rename(self):
 
71
        creator = self.prepare_shelve_rename()
 
72
        creator.shelve_change(('rename', 'foo-id', 'foo', 'bar'))
 
73
        self.check_shelve_rename(creator)
 
74
 
 
75
    def prepare_shelve_move(self):
 
76
        tree = self.make_branch_and_tree('.')
 
77
        self.build_tree(['foo/', 'bar/', 'foo/baz'])
 
78
        tree.add(['foo', 'bar', 'foo/baz'], ['foo-id', 'bar-id', 'baz-id'])
 
79
        tree.commit('foo')
 
80
        tree.rename_one('foo/baz', 'bar/baz')
 
81
        tree.lock_tree_write()
 
82
        self.addCleanup(tree.unlock)
 
83
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
84
        self.addCleanup(creator.finalize)
 
85
        self.assertEqual([('rename', 'baz-id', 'foo/baz', 'bar/baz')],
 
86
                         list(creator.iter_shelvable()))
 
87
        return creator, tree
 
88
 
 
89
    def check_shelve_move(self, creator, tree):
 
90
        work_trans_id = creator.work_transform.trans_id_file_id('baz-id')
 
91
        work_foo = creator.work_transform.trans_id_file_id('foo-id')
 
92
        self.assertEqual(work_foo, creator.work_transform.final_parent(
 
93
                         work_trans_id))
 
94
        shelf_trans_id = creator.shelf_transform.trans_id_file_id('baz-id')
 
95
        shelf_bar = creator.shelf_transform.trans_id_file_id('bar-id')
 
96
        self.assertEqual(shelf_bar, creator.shelf_transform.final_parent(
 
97
                         shelf_trans_id))
 
98
        creator.transform()
 
99
        self.assertEqual('foo/baz', tree.id2path('baz-id'))
 
100
 
 
101
    def test_shelve_move(self):
 
102
        creator, tree = self.prepare_shelve_move()
 
103
        creator.shelve_rename('baz-id')
 
104
        self.check_shelve_move(creator, tree)
 
105
 
 
106
    def test_shelve_change_handles_move(self):
 
107
        creator, tree = self.prepare_shelve_move()
 
108
        creator.shelve_change(('rename', 'baz-id', 'foo/baz', 'bar/baz'))
 
109
        self.check_shelve_move(creator, tree)
 
110
 
 
111
    def test_shelve_changed_root_id(self):
 
112
        tree = self.make_branch_and_tree('.')
 
113
        self.build_tree(['foo'])
 
114
        tree.set_root_id('first-root-id')
 
115
        tree.add(['foo'], ['foo-id'])
 
116
        tree.commit('foo')
 
117
        tree.set_root_id('second-root-id')
 
118
        tree.lock_tree_write()
 
119
        self.addCleanup(tree.unlock)
 
120
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
121
        self.addCleanup(creator.finalize)
 
122
        self.expectFailure('shelf doesn\'t support shelving root changes yet',
 
123
            self.assertEqual, [
 
124
                ('delete file', 'first-root-id', 'directory', ''),
 
125
                ('add file', 'second-root-id', 'directory', ''),
 
126
                ('rename', 'foo-id', u'foo', u'foo'),
 
127
                ], list(creator.iter_shelvable()))
 
128
 
 
129
        self.assertEqual([('delete file', 'first-root-id', 'directory', ''),
 
130
                          ('add file', 'second-root-id', 'directory', ''),
 
131
                          ('rename', 'foo-id', u'foo', u'foo'),
 
132
                         ], list(creator.iter_shelvable()))
 
133
 
 
134
    def assertShelvedFileEqual(self, expected_content, creator, file_id):
 
135
        s_trans_id = creator.shelf_transform.trans_id_file_id(file_id)
 
136
        shelf_file = creator.shelf_transform._limbo_name(s_trans_id)
 
137
        self.assertFileEqual(expected_content, shelf_file)
 
138
 
 
139
    def prepare_content_change(self):
 
140
        tree = self.make_branch_and_tree('.')
 
141
        tree.lock_write()
 
142
        self.addCleanup(tree.unlock)
 
143
        self.build_tree_contents([('foo', 'a\n')])
 
144
        tree.add('foo', 'foo-id')
 
145
        tree.commit('Committed foo')
 
146
        self.build_tree_contents([('foo', 'b\na\nc\n')])
 
147
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
148
        self.addCleanup(creator.finalize)
 
149
        return creator
 
150
 
 
151
    def test_shelve_content_change(self):
 
152
        creator = self.prepare_content_change()
 
153
        self.assertEqual([('modify text', 'foo-id')],
 
154
                         list(creator.iter_shelvable()))
 
155
        creator.shelve_lines('foo-id', ['a\n', 'c\n'])
 
156
        creator.transform()
 
157
        self.assertFileEqual('a\nc\n', 'foo')
 
158
        self.assertShelvedFileEqual('b\na\n', creator, 'foo-id')
 
159
 
 
160
    def test_shelve_change_handles_modify_text(self):
 
161
        creator = self.prepare_content_change()
 
162
        creator.shelve_change(('modify text', 'foo-id'))
 
163
        creator.transform()
 
164
        self.assertFileEqual('a\n', 'foo')
 
165
        self.assertShelvedFileEqual('b\na\nc\n', creator, 'foo-id')
 
166
 
 
167
    def test_shelve_all(self):
 
168
        creator = self.prepare_content_change()
 
169
        creator.shelve_all()
 
170
        creator.transform()
 
171
        self.assertFileEqual('a\n', 'foo')
 
172
        self.assertShelvedFileEqual('b\na\nc\n', creator, 'foo-id')
 
173
 
 
174
    def prepare_shelve_creation(self):
 
175
        tree = self.make_branch_and_tree('.')
 
176
        tree.lock_write()
 
177
        self.addCleanup(tree.unlock)
 
178
        tree.commit('Empty tree')
 
179
        self.build_tree_contents([('foo', 'a\n'), ('bar/',)])
 
180
        tree.add(['foo', 'bar'], ['foo-id', 'bar-id'])
 
181
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
182
        self.addCleanup(creator.finalize)
 
183
        self.assertEqual([('add file', 'bar-id', 'directory', 'bar'),
 
184
                          ('add file', 'foo-id', 'file', 'foo')],
 
185
                          sorted(list(creator.iter_shelvable())))
 
186
        return creator, tree
 
187
 
 
188
    def check_shelve_creation(self, creator, tree):
 
189
        self.assertRaises(StopIteration,
 
190
                          tree.iter_entries_by_dir(['foo-id']).next)
 
191
        s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
 
192
        self.assertEqual('foo-id',
 
193
                         creator.shelf_transform.final_file_id(s_trans_id))
 
194
        self.failIfExists('foo')
 
195
        self.failIfExists('bar')
 
196
        self.assertShelvedFileEqual('a\n', creator, 'foo-id')
 
197
        s_bar_trans_id = creator.shelf_transform.trans_id_file_id('bar-id')
 
198
        self.assertEqual('directory',
 
199
            creator.shelf_transform.final_kind(s_bar_trans_id))
 
200
 
 
201
    def test_shelve_creation(self):
 
202
        creator, tree = self.prepare_shelve_creation()
 
203
        creator.shelve_creation('foo-id')
 
204
        creator.shelve_creation('bar-id')
 
205
        creator.transform()
 
206
        self.check_shelve_creation(creator, tree)
 
207
 
 
208
    def test_shelve_change_handles_creation(self):
 
209
        creator, tree = self.prepare_shelve_creation()
 
210
        creator.shelve_change(('add file', 'foo-id', 'file', 'foo'))
 
211
        creator.shelve_change(('add file', 'bar-id', 'directory', 'bar'))
 
212
        creator.transform()
 
213
        self.check_shelve_creation(creator, tree)
 
214
 
 
215
    def _test_shelve_symlink_creation(self, link_name, link_target,
 
216
                                      shelve_change=False):
 
217
        self.requireFeature(tests.SymlinkFeature)
 
218
        tree = self.make_branch_and_tree('.')
 
219
        tree.lock_write()
 
220
        self.addCleanup(tree.unlock)
 
221
        tree.commit('Empty tree')
 
222
        os.symlink(link_target, link_name)
 
223
        tree.add(link_name, 'foo-id')
 
224
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
225
        self.addCleanup(creator.finalize)
 
226
        self.assertEqual([('add file', 'foo-id', 'symlink', link_name)],
 
227
                         list(creator.iter_shelvable()))
 
228
        if shelve_change:
 
229
            creator.shelve_change(('add file', 'foo-id', 'symlink', link_name))
 
230
        else:
 
231
            creator.shelve_creation('foo-id')
 
232
        creator.transform()
 
233
        s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
 
234
        self.failIfExists(link_name)
 
235
        limbo_name = creator.shelf_transform._limbo_name(s_trans_id)
 
236
        self.assertEqual(link_target, osutils.readlink(limbo_name))
 
237
        ptree = creator.shelf_transform.get_preview_tree()
 
238
        self.assertEqual(link_target, ptree.get_symlink_target('foo-id'))
 
239
 
 
240
    def test_shelve_symlink_creation(self):
 
241
        self._test_shelve_symlink_creation('foo', 'bar')
 
242
 
 
243
    def test_shelve_unicode_symlink_creation(self):
 
244
        self.requireFeature(tests.UnicodeFilenameFeature)
 
245
        self._test_shelve_symlink_creation(u'fo\N{Euro Sign}o',
 
246
                                           u'b\N{Euro Sign}ar')
 
247
 
 
248
    def test_shelve_change_handles_symlink_creation(self):
 
249
        self._test_shelve_symlink_creation('foo', 'bar', shelve_change=True)
 
250
 
 
251
    def _test_shelve_symlink_target_change(self, link_name,
 
252
                                           old_target, new_target,
 
253
                                           shelve_change=False):
 
254
        self.requireFeature(tests.SymlinkFeature)
 
255
        tree = self.make_branch_and_tree('.')
 
256
        tree.lock_write()
 
257
        self.addCleanup(tree.unlock)
 
258
        os.symlink(old_target, link_name)
 
259
        tree.add(link_name, 'foo-id')
 
260
        tree.commit("commit symlink")
 
261
        os.unlink(link_name)
 
262
        os.symlink(new_target, link_name)
 
263
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
264
        self.addCleanup(creator.finalize)
 
265
        self.assertEqual([('modify target', 'foo-id', link_name,
 
266
                           old_target, new_target)],
 
267
                         list(creator.iter_shelvable()))
 
268
        if shelve_change:
 
269
            creator.shelve_change(('modify target', 'foo-id', link_name,
 
270
                                   old_target, new_target))
 
271
        else:
 
272
            creator.shelve_modify_target('foo-id')
 
273
        creator.transform()
 
274
        self.assertEqual(old_target, osutils.readlink(link_name))
 
275
        s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
 
276
        limbo_name = creator.shelf_transform._limbo_name(s_trans_id)
 
277
        self.assertEqual(new_target, osutils.readlink(limbo_name))
 
278
        ptree = creator.shelf_transform.get_preview_tree()
 
279
        self.assertEqual(new_target, ptree.get_symlink_target('foo-id'))
 
280
 
 
281
    def test_shelve_symlink_target_change(self):
 
282
        self._test_shelve_symlink_target_change('foo', 'bar', 'baz')
 
283
 
 
284
    def test_shelve_unicode_symlink_target_change(self):
 
285
        self.requireFeature(tests.UnicodeFilenameFeature)
 
286
        self._test_shelve_symlink_target_change(
 
287
            u'fo\N{Euro Sign}o', u'b\N{Euro Sign}ar', u'b\N{Euro Sign}az')
 
288
 
 
289
    def test_shelve_change_handles_symlink_target_change(self):
 
290
        self._test_shelve_symlink_target_change('foo', 'bar', 'baz',
 
291
                                                shelve_change=True)
 
292
 
 
293
    def test_shelve_creation_no_contents(self):
 
294
        tree = self.make_branch_and_tree('.')
 
295
        tree.lock_write()
 
296
        self.addCleanup(tree.unlock)
 
297
        tree.commit('Empty tree')
 
298
        self.build_tree(['foo'])
 
299
        tree.add('foo', 'foo-id')
 
300
        os.unlink('foo')
 
301
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
302
        self.addCleanup(creator.finalize)
 
303
        self.assertEqual([('add file', 'foo-id', None, 'foo')],
 
304
                         sorted(list(creator.iter_shelvable())))
 
305
        creator.shelve_creation('foo-id')
 
306
        creator.transform()
 
307
        self.assertRaises(StopIteration,
 
308
                          tree.iter_entries_by_dir(['foo-id']).next)
 
309
        self.assertShelvedFileEqual('', creator, 'foo-id')
 
310
        s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
 
311
        self.assertEqual('foo-id',
 
312
                         creator.shelf_transform.final_file_id(s_trans_id))
 
313
        self.failIfExists('foo')
 
314
 
 
315
    def prepare_shelve_deletion(self):
 
316
        tree = self.make_branch_and_tree('tree')
 
317
        tree.lock_write()
 
318
        self.addCleanup(tree.unlock)
 
319
        self.build_tree_contents([('tree/foo/',), ('tree/foo/bar', 'baz')])
 
320
        tree.add(['foo', 'foo/bar'], ['foo-id', 'bar-id'])
 
321
        tree.commit('Added file and directory')
 
322
        tree.unversion(['foo-id', 'bar-id'])
 
323
        os.unlink('tree/foo/bar')
 
324
        os.rmdir('tree/foo')
 
325
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
326
        self.addCleanup(creator.finalize)
 
327
        self.assertEqual([('delete file', 'bar-id', 'file', 'foo/bar'),
 
328
                          ('delete file', 'foo-id', 'directory', 'foo')],
 
329
                          sorted(list(creator.iter_shelvable())))
 
330
        return creator, tree
 
331
 
 
332
    def check_shelve_deletion(self, tree):
 
333
        self.assertTrue('foo-id' in tree)
 
334
        self.assertTrue('bar-id' in tree)
 
335
        self.assertFileEqual('baz', 'tree/foo/bar')
 
336
 
 
337
    def test_shelve_deletion(self):
 
338
        creator, tree = self.prepare_shelve_deletion()
 
339
        creator.shelve_deletion('foo-id')
 
340
        creator.shelve_deletion('bar-id')
 
341
        creator.transform()
 
342
        self.check_shelve_deletion(tree)
 
343
 
 
344
    def test_shelve_change_handles_deletion(self):
 
345
        creator, tree = self.prepare_shelve_deletion()
 
346
        creator.shelve_change(('delete file', 'foo-id', 'directory', 'foo'))
 
347
        creator.shelve_change(('delete file', 'bar-id', 'file', 'foo/bar'))
 
348
        creator.transform()
 
349
        self.check_shelve_deletion(tree)
 
350
 
 
351
    def test_shelve_delete_contents(self):
 
352
        tree = self.make_branch_and_tree('tree')
 
353
        self.build_tree(['tree/foo',])
 
354
        tree.add('foo', 'foo-id')
 
355
        tree.commit('Added file and directory')
 
356
        os.unlink('tree/foo')
 
357
        tree.lock_tree_write()
 
358
        self.addCleanup(tree.unlock)
 
359
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
360
        self.addCleanup(creator.finalize)
 
361
        self.assertEqual([('delete file', 'foo-id', 'file', 'foo')],
 
362
                         sorted(list(creator.iter_shelvable())))
 
363
        creator.shelve_deletion('foo-id')
 
364
        creator.transform()
 
365
        self.failUnlessExists('tree/foo')
 
366
 
 
367
    def prepare_shelve_change_kind(self):
 
368
        tree = self.make_branch_and_tree('tree')
 
369
        self.build_tree_contents([('tree/foo', 'bar')])
 
370
        tree.add('foo', 'foo-id')
 
371
        tree.commit('Added file and directory')
 
372
        os.unlink('tree/foo')
 
373
        os.mkdir('tree/foo')
 
374
        tree.lock_tree_write()
 
375
        self.addCleanup(tree.unlock)
 
376
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
377
        self.addCleanup(creator.finalize)
 
378
        self.assertEqual([('change kind', 'foo-id', 'file', 'directory',
 
379
                           'foo')], sorted(list(creator.iter_shelvable())))
 
380
        return creator
 
381
 
 
382
    def check_shelve_change_kind(self, creator):
 
383
        self.assertFileEqual('bar', 'tree/foo')
 
384
        s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
 
385
        self.assertEqual('directory',
 
386
                         creator.shelf_transform._new_contents[s_trans_id])
 
387
 
 
388
    def test_shelve_change_kind(self):
 
389
        creator = self.prepare_shelve_change_kind()
 
390
        creator.shelve_content_change('foo-id')
 
391
        creator.transform()
 
392
        self.check_shelve_change_kind(creator)
 
393
 
 
394
    def test_shelve_change_handles_change_kind(self):
 
395
        creator = self.prepare_shelve_change_kind()
 
396
        creator.shelve_change(('change kind', 'foo-id', 'file', 'directory',
 
397
                               'foo'))
 
398
        creator.transform()
 
399
        self.check_shelve_change_kind(creator)
 
400
 
 
401
    def test_shelve_change_unknown_change(self):
 
402
        tree = self.make_branch_and_tree('tree')
 
403
        tree.lock_tree_write()
 
404
        self.addCleanup(tree.unlock)
 
405
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
406
        self.addCleanup(creator.finalize)
 
407
        e = self.assertRaises(ValueError, creator.shelve_change, ('unknown',))
 
408
        self.assertEqual('Unknown change kind: "unknown"', str(e))
 
409
 
 
410
    def test_shelve_unversion(self):
 
411
        tree = self.make_branch_and_tree('tree')
 
412
        self.build_tree(['tree/foo',])
 
413
        tree.add('foo', 'foo-id')
 
414
        tree.commit('Added file and directory')
 
415
        tree.unversion(['foo-id'])
 
416
        tree.lock_tree_write()
 
417
        self.addCleanup(tree.unlock)
 
418
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
419
        self.addCleanup(creator.finalize)
 
420
        self.assertEqual([('delete file', 'foo-id', 'file', 'foo')],
 
421
                         sorted(list(creator.iter_shelvable())))
 
422
        creator.shelve_deletion('foo-id')
 
423
        creator.transform()
 
424
        self.failUnlessExists('tree/foo')
 
425
 
 
426
    def test_shelve_serialization(self):
 
427
        tree = self.make_branch_and_tree('.')
 
428
        tree.lock_tree_write()
 
429
        self.addCleanup(tree.unlock)
 
430
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
431
        self.addCleanup(creator.finalize)
 
432
        shelf_file = open('shelf', 'wb')
 
433
        self.addCleanup(shelf_file.close)
 
434
        try:
 
435
            creator.write_shelf(shelf_file)
 
436
        finally:
 
437
            shelf_file.close()
 
438
        self.assertFileEqual(EMPTY_SHELF, 'shelf')
 
439
 
 
440
    def test_write_shelf(self):
 
441
        tree = self.make_branch_and_tree('tree')
 
442
        self.build_tree(['tree/foo'])
 
443
        tree.add('foo', 'foo-id')
 
444
        tree.lock_tree_write()
 
445
        self.addCleanup(tree.unlock)
 
446
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
447
        self.addCleanup(creator.finalize)
 
448
        list(creator.iter_shelvable())
 
449
        creator.shelve_creation('foo-id')
 
450
        shelf_file = open('shelf', 'wb')
 
451
        try:
 
452
            creator.write_shelf(shelf_file)
 
453
        finally:
 
454
            shelf_file.close()
 
455
        parser = pack.ContainerPushParser()
 
456
        shelf_file = open('shelf', 'rb')
 
457
        try:
 
458
            parser.accept_bytes(shelf_file.read())
 
459
        finally:
 
460
            shelf_file.close()
 
461
        tt = transform.TransformPreview(tree)
 
462
        self.addCleanup(tt.finalize)
 
463
        records = iter(parser.read_pending_records())
 
464
        #skip revision-id
 
465
        records.next()
 
466
        tt.deserialize(records)
 
467
 
 
468
    def test_shelve_unversioned(self):
 
469
        tree = self.make_branch_and_tree('tree')
 
470
        tree.lock_tree_write()
 
471
        try:
 
472
            self.assertRaises(errors.PathsNotVersionedError,
 
473
                              shelf.ShelfCreator, tree, tree.basis_tree(), ['foo'])
 
474
        finally:
 
475
            tree.unlock()
 
476
        # We should be able to lock/unlock the tree if ShelfCreator cleaned
 
477
        # after itself.
 
478
        wt = workingtree.WorkingTree.open('tree')
 
479
        wt.lock_tree_write()
 
480
        wt.unlock()
 
481
        # And a second tentative should raise the same error (no
 
482
        # limbo/pending_deletion leftovers).
 
483
        tree.lock_tree_write()
 
484
        try:
 
485
            self.assertRaises(errors.PathsNotVersionedError,
 
486
                              shelf.ShelfCreator, tree, tree.basis_tree(), ['foo'])
 
487
        finally:
 
488
            tree.unlock()
 
489
 
 
490
    def test_shelve_skips_added_root(self):
 
491
        """Skip adds of the root when iterating through shelvable changes."""
 
492
        tree = self.make_branch_and_tree('tree')
 
493
        tree.lock_tree_write()
 
494
        self.addCleanup(tree.unlock)
 
495
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
496
        self.addCleanup(creator.finalize)
 
497
        self.assertEqual([], list(creator.iter_shelvable()))
 
498
 
 
499
    def test_shelve_skips_added_root(self):
 
500
        """Skip adds of the root when iterating through shelvable changes."""
 
501
        tree = self.make_branch_and_tree('tree')
 
502
        tree.lock_tree_write()
 
503
        self.addCleanup(tree.unlock)
 
504
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
505
        self.addCleanup(creator.finalize)
 
506
        self.assertEqual([], list(creator.iter_shelvable()))
 
507
 
 
508
 
 
509
class TestUnshelver(tests.TestCaseWithTransport):
 
510
 
 
511
    def test_make_merger(self):
 
512
        tree = self.make_branch_and_tree('tree')
 
513
        tree.commit('first commit')
 
514
        self.build_tree_contents([('tree/foo', 'bar')])
 
515
        tree.lock_write()
 
516
        self.addCleanup(tree.unlock)
 
517
        tree.add('foo', 'foo-id')
 
518
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
519
        self.addCleanup(creator.finalize)
 
520
        list(creator.iter_shelvable())
 
521
        creator.shelve_creation('foo-id')
 
522
        shelf_file = open('shelf-file', 'w+b')
 
523
        try:
 
524
            creator.write_shelf(shelf_file)
 
525
            creator.transform()
 
526
            shelf_file.seek(0)
 
527
            unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)
 
528
            unshelver.make_merger().do_merge()
 
529
            self.addCleanup(unshelver.finalize)
 
530
            self.assertFileEqual('bar', 'tree/foo')
 
531
        finally:
 
532
            shelf_file.close()
 
533
 
 
534
    def test_unshelve_changed(self):
 
535
        tree = self.make_branch_and_tree('tree')
 
536
        tree.lock_write()
 
537
        self.addCleanup(tree.unlock)
 
538
        self.build_tree_contents([('tree/foo', 'a\nb\nc\n')])
 
539
        tree.add('foo', 'foo-id')
 
540
        tree.commit('first commit')
 
541
        self.build_tree_contents([('tree/foo', 'a\nb\nd\n')])
 
542
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
543
        self.addCleanup(creator.finalize)
 
544
        list(creator.iter_shelvable())
 
545
        creator.shelve_lines('foo-id', ['a\n', 'b\n', 'c\n'])
 
546
        shelf_file = open('shelf', 'w+b')
 
547
        self.addCleanup(shelf_file.close)
 
548
        creator.write_shelf(shelf_file)
 
549
        creator.transform()
 
550
        self.build_tree_contents([('tree/foo', 'z\na\nb\nc\n')])
 
551
        shelf_file.seek(0)
 
552
        unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)
 
553
        self.addCleanup(unshelver.finalize)
 
554
        unshelver.make_merger().do_merge()
 
555
        self.assertFileEqual('z\na\nb\nd\n', 'tree/foo')
 
556
 
 
557
    def test_unshelve_deleted(self):
 
558
        tree = self.make_branch_and_tree('tree')
 
559
        tree.lock_write()
 
560
        self.addCleanup(tree.unlock)
 
561
        self.build_tree_contents([('tree/foo/',), ('tree/foo/bar', 'baz')])
 
562
        tree.add(['foo', 'foo/bar'], ['foo-id', 'bar-id'])
 
563
        tree.commit('Added file and directory')
 
564
        tree.unversion(['foo-id', 'bar-id'])
 
565
        os.unlink('tree/foo/bar')
 
566
        os.rmdir('tree/foo')
 
567
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
568
        list(creator.iter_shelvable())
 
569
        creator.shelve_deletion('foo-id')
 
570
        creator.shelve_deletion('bar-id')
 
571
        shelf_file = open('shelf', 'w+b')
 
572
        self.addCleanup(shelf_file.close)
 
573
        creator.write_shelf(shelf_file)
 
574
        creator.transform()
 
575
        creator.finalize()
 
576
        # validate the test setup
 
577
        self.assertTrue('foo-id' in tree)
 
578
        self.assertTrue('bar-id' in tree)
 
579
        self.assertFileEqual('baz', 'tree/foo/bar')
 
580
        shelf_file.seek(0)
 
581
        unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)
 
582
        self.addCleanup(unshelver.finalize)
 
583
        unshelver.make_merger().do_merge()
 
584
        self.assertFalse('foo-id' in tree)
 
585
        self.assertFalse('bar-id' in tree)
 
586
 
 
587
    def test_unshelve_base(self):
 
588
        tree = self.make_branch_and_tree('tree')
 
589
        tree.lock_write()
 
590
        self.addCleanup(tree.unlock)
 
591
        tree.commit('rev1', rev_id='rev1')
 
592
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
593
        self.addCleanup(creator.finalize)
 
594
        manager = tree.get_shelf_manager()
 
595
        shelf_id, shelf_file = manager.new_shelf()
 
596
        try:
 
597
            creator.write_shelf(shelf_file)
 
598
        finally:
 
599
            shelf_file.close()
 
600
        tree.commit('rev2', rev_id='rev2')
 
601
        shelf_file = manager.read_shelf(1)
 
602
        self.addCleanup(shelf_file.close)
 
603
        unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)
 
604
        self.addCleanup(unshelver.finalize)
 
605
        self.assertEqual('rev1', unshelver.base_tree.get_revision_id())
 
606
 
 
607
    def test_unshelve_serialization(self):
 
608
        tree = self.make_branch_and_tree('.')
 
609
        self.build_tree_contents([('shelf', EMPTY_SHELF)])
 
610
        shelf_file = open('shelf', 'rb')
 
611
        self.addCleanup(shelf_file.close)
 
612
        unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)
 
613
        unshelver.finalize()
 
614
 
 
615
    def test_corrupt_shelf(self):
 
616
        tree = self.make_branch_and_tree('.')
 
617
        self.build_tree_contents([('shelf', EMPTY_SHELF.replace('metadata',
 
618
                                                                'foo'))])
 
619
        shelf_file = open('shelf', 'rb')
 
620
        self.addCleanup(shelf_file.close)
 
621
        e = self.assertRaises(errors.ShelfCorrupt,
 
622
                              shelf.Unshelver.from_tree_and_shelf, tree,
 
623
                              shelf_file)
 
624
        self.assertEqual('Shelf corrupt.', str(e))
 
625
 
 
626
 
 
627
class TestShelfManager(tests.TestCaseWithTransport):
 
628
 
 
629
    def test_get_shelf_manager(self):
 
630
        tree = self.make_branch_and_tree('.')
 
631
        manager = tree.get_shelf_manager()
 
632
        self.assertEqual(tree._transport.base + 'shelf/',
 
633
                         manager.transport.base)
 
634
 
 
635
    def get_manager(self):
 
636
        return self.make_branch_and_tree('.').get_shelf_manager()
 
637
 
 
638
    def test_get_shelf_filename(self):
 
639
        tree = self.make_branch_and_tree('.')
 
640
        manager = tree.get_shelf_manager()
 
641
        self.assertEqual('shelf-1', manager.get_shelf_filename(1))
 
642
 
 
643
    def test_get_shelf_ids(self):
 
644
        tree = self.make_branch_and_tree('.')
 
645
        manager = tree.get_shelf_manager()
 
646
        self.assertEqual([1, 3], manager.get_shelf_ids(
 
647
                         ['shelf-1', 'shelf-02', 'shelf-3']))
 
648
 
 
649
    def test_new_shelf(self):
 
650
        manager = self.get_manager()
 
651
        shelf_id, shelf_file = manager.new_shelf()
 
652
        shelf_file.close()
 
653
        self.assertEqual(1, shelf_id)
 
654
        shelf_id, shelf_file = manager.new_shelf()
 
655
        shelf_file.close()
 
656
        self.assertEqual(2, shelf_id)
 
657
        manager.delete_shelf(1)
 
658
        shelf_id, shelf_file = manager.new_shelf()
 
659
        shelf_file.close()
 
660
        self.assertEqual(3, shelf_id)
 
661
 
 
662
    def test_active_shelves(self):
 
663
        manager = self.get_manager()
 
664
        self.assertEqual([], manager.active_shelves())
 
665
        shelf_id, shelf_file = manager.new_shelf()
 
666
        shelf_file.close()
 
667
        self.assertEqual([1], manager.active_shelves())
 
668
 
 
669
    def test_delete_shelf(self):
 
670
        manager = self.get_manager()
 
671
        shelf_id, shelf_file = manager.new_shelf()
 
672
        shelf_file.close()
 
673
        self.assertEqual([1], manager.active_shelves())
 
674
        manager.delete_shelf(1)
 
675
        self.assertEqual([], manager.active_shelves())
 
676
 
 
677
    def test_last_shelf(self):
 
678
        manager = self.get_manager()
 
679
        self.assertIs(None, manager.last_shelf())
 
680
        shelf_id, shelf_file = manager.new_shelf()
 
681
        shelf_file.close()
 
682
        self.assertEqual(1, manager.last_shelf())
 
683
 
 
684
    def test_read_shelf(self):
 
685
        manager = self.get_manager()
 
686
        shelf_id, shelf_file = manager.new_shelf()
 
687
        try:
 
688
            shelf_file.write('foo')
 
689
        finally:
 
690
            shelf_file.close()
 
691
        shelf_id, shelf_file = manager.new_shelf()
 
692
        try:
 
693
            shelf_file.write('bar')
 
694
        finally:
 
695
            shelf_file.close()
 
696
        shelf_file = manager.read_shelf(1)
 
697
        try:
 
698
            self.assertEqual('foo', shelf_file.read())
 
699
        finally:
 
700
            shelf_file.close()
 
701
        shelf_file = manager.read_shelf(2)
 
702
        try:
 
703
            self.assertEqual('bar', shelf_file.read())
 
704
        finally:
 
705
            shelf_file.close()
 
706
 
 
707
    def test_read_non_existant(self):
 
708
        manager = self.get_manager()
 
709
        e = self.assertRaises(errors.NoSuchShelfId, manager.read_shelf, 1)
 
710
        self.assertEqual('No changes are shelved with id "1".', str(e))
 
711
 
 
712
    def test_shelve_changes(self):
 
713
        tree = self.make_branch_and_tree('tree')
 
714
        tree.commit('no-change commit')
 
715
        tree.lock_write()
 
716
        self.addCleanup(tree.unlock)
 
717
        self.build_tree_contents([('tree/foo', 'bar')])
 
718
        self.assertFileEqual('bar', 'tree/foo')
 
719
        tree.add('foo', 'foo-id')
 
720
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
721
        self.addCleanup(creator.finalize)
 
722
        list(creator.iter_shelvable())
 
723
        creator.shelve_creation('foo-id')
 
724
        shelf_manager = tree.get_shelf_manager()
 
725
        shelf_id = shelf_manager.shelve_changes(creator)
 
726
        self.failIfExists('tree/foo')
 
727
        unshelver = shelf_manager.get_unshelver(shelf_id)
 
728
        self.addCleanup(unshelver.finalize)
 
729
        unshelver.make_merger().do_merge()
 
730
        self.assertFileEqual('bar', 'tree/foo')
 
731
 
 
732
    def test_get_metadata(self):
 
733
        tree = self.make_branch_and_tree('.')
 
734
        tree.lock_tree_write()
 
735
        self.addCleanup(tree.unlock)
 
736
        creator = shelf.ShelfCreator(tree, tree.basis_tree())
 
737
        self.addCleanup(creator.finalize)
 
738
        shelf_manager = tree.get_shelf_manager()
 
739
        shelf_id = shelf_manager.shelve_changes(creator, 'foo')
 
740
        metadata = shelf_manager.get_metadata(shelf_id)
 
741
        self.assertEqual('foo', metadata['message'])
 
742
        self.assertEqual('null:', metadata['revision_id'])