~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
18
import stat
 
19
from StringIO import StringIO
19
20
import sys
20
21
 
21
22
from bzrlib import (
 
23
    bencode,
22
24
    errors,
23
25
    generate_ids,
 
26
    osutils,
 
27
    progress,
 
28
    revision as _mod_revision,
24
29
    symbol_versioning,
25
30
    tests,
26
31
    urlutils,
27
32
    )
28
33
from bzrlib.bzrdir import BzrDir
29
34
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
30
 
                              UnversionedParent, ParentLoop, DeletingParent,)
 
35
                              UnversionedParent, ParentLoop, DeletingParent,
 
36
                              NonDirectoryParent)
 
37
from bzrlib.diff import show_diff_trees
31
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
32
 
                           ReusingTransform, CantMoveRoot, 
 
39
                           ReusingTransform, CantMoveRoot,
33
40
                           PathsNotVersionedError, ExistingLimbo,
34
 
                           ImmortalLimbo, LockError)
35
 
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
36
 
from bzrlib.merge import Merge3Merger
37
 
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
38
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
39
 
                              resolve_conflicts, cook_conflicts, 
40
 
                              find_interesting, build_tree, get_backup_name)
 
41
                           ExistingPendingDeletion, ImmortalLimbo,
 
42
                           ImmortalPendingDeletion, LockError)
 
43
from bzrlib.osutils import file_kind, pathjoin
 
44
from bzrlib.merge import Merge3Merger, Merger
 
45
from bzrlib.tests import (
 
46
    HardlinkFeature,
 
47
    SymlinkFeature,
 
48
    TestCase,
 
49
    TestCaseInTempDir,
 
50
    TestSkipped,
 
51
    )
 
52
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
 
53
                              resolve_conflicts, cook_conflicts,
 
54
                              build_tree, get_backup_name,
 
55
                              _FileMover, resolve_checkout,
 
56
                              TransformPreview, create_from_tree)
41
57
 
42
58
 
43
59
class TestTreeTransform(tests.TestCaseWithTransport):
49
65
 
50
66
    def get_transform(self):
51
67
        transform = TreeTransform(self.wt)
52
 
        #self.addCleanup(transform.finalize)
 
68
        self.addCleanup(transform.finalize)
53
69
        return transform, transform.root
54
70
 
55
71
    def test_existing_limbo(self):
56
 
        limbo_name = urlutils.local_path_from_url(
57
 
            self.wt._control_files.controlfilename('limbo'))
58
72
        transform, root = self.get_transform()
 
73
        limbo_name = transform._limbodir
 
74
        deletion_path = transform._deletiondir
59
75
        os.mkdir(pathjoin(limbo_name, 'hehe'))
60
76
        self.assertRaises(ImmortalLimbo, transform.apply)
61
77
        self.assertRaises(LockError, self.wt.unlock)
63
79
        self.assertRaises(LockError, self.wt.unlock)
64
80
        os.rmdir(pathjoin(limbo_name, 'hehe'))
65
81
        os.rmdir(limbo_name)
 
82
        os.rmdir(deletion_path)
66
83
        transform, root = self.get_transform()
67
84
        transform.apply()
68
85
 
 
86
    def test_existing_pending_deletion(self):
 
87
        transform, root = self.get_transform()
 
88
        deletion_path = self._limbodir = urlutils.local_path_from_url(
 
89
            transform._tree._transport.abspath('pending-deletion'))
 
90
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
 
91
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
 
92
        self.assertRaises(LockError, self.wt.unlock)
 
93
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
 
94
 
69
95
    def test_build(self):
70
 
        transform, root = self.get_transform() 
 
96
        transform, root = self.get_transform()
 
97
        self.wt.lock_tree_write()
 
98
        self.addCleanup(self.wt.unlock)
71
99
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
72
100
        imaginary_id = transform.trans_id_tree_path('imaginary')
73
101
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
101
129
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
102
130
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
103
131
        self.assertEqual(len(modified_paths), 3)
104
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
132
        tree_mod_paths = [self.wt.id2abspath(f) for f in
105
133
                          ('ozzie', 'my_pretties', 'my_pretties2')]
106
134
        self.assertSubset(tree_mod_paths, modified_paths)
107
135
        # is it safe to finalize repeatedly?
108
136
        transform.finalize()
109
137
        transform.finalize()
110
138
 
 
139
    def test_hardlink(self):
 
140
        self.requireFeature(HardlinkFeature)
 
141
        transform, root = self.get_transform()
 
142
        transform.new_file('file1', root, 'contents')
 
143
        transform.apply()
 
144
        target = self.make_branch_and_tree('target')
 
145
        target_transform = TreeTransform(target)
 
146
        trans_id = target_transform.create_path('file1', target_transform.root)
 
147
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
 
148
        target_transform.apply()
 
149
        self.failUnlessExists('target/file1')
 
150
        source_stat = os.stat(self.wt.abspath('file1'))
 
151
        target_stat = os.stat('target/file1')
 
152
        self.assertEqual(source_stat, target_stat)
 
153
 
111
154
    def test_convenience(self):
112
155
        transform, root = self.get_transform()
