/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Andrew Bennetts
  • Date: 2010-04-13 04:33:55 UTC
  • mfrom: (5147 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5149.
  • Revision ID: andrew.bennetts@canonical.com-20100413043355-lg3id0uwtju0k3zs
MergeĀ lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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
 
import stat
19
18
from StringIO import StringIO
20
19
import sys
 
20
import time
21
21
 
22
22
from bzrlib import (
 
23
    bencode,
23
24
    errors,
 
25
    filters,
24
26
    generate_ids,
25
27
    osutils,
26
28
    progress,
27
29
    revision as _mod_revision,
28
 
    symbol_versioning,
 
30
    rules,
29
31
    tests,
30
32
    urlutils,
31
33
    )
35
37
                              NonDirectoryParent)
36
38
from bzrlib.diff import show_diff_trees
37
39
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, CantMoveRoot, 
 
40
                           ReusingTransform, CantMoveRoot,
39
41
                           PathsNotVersionedError, ExistingLimbo,
40
42
                           ExistingPendingDeletion, ImmortalLimbo,
41
43
                           ImmortalPendingDeletion, LockError)
42
44
from bzrlib.osutils import file_kind, pathjoin
43
45
from bzrlib.merge import Merge3Merger, Merger
44
46
from bzrlib.tests import (
45
 
    CaseInsensitiveFilesystemFeature,
46
47
    HardlinkFeature,
47
48
    SymlinkFeature,
48
49
    TestCase,
49
50
    TestCaseInTempDir,
50
51
    TestSkipped,
51
52
    )
52
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
53
 
                              resolve_conflicts, cook_conflicts, 
 
53
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
 
54
                              resolve_conflicts, cook_conflicts,
54
55
                              build_tree, get_backup_name,
55
56
                              _FileMover, resolve_checkout,
56
 
                              TransformPreview)
 
57
                              TransformPreview, create_from_tree)
 
58
 
57
59
 
58
60
class TestTreeTransform(tests.TestCaseWithTransport):
59
61
 
128
130
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
129
131
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
130
132
        self.assertEqual(len(modified_paths), 3)
131
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
133
        tree_mod_paths = [self.wt.id2abspath(f) for f in
132
134
                          ('ozzie', 'my_pretties', 'my_pretties2')]
133
135
        self.assertSubset(tree_mod_paths, modified_paths)
134
136
        # is it safe to finalize repeatedly?
135
137
        transform.finalize()
136
138
        transform.finalize()
137
139
 
 
140
    def test_create_files_same_timestamp(self):
 
141
        transform, root = self.get_transform()
 
142
        self.wt.lock_tree_write()
 
143
        self.addCleanup(self.wt.unlock)
 
144
        # Roll back the clock, so that we know everything is being set to the
 
145
        # exact time
 
146
        transform._creation_mtime = creation_mtime = time.time() - 20.0
 
147
        transform.create_file('content-one',
 
148
                              transform.create_path('one', root))
 
149
        time.sleep(1) # *ugly*
 
150
        transform.create_file('content-two',
 
151
                              transform.create_path('two', root))
 
152
        transform.apply()
 
153
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
 
154
        fo.close()
 
155
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
 
156
        fo.close()
 
157
        # We only guarantee 2s resolution
 
158
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
 
159
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
 
160
        # But if we have more than that, all files should get the same result
 
161
        self.assertEqual(st1.st_mtime, st2.st_mtime)
 
162
 
 
163
    def test_change_root_id(self):
 
164
        transform, root = self.get_transform()
 
165
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
 
166
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
167
        transform.delete_contents(root)
 
168
        transform.unversion_file(root)
 
169
        transform.fixup_new_roots()
 
170
        transform.apply()
 
171
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
172
 
 
173
    def test_change_root_id_add_files(self):
 
174
        transform, root = self.get_transform()
 
175
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
 
176
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
177
        transform.new_file('file', new_trans_id, ['new-contents\n'],
 
178
                           'new-file-id')
 
179
        transform.delete_contents(root)
 
180
        transform.unversion_file(root)
 
181
        transform.fixup_new_roots()
 
182
        transform.apply()
 
183
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
184
        self.assertEqual('new-file-id', self.wt.path2id('file'))
 
185
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
 
186
 
 
187
    def test_add_two_roots(self):
 
188
        transform, root = self.get_transform()
 
189
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
190
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
 
191
        self.assertRaises(ValueError, transform.fixup_new_roots)
 
192
 
138
193
    def test_hardlink(self):
139
194
        self.requireFeature(HardlinkFeature)
140
195
        transform, root = self.get_transform()
154
209
        transform, root = self.get_transform()
155
210
        self.wt.lock_tree_write()
156
211
        self.addCleanup(self.wt.unlock)
