~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

Replace remaining to unittest.TestResult methods with super

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
 
    progress,
27
28
    revision as _mod_revision,
28
 
    symbol_versioning,
 
29
    rules,
29
30
    tests,
30
31
    urlutils,
31
32
    )
32
33
from bzrlib.bzrdir import BzrDir
33
 
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
34
 
                              UnversionedParent, ParentLoop, DeletingParent,
35
 
                              NonDirectoryParent)
 
34
from bzrlib.conflicts import (
 
35
    DeletingParent,
 
36
    DuplicateEntry,
 
37
    DuplicateID,
 
38
    MissingParent,
 
39
    NonDirectoryParent,
 
40
    ParentLoop,
 
41
    UnversionedParent,
 
42
)
36
43
from bzrlib.diff import show_diff_trees
37
 
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, CantMoveRoot, 
39
 
                           PathsNotVersionedError, ExistingLimbo,
40
 
                           ExistingPendingDeletion, ImmortalLimbo,
41
 
                           ImmortalPendingDeletion, LockError)
42
 
from bzrlib.osutils import file_kind, pathjoin
 
44
from bzrlib.errors import (
 
45
    DuplicateKey,
 
46
    ExistingLimbo,
 
47
    ExistingPendingDeletion,
 
48
    ImmortalLimbo,
 
49
    ImmortalPendingDeletion,
 
50
    LockError,
 
51
    MalformedTransform,
 
52
    NoSuchFile,
 
53
    ReusingTransform,
 
54
)
 
55
from bzrlib.osutils import (
 
56
    file_kind,
 
57
    pathjoin,
 
58
)
43
59
from bzrlib.merge import Merge3Merger, Merger
44
60
from bzrlib.tests import (
45
61
    HardlinkFeature,
47
63
    TestCase,
48
64
    TestCaseInTempDir,
49
65
    TestSkipped,
50
 
    )
51
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
52
 
                              resolve_conflicts, cook_conflicts, 
53
 
                              build_tree, get_backup_name,
54
 
                              _FileMover, resolve_checkout,
55
 
                              TransformPreview, create_from_tree)
56
 
from bzrlib.util import bencode
 
66
)
 
67
from bzrlib.transform import (
 
68
    build_tree,
 
69
    create_from_tree,
 
70
    cook_conflicts,
 
71
    _FileMover,
 
72
    FinalPaths,
 
73
    get_backup_name,
 
74
    resolve_conflicts,
 
75
    resolve_checkout,
 
76
    ROOT_PARENT,
 
77
    TransformPreview,
 
78
    TreeTransform,
 
79
)
57
80
 
58
81
 
59
82
class TestTreeTransform(tests.TestCaseWithTransport):
129
152
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
130
153
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
131
154
        self.assertEqual(len(modified_paths), 3)
132
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
155
        tree_mod_paths = [self.wt.id2abspath(f) for f in
133
156
                          ('ozzie', 'my_pretties', 'my_pretties2')]
134
157
        self.assertSubset(tree_mod_paths, modified_paths)
135
158
        # is it safe to finalize repeatedly?
136
159
        transform.finalize()
137
160
        transform.finalize()
138
161
 
 
162
    def test_create_files_same_timestamp(self):
 
163
        transform, root = self.get_transform()
 
164
        self.wt.lock_tree_write()
 
165
        self.addCleanup(self.wt.unlock)
 
166
        # Roll back the clock, so that we know everything is being set to the
 
167
        # exact time
 
168
        transform._creation_mtime = creation_mtime = time.time() - 20.0
 
169
        transform.create_file('content-one',
 
170
                              transform.create_path('one', root))
 
171
        time.sleep(1) # *ugly*
 
172
        transform.create_file('content-two',
 
173
                              transform.create_path('two', root))
 
174
        transform.apply()
 
175
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
 
176
        fo.close()
 
177
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
 
178
        fo.close()
 
179
        # We only guarantee 2s resolution
 
180
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
 
181
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
 
182
        # But if we have more than that, all files should get the same result
 
183
        self.assertEqual(st1.st_mtime, st2.st_mtime)
 
184
 
 
185
    def test_change_root_id(self):
 
186
        transform, root = self.get_transform()
 
187
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
 
188
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
189
        transform.delete_contents(root)
 
190
        transform.unversion_file(root)
 
191
        transform.fixup_new_roots()
 
192
        transform.apply()
 
193
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
194
 
 
195
    def test_change_root_id_add_files(self):
 
196
        transform, root = self.get_transform()
 
197
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
 
198
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
199
        transform.new_file('file', new_trans_id, ['new-contents\n'],
 
200
                           'new-file-id')
 
201
        transform.delete_contents(root)
 
202
        transform.unversion_file(root)
 
203
        transform.fixup_new_roots()
 
204
        transform.apply()
 
205
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
206
        self.assertEqual('new-file-id', self.wt.path2id('file'))
 
207
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
 
208
 
 
209
    def test_add_two_roots(self):
 
210
        transform, root = self.get_transform()
 
211
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
212
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
 
213
        self.assertRaises(ValueError, transform.fixup_new_roots)
 
214
 
139
215
    def test_hardlink(self):