113
 
        trans_id = transform.new_file('name', root, 'contents', 
 
156
        self.wt.lock_tree_write()
 
157
        self.addCleanup(self.wt.unlock)
 
158
        trans_id = transform.new_file('name', root, 'contents',
114
159
                                      'my_pretties', True)
115
160
        oz = transform.new_directory('oz', root, 'oz-id')
116
161
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
117
 
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
162
        toto = transform.new_file('toto', dorothy, 'toto-contents',
118
163
                                  'toto-id', False)
119
164
 
120
165
        self.assertEqual(len(transform.find_conflicts()), 0)
144
189
 
145
190
    def test_conflicts(self):
146
191
        transform, root = self.get_transform()
147
 
        trans_id = transform.new_file('name', root, 'contents', 
 
192
        trans_id = transform.new_file('name', root, 'contents',
148
193
                                      'my_pretties')
149
194
        self.assertEqual(len(transform.find_conflicts()), 0)
150
195
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
151
 
        self.assertEqual(transform.find_conflicts(), 
 
196
        self.assertEqual(transform.find_conflicts(),
152
197
                         [('duplicate', trans_id, trans_id2, 'name')])
153
198
        self.assertRaises(MalformedTransform, transform.apply)
154
199
        transform.adjust_path('name', trans_id, trans_id2)
155
 
        self.assertEqual(transform.find_conflicts(), 
 
200
        self.assertEqual(transform.find_conflicts(),
156
201
                         [('non-directory parent', trans_id)])
157
202
        tinman_id = transform.trans_id_tree_path('tinman')
158
203
        transform.adjust_path('name', tinman_id, trans_id2)
159
 
        self.assertEqual(transform.find_conflicts(), 
160
 
                         [('unversioned parent', tinman_id), 
 
204
        self.assertEqual(transform.find_conflicts(),
 
205
                         [('unversioned parent', tinman_id),
161
206
                          ('missing parent', tinman_id)])
162
207
        lion_id = transform.create_path('lion', root)
163
 
        self.assertEqual(transform.find_conflicts(), 
164
 
                         [('unversioned parent', tinman_id), 
 
208
        self.assertEqual(transform.find_conflicts(),
 
209
                         [('unversioned parent', tinman_id),
165
210
                          ('missing parent', tinman_id)])
166
211
        transform.adjust_path('name', lion_id, trans_id2)
167
 
        self.assertEqual(transform.find_conflicts(), 
 
212
        self.assertEqual(transform.find_conflicts(),
168
213
                         [('unversioned parent', lion_id),
169
214
                          ('missing parent', lion_id)])
170
215
        transform.version_file("Courage", lion_id)
171
 
        self.assertEqual(transform.find_conflicts(), 
172
 
                         [('missing parent', lion_id), 
 
216
        self.assertEqual(transform.find_conflicts(),
 
217
                         [('missing parent', lion_id),
173
218
                          ('versioning no contents', lion_id)])
174
219
        transform.adjust_path('name2', root, trans_id2)
175
 
        self.assertEqual(transform.find_conflicts(), 
 
220
        self.assertEqual(transform.find_conflicts(),
176
221
                         [('versioning no contents', lion_id)])
177
222
        transform.create_file('Contents, okay?', lion_id)
178
223
        transform.adjust_path('name2', trans_id2, trans_id2)
179
 
        self.assertEqual(transform.find_conflicts(), 
180
 
                         [('parent loop', trans_id2), 
 
224
        self.assertEqual(transform.find_conflicts(),
 
225
                         [('parent loop', trans_id2),
181
226
                          ('non-directory parent', trans_id2)])
182
227
        transform.adjust_path('name2', root, trans_id2)
183
228
        oz_id = transform.new_directory('oz', root)
184
229
        transform.set_executability(True, oz_id)
185
 
        self.assertEqual(transform.find_conflicts(), 
 
230
        self.assertEqual(transform.find_conflicts(),
186
231
                         [('unversioned executability', oz_id)])
187
232
        transform.version_file('oz-id', oz_id)
188
 
        self.assertEqual(transform.find_conflicts(), 
 
233
        self.assertEqual(transform.find_conflicts(),
189
234
                         [('non-file executability', oz_id)])
190
235
        transform.set_executability(None, oz_id)
191
236
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
200
245
        self.assert_('oz/tip' in transform2._tree_path_ids)
201
246
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
202
247
        self.assertEqual(len(result), 2)
203
 
        self.assertEqual((result[0][0], result[0][1]), 
 
248
        self.assertEqual((result[0][0], result[0][1]),
204
249
                         ('duplicate', newtip))
205
 
        self.assertEqual((result[1][0], result[1][2]), 
 
250
        self.assertEqual((result[1][0], result[1][2]),
206
251
                         ('duplicate id', newtip))
207
252
        transform2.finalize()
208
253
        transform3 = TreeTransform(self.wt)
209
254
        self.addCleanup(transform3.finalize)
210
255
        oz_id = transform3.trans_id_tree_file_id('oz-id')
211
256
        transform3.delete_contents(oz_id)
212
 
        self.assertEqual(transform3.find_conflicts(), 
 
257
        self.assertEqual(transform3.find_conflicts(),
213
258
                         [('missing parent', oz_id)])
214
259
        root_id = transform3.root
215
260
        tip_id = transform3.trans_id_tree_file_id('tip-id')
216
261
        transform3.adjust_path('tip', root_id, tip_id)
217
262
        transform3.apply()
218
263
 
 
264
    def test_conflict_on_case_insensitive(self):
 
265
        tree = self.make_branch_and_tree('tree')
 
266
        # Don't try this at home, kids!
 
267
        # Force the tree to report that it is case sensitive, for conflict
 
268
        # resolution tests
 
269
        tree.case_sensitive = True
 
270
        transform = TreeTransform(tree)
 
271
        self.addCleanup(transform.finalize)
 
272
        transform.new_file('file', transform.root, 'content')
 
273
        transform.new_file('FiLe', transform.root, 'content')
 
274
        result = transform.find_conflicts()
 
275
        self.assertEqual([], result)
 
276
        transform.finalize()
 
277
        # Force the tree to report that it is case insensitive, for conflict
 
278
        # generation tests
 
279
        tree.case_sensitive = False
 
280
        transform = TreeTransform(tree)
 
281
        self.addCleanup(transform.finalize)
 
282
        transform.new_file('file', transform.root, 'content')
 
283
        transform.new_file('FiLe', transform.root, 'content')
 
284
        result = transform.find_conflicts()
 
285
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
 
286
 
 
287
    def test_conflict_on_case_insensitive_existing(self):
 
288
        tree = self.make_branch_and_tree('tree')
 
289
        self.build_tree(['tree/FiLe'])
 
290
        # Don't try this at home, kids!
 
291
        # Force the tree to report that it is case sensitive, for conflict
 
292
        # resolution tests
 
293
        tree.case_sensitive = True
 
294
        transform = TreeTransform(tree)
 
295
        self.addCleanup(transform.finalize)
 
296
        transform.new_file('file', transform.root, 'content')
 
297
        result = transform.find_conflicts()
 
298
        self.assertEqual([], result)
 
299
        transform.finalize()
 
300
        # Force the tree to report that it is case insensitive, for conflict
 
301
        # generation tests
 
302
        tree.case_sensitive = False
 
303
        transform = TreeTransform(tree)
 
304
        self.addCleanup(transform.finalize)
 
305
        transform.new_file('file', transform.root, 'content')
 
306
        result = transform.find_conflicts()
 
307
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
 
308
 
 
309
    def test_resolve_case_insensitive_conflict(self):
 
310
        tree = self.make_branch_and_tree('tree')
 
311
        # Don't try this at home, kids!
 
312
        # Force the tree to report that it is case insensitive, for conflict
 
313
        # resolution tests
 
314
        tree.case_sensitive = False
 
315
        transform = TreeTransform(tree)
 
316
        self.addCleanup(transform.finalize)
 
317
        transform.new_file('file', transform.root, 'content')
 
318
        transform.new_file('FiLe', transform.root, 'content')
 
319
        resolve_conflicts(transform)
 
320
        transform.apply()
 
321
        self.failUnlessExists('tree/file')
 
322
        self.failUnlessExists('tree/FiLe.moved')
 
323
 
 
324
    def test_resolve_checkout_case_conflict(self):
 
325
        tree = self.make_branch_and_tree('tree')
 
326
        # Don't try this at home, kids!
 
327
        # Force the tree to report that it is case insensitive, for conflict
 
328
        # resolution tests
 
329
        tree.case_sensitive = False
 
330
        transform = TreeTransform(tree)
 
331
        self.addCleanup(transform.finalize)
 
332
        transform.new_file('file', transform.root, 'content')
 
333
        transform.new_file('FiLe', transform.root, 'content')
 
334
        resolve_conflicts(transform,
 
335
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
 
336
        transform.apply()
 
337
        self.failUnlessExists('tree/file')
 
338
        self.failUnlessExists('tree/FiLe.moved')
 
339
 
 
340
    def test_apply_case_conflict(self):
 
341
        """Ensure that a transform with case conflicts can always be applied"""
 
342
        tree = self.make_branch_and_tree('tree')
 
343
        transform = TreeTransform(tree)
 
344
        self.addCleanup(transform.finalize)
 
345
        transform.new_file('file', transform.root, 'content')
 
346
        transform.new_file('FiLe', transform.root, 'content')
 
347
        dir = transform.new_directory('dir', transform.root)
 
348
        transform.new_file('dirfile', dir, 'content')
 
349
        transform.new_file('dirFiLe', dir, 'content')
 
350
        resolve_conflicts(transform)
 
351
        transform.apply()
 
352
        self.failUnlessExists('tree/file')
 
353
        if not os.path.exists('tree/FiLe.moved'):
 
354
            self.failUnlessExists('tree/FiLe')
 
355
        self.failUnlessExists('tree/dir/dirfile')
 
356
        if not os.path.exists('tree/dir/dirFiLe.moved'):
 
357
            self.failUnlessExists('tree/dir/dirFiLe')
 
358
 
 
359
    def test_case_insensitive_limbo(self):
 
360
        tree = self.make_branch_and_tree('tree')
 
361
        # Don't try this at home, kids!
 
362
        # Force the tree to report that it is case insensitive
 
363
        tree.case_sensitive = False
 
364
        transform = TreeTransform(tree)
 
365
        self.addCleanup(transform.finalize)
 
366
        dir = transform.new_directory('dir', transform.root)
 
367
        first = transform.new_file('file', dir, 'content')
 
368
        second = transform.new_file('FiLe', dir, 'content')
 
369
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
 
370
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
 
371
 
219
372
    def test_add_del(self):
220
373
        start, root = self.get_transform()
221
374
        start.new_directory('a', root, 'a')
234
387
        self.addCleanup(unversion.finalize)
235
388
        parent = unversion.trans_id_tree_path('parent')
236
389
        unversion.unversion_file(parent)
237
 
        self.assertEqual(unversion.find_conflicts(), 
 
390
        self.assertEqual(unversion.find_conflicts(),
238
391
                         [('unversioned parent', parent_id)])
239
392
        file_id = unversion.trans_id_tree_file_id('child-id')
240
393
        unversion.unversion_file(file_id)
260
413
        mangle_tree.adjust_path('name2', root, name1)
261
414
        mangle_tree.adjust_path('name1', root, name2)
262
415
 
263
 
        #tests for deleting parent directories 
 
416
        #tests for deleting parent directories
264
417
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
265
418
        mangle_tree.delete_contents(ddir)
266
419
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
295
448
        create_tree,root = self.get_transform()
296
449
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
297
450
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
298
 
        create_tree.apply()        
 
451
        create_tree.apply()
299
452
        mangle_tree,root = self.get_transform()
300
453
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
301
454
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
309
462
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
310
463
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
311
464
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
312
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
465
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
313
466
                             'test_too_much-id')
314
 
        create_tree.apply()        
 
467
        create_tree.apply()
315
468
        mangle_tree,root = self.get_transform()
316
469
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
317
470
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
318
471
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
319
472
        mangle_tree.adjust_path('selftest', bzrlib, tests)
320
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
473
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
321
474
        mangle_tree.set_executability(True, test_too_much)
322
475
        mangle_tree.apply()
323
476
 
324
477
    def test_both_rename3(self):
325
478
        create_tree,root = self.get_transform()
326
479
        tests = create_tree.new_directory('tests', root, 'tests-id')
327
 
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
480
        create_tree.new_file('test_too_much.py', tests, 'hello1',
328
481
                             'test_too_much-id')
329
 
        create_tree.apply()        
 
482
        create_tree.apply()
330
483
        mangle_tree,root = self.get_transform()
331
484
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
332
485
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
333
486
        mangle_tree.adjust_path('selftest', root, tests)
334
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
487
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
335
488
        mangle_tree.set_executability(True, test_too_much)
336
489
        mangle_tree.apply()
337
490
 
350
503
        newdir = move_id.new_directory('dir', root, 'newdir')
351
504
        move_id.adjust_path('name2', newdir, name1)
352
505
        move_id.apply()
353
 
        
 
506
 
354
507
    def test_replace_dangling_ie(self):
355
508
        create_tree, root = self.get_transform()
356
509
        # prepare tree
372
525
        resolve_conflicts(replace)
373
526
        replace.apply()
374
527
 
375
 
    def test_symlinks(self):
376
 
        if not has_symlinks():
377
 
            raise TestSkipped('Symlinks are not supported on this platform')
378
 
        transform,root = self.get_transform()
 
528
    def _test_symlinks(self, link_name1,link_target1,
 
529
                       link_name2, link_target2):
 
530
 
 
531
        def ozpath(p): return 'oz/' + p
 
532
 
 
533
        self.requireFeature(SymlinkFeature)
 
534
        transform, root = self.get_transform()
379
535
        oz_id = transform.new_directory('oz', root, 'oz-id')
380
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
536
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
381
537
                                       'wizard-id')
382
 
        wiz_id = transform.create_path('wizard2', oz_id)
383
 
        transform.create_symlink('behind_curtain', wiz_id)
384
 
        transform.version_file('wiz-id2', wiz_id)            
 
538
        wiz_id = transform.create_path(link_name2, oz_id)
 
539
        transform.create_symlink(link_target2, wiz_id)
 
540
        transform.version_file('wiz-id2', wiz_id)
385
541
        transform.set_executability(True, wiz_id)
386
 
        self.assertEqual(transform.find_conflicts(), 
 
542
        self.assertEqual(transform.find_conflicts(),
387
543
                         [('non-file executability', wiz_id)])
388
544
        transform.set_executability(None, wiz_id)
389
545
        transform.apply()
390
 
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
391
 
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
392
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
393
 
                         'behind_curtain')
394
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
395
 
                         'wizard-target')
 
546
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
 
547
        self.assertEqual('symlink',
 
548
                         file_kind(self.wt.abspath(ozpath(link_name1))))
 
549
        self.assertEqual(link_target2,
 
550
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
 
551
        self.assertEqual(link_target1,
 
552
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
 
553
 
 
554
    def test_symlinks(self):
 
555
        self._test_symlinks('wizard', 'wizard-target',
 
556
                            'wizard2', 'behind_curtain')
 
557
 
 
558
    def test_symlinks_unicode(self):
 
559
        self.requireFeature(tests.UnicodeFilenameFeature)
 
560
        self._test_symlinks(u'\N{Euro Sign}wizard',
 
561
                            u'wizard-targ\N{Euro Sign}t',
 
562
                            u'\N{Euro Sign}wizard2',
 
563
                            u'b\N{Euro Sign}hind_curtain')
 
564
 
 
565
    def test_unable_create_symlink(self):
 
566
        def tt_helper():
 
567
            wt = self.make_branch_and_tree('.')
 
568
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
569
            try:
 
570
                tt.new_symlink('foo', tt.root, 'bar')
 
571
                tt.apply()
 
572
            finally:
 
573
                wt.unlock()
 
574
        os_symlink = getattr(os, 'symlink', None)
 
575
        os.symlink = None
 
576
        try:
 
577
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
 
578
            self.assertEquals(
 
579
                "Unable to create symlink 'foo' on this platform",
 
580
                str(err))
 
581
        finally:
 
582
            if os_symlink:
 
583
                os.symlink = os_symlink
396
584
 
397
585
    def get_conflicted(self):
398
586
        create,root = self.get_transform()
402
590
        create.apply()
403
591
        conflicts,root = self.get_transform()
404
592
        # set up duplicate entry, duplicate id
405
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
593
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
406
594
                                         'dorothy-id')
407
595
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
408
596
        oz = conflicts.trans_id_tree_file_id('oz-id')
432
620
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
433
621
        raw_conflicts = resolve_conflicts(tt)
434
622
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
435
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
623
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
436
624
                                   'dorothy', None, 'dorothy-id')
437
625
        self.assertEqual(cooked_conflicts[0], duplicate)
438
 
        duplicate_id = DuplicateID('Unversioned existing file', 
 
626
        duplicate_id = DuplicateID('Unversioned existing file',
439
627
                                   'dorothy.moved', 'dorothy', None,
440
628
                                   'dorothy-id')
441
629
        self.assertEqual(cooked_conflicts[1], duplicate_id)
449
637
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
450
638
                                               'oz-id')
451
639
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
452
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
640
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
453
641
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
454
642
        self.assertEqual(cooked_conflicts[4], deleted_parent)
455
643
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
483
671
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
484
672
                                         ' oz/emeraldcity.  Cancelled move.')
485
673
 
 
674
    def prepare_wrong_parent_kind(self):
 
675
        tt, root = self.get_transform()
 
676
        tt.new_file('parent', root, 'contents', 'parent-id')
 
677
        tt.apply()
 
678
        tt, root = self.get_transform()
 
679
        parent_id = tt.trans_id_file_id('parent-id')
 
680
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
681
        return tt
 
682
 
 
683
    def test_find_conflicts_wrong_parent_kind(self):
 
684
        tt = self.prepare_wrong_parent_kind()
 
685
        tt.find_conflicts()
 
686
 
 
687
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
 
688
        tt = self.prepare_wrong_parent_kind()
 
689
        raw_conflicts = resolve_conflicts(tt)
 
690
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
691
                         'new-3')]), raw_conflicts)
 
692
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
 
693
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
 
694
        'parent-id')], cooked_conflicts)
 
695
        tt.apply()
 
696
        self.assertEqual(None, self.wt.path2id('parent'))
 
697
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
698
 
 
699
    def test_resolve_conflicts_wrong_new_parent_kind(self):
 
700
        tt, root = self.get_transform()
 
701
        parent_id = tt.new_directory('parent', root, 'parent-id')
 
702
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
703
        tt.apply()
 
704
        tt, root = self.get_transform()
 
705
        parent_id = tt.trans_id_file_id('parent-id')
 
706
        tt.delete_contents(parent_id)
 
707
        tt.create_file('contents', parent_id)
 
708
        raw_conflicts = resolve_conflicts(tt)
 
709
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
710
                         'new-3')]), raw_conflicts)
 
711
        tt.apply()
 
712
        self.assertEqual(None, self.wt.path2id('parent'))
 
713
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
714
 
 
715
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
 
716
        tt, root = self.get_transform()
 
717
        parent_id = tt.new_directory('parent', root)
 
718
        tt.new_file('child,', parent_id, 'contents2')
 
719
        tt.apply()
 
720
        tt, root = self.get_transform()
 
721
        parent_id = tt.trans_id_tree_path('parent')
 
722
        tt.delete_contents(parent_id)
 
723
        tt.create_file('contents', parent_id)
 
724
        resolve_conflicts(tt)
 
725
        tt.apply()
 
726
        self.assertIs(None, self.wt.path2id('parent'))
 
727
        self.assertIs(None, self.wt.path2id('parent.new'))
 
728
 
486
729
    def test_moving_versioned_directories(self):
487
730
        create, root = self.get_transform()
488
731
        kansas = create.new_directory('kansas', root, 'kansas-id')
521
764
        rename.set_executability(True, myfile)
522
765
        rename.apply()
523
766
 
524
 
    def test_find_interesting(self):
525
 
        create, root = self.get_transform()
526
 
        wt = create._tree
527
 
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
528
 
        create.new_file('uvfile', root, 'othertext')
529
 
        create.apply()
530
 
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
531
 
            find_interesting, wt, wt, ['vfile'])
532
 
        self.assertEqual(result, set(['myfile-id']))
533
 
 
534
767
    def test_set_executability_order(self):
535
768
        """Ensure that executability behaves the same, no matter what order.
536
 
        
 
769
 
537
770
        - create file and set executability simultaneously
538
771
        - create file and set executability afterward
539
772
        - unsetting the executability of a file whose executability has not been
542
775
        """
543
776
        transform, root = self.get_transform()
544
777
        wt = transform._tree
 
778
        wt.lock_read()
 
779
        self.addCleanup(wt.unlock)
545
780
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
546
781
                           True)
547
782
        sac = transform.new_file('set_after_creation', root,
561
796
        transform, root = self.get_transform()
562
797
        transform.new_file('file1', root, 'contents', 'file1-id', True)
563
798
        transform.apply()
 
799
        self.wt.lock_write()
 
800
        self.addCleanup(self.wt.unlock)
564
801
        self.assertTrue(self.wt.is_executable('file1-id'))
565
802
        transform, root = self.get_transform()
566
803
        file1_id = transform.trans_id_tree_file_id('file1-id')
606
843
        transform.apply()
607
844
        transform, root = self.get_transform()
608
845
        try:
609
 
            self.assertEqual([], list(transform._iter_changes()))
 
846
            self.assertEqual([], list(transform.iter_changes()))
610
847
            old = transform.trans_id_tree_file_id('id-1')
611
848
            transform.unversion_file(old)
612
849
            self.assertEqual([('id-1', ('old', None), False, (True, False),
613
850
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
614
 
                (True, True))], list(transform._iter_changes()))
 
851
                (True, True))], list(transform.iter_changes()))
615
852
            transform.new_directory('new', root, 'id-1')
616
853
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
617
854
                ('eert_toor', 'eert_toor'), ('old', 'new'),
618
855
                ('file', 'directory'),
619
 
                (True, False))], list(transform._iter_changes()))
 