157
 
        trans_id = transform.new_file('name', root, 'contents', 
 
212
        trans_id = transform.new_file('name', root, 'contents',
158
213
                                      'my_pretties', True)
159
214
        oz = transform.new_directory('oz', root, 'oz-id')
160
215
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
161
 
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
216
        toto = transform.new_file('toto', dorothy, 'toto-contents',
162
217
                                  'toto-id', False)
163
218
 
164
219
        self.assertEqual(len(transform.find_conflicts()), 0)
188
243
 
189
244
    def test_conflicts(self):
190
245
        transform, root = self.get_transform()
191
 
        trans_id = transform.new_file('name', root, 'contents', 
 
246
        trans_id = transform.new_file('name', root, 'contents',
192
247
                                      'my_pretties')
193
248
        self.assertEqual(len(transform.find_conflicts()), 0)
194
249
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
195
 
        self.assertEqual(transform.find_conflicts(), 
 
250
        self.assertEqual(transform.find_conflicts(),
196
251
                         [('duplicate', trans_id, trans_id2, 'name')])
197
252
        self.assertRaises(MalformedTransform, transform.apply)
198
253
        transform.adjust_path('name', trans_id, trans_id2)
199
 
        self.assertEqual(transform.find_conflicts(), 
 
254
        self.assertEqual(transform.find_conflicts(),
200
255
                         [('non-directory parent', trans_id)])
201
256
        tinman_id = transform.trans_id_tree_path('tinman')
202
257
        transform.adjust_path('name', tinman_id, trans_id2)
203
 
        self.assertEqual(transform.find_conflicts(), 
204
 
                         [('unversioned parent', tinman_id), 
 
258
        self.assertEqual(transform.find_conflicts(),
 
259
                         [('unversioned parent', tinman_id),
205
260
                          ('missing parent', tinman_id)])
206
261
        lion_id = transform.create_path('lion', root)
207
 
        self.assertEqual(transform.find_conflicts(), 
208
 
                         [('unversioned parent', tinman_id), 
 
262
        self.assertEqual(transform.find_conflicts(),
 
263
                         [('unversioned parent', tinman_id),
209
264
                          ('missing parent', tinman_id)])
210
265
        transform.adjust_path('name', lion_id, trans_id2)
211
 
        self.assertEqual(transform.find_conflicts(), 
 
266
        self.assertEqual(transform.find_conflicts(),
212
267
                         [('unversioned parent', lion_id),
213
268
                          ('missing parent', lion_id)])
214
269
        transform.version_file("Courage", lion_id)
215
 
        self.assertEqual(transform.find_conflicts(), 
216
 
                         [('missing parent', lion_id), 
 
270
        self.assertEqual(transform.find_conflicts(),
 
271
                         [('missing parent', lion_id),
217
272
                          ('versioning no contents', lion_id)])
218
273
        transform.adjust_path('name2', root, trans_id2)
219
 
        self.assertEqual(transform.find_conflicts(), 
 
274
        self.assertEqual(transform.find_conflicts(),
220
275
                         [('versioning no contents', lion_id)])
221
276
        transform.create_file('Contents, okay?', lion_id)
222
277
        transform.adjust_path('name2', trans_id2, trans_id2)
223
 
        self.assertEqual(transform.find_conflicts(), 
224
 
                         [('parent loop', trans_id2), 
 
278
        self.assertEqual(transform.find_conflicts(),
 
279
                         [('parent loop', trans_id2),
225
280
                          ('non-directory parent', trans_id2)])
226
281
        transform.adjust_path('name2', root, trans_id2)
227
282
        oz_id = transform.new_directory('oz', root)
228
283
        transform.set_executability(True, oz_id)
229
 
        self.assertEqual(transform.find_conflicts(), 
 
284
        self.assertEqual(transform.find_conflicts(),
230
285
                         [('unversioned executability', oz_id)])
231
286
        transform.version_file('oz-id', oz_id)
232
 
        self.assertEqual(transform.find_conflicts(), 
 
287
        self.assertEqual(transform.find_conflicts(),
233
288
                         [('non-file executability', oz_id)])
234
289
        transform.set_executability(None, oz_id)
235
290
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
244
299
        self.assert_('oz/tip' in transform2._tree_path_ids)
245
300
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
246
301
        self.assertEqual(len(result), 2)
247
 
        self.assertEqual((result[0][0], result[0][1]), 
 
302
        self.assertEqual((result[0][0], result[0][1]),
248
303
                         ('duplicate', newtip))
249
 
        self.assertEqual((result[1][0], result[1][2]), 
 
304
        self.assertEqual((result[1][0], result[1][2]),
250
305
                         ('duplicate id', newtip))
251
306
        transform2.finalize()
252
307
        transform3 = TreeTransform(self.wt)
253
308
        self.addCleanup(transform3.finalize)
254
309
        oz_id = transform3.trans_id_tree_file_id('oz-id')
255
310
        transform3.delete_contents(oz_id)
256
 
        self.assertEqual(transform3.find_conflicts(), 
 
311
        self.assertEqual(transform3.find_conflicts(),
257
312
                         [('missing parent', oz_id)])
258
313
        root_id = transform3.root
259
314
        tip_id = transform3.trans_id_tree_file_id('tip-id')
368
423
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
369
424
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
370
425
 
 
426
    def test_adjust_path_updates_child_limbo_names(self):
 
427
        tree = self.make_branch_and_tree('tree')
 
428
        transform = TreeTransform(tree)
 
429
        self.addCleanup(transform.finalize)
 
430
        foo_id = transform.new_directory('foo', transform.root)
 
431
        bar_id = transform.new_directory('bar', foo_id)
 
432
        baz_id = transform.new_directory('baz', bar_id)
 
433
        qux_id = transform.new_directory('qux', baz_id)
 
434
        transform.adjust_path('quxx', foo_id, bar_id)
 
435
        self.assertStartsWith(transform._limbo_name(qux_id),
 
436
                              transform._limbo_name(bar_id))
 
437
 
371
438
    def test_add_del(self):
372
439
        start, root = self.get_transform()
373
440
        start.new_directory('a', root, 'a')
386
453
        self.addCleanup(unversion.finalize)
387
454
        parent = unversion.trans_id_tree_path('parent')
388
455
        unversion.unversion_file(parent)
389
 
        self.assertEqual(unversion.find_conflicts(), 
 
456
        self.assertEqual(unversion.find_conflicts(),
390
457
                         [('unversioned parent', parent_id)])
391
458
        file_id = unversion.trans_id_tree_file_id('child-id')
392
459
        unversion.unversion_file(file_id)
412
479
        mangle_tree.adjust_path('name2', root, name1)
413
480
        mangle_tree.adjust_path('name1', root, name2)
414
481
 
415
 
        #tests for deleting parent directories 
 
482
        #tests for deleting parent directories
416
483
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
417
484
        mangle_tree.delete_contents(ddir)
418
485
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
447
514
        create_tree,root = self.get_transform()
448
515
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
449
516
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
450
 
        create_tree.apply()        
 
517
        create_tree.apply()
451
518
        mangle_tree,root = self.get_transform()
452
519
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
453
520
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
461
528
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
462
529
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
463
530
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
464
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
531
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
465
532
                             'test_too_much-id')
466
 
        create_tree.apply()        
 
533
        create_tree.apply()
467
534
        mangle_tree,root = self.get_transform()
468
535
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
469
536
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
470
537
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
471
538
        mangle_tree.adjust_path('selftest', bzrlib, tests)
472
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
539
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
473
540
        mangle_tree.set_executability(True, test_too_much)
474
541
        mangle_tree.apply()
475
542
 
476
543
    def test_both_rename3(self):
477
544
        create_tree,root = self.get_transform()
478
545
        tests = create_tree.new_directory('tests', root, 'tests-id')
479
 
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
546
        create_tree.new_file('test_too_much.py', tests, 'hello1',
480
547
                             'test_too_much-id')
481
 
        create_tree.apply()        
 
548
        create_tree.apply()
482
549
        mangle_tree,root = self.get_transform()
483
550
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
484
551
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
485
552
        mangle_tree.adjust_path('selftest', root, tests)
486
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
553
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
487
554
        mangle_tree.set_executability(True, test_too_much)
488
555
        mangle_tree.apply()
489
556
 
502
569
        newdir = move_id.new_directory('dir', root, 'newdir')
503
570
        move_id.adjust_path('name2', newdir, name1)
504
571
        move_id.apply()
505
 
        
 
572
 
506
573
    def test_replace_dangling_ie(self):
507
574
        create_tree, root = self.get_transform()
508
575
        # prepare tree
524
591
        resolve_conflicts(replace)
525
592
        replace.apply()
526
593
 
527
 
    def test_symlinks(self):
 
594
    def _test_symlinks(self, link_name1,link_target1,
 
595
                       link_name2, link_target2):
 
596
 
 
597
        def ozpath(p): return 'oz/' + p
 
598
 
528
599
        self.requireFeature(SymlinkFeature)
529
 
        transform,root = self.get_transform()
 
600
        transform, root = self.get_transform()
530
601
        oz_id = transform.new_directory('oz', root, 'oz-id')
531
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
602
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
532
603
                                       'wizard-id')
533
 
        wiz_id = transform.create_path('wizard2', oz_id)
534
 
        transform.create_symlink('behind_curtain', wiz_id)
535
 
        transform.version_file('wiz-id2', wiz_id)            
 
604
        wiz_id = transform.create_path(link_name2, oz_id)
 
605
        transform.create_symlink(link_target2, wiz_id)
 
606
        transform.version_file('wiz-id2', wiz_id)
536
607
        transform.set_executability(True, wiz_id)
537
 
        self.assertEqual(transform.find_conflicts(), 
 
608
        self.assertEqual(transform.find_conflicts(),
538
609
                         [('non-file executability', wiz_id)])
539
610
        transform.set_executability(None, wiz_id)
540
611
        transform.apply()
541
 
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
542
 
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
543
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
544
 
                         'behind_curtain')
545
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
546
 
                         'wizard-target')
 
612
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
 
613
        self.assertEqual('symlink',
 
614
                         file_kind(self.wt.abspath(ozpath(link_name1))))
 
615
        self.assertEqual(link_target2,
 
616
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
 
617
        self.assertEqual(link_target1,
 
618
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
 
619
 
 
620
    def test_symlinks(self):
 
621
        self._test_symlinks('wizard', 'wizard-target',
 
622
                            'wizard2', 'behind_curtain')
 
623
 
 
624
    def test_symlinks_unicode(self):
 
625
        self.requireFeature(tests.UnicodeFilenameFeature)
 
626
        self._test_symlinks(u'\N{Euro Sign}wizard',
 
627
                            u'wizard-targ\N{Euro Sign}t',
 
628
                            u'\N{Euro Sign}wizard2',
 
629
                            u'b\N{Euro Sign}hind_curtain')
547
630
 
548
631
    def test_unable_create_symlink(self):
549
632
        def tt_helper():
573
656
        create.apply()
574
657
        conflicts,root = self.get_transform()
575
658
        # set up duplicate entry, duplicate id
576
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
659
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
577
660
                                         'dorothy-id')
578
661
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
579
662
        oz = conflicts.trans_id_tree_file_id('oz-id')
603
686
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
604
687
        raw_conflicts = resolve_conflicts(tt)
605
688
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
606
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
689
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
607
690
                                   'dorothy', None, 'dorothy-id')
608
691
        self.assertEqual(cooked_conflicts[0], duplicate)
609
 
        duplicate_id = DuplicateID('Unversioned existing file', 
 
692
        duplicate_id = DuplicateID('Unversioned existing file',
610
693
                                   'dorothy.moved', 'dorothy', None,
611
694
                                   'dorothy-id')
612
695
        self.assertEqual(cooked_conflicts[1], duplicate_id)
620
703
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
621
704
                                               'oz-id')
622
705
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
623
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
706
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
624
707
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
625
708
        self.assertEqual(cooked_conflicts[4], deleted_parent)
626
709
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
652
735
                                         ' versioned, but has versioned'
653
736
                                         ' children.  Versioned directory.')
654
737
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
655
 
                                         ' oz/emeraldcity.  Cancelled move.')
 
738
                                         ' oz/emeraldcity. Cancelled move.')
656
739
 
657
740
    def prepare_wrong_parent_kind(self):
658
741
        tt, root = self.get_transform()
729
812
        create.apply()
730
813
        transform, root = self.get_transform()
731
814
        transform.adjust_root_path('oldroot', fun)
732
 
        new_root=transform.trans_id_tree_path('')
 
815
        new_root = transform.trans_id_tree_path('')
733
816
        transform.version_file('new-root', new_root)
734
817
        transform.apply()
735
818
 
749
832
 
750
833
    def test_set_executability_order(self):
751
834
        """Ensure that executability behaves the same, no matter what order.
752
 
        
 
835
 
753
836
        - create file and set executability simultaneously
754
837
        - create file and set executability afterward
755
838
        - unsetting the executability of a file whose executability has not been
1313
1396
        transform.cancel_creation(trans_id)
1314
1397
        transform.apply()
1315
1398
 
 
1399
    def test_create_from_tree(self):
 
1400
        tree1 = self.make_branch_and_tree('tree1')
 
1401
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
 
1402
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
 
1403
        tree2 = self.make_branch_and_tree('tree2')
 
1404
        tt = TreeTransform(tree2)
 
1405
        foo_trans_id = tt.create_path('foo', tt.root)
 
1406
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1407
        bar_trans_id = tt.create_path('bar', tt.root)
 
1408
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
 
1409
        tt.apply()
 
1410
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
 
1411
        self.assertFileEqual('baz', 'tree2/bar')
 
1412
 
 
1413
    def test_create_from_tree_bytes(self):
 
1414
        """Provided lines are used instead of tree content."""
 
1415
        tree1 = self.make_branch_and_tree('tree1')
 
1416
        self.build_tree_contents([('tree1/foo', 'bar'),])
 
1417
        tree1.add('foo', 'foo-id')
 
1418
        tree2 = self.make_branch_and_tree('tree2')
 
1419
        tt = TreeTransform(tree2)
 
1420
        foo_trans_id = tt.create_path('foo', tt.root)
 
1421
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
 
1422
        tt.apply()
 
1423
        self.assertFileEqual('qux', 'tree2/foo')
 
1424
 
 
1425
    def test_create_from_tree_symlink(self):
 
1426
        self.requireFeature(SymlinkFeature)
 
1427
        tree1 = self.make_branch_and_tree('tree1')
 
1428
        os.symlink('bar', 'tree1/foo')
 
1429
        tree1.add('foo', 'foo-id')
 
1430
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
 
1431
        foo_trans_id = tt.create_path('foo', tt.root)
 
1432
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1433
        tt.apply()
 
1434
        self.assertEqual('bar', os.readlink('tree2/foo'))
 
1435
 
1316
1436
 
1317
1437
class TransformGroup(object):
1318
1438
 
1372
1492
        # textual merge
1373
1493
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1374
1494
        # three-way text conflict
1375
 
        self.assertEqual(this.wt.get_file('b').read(), 
 
1495
        self.assertEqual(this.wt.get_file('b').read(),
1376
1496
                         conflict_text('b', 'b2'))
1377
1497
        # OTHER wins
1378
1498
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1382
1502
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1383
1503
        # No change
1384
1504
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1385
 
        # Correct correct results when THIS == OTHER 
 
1505
        # Correct correct results when THIS == OTHER
1386
1506
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1387
1507
        # Text conflict when THIS & OTHER are text and BASE is dir
1388
 
        self.assertEqual(this.wt.get_file('h').read(), 
 
1508
        self.assertEqual(this.wt.get_file('h').read(),
1389
1509
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1390
1510
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1391
1511
                         '1\n2\n3\n4\n')
1392
1512
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1393
1513
                         'h\ni\nj\nk\n')
1394
1514
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1395
 
        self.assertEqual(this.wt.get_file('i').read(), 
 
1515
        self.assertEqual(this.wt.get_file('i').read(),
1396
1516
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1397
1517
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1398
1518
                         '1\n2\n3\n4\n')
1422
1542
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1423
1543
            tg.tt.new_file('c', tg.root, 'c', 'c')
1424
1544
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1425
 
        targets = ((base, 'base-e', 'base-f', None, None), 
1426
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
 
1545
        targets = ((base, 'base-e', 'base-f', None, None),
 
1546
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1427
1547
                   (other, 'other-e', None, 'other-g', 'other-h'))
1428
1548
        for tg, e_target, f_target, g_target, h_target in targets:
1429
 
            for link, target in (('e', e_target), ('f', f_target), 
 
1549
            for link, target in (('e', e_target), ('f', f_target),
1430
1550
                                 ('g', g_target), ('h', h_target)):
1431
1551
                if target is not None:
1432
1552
                    tg.tt.new_symlink(link, tg.root, target, link)
1458
1578
        base = TransformGroup("BASE", root_id)
1459
1579
        this = TransformGroup("THIS", root_id)
1460
1580
        other = TransformGroup("OTHER", root_id)
1461
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1581
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1462
1582
                                   for t in [base, this, other]]
1463
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1583
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1464
1584
                                   for t in [base, this, other]]
1465
1585
        base.tt.new_directory('c', base_a, 'c')
1466
1586
        this.tt.new_directory('c1', this_a, 'c')
1491
1611
        base = TransformGroup("BASE", root_id)
1492
1612
        this = TransformGroup("THIS", root_id)
1493
1613
        other = TransformGroup("OTHER", root_id)
1494
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1614
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1495
1615
                                   for t in [base, this, other]]
1496
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1616
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1497
1617
                                   for t in [base, this, other]]
1498
1618
 
1499
1619
        base.tt.new_file('g', base_a, 'g', 'g')
1590
1710
        os.symlink('foo', 'target2/symlink')
1591
1711
        build_tree(source.basis_tree(), target)
1592
1712
        self.assertEqual([], target.conflicts())
1593
 
        
 
1713
 
1594
1714
    def test_directory_conflict_handling(self):
1595
1715
        """Ensure that when building trees, conflict handling is done"""
1596
1716
        source = self.make_branch_and_tree('source')
1652
1772
        target = self.make_branch_and_tree('target')
1653
1773
        self.build_tree(['target/name'])
1654
1774
        target.add('name')
1655
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1775
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1656
1776
            build_tree, source.basis_tree(), target)
1657
1777
 
1658
1778
    def test_build_tree_rename_count(self):
1802
1922
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1803
1923
        self.assertTrue(source.is_executable('file1-id'))
1804
1924
 
 
1925
    def install_rot13_content_filter(self, pattern):
 
1926
        # We could use
 
1927
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
 
1928
        # below, but that looks a bit... hard to read even if it's exactly
 
1929
        # the same thing.
 
1930
        original_registry = filters._reset_registry()
 
1931
        def restore_registry():
 
1932
            filters._reset_registry(original_registry)
 
1933
        self.addCleanup(restore_registry)
 
1934
        def rot13(chunks, context=None):
 
1935
            return [''.join(chunks).encode('rot13')]
 
1936
        rot13filter = filters.ContentFilter(rot13, rot13)
 
1937
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
 
1938
        os.mkdir(self.test_home_dir + '/.bazaar')
 
1939
        rules_filename = self.test_home_dir + '/.bazaar/rules'
 
1940
        f = open(rules_filename, 'wb')
 
1941
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
 
1942
        f.close()
 
1943
        def uninstall_rules():
 
1944
            os.remove(rules_filename)
 
1945
            rules.reset_rules()
 
1946
        self.addCleanup(uninstall_rules)
 
1947
        rules.reset_rules()
 
1948
 
 
1949
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
 
1950
        """build_tree will not hardlink files that have content filtering rules
 
1951
        applied to them (but will still hardlink other files from the same tree
 
1952
        if it can).
 
1953
        """
 
1954
        self.requireFeature(HardlinkFeature)
 
1955
        self.install_rot13_content_filter('file1')
 
1956
        source = self.create_ab_tree()
 
1957
        target = self.make_branch_and_tree('target')
 
1958
        revision_tree = source.basis_tree()
 
1959
        revision_tree.lock_read()
 
1960
        self.addCleanup(revision_tree.unlock)
 
1961
        build_tree(revision_tree, target, source, hardlink=True)
 
1962
        target.lock_read()
 
1963
        self.addCleanup(target.unlock)
 
1964
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1965
        source_stat = os.stat('source/file1')
 
1966
        target_stat = os.stat('target/file1')
 
1967
        self.assertNotEqual(source_stat, target_stat)
 
1968
        source_stat = os.stat('source/file2')
 
1969
        target_stat = os.stat('target/file2')
 
1970
        self.assertEqualStat(source_stat, target_stat)
 
1971
 
1805
1972
    def test_case_insensitive_build_tree_inventory(self):
 
1973
        if (tests.CaseInsensitiveFilesystemFeature.available()
 
1974
            or tests.CaseInsCasePresFilenameFeature.available()):
 
1975
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1806
1976
        source = self.make_branch_and_tree('source')
1807
1977
        self.build_tree(['source/file', 'source/FILE'])
1808
1978
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1816
1986
        self.assertEqual('FILE', target.id2path('upper-id'))
1817
1987
 
1818
1988
 
 
1989
class TestCommitTransform(tests.TestCaseWithTransport):
 
1990
 
 
1991
    def get_branch(self):
 
1992
        tree = self.make_branch_and_tree('tree')
 
1993
        tree.lock_write()
 
1994
        self.addCleanup(tree.unlock)
 
1995
        tree.commit('empty commit')
 
1996
        return tree.branch
 
1997
 
 
1998
    def get_branch_and_transform(self):
 
1999
        branch = self.get_branch()
 
2000
        tt = TransformPreview(branch.basis_tree())
 
2001
        self.addCleanup(tt.finalize)
 
2002
        return branch, tt
 
2003
 
 
2004
    def test_commit_wrong_basis(self):
 
2005
        branch = self.get_branch()
 
2006
        basis = branch.repository.revision_tree(
 
2007
            _mod_revision.NULL_REVISION)
 
2008
        tt = TransformPreview(basis)
 
2009
        self.addCleanup(tt.finalize)
 
2010
        e = self.assertRaises(ValueError, tt.commit, branch, '')
 
2011
        self.assertEqual('TreeTransform not based on branch basis: null:',
 
2012
                         str(e))
 
2013
 
 
2014
    def test_empy_commit(self):
 
2015
        branch, tt = self.get_branch_and_transform()
 
2016
        rev = tt.commit(branch, 'my message')
 
2017
        self.assertEqual(2, branch.revno())
 
2018
        repo = branch.repository
 
2019
        self.assertEqual('my message', repo.get_revision(rev).message)
 
2020
 
 
2021
    def test_merge_parents(self):
 
2022
        branch, tt = self.get_branch_and_transform()
 
2023
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
 
2024
        self.assertEqual(['rev1b', 'rev1c'],
 
2025
                         branch.basis_tree().get_parent_ids()[1:])
 
2026
 
 
2027
    def test_first_commit(self):
 
2028
        branch = self.make_branch('branch')
 
2029
        branch.lock_write()
 
2030
        self.addCleanup(branch.unlock)
 
2031
        tt = TransformPreview(branch.basis_tree())
 
2032
        self.addCleanup(tt.finalize)
 
2033
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
 
2034
        rev = tt.commit(branch, 'my message')
 
2035
        self.assertEqual([], branch.basis_tree().get_parent_ids())
 
2036
        self.assertNotEqual(_mod_revision.NULL_REVISION,
 
2037
                            branch.last_revision())
 
2038
 
 
2039
    def test_first_commit_with_merge_parents(self):
 
2040
        branch = self.make_branch('branch')
 
2041
        branch.lock_write()
 
2042
        self.addCleanup(branch.unlock)
 
2043
        tt = TransformPreview(branch.basis_tree())
 
2044
        self.addCleanup(tt.finalize)
 
2045
        e = self.assertRaises(ValueError, tt.commit, branch,
 
2046
                          'my message', ['rev1b-id'])
 
2047
        self.assertEqual('Cannot supply merge parents for first commit.',
 
2048
                         str(e))
 
2049
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
 
2050
 
 
2051
    def test_add_files(self):
 
2052
        branch, tt = self.get_branch_and_transform()
 
2053
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2054
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
 
2055
        if SymlinkFeature.available():
 
2056
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
 
2057
        rev = tt.commit(branch, 'message')
 
2058
        tree = branch.basis_tree()
 
2059
        self.assertEqual('file', tree.id2path('file-id'))
 
2060
        self.assertEqual('contents', tree.get_file_text('file-id'))
 
2061
        self.assertEqual('dir', tree.id2path('dir-id'))
 
2062
        if SymlinkFeature.available():
 
2063
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
 
2064
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
2065
 
 
2066
    def test_add_unversioned(self):
 
2067
        branch, tt = self.get_branch_and_transform()
 
2068
        tt.new_file('file', tt.root, 'contents')
 
2069
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
 
2070
                          'message', strict=True)
 
2071
 
 
2072
    def test_modify_strict(self):
 
2073
        branch, tt = self.get_branch_and_transform()
 
2074
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2075
        tt.commit(branch, 'message', strict=True)
 
2076
        tt = TransformPreview(branch.basis_tree())
 
2077
        self.addCleanup(tt.finalize)
 
2078
        trans_id = tt.trans_id_file_id('file-id')
 
2079
        tt.delete_contents(trans_id)
 
2080
        tt.create_file('contents', trans_id)
 
2081
        tt.commit(branch, 'message', strict=True)
 
2082
 
 
2083
    def test_commit_malformed(self):
 
2084
        """Committing a malformed transform should raise an exception.
 
2085
 
 
2086
        In this case, we are adding a file without adding its parent.
 
2087
        """
 
2088
        branch, tt = self.get_branch_and_transform()
 
2089
        parent_id = tt.trans_id_file_id('parent-id')
 
2090
        tt.new_file('file', parent_id, 'contents', 'file-id')
 
2091
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
 
2092
                          'message')
 
2093
 
 
2094
 
1819
2095
class MockTransform(object):
1820
2096
 
1821
2097
    def has_named_child(self, by_parent, parent_id, name):
1976
2252
        resolve_conflicts(tt)
1977
2253
 
1978
2254
 
 
2255
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
 
2256
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
 
2257
                  (False, False))
 
2258
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
 
2259
              ('', ''), ('directory', 'directory'), (False, None))
 
2260
 
 
2261
 
1979
2262
class TestTransformPreview(tests.TestCaseWithTransport):
1980
2263
 
1981
2264
    def create_tree(self):
1982
2265
        tree = self.make_branch_and_tree('.')
1983
2266
        self.build_tree_contents([('a', 'content 1')])
 
2267
        tree.set_root_id('TREE_ROOT')
1984
2268
        tree.add('a', 'a-id')
1985
2269
        tree.commit('rev1', rev_id='rev1')
1986
2270
        return tree.branch.repository.revision_tree('rev1')
2053
2337
                          (False, False))],
2054
2338
                          list(preview_tree.iter_changes(revision_tree)))
2055
2339
 
2056
 
    def test_include_unchanged_value_error(self):
 
2340
    def test_include_unchanged_succeeds(self):
2057
2341
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2058
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2059
 
                              revision_tree, include_unchanged=True)
2060
 
        self.assertEqual('include_unchanged is not supported', str(e))
 
2342
        changes = preview_tree.iter_changes(revision_tree,
 
2343
                                            include_unchanged=True)
 
2344
        root = revision_tree.inventory.root.file_id
 
2345
 
 
2346
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2061
2347
 
2062
2348
    def test_specific_files(self):
2063
2349
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2064
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2065
 
                              revision_tree, specific_files=['pete'])
2066
 
        self.assertEqual('specific_files is not supported', str(e))
 
2350
        changes = preview_tree.iter_changes(revision_tree,
 
2351
                                            specific_files=[''])
 
2352
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2067
2353
 
2068
 
    def test_want_unversioned_value_error(self):
 
2354
    def test_want_unversioned(self):
2069
2355
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2070
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2071
 
                              revision_tree, want_unversioned=True)
2072
 
        self.assertEqual('want_unversioned is not supported', str(e))
 
2356
        changes = preview_tree.iter_changes(revision_tree,
 
2357
                                            want_unversioned=True)
 
2358
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2073
2359
 
2074
2360
    def test_ignore_extra_trees_no_specific_files(self):
2075
2361
        # extra_trees is harmless without specific_files, so we'll silently
2085
2371
    def test_ignore_pb(self):
2086
2372
        # pb could be supported, but TT.iter_changes doesn't support it.
2087
2373
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2088
 
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
 
2374
        preview_tree.iter_changes(revision_tree)
2089
2375
 
2090
2376
    def test_kind(self):
2091
2377
        revision_tree = self.create_tree()
2106
2392
        self.assertEqual(os.stat(limbo_path).st_mtime,
2107
2393
                         preview_tree.get_file_mtime('file-id'))
2108
2394
 
 
2395
    def test_get_file_mtime_renamed(self):
 
2396
        work_tree = self.make_branch_and_tree('tree')
 
2397
        self.build_tree(['tree/file'])
 
2398
        work_tree.add('file', 'file-id')
 
2399
        preview = TransformPreview(work_tree)
 
2400
        self.addCleanup(preview.finalize)
 
2401
        file_trans_id = preview.trans_id_tree_file_id('file-id')
 
2402
        preview.adjust_path('renamed', preview.root, file_trans_id)
 
2403
        preview_tree = preview.get_preview_tree()
 
2404
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
 
2405
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
 
2406
 
2109
2407
    def test_get_file(self):
2110
2408
        preview = self.get_empty_preview()
2111
2409
        preview.new_file('file', preview.root, 'contents', 'file-id')
2259
2557
        self.assertEqual(('missing', None, None, None), summary)
2260
2558
 
2261
2559
    def test_file_content_summary_executable(self):
2262
 
        if not osutils.supports_executable():
2263
 
            raise TestNotApplicable()
2264
2560
        preview = self.get_empty_preview()
2265
2561
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2266
2562
        preview.set_executability(True, path_id)
2275
2571
        self.assertIs(None, summary[3])
2276
2572
 
2277
2573
    def test_change_executability(self):
2278
 
        if not osutils.supports_executable():
2279
 
            raise TestNotApplicable()
2280
2574
        tree = self.make_branch_and_tree('tree')
2281
2575
        self.build_tree(['tree/path'])
2282
2576
        tree.add('path')
2296
2590
        # size must be known
2297
2591
        self.assertEqual(len('contents'), summary[1])
2298
2592
        # not executable
2299
 
        if osutils.supports_executable():
2300
 
            self.assertEqual(False, summary[2])
2301
 
        else:
2302
 
            self.assertEqual(None, summary[2])
 
2593
        self.assertEqual(False, summary[2])
2303
2594
        # will not have hash (not cheap to determine)
2304
2595
        self.assertIs(None, summary[3])
2305
2596
 
2446
2737
 
2447
2738
    def test_walkdirs(self):
2448
2739
        preview = self.get_empty_preview()
2449
 
        preview.version_file('tree-root', preview.root)
 
2740
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
2741
        # FIXME: new_directory should mark root.
 
2742
        preview.fixup_new_roots()
2450
2743
        preview_tree = preview.get_preview_tree()
2451
2744
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2452
2745
                                         'a-id')
2483
2776
        self.addCleanup(work_tree.unlock)
2484
2777
        preview = TransformPreview(work_tree)
2485
2778
        self.addCleanup(preview.finalize)
2486
 
        preview_tree = preview.get_preview_tree()
2487
2779
        file_trans_id = preview.trans_id_file_id('file-id')
2488
2780
        preview.delete_contents(file_trans_id)
2489
2781
        preview.create_file('a\nb\n', file_trans_id)
2490
 
        pb = progress.DummyProgress()
2491
 
        merger = Merger.from_revision_ids(pb, preview_tree,
 
2782
        preview_tree = preview.get_preview_tree()
 
2783
        merger = Merger.from_revision_ids(None, preview_tree,
2492
2784
                                          child_tree.branch.last_revision(),
2493
2785
                                          other_branch=child_tree.branch,
2494
2786
                                          tree_branch=work_tree.branch)
2497
2789
        self.addCleanup(tt.finalize)
2498
2790
        final_tree = tt.get_preview_tree()
2499
2791
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
 
2792
 
 
2793
    def test_merge_preview_into_workingtree(self):
 
2794
        tree = self.make_branch_and_tree('tree')
 
2795
        tree.set_root_id('TREE_ROOT')
 
2796
        tt = TransformPreview(tree)
 
2797
        self.addCleanup(tt.finalize)
 
2798
        tt.new_file('name', tt.root, 'content', 'file-id')
 
2799
        tree2 = self.make_branch_and_tree('tree2')
 
2800
        tree2.set_root_id('TREE_ROOT')
 
2801
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2802
                                         None, tree.basis_tree())
 
2803
        merger.merge_type = Merge3Merger
 
2804
        merger.do_merge()
 
2805
 
 
2806
    def test_merge_preview_into_workingtree_handles_conflicts(self):
 
2807
        tree = self.make_branch_and_tree('tree')
 
2808
        self.build_tree_contents([('tree/foo', 'bar')])
 
2809
        tree.add('foo', 'foo-id')
 
2810
        tree.commit('foo')
 
2811
        tt = TransformPreview(tree)
 
2812
        self.addCleanup(tt.finalize)
 
2813
        trans_id = tt.trans_id_file_id('foo-id')
 
2814
        tt.delete_contents(trans_id)
 
2815
        tt.create_file('baz', trans_id)
 
2816
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
2817
        self.build_tree_contents([('tree2/foo', 'qux')])
 
2818
        pb = None
 
2819
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2820
                                         pb, tree.basis_tree())
 
2821
        merger.merge_type = Merge3Merger
 
2822
        merger.do_merge()
 
2823
 
 
2824
    def test_is_executable(self):
 
2825
        tree = self.make_branch_and_tree('tree')
 
2826
        preview = TransformPreview(tree)
 
2827
        self.addCleanup(preview.finalize)
 
2828
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
 
2829
        preview_tree = preview.get_preview_tree()
 
2830
        self.assertEqual(False, preview_tree.is_executable('baz-id',
 
2831
                                                           'tree/foo'))
 
2832
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
 
2833
 
 
2834
    def test_commit_preview_tree(self):
 
2835
        tree = self.make_branch_and_tree('tree')
 
2836
        rev_id = tree.commit('rev1')
 
2837
        tree.branch.lock_write()
 
2838
        self.addCleanup(tree.branch.unlock)
 
2839
        tt = TransformPreview(tree)
 
2840
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
2841
        self.addCleanup(tt.finalize)
 
2842
        preview = tt.get_preview_tree()
 
2843
        preview.set_parent_ids([rev_id])
 
2844
        builder = tree.branch.get_commit_builder([rev_id])
 
2845
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
2846
        builder.finish_inventory()
 
2847
        rev2_id = builder.commit('rev2')
 
2848
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
2849
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
2850
 
 
2851
    def test_ascii_limbo_paths(self):
 
2852
        self.requireFeature(tests.UnicodeFilenameFeature)
 
2853
        branch = self.make_branch('any')
 
2854
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
 
2855
        tt = TransformPreview(tree)
 
2856
        self.addCleanup(tt.finalize)
 
2857
        foo_id = tt.new_directory('', ROOT_PARENT)
 
2858
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
 
2859
        limbo_path = tt._limbo_name(bar_id)
 
2860
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
 
2861
 
 
2862
 
 
2863
class FakeSerializer(object):
 
2864
    """Serializer implementation that simply returns the input.
 
2865
 
 
2866
    The input is returned in the order used by pack.ContainerPushParser.
 
2867
    """
 
2868
    @staticmethod
 
2869
    def bytes_record(bytes, names):
 
2870
        return names, bytes
 
2871
 
 
2872
 
 
2873
class TestSerializeTransform(tests.TestCaseWithTransport):
 
2874
 
 
2875
    _test_needs_features = [tests.UnicodeFilenameFeature]
 
2876
 
 
2877
    def get_preview(self, tree=None):
 
2878
        if tree is None:
 
2879
            tree = self.make_branch_and_tree('tree')
 
2880
        tt = TransformPreview(tree)
 
2881
        self.addCleanup(tt.finalize)
 
2882
        return tt
 
2883
 
 
2884
    def assertSerializesTo(self, expected, tt):
 
2885
        records = list(tt.serialize(FakeSerializer()))
 
2886
        self.assertEqual(expected, records)
 
2887
 
 
2888
    @staticmethod
 
2889
    def default_attribs():
 
2890
        return {
 
2891
            '_id_number': 1,
 
2892
            '_new_name': {},
 
2893
            '_new_parent': {},
 
2894
            '_new_executability': {},
 
2895
            '_new_id': {},
 
2896
            '_tree_path_ids': {'': 'new-0'},
 
2897
            '_removed_id': [],
 
2898
            '_removed_contents': [],
 
2899
            '_non_present_ids': {},
 
2900
            }
 
2901
 
 
2902
    def make_records(self, attribs, contents):
 
2903
        records = [
 
2904
            (((('attribs'),),), bencode.bencode(attribs))]
 
2905
        records.extend([(((n, k),), c) for n, k, c in contents])
 
2906
        return records
 
2907
 
 
2908
    def creation_records(self):
 
2909
        attribs = self.default_attribs()
 
2910
        attribs['_id_number'] = 3
 
2911
        attribs['_new_name'] = {
 
2912
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
 
2913
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
 
2914
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
 
2915
        attribs['_new_executability'] = {'new-1': 1}
 
2916
        contents = [
 
2917
            ('new-1', 'file', 'i 1\nbar\n'),
 
2918
            ('new-2', 'directory', ''),
 
2919
            ]
 
2920
        return self.make_records(attribs, contents)
 
2921
 
 
2922
    def test_serialize_creation(self):
 
2923
        tt = self.get_preview()
 
2924
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
 
2925
        tt.new_directory('qux', tt.root, 'quxx')
 
2926
        self.assertSerializesTo(self.creation_records(), tt)
 
2927
 
 
2928
    def test_deserialize_creation(self):
 
2929
        tt = self.get_preview()
 
2930
        tt.deserialize(iter(self.creation_records()))
 
2931
        self.assertEqual(3, tt._id_number)
 
2932
        self.assertEqual({'new-1': u'foo\u1234',
 
2933
                          'new-2': 'qux'}, tt._new_name)
 
2934
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
 
2935
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
 
2936
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
 
2937
        self.assertEqual({'new-1': True}, tt._new_executability)
 
2938
        self.assertEqual({'new-1': 'file',
 
2939
                          'new-2': 'directory'}, tt._new_contents)
 
2940
        foo_limbo = open(tt._limbo_name('new-1'), 'rb')
 
2941
        try:
 
2942
            foo_content = foo_limbo.read()
 
2943
        finally:
 
2944
            foo_limbo.close()
 
2945
        self.assertEqual('bar', foo_content)
 
2946
 
 
2947
    def symlink_creation_records(self):
 
2948
        attribs = self.default_attribs()
 
2949
        attribs['_id_number'] = 2
 
2950
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
 
2951
        attribs['_new_parent'] = {'new-1': 'new-0'}
 
2952
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
 
2953
        return self.make_records(attribs, contents)
 
2954
 
 
2955
    def test_serialize_symlink_creation(self):
 
2956
        self.requireFeature(tests.SymlinkFeature)
 
2957
        tt = self.get_preview()
 
2958
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
 
2959
        self.assertSerializesTo(self.symlink_creation_records(), tt)
 
2960
 
 
2961
    def test_deserialize_symlink_creation(self):
 
2962
        self.requireFeature(tests.SymlinkFeature)
 
2963
        tt = self.get_preview()
 
2964
        tt.deserialize(iter(self.symlink_creation_records()))
 
2965
        abspath = tt._limbo_name('new-1')
 
2966
        foo_content = osutils.readlink(abspath)
 
2967
        self.assertEqual(u'bar\u1234', foo_content)
 
2968
 
 
2969
    def make_destruction_preview(self):
 
2970
        tree = self.make_branch_and_tree('.')
 
2971
        self.build_tree([u'foo\u1234', 'bar'])
 
2972
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
 
2973
        return self.get_preview(tree)
 
2974
 
 
2975
    def destruction_records(self):
 
2976
        attribs = self.default_attribs()
 
2977
        attribs['_id_number'] = 3
 
2978
        attribs['_removed_id'] = ['new-1']
 
2979
        attribs['_removed_contents'] = ['new-2']
 
2980
        attribs['_tree_path_ids'] = {
 
2981
            '': 'new-0',
 
2982
            u'foo\u1234'.encode('utf-8'): 'new-1',
 
2983
            'bar': 'new-2',
 
2984
            }
 
2985
        return self.make_records(attribs, [])
 
2986
 
 
2987
    def test_serialize_destruction(self):
 
2988
        tt = self.make_destruction_preview()
 
2989
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
 
2990
        tt.unversion_file(foo_trans_id)
 
2991
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
 
2992
        tt.delete_contents(bar_trans_id)
 
2993
        self.assertSerializesTo(self.destruction_records(), tt)
 
2994
 
 
2995
    def test_deserialize_destruction(self):
 
2996
        tt = self.make_destruction_preview()
 
2997
        tt.deserialize(iter(self.destruction_records()))
 
2998
        self.assertEqual({u'foo\u1234': 'new-1',
 
2999
                          'bar': 'new-2',
 
3000
                          '': tt.root}, tt._tree_path_ids)
 
3001
        self.assertEqual({'new-1': u'foo\u1234',
 
3002
                          'new-2': 'bar',
 
3003
                          tt.root: ''}, tt._tree_id_paths)
 
3004
        self.assertEqual(set(['new-1']), tt._removed_id)
 
3005
        self.assertEqual(set(['new-2']), tt._removed_contents)
 
3006
 
 
3007
    def missing_records(self):
 
3008
        attribs = self.default_attribs()
 
3009
        attribs['_id_number'] = 2
 
3010
        attribs['_non_present_ids'] = {
 
3011
            'boo': 'new-1',}
 
3012
        return self.make_records(attribs, [])
 
3013
 
 
3014
    def test_serialize_missing(self):
 
3015
        tt = self.get_preview()
 
3016
        boo_trans_id = tt.trans_id_file_id('boo')
 
3017
        self.assertSerializesTo(self.missing_records(), tt)
 
3018
 
 
3019
    def test_deserialize_missing(self):
 
3020
        tt = self.get_preview()
 
3021
        tt.deserialize(iter(self.missing_records()))
 
3022
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
 
3023
 
 
3024
    def make_modification_preview(self):
 
3025
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
3026
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3027
        tree = self.make_branch_and_tree('tree')
 
3028
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
3029
        tree.add('file', 'file-id')
 
3030
        return self.get_preview(tree), LINES_TWO
 
3031
 
 
3032
    def modification_records(self):
 
3033
        attribs = self.default_attribs()
 
3034
        attribs['_id_number'] = 2
 
3035
        attribs['_tree_path_ids'] = {
 
3036
            'file': 'new-1',
 
3037
            '': 'new-0',}
 
3038
        attribs['_removed_contents'] = ['new-1']
 
3039
        contents = [('new-1', 'file',
 
3040
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
 
3041
        return self.make_records(attribs, contents)
 
3042
 
 
3043
    def test_serialize_modification(self):
 
3044
        tt, LINES = self.make_modification_preview()
 
3045
        trans_id = tt.trans_id_file_id('file-id')
 
3046
        tt.delete_contents(trans_id)
 
3047
        tt.create_file(LINES, trans_id)
 
3048
        self.assertSerializesTo(self.modification_records(), tt)
 
3049
 
 
3050
    def test_deserialize_modification(self):
 
3051
        tt, LINES = self.make_modification_preview()
 
3052
        tt.deserialize(iter(self.modification_records()))
 
3053
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3054
 
 
3055
    def make_kind_change_preview(self):
 
3056
        LINES = 'a\nb\nc\nd\n'
 
3057
        tree = self.make_branch_and_tree('tree')
 
3058
        self.build_tree(['tree/foo/'])
 
3059
        tree.add('foo', 'foo-id')
 
3060
        return self.get_preview(tree), LINES
 
3061
 
 
3062
    def kind_change_records(self):
 
3063
        attribs = self.default_attribs()
 
3064
        attribs['_id_number'] = 2
 
3065
        attribs['_tree_path_ids'] = {
 
3066
            'foo': 'new-1',
 
3067
            '': 'new-0',}
 
3068
        attribs['_removed_contents'] = ['new-1']
 
3069
        contents = [('new-1', 'file',
 
3070
                     'i 4\na\nb\nc\nd\n\n')]
 
3071
        return self.make_records(attribs, contents)
 
3072
 
 
3073
    def test_serialize_kind_change(self):
 
3074
        tt, LINES = self.make_kind_change_preview()
 
3075
        trans_id = tt.trans_id_file_id('foo-id')
 
3076
        tt.delete_contents(trans_id)
 
3077
        tt.create_file(LINES, trans_id)
 
3078
        self.assertSerializesTo(self.kind_change_records(), tt)
 
3079
 
 
3080
    def test_deserialize_kind_change(self):
 
3081
        tt, LINES = self.make_kind_change_preview()
 
3082
        tt.deserialize(iter(self.kind_change_records()))
 
3083
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3084
 
 
3085
    def make_add_contents_preview(self):
 
3086
        LINES = 'a\nb\nc\nd\n'
 
3087
        tree = self.make_branch_and_tree('tree')
 
3088
        self.build_tree(['tree/foo'])
 
3089
        tree.add('foo')
 
3090
        os.unlink('tree/foo')
 
3091
        return self.get_preview(tree), LINES
 
3092
 
 
3093
    def add_contents_records(self):
 
3094
        attribs = self.default_attribs()
 
3095
        attribs['_id_number'] = 2
 
3096
        attribs['_tree_path_ids'] = {
 
3097
            'foo': 'new-1',
 
3098
            '': 'new-0',}
 
3099
        contents = [('new-1', 'file',
 
3100
                     'i 4\na\nb\nc\nd\n\n')]
 
3101
        return self.make_records(attribs, contents)
 
3102
 
 
3103
    def test_serialize_add_contents(self):
 
3104
        tt, LINES = self.make_add_contents_preview()
 
3105
        trans_id = tt.trans_id_tree_path('foo')
 
3106
        tt.create_file(LINES, trans_id)
 
3107
        self.assertSerializesTo(self.add_contents_records(), tt)
 
3108
 
 
3109
    def test_deserialize_add_contents(self):
 
3110
        tt, LINES = self.make_add_contents_preview()
 
3111
        tt.deserialize(iter(self.add_contents_records()))
 
3112
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3113
 
 
3114
    def test_get_parents_lines(self):
 
3115
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
3116
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3117
        tree = self.make_branch_and_tree('tree')
 
3118
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
3119
        tree.add('file', 'file-id')
 
3120
        tt = self.get_preview(tree)
 
3121
        trans_id = tt.trans_id_tree_path('file')
 
3122
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
 
3123
            tt._get_parents_lines(trans_id))
 
3124
 
 
3125
    def test_get_parents_texts(self):
 
3126
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
3127
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3128
        tree = self.make_branch_and_tree('tree')
 
3129
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
3130
        tree.add('file', 'file-id')
 
3131
        tt = self.get_preview(tree)
 
3132
        trans_id = tt.trans_id_tree_path('file')
 
3133
        self.assertEqual((LINES_ONE,),
 
3134
            tt._get_parents_texts(trans_id))