140
216
        self.requireFeature(HardlinkFeature)
141
217
        transform, root = self.get_transform()
155
231
        transform, root = self.get_transform()
156
232
        self.wt.lock_tree_write()
157
233
        self.addCleanup(self.wt.unlock)
158
 
        trans_id = transform.new_file('name', root, 'contents', 
 
234
        trans_id = transform.new_file('name', root, 'contents',
159
235
                                      'my_pretties', True)
160
236
        oz = transform.new_directory('oz', root, 'oz-id')
161
237
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
162
 
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
238
        toto = transform.new_file('toto', dorothy, 'toto-contents',
163
239
                                  'toto-id', False)
164
240
 
165
241
        self.assertEqual(len(transform.find_conflicts()), 0)
189
265
 
190
266
    def test_conflicts(self):
191
267
        transform, root = self.get_transform()
192
 
        trans_id = transform.new_file('name', root, 'contents', 
 
268
        trans_id = transform.new_file('name', root, 'contents',
193
269
                                      'my_pretties')
194
270
        self.assertEqual(len(transform.find_conflicts()), 0)
195
271
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
196
 
        self.assertEqual(transform.find_conflicts(), 
 
272
        self.assertEqual(transform.find_conflicts(),
197
273
                         [('duplicate', trans_id, trans_id2, 'name')])
198
274
        self.assertRaises(MalformedTransform, transform.apply)
199
275
        transform.adjust_path('name', trans_id, trans_id2)
200
 
        self.assertEqual(transform.find_conflicts(), 
 
276
        self.assertEqual(transform.find_conflicts(),
201
277
                         [('non-directory parent', trans_id)])
202
278
        tinman_id = transform.trans_id_tree_path('tinman')
203
279
        transform.adjust_path('name', tinman_id, trans_id2)
204
 
        self.assertEqual(transform.find_conflicts(), 
205
 
                         [('unversioned parent', tinman_id), 
 
280
        self.assertEqual(transform.find_conflicts(),
 
281
                         [('unversioned parent', tinman_id),
206
282
                          ('missing parent', tinman_id)])
207
283
        lion_id = transform.create_path('lion', root)
208
 
        self.assertEqual(transform.find_conflicts(), 
209
 
                         [('unversioned parent', tinman_id), 
 
284
        self.assertEqual(transform.find_conflicts(),
 
285
                         [('unversioned parent', tinman_id),
210
286
                          ('missing parent', tinman_id)])
211
287
        transform.adjust_path('name', lion_id, trans_id2)
212
 
        self.assertEqual(transform.find_conflicts(), 
 
288
        self.assertEqual(transform.find_conflicts(),
213
289
                         [('unversioned parent', lion_id),
214
290
                          ('missing parent', lion_id)])
215
291
        transform.version_file("Courage", lion_id)
216
 
        self.assertEqual(transform.find_conflicts(), 
217
 
                         [('missing parent', lion_id), 
 
292
        self.assertEqual(transform.find_conflicts(),
 
293
                         [('missing parent', lion_id),
218
294
                          ('versioning no contents', lion_id)])
219
295
        transform.adjust_path('name2', root, trans_id2)
220
 
        self.assertEqual(transform.find_conflicts(), 
 
296
        self.assertEqual(transform.find_conflicts(),
221
297
                         [('versioning no contents', lion_id)])
222
298
        transform.create_file('Contents, okay?', lion_id)
223
299
        transform.adjust_path('name2', trans_id2, trans_id2)
224
 
        self.assertEqual(transform.find_conflicts(), 
225
 
                         [('parent loop', trans_id2), 
 
300
        self.assertEqual(transform.find_conflicts(),
 
301
                         [('parent loop', trans_id2),
226
302
                          ('non-directory parent', trans_id2)])
227
303
        transform.adjust_path('name2', root, trans_id2)
228
304
        oz_id = transform.new_directory('oz', root)
229
305
        transform.set_executability(True, oz_id)
230
 
        self.assertEqual(transform.find_conflicts(), 
 
306
        self.assertEqual(transform.find_conflicts(),
231
307
                         [('unversioned executability', oz_id)])
232
308
        transform.version_file('oz-id', oz_id)
233
 
        self.assertEqual(transform.find_conflicts(), 
 
309
        self.assertEqual(transform.find_conflicts(),
234
310
                         [('non-file executability', oz_id)])
235
311
        transform.set_executability(None, oz_id)
236
312
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
245
321
        self.assert_('oz/tip' in transform2._tree_path_ids)
246
322
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
247
323
        self.assertEqual(len(result), 2)
248
 
        self.assertEqual((result[0][0], result[0][1]), 
 
324
        self.assertEqual((result[0][0], result[0][1]),
249
325
                         ('duplicate', newtip))
250
 
        self.assertEqual((result[1][0], result[1][2]), 
 
326
        self.assertEqual((result[1][0], result[1][2]),
251
327
                         ('duplicate id', newtip))
252
328
        transform2.finalize()
253
329
        transform3 = TreeTransform(self.wt)
254
330
        self.addCleanup(transform3.finalize)
255
331
        oz_id = transform3.trans_id_tree_file_id('oz-id')
256
332
        transform3.delete_contents(oz_id)
257
 
        self.assertEqual(transform3.find_conflicts(), 
 
333
        self.assertEqual(transform3.find_conflicts(),
258
334
                         [('missing parent', oz_id)])
259
335
        root_id = transform3.root
260
336
        tip_id = transform3.trans_id_tree_file_id('tip-id')
369
445
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
370
446
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
371
447
 
 
448
    def test_adjust_path_updates_child_limbo_names(self):
 
449
        tree = self.make_branch_and_tree('tree')
 
450
        transform = TreeTransform(tree)
 
451
        self.addCleanup(transform.finalize)
 
452
        foo_id = transform.new_directory('foo', transform.root)
 
453
        bar_id = transform.new_directory('bar', foo_id)
 
454
        baz_id = transform.new_directory('baz', bar_id)
 
455
        qux_id = transform.new_directory('qux', baz_id)
 
456
        transform.adjust_path('quxx', foo_id, bar_id)
 
457
        self.assertStartsWith(transform._limbo_name(qux_id),
 
458
                              transform._limbo_name(bar_id))
 
459
 
372
460
    def test_add_del(self):
373
461
        start, root = self.get_transform()
374
462
        start.new_directory('a', root, 'a')
387
475
        self.addCleanup(unversion.finalize)
388
476
        parent = unversion.trans_id_tree_path('parent')
389
477
        unversion.unversion_file(parent)
390
 
        self.assertEqual(unversion.find_conflicts(), 
 
478
        self.assertEqual(unversion.find_conflicts(),
391
479
                         [('unversioned parent', parent_id)])
392
480
        file_id = unversion.trans_id_tree_file_id('child-id')
393
481
        unversion.unversion_file(file_id)
413
501
        mangle_tree.adjust_path('name2', root, name1)
414
502
        mangle_tree.adjust_path('name1', root, name2)
415
503
 
416
 
        #tests for deleting parent directories 
 
504
        #tests for deleting parent directories
417
505
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
418
506
        mangle_tree.delete_contents(ddir)
419
507
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
448
536
        create_tree,root = self.get_transform()
449
537
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
450
538
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
451
 
        create_tree.apply()        
 
539
        create_tree.apply()
452
540
        mangle_tree,root = self.get_transform()
453
541
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
454
542
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
462
550
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
463
551
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
464
552
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
465
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
553
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
466
554
                             'test_too_much-id')
467
 
        create_tree.apply()        
 
555
        create_tree.apply()
468
556
        mangle_tree,root = self.get_transform()
469
557
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
470
558
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
471
559
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
472
560
        mangle_tree.adjust_path('selftest', bzrlib, tests)
473
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
561
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
474
562
        mangle_tree.set_executability(True, test_too_much)
475
563
        mangle_tree.apply()
476
564
 
477
565
    def test_both_rename3(self):
478
566
        create_tree,root = self.get_transform()
479
567
        tests = create_tree.new_directory('tests', root, 'tests-id')
480
 
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
568
        create_tree.new_file('test_too_much.py', tests, 'hello1',
481
569
                             'test_too_much-id')
482
 
        create_tree.apply()        
 
570
        create_tree.apply()
483
571
        mangle_tree,root = self.get_transform()
484
572
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
485
573
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
486
574
        mangle_tree.adjust_path('selftest', root, tests)
487
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
575
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
488
576
        mangle_tree.set_executability(True, test_too_much)
489
577
        mangle_tree.apply()
490
578
 
503
591
        newdir = move_id.new_directory('dir', root, 'newdir')
504
592
        move_id.adjust_path('name2', newdir, name1)
505
593
        move_id.apply()
506
 
        
 
594
 
507
595
    def test_replace_dangling_ie(self):
508
596
        create_tree, root = self.get_transform()
509
597
        # prepare tree
525
613
        resolve_conflicts(replace)
526
614
        replace.apply()
527
615
 
528
 
    def test_symlinks(self):
 
616
    def _test_symlinks(self, link_name1,link_target1,
 
617
                       link_name2, link_target2):
 
618
 
 
619
        def ozpath(p): return 'oz/' + p
 
620
 
529
621
        self.requireFeature(SymlinkFeature)
530
 
        transform,root = self.get_transform()
 
622
        transform, root = self.get_transform()
531
623
        oz_id = transform.new_directory('oz', root, 'oz-id')
532
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
624
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
533
625
                                       'wizard-id')
534
 
        wiz_id = transform.create_path('wizard2', oz_id)
535
 
        transform.create_symlink('behind_curtain', wiz_id)
536
 
        transform.version_file('wiz-id2', wiz_id)            
 
626
        wiz_id = transform.create_path(link_name2, oz_id)
 
627
        transform.create_symlink(link_target2, wiz_id)
 
628
        transform.version_file('wiz-id2', wiz_id)
537
629
        transform.set_executability(True, wiz_id)
538
 
        self.assertEqual(transform.find_conflicts(), 
 
630
        self.assertEqual(transform.find_conflicts(),
539
631
                         [('non-file executability', wiz_id)])
540
632
        transform.set_executability(None, wiz_id)
541
633
        transform.apply()
542
 
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
543
 
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
544
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
545
 
                         'behind_curtain')
546
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
547
 
                         'wizard-target')
 
634
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
 
635
        self.assertEqual('symlink',
 
636
                         file_kind(self.wt.abspath(ozpath(link_name1))))
 
637
        self.assertEqual(link_target2,
 
638
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
 
639
        self.assertEqual(link_target1,
 
640
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
 
641
 
 
642
    def test_symlinks(self):
 
643
        self._test_symlinks('wizard', 'wizard-target',
 
644
                            'wizard2', 'behind_curtain')
 
645
 
 
646
    def test_symlinks_unicode(self):
 
647
        self.requireFeature(tests.UnicodeFilenameFeature)
 
648
        self._test_symlinks(u'\N{Euro Sign}wizard',
 
649
                            u'wizard-targ\N{Euro Sign}t',
 
650
                            u'\N{Euro Sign}wizard2',
 
651
                            u'b\N{Euro Sign}hind_curtain')
548
652
 
549
653
    def test_unable_create_symlink(self):
550
654
        def tt_helper():
574
678
        create.apply()
575
679
        conflicts,root = self.get_transform()
576
680
        # set up duplicate entry, duplicate id
577
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
681
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
578
682
                                         'dorothy-id')
579
683
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
580
684
        oz = conflicts.trans_id_tree_file_id('oz-id')
604
708
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
605
709
        raw_conflicts = resolve_conflicts(tt)
606
710
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
607
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
711
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
608
712
                                   'dorothy', None, 'dorothy-id')
609
713
        self.assertEqual(cooked_conflicts[0], duplicate)
610
 
        duplicate_id = DuplicateID('Unversioned existing file', 
 
714
        duplicate_id = DuplicateID('Unversioned existing file',
611
715
                                   'dorothy.moved', 'dorothy', None,
612
716
                                   'dorothy-id')
613
717
        self.assertEqual(cooked_conflicts[1], duplicate_id)
621
725
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
622
726
                                               'oz-id')
623
727
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
624
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
728
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
625
729
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
626
730
        self.assertEqual(cooked_conflicts[4], deleted_parent)
627
731
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
653
757
                                         ' versioned, but has versioned'
654
758
                                         ' children.  Versioned directory.')
655
759
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
656
 
                                         ' oz/emeraldcity.  Cancelled move.')
 
760
                                         ' oz/emeraldcity. Cancelled move.')
657
761
 
658
762
    def prepare_wrong_parent_kind(self):
659
763
        tt, root = self.get_transform()
730
834
        create.apply()
731
835
        transform, root = self.get_transform()
732
836
        transform.adjust_root_path('oldroot', fun)
733
 
        new_root=transform.trans_id_tree_path('')
 
837
        new_root = transform.trans_id_tree_path('')
734
838
        transform.version_file('new-root', new_root)
735
839
        transform.apply()
736
840
 
748
852
        rename.set_executability(True, myfile)
749
853
        rename.apply()
750
854
 
 
855
    def test_rename_fails(self):
 
856
        # see https://bugs.launchpad.net/bzr/+bug/491763
 
857
        create, root_id = self.get_transform()
 
858
        first_dir = create.new_directory('first-dir', root_id, 'first-id')
 
859
        myfile = create.new_file('myfile', root_id, 'myfile-text',
 
860
                                 'myfile-id')
 
861
        create.apply()
 
862
        # make the file and directory readonly in the hope this will prevent
 
863
        # renames
 
864
        osutils.make_readonly(self.wt.abspath('first-dir'))
 
865
        osutils.make_readonly(self.wt.abspath('myfile'))
 
866
        # now transform to rename
 
867
        rename_transform, root_id = self.get_transform()
 
868
        file_trans_id = rename_transform.trans_id_file_id('myfile-id')
 
869
        dir_id = rename_transform.trans_id_file_id('first-id')
 
870
        rename_transform.adjust_path('newname', dir_id, file_trans_id)
 
871
        e = self.assertRaises(errors.TransformRenameFailed,
 
872
            rename_transform.apply)
 
873
        # Looks like: 
 
874
        # "Failed to rename .../work/.bzr/checkout/limbo/new-1
 
875
        # to .../first-dir/newname: [Errno 13] Permission denied"
 
876
        # so the first filename is not visible in it; we expect a strerror but
 
877
        # it may vary per OS and language so it's not checked here
 
878
        self.assertContainsRe(str(e),
 
879
            "Failed to rename .*first-dir.newname:")
 
880
 
751
881
    def test_set_executability_order(self):
752
882
        """Ensure that executability behaves the same, no matter what order.
753
 
        
 
883
 
754
884
        - create file and set executability simultaneously
755
885
        - create file and set executability afterward
756
886
        - unsetting the executability of a file whose executability has not been
1410
1540
        # textual merge
1411
1541
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1412
1542
        # three-way text conflict
1413
 
        self.assertEqual(this.wt.get_file('b').read(), 
 
1543
        self.assertEqual(this.wt.get_file('b').read(),
1414
1544
                         conflict_text('b', 'b2'))
1415
1545
        # OTHER wins
1416
1546
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1420
1550
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1421
1551
        # No change
1422
1552
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1423
 
        # Correct correct results when THIS == OTHER 
 
1553
        # Correct correct results when THIS == OTHER
1424
1554
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1425
1555
        # Text conflict when THIS & OTHER are text and BASE is dir
1426
 
        self.assertEqual(this.wt.get_file('h').read(), 
 
1556
        self.assertEqual(this.wt.get_file('h').read(),
1427
1557
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1428
1558
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1429
1559
                         '1\n2\n3\n4\n')
1430
1560
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1431
1561
                         'h\ni\nj\nk\n')
1432
1562
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1433
 
        self.assertEqual(this.wt.get_file('i').read(), 
 
1563
        self.assertEqual(this.wt.get_file('i').read(),
1434
1564
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1435
1565
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1436
1566
                         '1\n2\n3\n4\n')
1460
1590
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1461
1591
            tg.tt.new_file('c', tg.root, 'c', 'c')
1462
1592
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1463
 
        targets = ((base, 'base-e', 'base-f', None, None), 
1464
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
 
1593
        targets = ((base, 'base-e', 'base-f', None, None),
 
1594
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1465
1595
                   (other, 'other-e', None, 'other-g', 'other-h'))
1466
1596
        for tg, e_target, f_target, g_target, h_target in targets:
1467
 
            for link, target in (('e', e_target), ('f', f_target), 
 
1597
            for link, target in (('e', e_target), ('f', f_target),
1468
1598
                                 ('g', g_target), ('h', h_target)):
1469
1599
                if target is not None:
1470
1600
                    tg.tt.new_symlink(link, tg.root, target, link)
1496
1626
        base = TransformGroup("BASE", root_id)
1497
1627
        this = TransformGroup("THIS", root_id)
1498
1628
        other = TransformGroup("OTHER", root_id)
1499
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1629
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1500
1630
                                   for t in [base, this, other]]
1501
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1631
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1502
1632
                                   for t in [base, this, other]]
1503
1633
        base.tt.new_directory('c', base_a, 'c')
1504
1634
        this.tt.new_directory('c1', this_a, 'c')
1529
1659
        base = TransformGroup("BASE", root_id)
1530
1660
        this = TransformGroup("THIS", root_id)
1531
1661
        other = TransformGroup("OTHER", root_id)
1532
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1662
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1533
1663
                                   for t in [base, this, other]]
1534
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1664
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1535
1665
                                   for t in [base, this, other]]
1536
1666
 
1537
1667
        base.tt.new_file('g', base_a, 'g', 'g')
1628
1758
        os.symlink('foo', 'target2/symlink')
1629
1759
        build_tree(source.basis_tree(), target)
1630
1760
        self.assertEqual([], target.conflicts())
1631
 
        
 
1761
 
1632
1762
    def test_directory_conflict_handling(self):
1633
1763
        """Ensure that when building trees, conflict handling is done"""
1634
1764
        source = self.make_branch_and_tree('source')
1690
1820
        target = self.make_branch_and_tree('target')
1691
1821
        self.build_tree(['target/name'])
1692
1822
        target.add('name')
1693
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1823
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1694
1824
            build_tree, source.basis_tree(), target)
1695
1825
 
1696
1826
    def test_build_tree_rename_count(self):
1840
1970
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1841
1971
        self.assertTrue(source.is_executable('file1-id'))
1842
1972
 
 
1973
    def install_rot13_content_filter(self, pattern):
 
1974
        # We could use
 
1975
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
 
1976
        # below, but that looks a bit... hard to read even if it's exactly
 
1977
        # the same thing.
 
1978
        original_registry = filters._reset_registry()
 
1979
        def restore_registry():
 
1980
            filters._reset_registry(original_registry)
 
1981
        self.addCleanup(restore_registry)
 
1982
        def rot13(chunks, context=None):
 
1983
            return [''.join(chunks).encode('rot13')]
 
1984
        rot13filter = filters.ContentFilter(rot13, rot13)
 
1985
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
 
1986
        os.mkdir(self.test_home_dir + '/.bazaar')
 
1987
        rules_filename = self.test_home_dir + '/.bazaar/rules'
 
1988
        f = open(rules_filename, 'wb')
 
1989
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
 
1990
        f.close()
 
1991
        def uninstall_rules():
 
1992
            os.remove(rules_filename)
 
1993
            rules.reset_rules()
 
1994
        self.addCleanup(uninstall_rules)
 
1995
        rules.reset_rules()
 
1996
 
 
1997
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
 
1998
        """build_tree will not hardlink files that have content filtering rules
 
1999
        applied to them (but will still hardlink other files from the same tree
 
2000
        if it can).
 
2001
        """
 
2002
        self.requireFeature(HardlinkFeature)
 
2003
        self.install_rot13_content_filter('file1')
 
2004
        source = self.create_ab_tree()
 
2005
        target = self.make_branch_and_tree('target')
 
2006
        revision_tree = source.basis_tree()
 
2007
        revision_tree.lock_read()
 
2008
        self.addCleanup(revision_tree.unlock)
 
2009
        build_tree(revision_tree, target, source, hardlink=True)
 
2010
        target.lock_read()
 
2011
        self.addCleanup(target.unlock)
 
2012
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
2013
        source_stat = os.stat('source/file1')
 
2014
        target_stat = os.stat('target/file1')
 
2015
        self.assertNotEqual(source_stat, target_stat)
 
2016
        source_stat = os.stat('source/file2')
 
2017
        target_stat = os.stat('target/file2')
 
2018
        self.assertEqualStat(source_stat, target_stat)
 
2019
 
1843
2020
    def test_case_insensitive_build_tree_inventory(self):
 
2021
        if (tests.CaseInsensitiveFilesystemFeature.available()
 
2022
            or tests.CaseInsCasePresFilenameFeature.available()):
 
2023
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1844
2024
        source = self.make_branch_and_tree('source')
1845
2025
        self.build_tree(['source/file', 'source/FILE'])
1846
2026
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1854
2034
        self.assertEqual('FILE', target.id2path('upper-id'))
1855
2035
 
1856
2036
 
 
2037
class TestCommitTransform(tests.TestCaseWithTransport):
 
2038
 
 
2039
    def get_branch(self):
 
2040
        tree = self.make_branch_and_tree('tree')
 
2041
        tree.lock_write()
 
2042
        self.addCleanup(tree.unlock)
 
2043
        tree.commit('empty commit')
 
2044
        return tree.branch
 
2045
 
 
2046
    def get_branch_and_transform(self):
 
2047
        branch = self.get_branch()
 
2048
        tt = TransformPreview(branch.basis_tree())
 
2049
        self.addCleanup(tt.finalize)
 
2050
        return branch, tt
 
2051
 
 
2052
    def test_commit_wrong_basis(self):
 
2053
        branch = self.get_branch()
 
2054
        basis = branch.repository.revision_tree(
 
2055
            _mod_revision.NULL_REVISION)
 
2056
        tt = TransformPreview(basis)
 
2057
        self.addCleanup(tt.finalize)
 
2058
        e = self.assertRaises(ValueError, tt.commit, branch, '')
 
2059
        self.assertEqual('TreeTransform not based on branch basis: null:',
 
2060
                         str(e))
 
2061
 
 
2062
    def test_empy_commit(self):
 
2063
        branch, tt = self.get_branch_and_transform()
 
2064
        rev = tt.commit(branch, 'my message')
 
2065
        self.assertEqual(2, branch.revno())
 
2066
        repo = branch.repository
 
2067
        self.assertEqual('my message', repo.get_revision(rev).message)
 
2068
 
 
2069
    def test_merge_parents(self):
 
2070
        branch, tt = self.get_branch_and_transform()
 
2071
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
 
2072
        self.assertEqual(['rev1b', 'rev1c'],
 
2073
                         branch.basis_tree().get_parent_ids()[1:])
 
2074
 
 
2075
    def test_first_commit(self):
 
2076
        branch = self.make_branch('branch')
 
2077
        branch.lock_write()
 
2078
        self.addCleanup(branch.unlock)
 
2079
        tt = TransformPreview(branch.basis_tree())
 
2080
        self.addCleanup(tt.finalize)
 
2081
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
 
2082
        rev = tt.commit(branch, 'my message')
 
2083
        self.assertEqual([], branch.basis_tree().get_parent_ids())
 
2084
        self.assertNotEqual(_mod_revision.NULL_REVISION,
 
2085
                            branch.last_revision())
 
2086
 
 
2087
    def test_first_commit_with_merge_parents(self):
 
2088
        branch = self.make_branch('branch')
 
2089
        branch.lock_write()
 
2090
        self.addCleanup(branch.unlock)
 
2091
        tt = TransformPreview(branch.basis_tree())
 
2092
        self.addCleanup(tt.finalize)
 
2093
        e = self.assertRaises(ValueError, tt.commit, branch,
 
2094
                          'my message', ['rev1b-id'])
 
2095
        self.assertEqual('Cannot supply merge parents for first commit.',
 
2096
                         str(e))
 
2097
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
 
2098
 
 
2099
    def test_add_files(self):
 
2100
        branch, tt = self.get_branch_and_transform()
 
2101
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2102
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
 
2103
        if SymlinkFeature.available():
 
2104
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
 
2105
        rev = tt.commit(branch, 'message')
 
2106
        tree = branch.basis_tree()
 
2107
        self.assertEqual('file', tree.id2path('file-id'))
 
2108
        self.assertEqual('contents', tree.get_file_text('file-id'))
 
2109
        self.assertEqual('dir', tree.id2path('dir-id'))
 
2110
        if SymlinkFeature.available():
 
2111
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
 
2112
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
2113
 
 
2114
    def test_add_unversioned(self):
 
2115
        branch, tt = self.get_branch_and_transform()
 
2116
        tt.new_file('file', tt.root, 'contents')
 
2117
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
 
2118
                          'message', strict=True)
 
2119
 
 
2120
    def test_modify_strict(self):
 
2121
        branch, tt = self.get_branch_and_transform()
 
2122
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2123
        tt.commit(branch, 'message', strict=True)
 
2124
        tt = TransformPreview(branch.basis_tree())
 
2125
        self.addCleanup(tt.finalize)
 
2126
        trans_id = tt.trans_id_file_id('file-id')
 
2127
        tt.delete_contents(trans_id)
 
2128
        tt.create_file('contents', trans_id)
 
2129
        tt.commit(branch, 'message', strict=True)
 
2130
 
 
2131
    def test_commit_malformed(self):
 
2132
        """Committing a malformed transform should raise an exception.
 
2133
 
 
2134
        In this case, we are adding a file without adding its parent.
 
2135
        """
 
2136
        branch, tt = self.get_branch_and_transform()
 
2137
        parent_id = tt.trans_id_file_id('parent-id')
 
2138
        tt.new_file('file', parent_id, 'contents', 'file-id')
 
2139
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
 
2140
                          'message')
 
2141
 
 
2142
    def test_commit_rich_revision_data(self):
 
2143
        branch, tt = self.get_branch_and_transform()
 
2144
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
 
2145
                           committer='me <me@example.com>',
 
2146
                           revprops={'foo': 'bar'}, revision_id='revid-1',
 
2147
                           authors=['Author1 <author1@example.com>',
 
2148
                              'Author2 <author2@example.com>',
 
2149
                               ])
 
2150
        self.assertEqual('revid-1', rev_id)
 
2151
        revision = branch.repository.get_revision(rev_id)
 
2152
        self.assertEqual(1, revision.timestamp)
 
2153
        self.assertEqual(43201, revision.timezone)
 
2154
        self.assertEqual('me <me@example.com>', revision.committer)
 
2155
        self.assertEqual(['Author1 <author1@example.com>',
 
2156
                          'Author2 <author2@example.com>'],
 
2157
                         revision.get_apparent_authors())
 
2158
        del revision.properties['authors']
 
2159
        self.assertEqual({'foo': 'bar',
 
2160
                          'branch-nick': 'tree'},
 
2161
                         revision.properties)
 
2162
 
 
2163
    def test_no_explicit_revprops(self):
 
2164
        branch, tt = self.get_branch_and_transform()
 
2165
        rev_id = tt.commit(branch, 'message', authors=[
 
2166
            'Author1 <author1@example.com>',
 
2167
            'Author2 <author2@example.com>', ])
 
2168
        revision = branch.repository.get_revision(rev_id)
 
2169
        self.assertEqual(['Author1 <author1@example.com>',
 
2170
                          'Author2 <author2@example.com>'],
 
2171
                         revision.get_apparent_authors())
 
2172
        self.assertEqual('tree', revision.properties['branch-nick'])
 
2173
 
 
2174
 
1857
2175
class MockTransform(object):
1858
2176
 
1859
2177
    def has_named_child(self, by_parent, parent_id, name):
2026
2344
    def create_tree(self):
2027
2345
        tree = self.make_branch_and_tree('.')
2028
2346
        self.build_tree_contents([('a', 'content 1')])
 
2347
        tree.set_root_id('TREE_ROOT')
2029
2348
        tree.add('a', 'a-id')
2030
2349
        tree.commit('rev1', rev_id='rev1')
2031
2350
        return tree.branch.repository.revision_tree('rev1')
2132
2451
    def test_ignore_pb(self):
2133
2452
        # pb could be supported, but TT.iter_changes doesn't support it.
2134
2453
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2135
 
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
 
2454
        preview_tree.iter_changes(revision_tree)
2136
2455
 
2137
2456
    def test_kind(self):
2138
2457
        revision_tree = self.create_tree()
2153
2472
        self.assertEqual(os.stat(limbo_path).st_mtime,
2154
2473
                         preview_tree.get_file_mtime('file-id'))
2155
2474
 
 
2475
    def test_get_file_mtime_renamed(self):
 
2476
        work_tree = self.make_branch_and_tree('tree')
 
2477
        self.build_tree(['tree/file'])
 
2478
        work_tree.add('file', 'file-id')
 
2479
        preview = TransformPreview(work_tree)
 
2480
        self.addCleanup(preview.finalize)
 
2481
        file_trans_id = preview.trans_id_tree_file_id('file-id')
 
2482
        preview.adjust_path('renamed', preview.root, file_trans_id)
 
2483
        preview_tree = preview.get_preview_tree()
 
2484
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
 
2485
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
 
2486
 
2156
2487
    def test_get_file(self):
2157
2488
        preview = self.get_empty_preview()
2158
2489
        preview.new_file('file', preview.root, 'contents', 'file-id')
2306
2637
        self.assertEqual(('missing', None, None, None), summary)
2307
2638
 
2308
2639
    def test_file_content_summary_executable(self):
2309
 
        if not osutils.supports_executable():
2310
 
            raise TestNotApplicable()
2311
2640
        preview = self.get_empty_preview()
2312
2641
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2313
2642
        preview.set_executability(True, path_id)
2322
2651
        self.assertIs(None, summary[3])
2323
2652
 
2324
2653
    def test_change_executability(self):
2325
 
        if not osutils.supports_executable():
2326
 
            raise TestNotApplicable()
2327
2654
        tree = self.make_branch_and_tree('tree')
2328
2655
        self.build_tree(['tree/path'])
2329
2656
        tree.add('path')
2343
2670
        # size must be known
2344
2671
        self.assertEqual(len('contents'), summary[1])
2345
2672
        # not executable
2346
 
        if osutils.supports_executable():
2347
 
            self.assertEqual(False, summary[2])
2348
 
        else:
2349
 
            self.assertEqual(None, summary[2])
 
2673
        self.assertEqual(False, summary[2])
2350
2674
        # will not have hash (not cheap to determine)
2351
2675
        self.assertIs(None, summary[3])
2352
2676
 
2493
2817
 
2494
2818
    def test_walkdirs(self):
2495
2819
        preview = self.get_empty_preview()
2496
 
        preview.version_file('tree-root', preview.root)
 
2820
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
2821
        # FIXME: new_directory should mark root.
 
2822
        preview.fixup_new_roots()
2497
2823
        preview_tree = preview.get_preview_tree()
2498
2824
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2499
2825
                                         'a-id')
2530
2856
        self.addCleanup(work_tree.unlock)
2531
2857
        preview = TransformPreview(work_tree)
2532
2858
        self.addCleanup(preview.finalize)
2533
 
        preview_tree = preview.get_preview_tree()
2534
2859
        file_trans_id = preview.trans_id_file_id('file-id')
2535
2860
        preview.delete_contents(file_trans_id)
2536
2861
        preview.create_file('a\nb\n', file_trans_id)
2537
 
        pb = progress.DummyProgress()
2538
 
        merger = Merger.from_revision_ids(pb, preview_tree,
 
2862
        preview_tree = preview.get_preview_tree()
 
2863
        merger = Merger.from_revision_ids(None, preview_tree,
2539
2864
                                          child_tree.branch.last_revision(),
2540
2865
                                          other_branch=child_tree.branch,
2541
2866
                                          tree_branch=work_tree.branch)
2547
2872
 
2548
2873
    def test_merge_preview_into_workingtree(self):
2549
2874
        tree = self.make_branch_and_tree('tree')
 
2875
        tree.set_root_id('TREE_ROOT')
2550
2876
        tt = TransformPreview(tree)
2551
2877
        self.addCleanup(tt.finalize)
2552
2878
        tt.new_file('name', tt.root, 'content', 'file-id')
2553
2879
        tree2 = self.make_branch_and_tree('tree2')
2554
 
        pb = progress.DummyProgress()
 
2880
        tree2.set_root_id('TREE_ROOT')
2555
2881
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2556
 
                                         pb, tree.basis_tree())
 
2882
                                         None, tree.basis_tree())
2557
2883
        merger.merge_type = Merge3Merger
2558
2884
        merger.do_merge()
2559
2885
 
2569
2895
        tt.create_file('baz', trans_id)
2570
2896
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2571
2897
        self.build_tree_contents([('tree2/foo', 'qux')])
2572
 
        pb = progress.DummyProgress()
 
2898
        pb = None
2573
2899
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2574
2900
                                         pb, tree.basis_tree())
2575
2901
        merger.merge_type = Merge3Merger
2585
2911
                                                           'tree/foo'))
2586
2912
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2587
2913
 
 
2914
    def test_commit_preview_tree(self):
 
2915
        tree = self.make_branch_and_tree('tree')
 
2916
        rev_id = tree.commit('rev1')
 
2917
        tree.branch.lock_write()
 
2918
        self.addCleanup(tree.branch.unlock)
 
2919
        tt = TransformPreview(tree)
 
2920
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
2921
        self.addCleanup(tt.finalize)
 
2922
        preview = tt.get_preview_tree()
 
2923
        preview.set_parent_ids([rev_id])
 
2924
        builder = tree.branch.get_commit_builder([rev_id])
 
2925
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
2926
        builder.finish_inventory()
 
2927
        rev2_id = builder.commit('rev2')
 
2928
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
2929
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
2930
 
 
2931
    def test_ascii_limbo_paths(self):
 
2932
        self.requireFeature(tests.UnicodeFilenameFeature)
 
2933
        branch = self.make_branch('any')
 
2934
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
 
2935
        tt = TransformPreview(tree)
 
2936
        self.addCleanup(tt.finalize)
 
2937
        foo_id = tt.new_directory('', ROOT_PARENT)
 
2938
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
 
2939
        limbo_path = tt._limbo_name(bar_id)
 
2940
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
 
2941
 
2588
2942
 
2589
2943
class FakeSerializer(object):
2590
2944
    """Serializer implementation that simply returns the input.
2685
3039
        self.assertSerializesTo(self.symlink_creation_records(), tt)
2686
3040
 
2687
3041
    def test_deserialize_symlink_creation(self):
 
3042
        self.requireFeature(tests.SymlinkFeature)
2688
3043
        tt = self.get_preview()
2689
3044
        tt.deserialize(iter(self.symlink_creation_records()))
2690
 
        # XXX readlink should be returning unicode, not utf-8
2691
 
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
 
3045
        abspath = tt._limbo_name('new-1')
 
3046
        foo_content = osutils.readlink(abspath)
2692
3047
        self.assertEqual(u'bar\u1234', foo_content)
2693
3048
 
2694
3049
    def make_destruction_preview(self):