856
                (True, False))], list(transform.iter_changes()))
620
857
        finally:
621
858
            transform.finalize()
622
859
 
631
868
            transform.version_file('id-1', old)
632
869
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
633
870
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
634
 
                (False, False))], list(transform._iter_changes()))
 
871
                (False, False))], list(transform.iter_changes()))
635
872
        finally:
636
873
            transform.finalize()
637
874
 
647
884
            old = transform.trans_id_tree_path('old')
648
885
            subdir = transform.trans_id_tree_file_id('subdir-id')
649
886
            new = transform.trans_id_tree_path('new')
650
 
            self.assertEqual([], list(transform._iter_changes()))
 
887
            self.assertEqual([], list(transform.iter_changes()))
651
888
 
652
889
            #content deletion
653
890
            transform.delete_contents(old)
654
891
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
655
892
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
656
 
                (False, False))], list(transform._iter_changes()))
 
893
                (False, False))], list(transform.iter_changes()))
657
894
 
658
895
            #content change
659
896
            transform.create_file('blah', old)
660
897
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
661
898
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
662
 
                (False, False))], list(transform._iter_changes()))
 
899
                (False, False))], list(transform.iter_changes()))
663
900
            transform.cancel_deletion(old)
664
901
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
665
902
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
666
 
                (False, False))], list(transform._iter_changes()))
 
903
                (False, False))], list(transform.iter_changes()))
667
904
            transform.cancel_creation(old)
668
905
 
669
906
            # move file_id to a different file
670
 
            self.assertEqual([], list(transform._iter_changes()))
 
907
            self.assertEqual([], list(transform.iter_changes()))
671
908
            transform.unversion_file(old)
672
909
            transform.version_file('id-1', new)
673
910
            transform.adjust_path('old', root, new)
674
911
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
675
912
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
676
 
                (False, False))], list(transform._iter_changes()))
 
913
                (False, False))], list(transform.iter_changes()))
677
914
            transform.cancel_versioning(new)
678
915
            transform._removed_id = set()
679
916
 
680
917
            #execute bit
681
 
            self.assertEqual([], list(transform._iter_changes()))
 
918
            self.assertEqual([], list(transform.iter_changes()))
682
919
            transform.set_executability(True, old)
683
920
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
684
921
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
685
 
                (False, True))], list(transform._iter_changes()))
 
922
                (False, True))], list(transform.iter_changes()))
686
923
            transform.set_executability(None, old)
687
924
 
688
925
            # filename
689
 
            self.assertEqual([], list(transform._iter_changes()))
 
926
            self.assertEqual([], list(transform.iter_changes()))
690
927
            transform.adjust_path('new', root, old)
691
928
            transform._new_parent = {}
692
929
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
693
930
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
694
 
                (False, False))], list(transform._iter_changes()))
 
931
                (False, False))], list(transform.iter_changes()))
695
932
            transform._new_name = {}
696
933
 
697
934
            # parent directory
698
 
            self.assertEqual([], list(transform._iter_changes()))
 
935
            self.assertEqual([], list(transform.iter_changes()))
699
936
            transform.adjust_path('new', subdir, old)
700
937
            transform._new_name = {}
701
938
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
702
939
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
703
940
                ('file', 'file'), (False, False))],
704
 
                list(transform._iter_changes()))
 
941
                list(transform.iter_changes()))
705
942
            transform._new_path = {}
706
943
 
707
944
        finally:
729
966
                ('id-2', (u'file2', u'file2'), False, (True, True),
730
967
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
731
968
                ('file', 'file'), (False, True))],
732
 
                list(transform._iter_changes()))
 
969
                list(transform.iter_changes()))
733
970
        finally:
734
971
            transform.finalize()
735
972
 
749
986
            transform.adjust_path('flitter', root, floater)
750
987
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
751
988
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
752
 
            (None, None), (False, False))], list(transform._iter_changes()))
 
989
            (None, None), (False, False))], list(transform.iter_changes()))
753
990
        finally:
754
991
            transform.finalize()
755
992
 
764
1001
        try:
765
1002
            old = transform.trans_id_tree_path('old')
766
1003
            subdir = transform.trans_id_tree_file_id('subdir-id')
767
 
            self.assertEqual([], list(transform._iter_changes()))
 
1004
            self.assertEqual([], list(transform.iter_changes()))
768
1005
            transform.delete_contents(subdir)
769
1006
            transform.create_directory(subdir)
770
1007
            transform.set_executability(False, old)
771
1008
            transform.unversion_file(old)
772
1009
            transform.version_file('id-1', old)
773
1010
            transform.adjust_path('old', root, old)
774
 
            self.assertEqual([], list(transform._iter_changes()))
 
1011
            self.assertEqual([], list(transform.iter_changes()))
775
1012
        finally:
776
1013
            transform.finalize()
777
1014
 
942
1179
        transform.cancel_creation(parent)
943
1180
        transform.finalize()
944
1181
 
 
1182
    def test_rollback_on_directory_clash(self):
 
1183
        def tt_helper():
 
1184
            wt = self.make_branch_and_tree('.')
 
1185
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1186
            try:
 
1187
                foo = tt.new_directory('foo', tt.root)
 
1188
                tt.new_file('bar', foo, 'foobar')
 
1189
                baz = tt.new_directory('baz', tt.root)
 
1190
                tt.new_file('qux', baz, 'quux')
 
1191
                # Ask for a rename 'foo' -> 'baz'
 
1192
                tt.adjust_path('baz', tt.root, foo)
 
1193
                # Lie to tt that we've already resolved all conflicts.
 
1194
                tt.apply(no_conflicts=True)
 
1195
            except:
 
1196
                wt.unlock()
 
1197
                raise
 
1198
        # The rename will fail because the target directory is not empty (but
 
1199
        # raises FileExists anyway).
 
1200
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1201
        self.assertContainsRe(str(err),
 
1202
            "^File exists: .+/baz")
 
1203
 
 
1204
    def test_two_directories_clash(self):
 
1205
        def tt_helper():
 
1206
            wt = self.make_branch_and_tree('.')
 
1207
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1208
            try:
 
1209
                foo_1 = tt.new_directory('foo', tt.root)
 
1210
                tt.new_directory('bar', foo_1)
 
1211
                # Adding the same directory with a different content
 
1212
                foo_2 = tt.new_directory('foo', tt.root)
 
1213
                tt.new_directory('baz', foo_2)
 
1214
                # Lie to tt that we've already resolved all conflicts.
 
1215
                tt.apply(no_conflicts=True)
 
1216
            except:
 
1217
                wt.unlock()
 
1218
                raise
 
1219
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1220
        self.assertContainsRe(str(err),
 
1221
            "^File exists: .+/foo")
 
1222
 
 
1223
    def test_two_directories_clash_finalize(self):
 
1224
        def tt_helper():
 
1225
            wt = self.make_branch_and_tree('.')
 
1226
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1227
            try:
 
1228
                foo_1 = tt.new_directory('foo', tt.root)
 
1229
                tt.new_directory('bar', foo_1)
 
1230
                # Adding the same directory with a different content
 
1231
                foo_2 = tt.new_directory('foo', tt.root)
 
1232
                tt.new_directory('baz', foo_2)
 
1233
                # Lie to tt that we've already resolved all conflicts.
 
1234
                tt.apply(no_conflicts=True)
 
1235
            except:
 
1236
                tt.finalize()
 
1237
                raise
 
1238
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1239
        self.assertContainsRe(str(err),
 
1240
            "^File exists: .+/foo")
 
1241
 
 
1242
    def test_file_to_directory(self):
 
1243
        wt = self.make_branch_and_tree('.')
 
1244
        self.build_tree(['foo'])
 
1245
        wt.add(['foo'])
 
1246
        wt.commit("one")
 
1247
        tt = TreeTransform(wt)
 
1248
        self.addCleanup(tt.finalize)
 
1249
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1250
        tt.delete_contents(foo_trans_id)
 
1251
        tt.create_directory(foo_trans_id)
 
1252
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
 
1253
        tt.create_file(["aa\n"], bar_trans_id)
 
1254
        tt.version_file("bar-1", bar_trans_id)
 
1255
        tt.apply()
 
1256
        self.failUnlessExists("foo/bar")
 
1257
        wt.lock_read()
 
1258
        try:
 
1259
            self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1260
                    "directory")
 
1261
        finally:
 
1262
            wt.unlock()
 
1263
        wt.commit("two")
 
1264
        changes = wt.changes_from(wt.basis_tree())
 
1265
        self.assertFalse(changes.has_changed(), changes)
 
1266
 
 
1267
    def test_file_to_symlink(self):
 
1268
        self.requireFeature(SymlinkFeature)
 
1269
        wt = self.make_branch_and_tree('.')
 
1270
        self.build_tree(['foo'])
 
1271
        wt.add(['foo'])
 
1272
        wt.commit("one")
 
1273
        tt = TreeTransform(wt)
 
1274
        self.addCleanup(tt.finalize)
 
1275
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1276
        tt.delete_contents(foo_trans_id)
 
1277
        tt.create_symlink("bar", foo_trans_id)
 
1278
        tt.apply()
 
1279
        self.failUnlessExists("foo")
 
1280
        wt.lock_read()
 
1281
        self.addCleanup(wt.unlock)
 
1282
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1283
                "symlink")
 
1284
 
 
1285
    def test_dir_to_file(self):
 
1286
        wt = self.make_branch_and_tree('.')
 
1287
        self.build_tree(['foo/', 'foo/bar'])
 
1288
        wt.add(['foo', 'foo/bar'])
 
1289
        wt.commit("one")
 
1290
        tt = TreeTransform(wt)
 
1291
        self.addCleanup(tt.finalize)
 
1292
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1293
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
 
1294
        tt.delete_contents(foo_trans_id)
 
1295
        tt.delete_versioned(bar_trans_id)
 
1296
        tt.create_file(["aa\n"], foo_trans_id)
 
1297
        tt.apply()
 
1298
        self.failUnlessExists("foo")
 
1299
        wt.lock_read()
 
1300
        self.addCleanup(wt.unlock)
 
1301
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1302
                "file")
 
1303
 
 
1304
    def test_dir_to_hardlink(self):
 
1305
        self.requireFeature(HardlinkFeature)
 
1306
        wt = self.make_branch_and_tree('.')
 
1307
        self.build_tree(['foo/', 'foo/bar'])
 
1308
        wt.add(['foo', 'foo/bar'])
 
1309
        wt.commit("one")
 
1310
        tt = TreeTransform(wt)
 
1311
        self.addCleanup(tt.finalize)
 
1312
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1313
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
 
1314
        tt.delete_contents(foo_trans_id)
 
1315
        tt.delete_versioned(bar_trans_id)
 
1316
        self.build_tree(['baz'])
 
1317
        tt.create_hardlink("baz", foo_trans_id)
 
1318
        tt.apply()
 
1319
        self.failUnlessExists("foo")
 
1320
        self.failUnlessExists("baz")
 
1321
        wt.lock_read()
 
1322
        self.addCleanup(wt.unlock)
 
1323
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1324
                "file")
 
1325
 
 
1326
    def test_no_final_path(self):
 
1327
        transform, root = self.get_transform()
 
1328
        trans_id = transform.trans_id_file_id('foo')
 
1329
        transform.create_file('bar', trans_id)
 
1330
        transform.cancel_creation(trans_id)
 
1331
        transform.apply()
 
1332
 
 
1333
    def test_create_from_tree(self):
 
1334
        tree1 = self.make_branch_and_tree('tree1')
 
1335
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
 
1336
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
 
1337
        tree2 = self.make_branch_and_tree('tree2')
 
1338
        tt = TreeTransform(tree2)
 
1339
        foo_trans_id = tt.create_path('foo', tt.root)
 
1340
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1341
        bar_trans_id = tt.create_path('bar', tt.root)
 
1342
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
 
1343
        tt.apply()
 
1344
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
 
1345
        self.assertFileEqual('baz', 'tree2/bar')
 
1346
 
 
1347
    def test_create_from_tree_bytes(self):
 
1348
        """Provided lines are used instead of tree content."""
 
1349
        tree1 = self.make_branch_and_tree('tree1')
 
1350
        self.build_tree_contents([('tree1/foo', 'bar'),])
 
1351
        tree1.add('foo', 'foo-id')
 
1352
        tree2 = self.make_branch_and_tree('tree2')
 
1353
        tt = TreeTransform(tree2)
 
1354
        foo_trans_id = tt.create_path('foo', tt.root)
 
1355
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
 
1356
        tt.apply()
 
1357
        self.assertFileEqual('qux', 'tree2/foo')
 
1358
 
 
1359
    def test_create_from_tree_symlink(self):
 
1360
        self.requireFeature(SymlinkFeature)
 
1361
        tree1 = self.make_branch_and_tree('tree1')
 
1362
        os.symlink('bar', 'tree1/foo')
 
1363
        tree1.add('foo', 'foo-id')
 
1364
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
 
1365
        foo_trans_id = tt.create_path('foo', tt.root)
 
1366
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1367
        tt.apply()
 
1368
        self.assertEqual('bar', os.readlink('tree2/foo'))
 
1369
 
945
1370
 
946
1371
class TransformGroup(object):
 
1372
 
947
1373
    def __init__(self, dirname, root_id):
948
1374
        self.name = dirname
949
1375
        os.mkdir(dirname)
960
1386
 
961
1387
 
962
1388
class TestTransformMerge(TestCaseInTempDir):
 
1389
 
963
1390
    def test_text_merge(self):
964
1391
        root_id = generate_ids.gen_root_id()
965
1392
        base = TransformGroup("base", root_id)
995
1422
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
996
1423
        this.tt.apply()
997
1424
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1425
 
998
1426
        # textual merge
999
1427
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1000
1428
        # three-way text conflict
1001
 
        self.assertEqual(this.wt.get_file('b').read(), 
 
1429
        self.assertEqual(this.wt.get_file('b').read(),
1002
1430
                         conflict_text('b', 'b2'))
1003
1431
        # OTHER wins
1004
1432
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1008
1436
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1009
1437
        # No change
1010
1438
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1011
 
        # Correct correct results when THIS == OTHER 
 
1439
        # Correct correct results when THIS == OTHER
1012
1440
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1013
1441
        # Text conflict when THIS & OTHER are text and BASE is dir
1014
 
        self.assertEqual(this.wt.get_file('h').read(), 
 
1442
        self.assertEqual(this.wt.get_file('h').read(),
1015
1443
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1016
1444
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1017
1445
                         '1\n2\n3\n4\n')
1018
1446
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1019
1447
                         'h\ni\nj\nk\n')
1020
1448
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1021
 
        self.assertEqual(this.wt.get_file('i').read(), 
 
1449
        self.assertEqual(this.wt.get_file('i').read(),
1022
1450
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1023
1451
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1024
1452
                         '1\n2\n3\n4\n')
1035
1463
        self.assertSubset(merge_modified, modified)
1036
1464
        self.assertEqual(len(merge_modified), len(modified))
1037
1465
        this.wt.remove('b')
1038
 
        this.wt.revert([])
 
1466
        this.wt.revert()
1039
1467
 
1040
1468
    def test_file_merge(self):
1041
 
        if not has_symlinks():
1042
 
            raise TestSkipped('Symlinks are not supported on this platform')
 
1469
        self.requireFeature(SymlinkFeature)
1043
1470
        root_id = generate_ids.gen_root_id()
1044
1471
        base = TransformGroup("BASE", root_id)
1045
1472
        this = TransformGroup("THIS", root_id)
1049
1476
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1050
1477
            tg.tt.new_file('c', tg.root, 'c', 'c')
1051
1478
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1052
 
        targets = ((base, 'base-e', 'base-f', None, None), 
1053
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
 
1479
        targets = ((base, 'base-e', 'base-f', None, None),
 
1480
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1054
1481
                   (other, 'other-e', None, 'other-g', 'other-h'))
1055
1482
        for tg, e_target, f_target, g_target, h_target in targets:
1056
 
            for link, target in (('e', e_target), ('f', f_target), 
 
1483
            for link, target in (('e', e_target), ('f', f_target),
1057
1484
                                 ('g', g_target), ('h', h_target)):
1058
1485
                if target is not None:
1059
1486
                    tg.tt.new_symlink(link, tg.root, target, link)
1085
1512
        base = TransformGroup("BASE", root_id)
1086
1513
        this = TransformGroup("THIS", root_id)
1087
1514
        other = TransformGroup("OTHER", root_id)
1088
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1515
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1089
1516
                                   for t in [base, this, other]]
1090
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1517
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1091
1518
                                   for t in [base, this, other]]
1092
1519
        base.tt.new_directory('c', base_a, 'c')
1093
1520
        this.tt.new_directory('c1', this_a, 'c')
1118
1545
        base = TransformGroup("BASE", root_id)
1119
1546
        this = TransformGroup("THIS", root_id)
1120
1547
        other = TransformGroup("OTHER", root_id)
1121
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1548
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1122
1549
                                   for t in [base, this, other]]
1123
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1550
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1124
1551
                                   for t in [base, this, other]]
1125
1552
 
1126
1553
        base.tt.new_file('g', base_a, 'g', 'g')
1147
1574
 
1148
1575
class TestBuildTree(tests.TestCaseWithTransport):
1149
1576
 
1150
 
    def test_build_tree(self):
1151
 
        if not has_symlinks():
1152
 
            raise TestSkipped('Test requires symlink support')
 
1577
    def test_build_tree_with_symlinks(self):
 
1578
        self.requireFeature(SymlinkFeature)
1153
1579
        os.mkdir('a')
1154
1580
        a = BzrDir.create_standalone_workingtree('a')
1155
1581
        os.mkdir('a/foo')
1203
1629
 
1204
1630
    def test_symlink_conflict_handling(self):
1205
1631
        """Ensure that when building trees, conflict handling is done"""
1206
 
        if not has_symlinks():
1207
 
            raise TestSkipped('Test requires symlink support')
 
1632
        self.requireFeature(SymlinkFeature)
1208
1633
        source = self.make_branch_and_tree('source')
1209
1634
        os.symlink('foo', 'source/symlink')
1210
1635
        source.add('symlink', 'new-symlink')
1219
1644
        os.symlink('foo', 'target2/symlink')
1220
1645
        build_tree(source.basis_tree(), target)
1221
1646
        self.assertEqual([], target.conflicts())
1222
 
        
 
1647
 
1223
1648
    def test_directory_conflict_handling(self):
1224
1649
        """Ensure that when building trees, conflict handling is done"""
1225
1650
        source = self.make_branch_and_tree('source')
1281
1706
        target = self.make_branch_and_tree('target')
1282
1707
        self.build_tree(['target/name'])
1283
1708
        target.add('name')
1284
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1709
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1285
1710
            build_tree, source.basis_tree(), target)
1286
1711
 
1287
1712
    def test_build_tree_rename_count(self):
1301
1726
        # children of non-root directories should not be renamed
1302
1727
        self.assertEqual(2, transform_result.rename_count)
1303
1728
 
 
1729
    def create_ab_tree(self):
 
1730
        """Create a committed test tree with two files"""
 
1731
        source = self.make_branch_and_tree('source')
 
1732
        self.build_tree_contents([('source/file1', 'A')])
 
1733
        self.build_tree_contents([('source/file2', 'B')])
 
1734
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1735
        source.commit('commit files')
 
1736
        source.lock_write()
 
1737
        self.addCleanup(source.unlock)
 
1738
        return source
 
1739
 
 
1740
    def test_build_tree_accelerator_tree(self):
 
1741
        source = self.create_ab_tree()
 
1742
        self.build_tree_contents([('source/file2', 'C')])
 
1743
        calls = []
 
1744
        real_source_get_file = source.get_file
 
1745
        def get_file(file_id, path=None):
 
1746
            calls.append(file_id)
 
1747
            return real_source_get_file(file_id, path)
 
1748
        source.get_file = get_file
 
1749
        target = self.make_branch_and_tree('target')
 
1750
        revision_tree = source.basis_tree()
 
1751
        revision_tree.lock_read()
 
1752
        self.addCleanup(revision_tree.unlock)
 
1753
        build_tree(revision_tree, target, source)
 
1754
        self.assertEqual(['file1-id'], calls)
 
1755
        target.lock_read()
 
1756
        self.addCleanup(target.unlock)
 
1757
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1758
 
 
1759
    def test_build_tree_accelerator_tree_missing_file(self):
 
1760
        source = self.create_ab_tree()
 
1761
        os.unlink('source/file1')
 
1762
        source.remove(['file2'])
 
1763
        target = self.make_branch_and_tree('target')
 
1764
        revision_tree = source.basis_tree()
 
1765
        revision_tree.lock_read()
 
1766
        self.addCleanup(revision_tree.unlock)
 
1767
        build_tree(revision_tree, target, source)
 
1768
        target.lock_read()
 
1769
        self.addCleanup(target.unlock)
 
1770
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1771
 
 
1772
    def test_build_tree_accelerator_wrong_kind(self):
 
1773
        self.requireFeature(SymlinkFeature)
 
1774
        source = self.make_branch_and_tree('source')
 
1775
        self.build_tree_contents([('source/file1', '')])
 
1776
        self.build_tree_contents([('source/file2', '')])
 
1777
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1778
        source.commit('commit files')
 
1779
        os.unlink('source/file2')
 
1780
        self.build_tree_contents([('source/file2/', 'C')])
 
1781
        os.unlink('source/file1')
 
1782
        os.symlink('file2', 'source/file1')
 
1783
        calls = []
 
1784
        real_source_get_file = source.get_file
 
1785
        def get_file(file_id, path=None):
 
1786
            calls.append(file_id)
 
1787
            return real_source_get_file(file_id, path)
 
1788
        source.get_file = get_file
 
1789
        target = self.make_branch_and_tree('target')
 
1790
        revision_tree = source.basis_tree()
 
1791
        revision_tree.lock_read()
 
1792
        self.addCleanup(revision_tree.unlock)
 
1793
        build_tree(revision_tree, target, source)
 
1794
        self.assertEqual([], calls)
 
1795
        target.lock_read()
 
1796
        self.addCleanup(target.unlock)
 
1797
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1798
 
 
1799
    def test_build_tree_hardlink(self):
 
1800
        self.requireFeature(HardlinkFeature)
 
1801
        source = self.create_ab_tree()
 
1802
        target = self.make_branch_and_tree('target')
 
1803
        revision_tree = source.basis_tree()
 
1804
        revision_tree.lock_read()
 
1805
        self.addCleanup(revision_tree.unlock)
 
1806
        build_tree(revision_tree, target, source, hardlink=True)
 
1807
        target.lock_read()
 
1808
        self.addCleanup(target.unlock)
 
1809
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1810
        source_stat = os.stat('source/file1')
 
1811
        target_stat = os.stat('target/file1')
 
1812
        self.assertEqual(source_stat, target_stat)
 
1813
 
 
1814
        # Explicitly disallowing hardlinks should prevent them.
 
1815
        target2 = self.make_branch_and_tree('target2')
 
1816
        build_tree(revision_tree, target2, source, hardlink=False)
 
1817
        target2.lock_read()
 
1818
        self.addCleanup(target2.unlock)
 
1819
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
 
1820
        source_stat = os.stat('source/file1')
 
1821
        target2_stat = os.stat('target2/file1')
 
1822
        self.assertNotEqual(source_stat, target2_stat)
 
1823
 
 
1824
    def test_build_tree_accelerator_tree_moved(self):
 
1825
        source = self.make_branch_and_tree('source')
 
1826
        self.build_tree_contents([('source/file1', 'A')])
 
1827
        source.add(['file1'], ['file1-id'])
 
1828
        source.commit('commit files')
 
1829
        source.rename_one('file1', 'file2')
 
1830
        source.lock_read()
 
1831
        self.addCleanup(source.unlock)
 
1832
        target = self.make_branch_and_tree('target')
 
1833
        revision_tree = source.basis_tree()
 
1834
        revision_tree.lock_read()
 
1835
        self.addCleanup(revision_tree.unlock)
 
1836
        build_tree(revision_tree, target, source)
 
1837
        target.lock_read()
 
1838
        self.addCleanup(target.unlock)
 
1839
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1840
 
 
1841
    def test_build_tree_hardlinks_preserve_execute(self):
 
1842
        self.requireFeature(HardlinkFeature)
 
1843
        source = self.create_ab_tree()
 
1844
        tt = TreeTransform(source)
 
1845
        trans_id = tt.trans_id_tree_file_id('file1-id')
 
1846
        tt.set_executability(True, trans_id)
 
1847
        tt.apply()
 
1848
        self.assertTrue(source.is_executable('file1-id'))
 
1849
        target = self.make_branch_and_tree('target')
 
1850
        revision_tree = source.basis_tree()
 
1851
        revision_tree.lock_read()
 
1852
        self.addCleanup(revision_tree.unlock)
 
1853
        build_tree(revision_tree, target, source, hardlink=True)
 
1854
        target.lock_read()
 
1855
        self.addCleanup(target.unlock)
 
1856
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1857
        self.assertTrue(source.is_executable('file1-id'))
 
1858
 
 
1859
    def test_case_insensitive_build_tree_inventory(self):
 
1860
        if (tests.CaseInsensitiveFilesystemFeature.available()
 
1861
            or tests.CaseInsCasePresFilenameFeature.available()):
 
1862
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
 
1863
        source = self.make_branch_and_tree('source')
 
1864
        self.build_tree(['source/file', 'source/FILE'])
 
1865
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
 
1866
        source.commit('added files')
 
1867
        # Don't try this at home, kids!
 
1868
        # Force the tree to report that it is case insensitive
 
1869
        target = self.make_branch_and_tree('target')
 
1870
        target.case_sensitive = False
 
1871
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
 
1872
        self.assertEqual('file.moved', target.id2path('lower-id'))
 
1873
        self.assertEqual('FILE', target.id2path('upper-id'))
 
1874
 
 
1875
 
 
1876
class TestCommitTransform(tests.TestCaseWithTransport):
 
1877
 
 
1878
    def get_branch(self):
 
1879
        tree = self.make_branch_and_tree('tree')
 
1880
        tree.lock_write()
 
1881
        self.addCleanup(tree.unlock)
 
1882
        tree.commit('empty commit')
 
1883
        return tree.branch
 
1884
 
 
1885
    def get_branch_and_transform(self):
 
1886
        branch = self.get_branch()
 
1887
        tt = TransformPreview(branch.basis_tree())
 
1888
        self.addCleanup(tt.finalize)
 
1889
        return branch, tt
 
1890
 
 
1891
    def test_commit_wrong_basis(self):
 
1892
        branch = self.get_branch()
 
1893
        basis = branch.repository.revision_tree(
 
1894
            _mod_revision.NULL_REVISION)
 
1895
        tt = TransformPreview(basis)
 
1896
        self.addCleanup(tt.finalize)
 
1897
        e = self.assertRaises(ValueError, tt.commit, branch, '')
 
1898
        self.assertEqual('TreeTransform not based on branch basis: null:',
 
1899
                         str(e))
 
1900
 
 
1901
    def test_empy_commit(self):
 
1902
        branch, tt = self.get_branch_and_transform()
 
1903
        rev = tt.commit(branch, 'my message')
 
1904
        self.assertEqual(2, branch.revno())
 
1905
        repo = branch.repository
 
1906
        self.assertEqual('my message', repo.get_revision(rev).message)
 
1907
 
 
1908
    def test_merge_parents(self):
 
1909
        branch, tt = self.get_branch_and_transform()
 
1910
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
 
1911
        self.assertEqual(['rev1b', 'rev1c'],
 
1912
                         branch.basis_tree().get_parent_ids()[1:])
 
1913
 
 
1914
    def test_first_commit(self):
 
1915
        branch = self.make_branch('branch')
 
1916
        branch.lock_write()
 
1917
        self.addCleanup(branch.unlock)
 
1918
        tt = TransformPreview(branch.basis_tree())
 
1919
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
 
1920
        rev = tt.commit(branch, 'my message')
 
1921
        self.assertEqual([], branch.basis_tree().get_parent_ids())
 
1922
        self.assertNotEqual(_mod_revision.NULL_REVISION,
 
1923
                            branch.last_revision())
 
1924
 
 
1925
    def test_first_commit_with_merge_parents(self):
 
1926
        branch = self.make_branch('branch')
 
1927
        branch.lock_write()
 
1928
        self.addCleanup(branch.unlock)
 
1929
        tt = TransformPreview(branch.basis_tree())
 
1930
        e = self.assertRaises(ValueError, tt.commit, branch,
 
1931
                          'my message', ['rev1b-id'])
 
1932
        self.assertEqual('Cannot supply merge parents for first commit.',
 
1933
                         str(e))
 
1934
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
 
1935
 
 
1936
    def test_add_files(self):
 
1937
        branch, tt = self.get_branch_and_transform()
 
1938
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
1939
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
 
1940
        tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
 
1941
        rev = tt.commit(branch, 'message')
 
1942
        tree = branch.basis_tree()
 
1943
        self.assertEqual('file', tree.id2path('file-id'))
 
1944
        self.assertEqual('contents', tree.get_file_text('file-id'))
 
1945
        self.assertEqual('dir', tree.id2path('dir-id'))
 
1946
        self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
 
1947
        self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
1948
 
 
1949
    def test_add_unversioned(self):
 
1950
        branch, tt = self.get_branch_and_transform()
 
1951
        tt.new_file('file', tt.root, 'contents')
 
1952
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
 
1953
                          'message', strict=True)
 
1954
 
 
1955
    def test_modify_strict(self):
 
1956
        branch, tt = self.get_branch_and_transform()
 
1957
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
1958
        tt.commit(branch, 'message', strict=True)
 
1959
        tt = TransformPreview(branch.basis_tree())
 
1960
        trans_id = tt.trans_id_file_id('file-id')
 
1961
        tt.delete_contents(trans_id)
 
1962
        tt.create_file('contents', trans_id)
 
1963
        tt.commit(branch, 'message', strict=True)
 
1964
 
 
1965
    def test_commit_malformed(self):
 
1966
        """Committing a malformed transform should raise an exception.
 
1967
 
 
1968
        In this case, we are adding a file without adding its parent.
 
1969
        """
 
1970
        branch, tt = self.get_branch_and_transform()
 
1971
        parent_id = tt.trans_id_file_id('parent-id')
 
1972
        tt.new_file('file', parent_id, 'contents', 'file-id')
 
1973
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
 
1974
                          'message')
 
1975
 
1304
1976
 
1305
1977
class MockTransform(object):
1306
1978
 
1319
1991
        object.__init__(self)
1320
1992
        self.name = "name"
1321
1993
 
 
1994
 
1322
1995
class TestGetBackupName(TestCase):
1323
1996
    def test_get_backup_name(self):
1324
1997
        tt = MockTransform()
1332
2005
        self.assertEqual(name, 'name.~1~')
1333
2006
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1334
2007
        self.assertEqual(name, 'name.~4~')
 
2008
 
 
2009
 
 
2010
class TestFileMover(tests.TestCaseWithTransport):
 
2011
 
 
2012
    def test_file_mover(self):
 
2013
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
 
2014
        mover = _FileMover()
 
2015
        mover.rename('a', 'q')
 
2016
        self.failUnlessExists('q')
 
2017
        self.failIfExists('a')
 
2018
        self.failUnlessExists('q/b')
 
2019
        self.failUnlessExists('c')
 
2020
        self.failUnlessExists('c/d')
 
2021
 
 
2022
    def test_pre_delete_rollback(self):
 
2023
        self.build_tree(['a/'])
 
2024
        mover = _FileMover()
 
2025
        mover.pre_delete('a', 'q')
 
2026
        self.failUnlessExists('q')
 
2027
        self.failIfExists('a')
 
2028
        mover.rollback()
 
2029
        self.failIfExists('q')
 
2030
        self.failUnlessExists('a')
 
2031
 
 
2032
    def test_apply_deletions(self):
 
2033
        self.build_tree(['a/', 'b/'])
 
2034
        mover = _FileMover()
 
2035
        mover.pre_delete('a', 'q')
 
2036
        mover.pre_delete('b', 'r')
 
2037
        self.failUnlessExists('q')
 
2038
        self.failUnlessExists('r')
 
2039
        self.failIfExists('a')
 
2040
        self.failIfExists('b')
 
2041
        mover.apply_deletions()
 
2042
        self.failIfExists('q')
 
2043
        self.failIfExists('r')
 
2044
        self.failIfExists('a')
 
2045
        self.failIfExists('b')
 
2046
 
 
2047
    def test_file_mover_rollback(self):
 
2048
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
 
2049
        mover = _FileMover()
 
2050
        mover.rename('c/d', 'c/f')
 
2051
        mover.rename('c/e', 'c/d')
 
2052
        try:
 
2053
            mover.rename('a', 'c')
 
2054
        except errors.FileExists, e:
 
2055
            mover.rollback()
 
2056
        self.failUnlessExists('a')
 
2057
        self.failUnlessExists('c/d')
 
2058
 
 
2059
 
 
2060
class Bogus(Exception):
 
2061
    pass
 
2062
 
 
2063
 
 
2064
class TestTransformRollback(tests.TestCaseWithTransport):
 
2065
 
 
2066
    class ExceptionFileMover(_FileMover):
 
2067
 
 
2068
        def __init__(self, bad_source=None, bad_target=None):
 
2069
            _FileMover.__init__(self)
 
2070
            self.bad_source = bad_source
 
2071
            self.bad_target = bad_target
 
2072
 
 
2073
        def rename(self, source, target):
 
2074
            if (self.bad_source is not None and
 
2075
                source.endswith(self.bad_source)):
 
2076
                raise Bogus
 
2077
            elif (self.bad_target is not None and
 
2078
                target.endswith(self.bad_target)):
 
2079
                raise Bogus
 
2080
            else:
 
2081
                _FileMover.rename(self, source, target)
 
2082
 
 
2083
    def test_rollback_rename(self):
 
2084
        tree = self.make_branch_and_tree('.')
 
2085
        self.build_tree(['a/', 'a/b'])
 
2086
        tt = TreeTransform(tree)
 
2087
        self.addCleanup(tt.finalize)
 
2088
        a_id = tt.trans_id_tree_path('a')
 
2089
        tt.adjust_path('c', tt.root, a_id)
 
2090
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
 
2091
        self.assertRaises(Bogus, tt.apply,
 
2092
                          _mover=self.ExceptionFileMover(bad_source='a'))
 
2093
        self.failUnlessExists('a')
 
2094
        self.failUnlessExists('a/b')
 
2095
        tt.apply()
 
2096
        self.failUnlessExists('c')
 
2097
        self.failUnlessExists('c/d')
 
2098
 
 
2099
    def test_rollback_rename_into_place(self):
 
2100
        tree = self.make_branch_and_tree('.')
 
2101
        self.build_tree(['a/', 'a/b'])
 
2102
        tt = TreeTransform(tree)
 
2103
        self.addCleanup(tt.finalize)
 
2104
        a_id = tt.trans_id_tree_path('a')
 
2105
        tt.adjust_path('c', tt.root, a_id)
 
2106
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
 
2107
        self.assertRaises(Bogus, tt.apply,
 
2108
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
 
2109
        self.failUnlessExists('a')
 
2110
        self.failUnlessExists('a/b')
 
2111
        tt.apply()
 
2112
        self.failUnlessExists('c')
 
2113
        self.failUnlessExists('c/d')
 
2114
 
 
2115
    def test_rollback_deletion(self):
 
2116
        tree = self.make_branch_and_tree('.')
 
2117
        self.build_tree(['a/', 'a/b'])
 
2118
        tt = TreeTransform(tree)
 
2119
        self.addCleanup(tt.finalize)
 
2120
        a_id = tt.trans_id_tree_path('a')
 
2121
        tt.delete_contents(a_id)
 
2122
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
 
2123
        self.assertRaises(Bogus, tt.apply,
 
2124
                          _mover=self.ExceptionFileMover(bad_target='d'))
 
2125
        self.failUnlessExists('a')
 
2126
        self.failUnlessExists('a/b')
 
2127
 
 
2128
    def test_resolve_no_parent(self):
 
2129
        wt = self.make_branch_and_tree('.')
 
2130
        tt = TreeTransform(wt)
 
2131
        self.addCleanup(tt.finalize)
 
2132
        parent = tt.trans_id_file_id('parent-id')
 
2133
        tt.new_file('file', parent, 'Contents')
 
2134
        resolve_conflicts(tt)
 
2135
 
 
2136
 
 
2137
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
 
2138
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
 
2139
                  (False, False))
 
2140
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
 
2141
              ('', ''), ('directory', 'directory'), (False, None))
 
2142
 
 
2143
 
 
2144
class TestTransformPreview(tests.TestCaseWithTransport):
 
2145
 
 
2146
    def create_tree(self):
 
2147
        tree = self.make_branch_and_tree('.')
 
2148
        self.build_tree_contents([('a', 'content 1')])
 
2149
        tree.set_root_id('TREE_ROOT')
 
2150
        tree.add('a', 'a-id')
 
2151
        tree.commit('rev1', rev_id='rev1')
 
2152
        return tree.branch.repository.revision_tree('rev1')
 
2153
 
 
2154
    def get_empty_preview(self):
 
2155
        repository = self.make_repository('repo')
 
2156
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
 
2157
        preview = TransformPreview(tree)
 
2158
        self.addCleanup(preview.finalize)
 
2159
        return preview
 
2160
 
 
2161
    def test_transform_preview(self):
 
2162
        revision_tree = self.create_tree()
 
2163
        preview = TransformPreview(revision_tree)
 
2164
        self.addCleanup(preview.finalize)
 
2165
 
 
2166
    def test_transform_preview_tree(self):
 
2167
        revision_tree = self.create_tree()
 
2168
        preview = TransformPreview(revision_tree)
 
2169
        self.addCleanup(preview.finalize)
 
2170
        preview.get_preview_tree()
 
2171
 
 
2172
    def test_transform_new_file(self):
 
2173
        revision_tree = self.create_tree()
 
2174
        preview = TransformPreview(revision_tree)
 
2175
        self.addCleanup(preview.finalize)
 
2176
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
2177
        preview_tree = preview.get_preview_tree()
 
2178
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
 
2179
        self.assertEqual(
 
2180
            preview_tree.get_file('file2-id').read(), 'content B\n')
 
2181
 
 
2182
    def test_diff_preview_tree(self):
 
2183
        revision_tree = self.create_tree()
 
2184
        preview = TransformPreview(revision_tree)
 
2185
        self.addCleanup(preview.finalize)
 
2186
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
2187
        preview_tree = preview.get_preview_tree()
 
2188
        out = StringIO()
 
2189
        show_diff_trees(revision_tree, preview_tree, out)
 
2190
        lines = out.getvalue().splitlines()
 
2191
        self.assertEqual(lines[0], "=== added file 'file2'")
 
2192
        # 3 lines of diff administrivia
 
2193
        self.assertEqual(lines[4], "+content B")
 
2194
 
 
2195
    def test_transform_conflicts(self):
 
2196
        revision_tree = self.create_tree()
 
2197
        preview = TransformPreview(revision_tree)
 
2198
        self.addCleanup(preview.finalize)
 
2199
        preview.new_file('a', preview.root, 'content 2')
 
2200
        resolve_conflicts(preview)
 
2201
        trans_id = preview.trans_id_file_id('a-id')
 
2202
        self.assertEqual('a.moved', preview.final_name(trans_id))
 
2203
 
 
2204
    def get_tree_and_preview_tree(self):
 
2205
        revision_tree = self.create_tree()
 
2206
        preview = TransformPreview(revision_tree)
 
2207
        self.addCleanup(preview.finalize)
 
2208
        a_trans_id = preview.trans_id_file_id('a-id')
 
2209
        preview.delete_contents(a_trans_id)
 
2210
        preview.create_file('b content', a_trans_id)
 
2211
        preview_tree = preview.get_preview_tree()
 
2212
        return revision_tree, preview_tree
 
2213
 
 
2214
    def test_iter_changes(self):
 
2215
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2216
        root = revision_tree.inventory.root.file_id
 
2217
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
 
2218
                          (root, root), ('a', 'a'), ('file', 'file'),
 
2219
                          (False, False))],
 
2220
                          list(preview_tree.iter_changes(revision_tree)))
 
2221
 
 
2222
    def test_include_unchanged_succeeds(self):
 
2223
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2224
        changes = preview_tree.iter_changes(revision_tree,
 
2225
                                            include_unchanged=True)
 
2226
        root = revision_tree.inventory.root.file_id
 
2227
 
 
2228
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2229
 
 
2230
    def test_specific_files(self):
 
2231
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2232
        changes = preview_tree.iter_changes(revision_tree,
 
2233
                                            specific_files=[''])
 
2234
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2235
 
 
2236
    def test_want_unversioned(self):
 
2237
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2238
        changes = preview_tree.iter_changes(revision_tree,
 
2239
                                            want_unversioned=True)
 
2240
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2241
 
 
2242
    def test_ignore_extra_trees_no_specific_files(self):
 
2243
        # extra_trees is harmless without specific_files, so we'll silently
 
2244
        # accept it, even though we won't use it.
 
2245
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2246
        preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
 
2247
 
 
2248
    def test_ignore_require_versioned_no_specific_files(self):
 
2249
        # require_versioned is meaningless without specific_files.
 
2250
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2251
        preview_tree.iter_changes(revision_tree, require_versioned=False)
 
2252
 
 
2253
    def test_ignore_pb(self):
 
2254
        # pb could be supported, but TT.iter_changes doesn't support it.
 
2255
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2256
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
 
2257
 
 
2258
    def test_kind(self):
 
2259
        revision_tree = self.create_tree()
 
2260
        preview = TransformPreview(revision_tree)
 
2261
        self.addCleanup(preview.finalize)
 
2262
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2263
        preview.new_directory('directory', preview.root, 'dir-id')
 
2264
        preview_tree = preview.get_preview_tree()
 
2265
        self.assertEqual('file', preview_tree.kind('file-id'))
 
2266
        self.assertEqual('directory', preview_tree.kind('dir-id'))
 
2267
 
 
2268
    def test_get_file_mtime(self):
 
2269
        preview = self.get_empty_preview()
 
2270
        file_trans_id = preview.new_file('file', preview.root, 'contents',
 
2271
                                         'file-id')
 
2272
        limbo_path = preview._limbo_name(file_trans_id)
 
2273
        preview_tree = preview.get_preview_tree()
 
2274
        self.assertEqual(os.stat(limbo_path).st_mtime,
 
2275
                         preview_tree.get_file_mtime('file-id'))
 
2276
 
 
2277
    def test_get_file(self):
 
2278
        preview = self.get_empty_preview()
 
2279
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2280
        preview_tree = preview.get_preview_tree()
 
2281
        tree_file = preview_tree.get_file('file-id')
 
2282
        try:
 
2283
            self.assertEqual('contents', tree_file.read())
 
2284
        finally:
 
2285
            tree_file.close()
 
2286
 
 
2287
    def test_get_symlink_target(self):
 
2288
        self.requireFeature(SymlinkFeature)
 
2289
        preview = self.get_empty_preview()
 
2290
        preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
 
2291
        preview_tree = preview.get_preview_tree()
 
2292
        self.assertEqual('target',
 
2293
                         preview_tree.get_symlink_target('symlink-id'))
 
2294
 
 
2295
    def test_all_file_ids(self):
 
2296
        tree = self.make_branch_and_tree('tree')
 
2297
        self.build_tree(['tree/a', 'tree/b', 'tree/c'])
 
2298
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
 
2299
        preview = TransformPreview(tree)
 
2300
        self.addCleanup(preview.finalize)
 
2301
        preview.unversion_file(preview.trans_id_file_id('b-id'))
 
2302
        c_trans_id = preview.trans_id_file_id('c-id')
 
2303
        preview.unversion_file(c_trans_id)
 
2304
        preview.version_file('c-id', c_trans_id)
 
2305
        preview_tree = preview.get_preview_tree()
 
2306
        self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
 
2307
                         preview_tree.all_file_ids())
 
2308
 
 
2309
    def test_path2id_deleted_unchanged(self):
 
2310
        tree = self.make_branch_and_tree('tree')
 
2311
        self.build_tree(['tree/unchanged', 'tree/deleted'])
 
2312
        tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
 
2313
        preview = TransformPreview(tree)
 
2314
        self.addCleanup(preview.finalize)
 
2315
        preview.unversion_file(preview.trans_id_file_id('deleted-id'))
 
2316
        preview_tree = preview.get_preview_tree()
 
2317
        self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
 
2318
        self.assertIs(None, preview_tree.path2id('deleted'))
 
2319
 
 
2320
    def test_path2id_created(self):
 
2321
        tree = self.make_branch_and_tree('tree')
 
2322
        self.build_tree(['tree/unchanged'])
 
2323
        tree.add(['unchanged'], ['unchanged-id'])
 
2324
        preview = TransformPreview(tree)
 
2325
        self.addCleanup(preview.finalize)
 
2326
        preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
 
2327
            'contents', 'new-id')
 
2328
        preview_tree = preview.get_preview_tree()
 
2329
        self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
 
2330
 
 
2331
    def test_path2id_moved(self):
 
2332
        tree = self.make_branch_and_tree('tree')
 
2333
        self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
 
2334
        tree.add(['old_parent', 'old_parent/child'],
 
2335
                 ['old_parent-id', 'child-id'])
 
2336
        preview = TransformPreview(tree)
 
2337
        self.addCleanup(preview.finalize)
 
2338
        new_parent = preview.new_directory('new_parent', preview.root,
 
2339
                                           'new_parent-id')
 
2340
        preview.adjust_path('child', new_parent,
 
2341
                            preview.trans_id_file_id('child-id'))
 
2342
        preview_tree = preview.get_preview_tree()
 
2343
        self.assertIs(None, preview_tree.path2id('old_parent/child'))
 
2344
        self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
 
2345
 
 
2346
    def test_path2id_renamed_parent(self):
 
2347
        tree = self.make_branch_and_tree('tree')
 
2348
        self.build_tree(['tree/old_name/', 'tree/old_name/child'])
 
2349
        tree.add(['old_name', 'old_name/child'],
 
2350
                 ['parent-id', 'child-id'])
 
2351
        preview = TransformPreview(tree)
 
2352
        self.addCleanup(preview.finalize)
 
2353
        preview.adjust_path('new_name', preview.root,
 
2354
                            preview.trans_id_file_id('parent-id'))
 
2355
        preview_tree = preview.get_preview_tree()
 
2356
        self.assertIs(None, preview_tree.path2id('old_name/child'))
 
2357
        self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
 
2358
 
 
2359
    def assertMatchingIterEntries(self, tt, specific_file_ids=None):
 
2360
        preview_tree = tt.get_preview_tree()
 
2361
        preview_result = list(preview_tree.iter_entries_by_dir(
 
2362
                              specific_file_ids))
 
2363
        tree = tt._tree
 
2364
        tt.apply()
 
2365
        actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
 
2366
        self.assertEqual(actual_result, preview_result)
 
2367
 
 
2368
    def test_iter_entries_by_dir_new(self):
 
2369
        tree = self.make_branch_and_tree('tree')
 
2370
        tt = TreeTransform(tree)
 
2371
        tt.new_file('new', tt.root, 'contents', 'new-id')
 
2372
        self.assertMatchingIterEntries(tt)
 
2373
 
 
2374
    def test_iter_entries_by_dir_deleted(self):
 
2375
        tree = self.make_branch_and_tree('tree')
 
2376
        self.build_tree(['tree/deleted'])
 
2377
        tree.add('deleted', 'deleted-id')
 
2378
        tt = TreeTransform(tree)
 
2379
        tt.delete_contents(tt.trans_id_file_id('deleted-id'))
 
2380
        self.assertMatchingIterEntries(tt)
 
2381
 
 
2382
    def test_iter_entries_by_dir_unversioned(self):
 
2383
        tree = self.make_branch_and_tree('tree')
 
2384
        self.build_tree(['tree/removed'])
 
2385
        tree.add('removed', 'removed-id')
 
2386
        tt = TreeTransform(tree)
 
2387
        tt.unversion_file(tt.trans_id_file_id('removed-id'))
 
2388
        self.assertMatchingIterEntries(tt)
 
2389
 
 
2390
    def test_iter_entries_by_dir_moved(self):
 
2391
        tree = self.make_branch_and_tree('tree')
 
2392
        self.build_tree(['tree/moved', 'tree/new_parent/'])
 
2393
        tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
 
2394
        tt = TreeTransform(tree)
 
2395
        tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
 
2396
                       tt.trans_id_file_id('moved-id'))
 
2397
        self.assertMatchingIterEntries(tt)
 
2398
 
 
2399
    def test_iter_entries_by_dir_specific_file_ids(self):
 
2400
        tree = self.make_branch_and_tree('tree')
 
2401
        tree.set_root_id('tree-root-id')
 
2402
        self.build_tree(['tree/parent/', 'tree/parent/child'])
 
2403
        tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
 
2404
        tt = TreeTransform(tree)
 
2405
        self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
 
2406
 
 
2407
    def test_symlink_content_summary(self):
 
2408
        self.requireFeature(SymlinkFeature)
 
2409
        preview = self.get_empty_preview()
 
2410
        preview.new_symlink('path', preview.root, 'target', 'path-id')
 
2411
        summary = preview.get_preview_tree().path_content_summary('path')
 
2412
        self.assertEqual(('symlink', None, None, 'target'), summary)
 
2413
 
 
2414
    def test_missing_content_summary(self):
 
2415
        preview = self.get_empty_preview()
 
2416
        summary = preview.get_preview_tree().path_content_summary('path')
 
2417
        self.assertEqual(('missing', None, None, None), summary)
 
2418
 
 
2419
    def test_deleted_content_summary(self):
 
2420
        tree = self.make_branch_and_tree('tree')
 
2421
        self.build_tree(['tree/path/'])
 
2422
        tree.add('path')
 
2423
        preview = TransformPreview(tree)
 
2424
        self.addCleanup(preview.finalize)
 
2425
        preview.delete_contents(preview.trans_id_tree_path('path'))
 
2426
        summary = preview.get_preview_tree().path_content_summary('path')
 
2427
        self.assertEqual(('missing', None, None, None), summary)
 
2428
 
 
2429
    def test_file_content_summary_executable(self):
 
2430
        if not osutils.supports_executable():
 
2431
            raise TestNotApplicable()
 
2432
        preview = self.get_empty_preview()
 
2433
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
 
2434
        preview.set_executability(True, path_id)
 
2435
        summary = preview.get_preview_tree().path_content_summary('path')
 
2436
        self.assertEqual(4, len(summary))
 
2437
        self.assertEqual('file', summary[0])
 
2438
        # size must be known
 
2439
        self.assertEqual(len('contents'), summary[1])
 
2440
        # executable
 
2441
        self.assertEqual(True, summary[2])
 
2442
        # will not have hash (not cheap to determine)
 
2443
        self.assertIs(None, summary[3])
 
2444
 
 
2445
    def test_change_executability(self):
 
2446
        if not osutils.supports_executable():
 
2447
            raise TestNotApplicable()
 
2448
        tree = self.make_branch_and_tree('tree')
 
2449
        self.build_tree(['tree/path'])
 
2450
        tree.add('path')
 
2451
        preview = TransformPreview(tree)
 
2452
        self.addCleanup(preview.finalize)
 
2453
        path_id = preview.trans_id_tree_path('path')
 
2454
        preview.set_executability(True, path_id)
 
2455
        summary = preview.get_preview_tree().path_content_summary('path')
 
2456
        self.assertEqual(True, summary[2])
 
2457
 
 
2458
    def test_file_content_summary_non_exec(self):
 
2459
        preview = self.get_empty_preview()
 
2460
        preview.new_file('path', preview.root, 'contents', 'path-id')
 
2461
        summary = preview.get_preview_tree().path_content_summary('path')
 
2462
        self.assertEqual(4, len(summary))
 
2463
        self.assertEqual('file', summary[0])
 
2464
        # size must be known
 
2465
        self.assertEqual(len('contents'), summary[1])
 
2466
        # not executable
 
2467
        if osutils.supports_executable():
 
2468
            self.assertEqual(False, summary[2])
 
2469
        else:
 
2470
            self.assertEqual(None, summary[2])
 
2471
        # will not have hash (not cheap to determine)
 
2472
        self.assertIs(None, summary[3])
 
2473
 
 
2474
    def test_dir_content_summary(self):
 
2475
        preview = self.get_empty_preview()
 
2476
        preview.new_directory('path', preview.root, 'path-id')
 
2477
        summary = preview.get_preview_tree().path_content_summary('path')
 
2478
        self.assertEqual(('directory', None, None, None), summary)
 
2479
 
 
2480
    def test_tree_content_summary(self):
 
2481
        preview = self.get_empty_preview()
 
2482
        path = preview.new_directory('path', preview.root, 'path-id')
 
2483
        preview.set_tree_reference('rev-1', path)
 
2484
        summary = preview.get_preview_tree().path_content_summary('path')
 
2485
        self.assertEqual(4, len(summary))
 
2486
        self.assertEqual('tree-reference', summary[0])
 
2487
 
 
2488
    def test_annotate(self):
 
2489
        tree = self.make_branch_and_tree('tree')
 
2490
        self.build_tree_contents([('tree/file', 'a\n')])
 
2491
        tree.add('file', 'file-id')
 
2492
        tree.commit('a', rev_id='one')
 
2493
        self.build_tree_contents([('tree/file', 'a\nb\n')])
 
2494
        preview = TransformPreview(tree)
 
2495
        self.addCleanup(preview.finalize)
 
2496
        file_trans_id = preview.trans_id_file_id('file-id')
 
2497
        preview.delete_contents(file_trans_id)
 
2498
        preview.create_file('a\nb\nc\n', file_trans_id)
 
2499
        preview_tree = preview.get_preview_tree()
 
2500
        expected = [
 
2501
            ('one', 'a\n'),
 
2502
            ('me:', 'b\n'),
 
2503
            ('me:', 'c\n'),
 
2504
        ]
 
2505
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2506
        self.assertEqual(expected, annotation)
 
2507
 
 
2508
    def test_annotate_missing(self):
 
2509
        preview = self.get_empty_preview()
 
2510
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
2511
        preview_tree = preview.get_preview_tree()
 
2512
        expected = [
 
2513
            ('me:', 'a\n'),
 
2514
            ('me:', 'b\n'),
 
2515
            ('me:', 'c\n'),
 
2516
         ]
 
2517
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2518
        self.assertEqual(expected, annotation)
 
2519
 
 
2520
    def test_annotate_rename(self):
 
2521
        tree = self.make_branch_and_tree('tree')
 
2522
        self.build_tree_contents([('tree/file', 'a\n')])
 
2523
        tree.add('file', 'file-id')
 
2524
        tree.commit('a', rev_id='one')
 
2525
        preview = TransformPreview(tree)
 
2526
        self.addCleanup(preview.finalize)
 
2527
        file_trans_id = preview.trans_id_file_id('file-id')
 
2528
        preview.adjust_path('newname', preview.root, file_trans_id)
 
2529
        preview_tree = preview.get_preview_tree()
 
2530
        expected = [
 
2531
            ('one', 'a\n'),
 
2532
        ]
 
2533
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2534
        self.assertEqual(expected, annotation)
 
2535
 
 
2536
    def test_annotate_deleted(self):
 
2537
        tree = self.make_branch_and_tree('tree')
 
2538
        self.build_tree_contents([('tree/file', 'a\n')])
 
2539
        tree.add('file', 'file-id')
 
2540
        tree.commit('a', rev_id='one')
 
2541
        self.build_tree_contents([('tree/file', 'a\nb\n')])
 
2542
        preview = TransformPreview(tree)
 
2543
        self.addCleanup(preview.finalize)
 
2544
        file_trans_id = preview.trans_id_file_id('file-id')
 
2545
        preview.delete_contents(file_trans_id)
 
2546
        preview_tree = preview.get_preview_tree()
 
2547
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2548
        self.assertIs(None, annotation)
 
2549
 
 
2550
    def test_stored_kind(self):
 
2551
        preview = self.get_empty_preview()
 
2552
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
2553
        preview_tree = preview.get_preview_tree()
 
2554
        self.assertEqual('file', preview_tree.stored_kind('file-id'))
 
2555
 
 
2556
    def test_is_executable(self):
 
2557
        preview = self.get_empty_preview()
 
2558
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
2559
        preview.set_executability(True, preview.trans_id_file_id('file-id'))
 
2560
        preview_tree = preview.get_preview_tree()
 
2561
        self.assertEqual(True, preview_tree.is_executable('file-id'))
 
2562
 
 
2563
    def test_get_set_parent_ids(self):
 
2564
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2565
        self.assertEqual([], preview_tree.get_parent_ids())
 
2566
        preview_tree.set_parent_ids(['rev-1'])
 
2567
        self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
 
2568
 
 
2569
    def test_plan_file_merge(self):
 
2570
        work_a = self.make_branch_and_tree('wta')
 
2571
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
 
2572
        work_a.add('file', 'file-id')
 
2573
        base_id = work_a.commit('base version')
 
2574
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
 
2575
        preview = TransformPreview(work_a)
 
2576
        self.addCleanup(preview.finalize)
 
2577
        trans_id = preview.trans_id_file_id('file-id')
 
2578
        preview.delete_contents(trans_id)
 
2579
        preview.create_file('b\nc\nd\ne\n', trans_id)
 
2580
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
 
2581
        tree_a = preview.get_preview_tree()
 
2582
        tree_a.set_parent_ids([base_id])
 
2583
        self.assertEqual([
 
2584
            ('killed-a', 'a\n'),
 
2585
            ('killed-b', 'b\n'),
 
2586
            ('unchanged', 'c\n'),
 
2587
            ('unchanged', 'd\n'),
 
2588
            ('new-a', 'e\n'),
 
2589
            ('new-b', 'f\n'),
 
2590
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
 
2591
 
 
2592
    def test_plan_file_merge_revision_tree(self):
 
2593
        work_a = self.make_branch_and_tree('wta')
 
2594
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
 
2595
        work_a.add('file', 'file-id')
 
2596
        base_id = work_a.commit('base version')
 
2597
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
 
2598
        preview = TransformPreview(work_a.basis_tree())
 
2599
        self.addCleanup(preview.finalize)
 
2600
        trans_id = preview.trans_id_file_id('file-id')
 
2601
        preview.delete_contents(trans_id)
 
2602
        preview.create_file('b\nc\nd\ne\n', trans_id)
 
2603
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
 
2604
        tree_a = preview.get_preview_tree()
 
2605
        tree_a.set_parent_ids([base_id])
 
2606
        self.assertEqual([
 
2607
            ('killed-a', 'a\n'),
 
2608
            ('killed-b', 'b\n'),
 
2609
            ('unchanged', 'c\n'),
 
2610
            ('unchanged', 'd\n'),
 
2611
            ('new-a', 'e\n'),
 
2612
            ('new-b', 'f\n'),
 
2613
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
 
2614
 
 
2615
    def test_walkdirs(self):
 
2616
        preview = self.get_empty_preview()
 
2617
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
2618
        # FIXME: new_directory should mark root.
 
2619
        preview.adjust_path('', ROOT_PARENT, root)
 
2620
        preview_tree = preview.get_preview_tree()
 
2621
        file_trans_id = preview.new_file('a', preview.root, 'contents',
 
2622
                                         'a-id')
 
2623
        expected = [(('', 'tree-root'),
 
2624
                    [('a', 'a', 'file', None, 'a-id', 'file')])]
 
2625
        self.assertEqual(expected, list(preview_tree.walkdirs()))
 
2626
 
 
2627
    def test_extras(self):
 
2628
        work_tree = self.make_branch_and_tree('tree')
 
2629
        self.build_tree(['tree/removed-file', 'tree/existing-file',
 
2630
                         'tree/not-removed-file'])
 
2631
        work_tree.add(['removed-file', 'not-removed-file'])
 
2632
        preview = TransformPreview(work_tree)
 
2633
        self.addCleanup(preview.finalize)
 
2634
        preview.new_file('new-file', preview.root, 'contents')
 
2635
        preview.new_file('new-versioned-file', preview.root, 'contents',
 
2636
                         'new-versioned-id')
 
2637
        tree = preview.get_preview_tree()
 
2638
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
 
2639
        self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
 
2640
                         set(tree.extras()))
 
2641
 
 
2642
    def test_merge_into_preview(self):
 
2643
        work_tree = self.make_branch_and_tree('tree')
 
2644
        self.build_tree_contents([('tree/file','b\n')])
 
2645
        work_tree.add('file', 'file-id')
 
2646
        work_tree.commit('first commit')
 
2647
        child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
 
2648
        self.build_tree_contents([('child/file','b\nc\n')])
 
2649
        child_tree.commit('child commit')
 
2650
        child_tree.lock_write()
 
2651
        self.addCleanup(child_tree.unlock)
 
2652
        work_tree.lock_write()
 
2653
        self.addCleanup(work_tree.unlock)
 
2654
        preview = TransformPreview(work_tree)
 
2655
        self.addCleanup(preview.finalize)
 
2656
        file_trans_id = preview.trans_id_file_id('file-id')
 
2657
        preview.delete_contents(file_trans_id)
 
2658
        preview.create_file('a\nb\n', file_trans_id)
 
2659
        pb = progress.DummyProgress()
 
2660
        preview_tree = preview.get_preview_tree()
 
2661
        merger = Merger.from_revision_ids(pb, preview_tree,
 
2662
                                          child_tree.branch.last_revision(),
 
2663
                                          other_branch=child_tree.branch,
 
2664
                                          tree_branch=work_tree.branch)
 
2665
        merger.merge_type = Merge3Merger
 
2666
        tt = merger.make_merger().make_preview_transform()
 
2667
        self.addCleanup(tt.finalize)
 
2668
        final_tree = tt.get_preview_tree()
 
2669
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
 
2670
 
 
2671
    def test_merge_preview_into_workingtree(self):
 
2672
        tree = self.make_branch_and_tree('tree')
 
2673
        tree.set_root_id('TREE_ROOT')
 
2674
        tt = TransformPreview(tree)
 
2675
        self.addCleanup(tt.finalize)
 
2676
        tt.new_file('name', tt.root, 'content', 'file-id')
 
2677
        tree2 = self.make_branch_and_tree('tree2')
 
2678
        tree2.set_root_id('TREE_ROOT')
 
2679
        pb = progress.DummyProgress()
 
2680
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2681
                                         pb, tree.basis_tree())
 
2682
        merger.merge_type = Merge3Merger
 
2683
        merger.do_merge()
 
2684
 
 
2685
    def test_merge_preview_into_workingtree_handles_conflicts(self):
 
2686
        tree = self.make_branch_and_tree('tree')
 
2687
        self.build_tree_contents([('tree/foo', 'bar')])
 
2688
        tree.add('foo', 'foo-id')
 
2689
        tree.commit('foo')
 
2690
        tt = TransformPreview(tree)
 
2691
        self.addCleanup(tt.finalize)
 
2692
        trans_id = tt.trans_id_file_id('foo-id')
 
2693
        tt.delete_contents(trans_id)
 
2694
        tt.create_file('baz', trans_id)
 
2695
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
2696
        self.build_tree_contents([('tree2/foo', 'qux')])
 
2697
        pb = progress.DummyProgress()
 
2698
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2699
                                         pb, tree.basis_tree())
 
2700
        merger.merge_type = Merge3Merger
 
2701
        merger.do_merge()
 
2702
 
 
2703
    def test_is_executable(self):
 
2704
        tree = self.make_branch_and_tree('tree')
 
2705
        preview = TransformPreview(tree)
 
2706
        self.addCleanup(preview.finalize)
 
2707
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
 
2708
        preview_tree = preview.get_preview_tree()
 
2709
        self.assertEqual(False, preview_tree.is_executable('baz-id',
 
2710
                                                           'tree/foo'))
 
2711
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
 
2712
 
 
2713
    def test_commit_preview_tree(self):
 
2714
        tree = self.make_branch_and_tree('tree')
 
2715
        rev_id = tree.commit('rev1')
 
2716
        tree.branch.lock_write()
 
2717
        self.addCleanup(tree.branch.unlock)
 
2718
        tt = TransformPreview(tree)
 
2719
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
2720
        self.addCleanup(tt.finalize)
 
2721
        preview = tt.get_preview_tree()
 
2722
        preview.set_parent_ids([rev_id])
 
2723
        builder = tree.branch.get_commit_builder([rev_id])
 
2724
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
2725
        builder.finish_inventory()
 
2726
        rev2_id = builder.commit('rev2')
 
2727
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
2728
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
2729
 
 
2730
 
 
2731
class FakeSerializer(object):
 
2732
    """Serializer implementation that simply returns the input.
 
2733
 
 
2734
    The input is returned in the order used by pack.ContainerPushParser.
 
2735
    """
 
2736
    @staticmethod
 
2737
    def bytes_record(bytes, names):
 
2738
        return names, bytes
 
2739
 
 
2740
 
 
2741
class TestSerializeTransform(tests.TestCaseWithTransport):
 
2742
 
 
2743
    _test_needs_features = [tests.UnicodeFilenameFeature]
 
2744
 
 
2745
    def get_preview(self, tree=None):
 
2746
        if tree is None:
 
2747
            tree = self.make_branch_and_tree('tree')
 
2748
        tt = TransformPreview(tree)
 
2749
        self.addCleanup(tt.finalize)
 
2750
        return tt
 
2751
 
 
2752
    def assertSerializesTo(self, expected, tt):
 
2753
        records = list(tt.serialize(FakeSerializer()))
 
2754
        self.assertEqual(expected, records)
 
2755
 
 
2756
    @staticmethod
 
2757
    def default_attribs():
 
2758
        return {
 
2759
            '_id_number': 1,
 
2760
            '_new_name': {},
 
2761
            '_new_parent': {},
 
2762
            '_new_executability': {},
 
2763
            '_new_id': {},
 
2764
            '_tree_path_ids': {'': 'new-0'},
 
2765
            '_removed_id': [],
 
2766
            '_removed_contents': [],
 
2767
            '_non_present_ids': {},
 
2768
            }
 
2769
 
 
2770
    def make_records(self, attribs, contents):
 
2771
        records = [
 
2772
            (((('attribs'),),), bencode.bencode(attribs))]
 
2773
        records.extend([(((n, k),), c) for n, k, c in contents])
 
2774
        return records
 
2775
 
 
2776
    def creation_records(self):
 
2777
        attribs = self.default_attribs()
 
2778
        attribs['_id_number'] = 3
 
2779
        attribs['_new_name'] = {
 
2780
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
 
2781
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
 
2782
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
 
2783
        attribs['_new_executability'] = {'new-1': 1}
 
2784
        contents = [
 
2785
            ('new-1', 'file', 'i 1\nbar\n'),
 
2786
            ('new-2', 'directory', ''),
 
2787
            ]
 
2788
        return self.make_records(attribs, contents)
 
2789
 
 
2790
    def test_serialize_creation(self):
 
2791
        tt = self.get_preview()
 
2792
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
 
2793
        tt.new_directory('qux', tt.root, 'quxx')
 
2794
        self.assertSerializesTo(self.creation_records(), tt)
 
2795
 
 
2796
    def test_deserialize_creation(self):
 
2797
        tt = self.get_preview()
 
2798
        tt.deserialize(iter(self.creation_records()))
 
2799
        self.assertEqual(3, tt._id_number)
 
2800
        self.assertEqual({'new-1': u'foo\u1234',
 
2801
                          'new-2': 'qux'}, tt._new_name)
 
2802
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
 
2803
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
 
2804
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
 
2805
        self.assertEqual({'new-1': True}, tt._new_executability)
 
2806
        self.assertEqual({'new-1': 'file',
 
2807
                          'new-2': 'directory'}, tt._new_contents)
 
2808
        foo_limbo = open(tt._limbo_name('new-1'), 'rb')
 
2809
        try:
 
2810
            foo_content = foo_limbo.read()
 
2811
        finally:
 
2812
            foo_limbo.close()
 
2813
        self.assertEqual('bar', foo_content)
 
2814
 
 
2815
    def symlink_creation_records(self):
 
2816
        attribs = self.default_attribs()
 
2817
        attribs['_id_number'] = 2
 
2818
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
 
2819
        attribs['_new_parent'] = {'new-1': 'new-0'}
 
2820
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
 
2821
        return self.make_records(attribs, contents)
 
2822
 
 
2823
    def test_serialize_symlink_creation(self):
 
2824
        self.requireFeature(tests.SymlinkFeature)
 
2825
        tt = self.get_preview()
 
2826
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
 
2827
        self.assertSerializesTo(self.symlink_creation_records(), tt)
 
2828
 
 
2829
    def test_deserialize_symlink_creation(self):
 
2830
        self.requireFeature(tests.SymlinkFeature)
 
2831
        tt = self.get_preview()
 
2832
        tt.deserialize(iter(self.symlink_creation_records()))
 
2833
        abspath = tt._limbo_name('new-1')
 
2834
        foo_content = osutils.readlink(abspath)
 
2835
        self.assertEqual(u'bar\u1234', foo_content)
 
2836
 
 
2837
    def make_destruction_preview(self):
 
2838
        tree = self.make_branch_and_tree('.')
 
2839
        self.build_tree([u'foo\u1234', 'bar'])
 
2840
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
 
2841
        return self.get_preview(tree)
 
2842
 
 
2843
    def destruction_records(self):
 
2844
        attribs = self.default_attribs()
 
2845
        attribs['_id_number'] = 3
 
2846
        attribs['_removed_id'] = ['new-1']
 
2847
        attribs['_removed_contents'] = ['new-2']
 
2848
        attribs['_tree_path_ids'] = {
 
2849
            '': 'new-0',
 
2850
            u'foo\u1234'.encode('utf-8'): 'new-1',
 
2851
            'bar': 'new-2',
 
2852
            }
 
2853
        return self.make_records(attribs, [])
 
2854
 
 
2855
    def test_serialize_destruction(self):
 
2856
        tt = self.make_destruction_preview()
 
2857
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
 
2858
        tt.unversion_file(foo_trans_id)
 
2859
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
 
2860
        tt.delete_contents(bar_trans_id)
 
2861
        self.assertSerializesTo(self.destruction_records(), tt)
 
2862
 
 
2863
    def test_deserialize_destruction(self):
 
2864
        tt = self.make_destruction_preview()
 
2865
        tt.deserialize(iter(self.destruction_records()))
 
2866
        self.assertEqual({u'foo\u1234': 'new-1',
 
2867
                          'bar': 'new-2',
 
2868
                          '': tt.root}, tt._tree_path_ids)
 
2869
        self.assertEqual({'new-1': u'foo\u1234',
 
2870
                          'new-2': 'bar',
 
2871
                          tt.root: ''}, tt._tree_id_paths)
 
2872
        self.assertEqual(set(['new-1']), tt._removed_id)
 
2873
        self.assertEqual(set(['new-2']), tt._removed_contents)
 
2874
 
 
2875
    def missing_records(self):
 
2876
        attribs = self.default_attribs()
 
2877
        attribs['_id_number'] = 2
 
2878
        attribs['_non_present_ids'] = {
 
2879
            'boo': 'new-1',}
 
2880
        return self.make_records(attribs, [])
 
2881
 
 
2882
    def test_serialize_missing(self):
 
2883
        tt = self.get_preview()
 
2884
        boo_trans_id = tt.trans_id_file_id('boo')
 
2885
        self.assertSerializesTo(self.missing_records(), tt)
 
2886
 
 
2887
    def test_deserialize_missing(self):
 
2888
        tt = self.get_preview()
 
2889
        tt.deserialize(iter(self.missing_records()))
 
2890
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
 
2891
 
 
2892
    def make_modification_preview(self):
 
2893
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
2894
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
2895
        tree = self.make_branch_and_tree('tree')
 
2896
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
2897
        tree.add('file', 'file-id')
 
2898
        return self.get_preview(tree), LINES_TWO
 
2899
 
 
2900
    def modification_records(self):
 
2901
        attribs = self.default_attribs()
 
2902
        attribs['_id_number'] = 2
 
2903
        attribs['_tree_path_ids'] = {
 
2904
            'file': 'new-1',
 
2905
            '': 'new-0',}
 
2906
        attribs['_removed_contents'] = ['new-1']
 
2907
        contents = [('new-1', 'file',
 
2908
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
 
2909
        return self.make_records(attribs, contents)
 
2910
 
 
2911
    def test_serialize_modification(self):
 
2912
        tt, LINES = self.make_modification_preview()
 
2913
        trans_id = tt.trans_id_file_id('file-id')
 
2914
        tt.delete_contents(trans_id)
 
2915
        tt.create_file(LINES, trans_id)
 
2916
        self.assertSerializesTo(self.modification_records(), tt)
 
2917
 
 
2918
    def test_deserialize_modification(self):
 
2919
        tt, LINES = self.make_modification_preview()
 
2920
        tt.deserialize(iter(self.modification_records()))
 
2921
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
2922
 
 
2923
    def make_kind_change_preview(self):
 
2924
        LINES = 'a\nb\nc\nd\n'
 
2925
        tree = self.make_branch_and_tree('tree')
 
2926
        self.build_tree(['tree/foo/'])
 
2927
        tree.add('foo', 'foo-id')
 
2928
        return self.get_preview(tree), LINES
 
2929
 
 
2930
    def kind_change_records(self):
 
2931
        attribs = self.default_attribs()
 
2932
        attribs['_id_number'] = 2
 
2933
        attribs['_tree_path_ids'] = {
 
2934
            'foo': 'new-1',
 
2935
            '': 'new-0',}
 
2936
        attribs['_removed_contents'] = ['new-1']
 
2937
        contents = [('new-1', 'file',
 
2938
                     'i 4\na\nb\nc\nd\n\n')]
 
2939
        return self.make_records(attribs, contents)
 
2940
 
 
2941
    def test_serialize_kind_change(self):
 
2942
        tt, LINES = self.make_kind_change_preview()
 
2943
        trans_id = tt.trans_id_file_id('foo-id')
 
2944
        tt.delete_contents(trans_id)
 
2945
        tt.create_file(LINES, trans_id)
 
2946
        self.assertSerializesTo(self.kind_change_records(), tt)
 
2947
 
 
2948
    def test_deserialize_kind_change(self):
 
2949
        tt, LINES = self.make_kind_change_preview()
 
2950
        tt.deserialize(iter(self.kind_change_records()))
 
2951
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
2952
 
 
2953
    def make_add_contents_preview(self):
 
2954
        LINES = 'a\nb\nc\nd\n'
 
2955
        tree = self.make_branch_and_tree('tree')
 
2956
        self.build_tree(['tree/foo'])
 
2957
        tree.add('foo')
 
2958
        os.unlink('tree/foo')
 
2959
        return self.get_preview(tree), LINES
 
2960
 
 
2961
    def add_contents_records(self):
 
2962
        attribs = self.default_attribs()
 
2963
        attribs['_id_number'] = 2
 
2964
        attribs['_tree_path_ids'] = {
 
2965
            'foo': 'new-1',
 
2966
            '': 'new-0',}
 
2967
        contents = [('new-1', 'file',
 
2968
                     'i 4\na\nb\nc\nd\n\n')]
 
2969
        return self.make_records(attribs, contents)
 
2970
 
 
2971
    def test_serialize_add_contents(self):
 
2972
        tt, LINES = self.make_add_contents_preview()
 
2973
        trans_id = tt.trans_id_tree_path('foo')
 
2974
        tt.create_file(LINES, trans_id)
 
2975
        self.assertSerializesTo(self.add_contents_records(), tt)
 
2976
 
 
2977
    def test_deserialize_add_contents(self):
 
2978
        tt, LINES = self.make_add_contents_preview()
 
2979
        tt.deserialize(iter(self.add_contents_records()))
 
2980
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
2981
 
 
2982
    def test_get_parents_lines(self):
 
2983
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
2984
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
2985
        tree = self.make_branch_and_tree('tree')
 
2986
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
2987
        tree.add('file', 'file-id')
 
2988
        tt = self.get_preview(tree)
 
2989
        trans_id = tt.trans_id_tree_path('file')
 
2990
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
 
2991
            tt._get_parents_lines(trans_id))
 
2992
 
 
2993
    def test_get_parents_texts(self):
 
2994
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
2995
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
2996
        tree = self.make_branch_and_tree('tree')
 
2997
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
2998
        tree.add('file', 'file-id')
 
2999
        tt = self.get_preview(tree)
 
3000
        trans_id = tt.trans_id_tree_path('file')
 
3001
        self.assertEqual((LINES_ONE,),
 
3002
            tt._get_parents_texts(trans_id))