/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: Colin D Bennett
  • Date: 2009-02-12 17:11:41 UTC
  • mto: This revision was merged to the branch mainline in revision 4008.
  • Revision ID: colin@gibibit.com-20090212171141-bq8gllubhwyfgr52
Ignore all generated PDFs, not just the A4 size document.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
 
18
import stat
18
19
from StringIO import StringIO
19
20
import sys
20
 
import time
21
21
 
22
22
from bzrlib import (
23
 
    bencode,
24
23
    errors,
25
 
    filters,
26
24
    generate_ids,
27
25
    osutils,
 
26
    progress,
28
27
    revision as _mod_revision,
29
 
    rules,
 
28
    symbol_versioning,
30
29
    tests,
31
30
    urlutils,
32
31
    )
33
32
from bzrlib.bzrdir import BzrDir
34
 
from bzrlib.conflicts import (
35
 
    DeletingParent,
36
 
    DuplicateEntry,
37
 
    DuplicateID,
38
 
    MissingParent,
39
 
    NonDirectoryParent,
40
 
    ParentLoop,
41
 
    UnversionedParent,
42
 
)
 
33
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
 
34
                              UnversionedParent, ParentLoop, DeletingParent,
 
35
                              NonDirectoryParent)
43
36
from bzrlib.diff import show_diff_trees
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
 
)
 
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
59
43
from bzrlib.merge import Merge3Merger, Merger
60
44
from bzrlib.tests import (
61
45
    HardlinkFeature,
63
47
    TestCase,
64
48
    TestCaseInTempDir,
65
49
    TestSkipped,
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
 
)
 
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
80
57
 
81
58
 
82
59
class TestTreeTransform(tests.TestCaseWithTransport):
152
129
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
153
130
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
154
131
        self.assertEqual(len(modified_paths), 3)
155
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in
 
132
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
156
133
                          ('ozzie', 'my_pretties', 'my_pretties2')]
157
134
        self.assertSubset(tree_mod_paths, modified_paths)
158
135
        # is it safe to finalize repeatedly?
159
136
        transform.finalize()
160
137
        transform.finalize()
161
138
 
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
 
 
215
139
    def test_hardlink(self):
216
140
        self.requireFeature(HardlinkFeature)
217
141
        transform, root = self.get_transform()
231
155
        transform, root = self.get_transform()
232
156
        self.wt.lock_tree_write()
233
157
        self.addCleanup(self.wt.unlock)
234
 
        trans_id = transform.new_file('name', root, 'contents',
 
158
        trans_id = transform.new_file('name', root, 'contents', 
235
159
                                      'my_pretties', True)
236
160
        oz = transform.new_directory('oz', root, 'oz-id')
237
161
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
238
 
        toto = transform.new_file('toto', dorothy, 'toto-contents',
 
162
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
239
163
                                  'toto-id', False)
240
164
 
241
165
        self.assertEqual(len(transform.find_conflicts()), 0)
265
189
 
266
190
    def test_conflicts(self):
267
191
        transform, root = self.get_transform()
268
 
        trans_id = transform.new_file('name', root, 'contents',
 
192
        trans_id = transform.new_file('name', root, 'contents', 
269
193
                                      'my_pretties')
270
194
        self.assertEqual(len(transform.find_conflicts()), 0)
271
195
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
272
 
        self.assertEqual(transform.find_conflicts(),
 
196
        self.assertEqual(transform.find_conflicts(), 
273
197
                         [('duplicate', trans_id, trans_id2, 'name')])
274
198
        self.assertRaises(MalformedTransform, transform.apply)
275
199
        transform.adjust_path('name', trans_id, trans_id2)
276
 
        self.assertEqual(transform.find_conflicts(),
 
200
        self.assertEqual(transform.find_conflicts(), 
277
201
                         [('non-directory parent', trans_id)])
278
202
        tinman_id = transform.trans_id_tree_path('tinman')
279
203
        transform.adjust_path('name', tinman_id, trans_id2)
280
 
        self.assertEqual(transform.find_conflicts(),
281
 
                         [('unversioned parent', tinman_id),
 
204
        self.assertEqual(transform.find_conflicts(), 
 
205
                         [('unversioned parent', tinman_id), 
282
206
                          ('missing parent', tinman_id)])
283
207
        lion_id = transform.create_path('lion', root)
284
 
        self.assertEqual(transform.find_conflicts(),
285
 
                         [('unversioned parent', tinman_id),
 
208
        self.assertEqual(transform.find_conflicts(), 
 
209
                         [('unversioned parent', tinman_id), 
286
210
                          ('missing parent', tinman_id)])
287
211
        transform.adjust_path('name', lion_id, trans_id2)
288
 
        self.assertEqual(transform.find_conflicts(),
 
212
        self.assertEqual(transform.find_conflicts(), 
289
213
                         [('unversioned parent', lion_id),
290
214
                          ('missing parent', lion_id)])
291
215
        transform.version_file("Courage", lion_id)
292
 
        self.assertEqual(transform.find_conflicts(),
293
 
                         [('missing parent', lion_id),
 
216
        self.assertEqual(transform.find_conflicts(), 
 
217
                         [('missing parent', lion_id), 
294
218
                          ('versioning no contents', lion_id)])
295
219
        transform.adjust_path('name2', root, trans_id2)
296
 
        self.assertEqual(transform.find_conflicts(),
 
220
        self.assertEqual(transform.find_conflicts(), 
297
221
                         [('versioning no contents', lion_id)])
298
222
        transform.create_file('Contents, okay?', lion_id)
299
223
        transform.adjust_path('name2', trans_id2, trans_id2)
300
 
        self.assertEqual(transform.find_conflicts(),
301
 
                         [('parent loop', trans_id2),
 
224
        self.assertEqual(transform.find_conflicts(), 
 
225
                         [('parent loop', trans_id2), 
302
226
                          ('non-directory parent', trans_id2)])
303
227
        transform.adjust_path('name2', root, trans_id2)
304
228
        oz_id = transform.new_directory('oz', root)
305
229
        transform.set_executability(True, oz_id)
306
 
        self.assertEqual(transform.find_conflicts(),
 
230
        self.assertEqual(transform.find_conflicts(), 
307
231
                         [('unversioned executability', oz_id)])
308
232
        transform.version_file('oz-id', oz_id)
309
 
        self.assertEqual(transform.find_conflicts(),
 
233
        self.assertEqual(transform.find_conflicts(), 
310
234
                         [('non-file executability', oz_id)])
311
235
        transform.set_executability(None, oz_id)
312
236
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
321
245
        self.assert_('oz/tip' in transform2._tree_path_ids)
322
246
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
323
247
        self.assertEqual(len(result), 2)
324
 
        self.assertEqual((result[0][0], result[0][1]),
 
248
        self.assertEqual((result[0][0], result[0][1]), 
325
249
                         ('duplicate', newtip))
326
 
        self.assertEqual((result[1][0], result[1][2]),
 
250
        self.assertEqual((result[1][0], result[1][2]), 
327
251
                         ('duplicate id', newtip))
328
252
        transform2.finalize()
329
253
        transform3 = TreeTransform(self.wt)
330
254
        self.addCleanup(transform3.finalize)
331
255
        oz_id = transform3.trans_id_tree_file_id('oz-id')
332
256
        transform3.delete_contents(oz_id)
333
 
        self.assertEqual(transform3.find_conflicts(),
 
257
        self.assertEqual(transform3.find_conflicts(), 
334
258
                         [('missing parent', oz_id)])
335
259
        root_id = transform3.root
336
260
        tip_id = transform3.trans_id_tree_file_id('tip-id')
445
369
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
446
370
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
447
371
 
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
 
 
460
372
    def test_add_del(self):
461
373
        start, root = self.get_transform()
462
374
        start.new_directory('a', root, 'a')
475
387
        self.addCleanup(unversion.finalize)
476
388
        parent = unversion.trans_id_tree_path('parent')
477
389
        unversion.unversion_file(parent)
478
 
        self.assertEqual(unversion.find_conflicts(),
 
390
        self.assertEqual(unversion.find_conflicts(), 
479
391
                         [('unversioned parent', parent_id)])
480
392
        file_id = unversion.trans_id_tree_file_id('child-id')
481
393
        unversion.unversion_file(file_id)
501
413
        mangle_tree.adjust_path('name2', root, name1)
502
414
        mangle_tree.adjust_path('name1', root, name2)
503
415
 
504
 
        #tests for deleting parent directories
 
416
        #tests for deleting parent directories 
505
417
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
506
418
        mangle_tree.delete_contents(ddir)
507
419
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
536
448
        create_tree,root = self.get_transform()
537
449
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
538
450
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
539
 
        create_tree.apply()
 
451
        create_tree.apply()        
540
452
        mangle_tree,root = self.get_transform()
541
453
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
542
454
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
550
462
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
551
463
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
552
464
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
553
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
 
465
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
554
466
                             'test_too_much-id')
555
 
        create_tree.apply()
 
467
        create_tree.apply()        
556
468
        mangle_tree,root = self.get_transform()
557
469
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
558
470
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
559
471
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
560
472
        mangle_tree.adjust_path('selftest', bzrlib, tests)
561
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
 
473
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
562
474
        mangle_tree.set_executability(True, test_too_much)
563
475
        mangle_tree.apply()
564
476
 
565
477
    def test_both_rename3(self):
566
478
        create_tree,root = self.get_transform()
567
479
        tests = create_tree.new_directory('tests', root, 'tests-id')
568
 
        create_tree.new_file('test_too_much.py', tests, 'hello1',
 
480
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
569
481
                             'test_too_much-id')
570
 
        create_tree.apply()
 
482
        create_tree.apply()        
571
483
        mangle_tree,root = self.get_transform()
572
484
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
573
485
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
574
486
        mangle_tree.adjust_path('selftest', root, tests)
575
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
 
487
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
576
488
        mangle_tree.set_executability(True, test_too_much)
577
489
        mangle_tree.apply()
578
490
 
591
503
        newdir = move_id.new_directory('dir', root, 'newdir')
592
504
        move_id.adjust_path('name2', newdir, name1)
593
505
        move_id.apply()
594
 
 
 
506
        
595
507
    def test_replace_dangling_ie(self):
596
508
        create_tree, root = self.get_transform()
597
509
        # prepare tree
613
525
        resolve_conflicts(replace)
614
526
        replace.apply()
615
527
 
616
 
    def _test_symlinks(self, link_name1,link_target1,
617
 
                       link_name2, link_target2):
618
 
 
619
 
        def ozpath(p): return 'oz/' + p
620
 
 
 
528
    def test_symlinks(self):
621
529
        self.requireFeature(SymlinkFeature)
622
 
        transform, root = self.get_transform()
 
530
        transform,root = self.get_transform()
623
531
        oz_id = transform.new_directory('oz', root, 'oz-id')
624
 
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
 
532
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
625
533
                                       'wizard-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)
 
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)            
629
537
        transform.set_executability(True, wiz_id)
630
 
        self.assertEqual(transform.find_conflicts(),
 
538
        self.assertEqual(transform.find_conflicts(), 
631
539
                         [('non-file executability', wiz_id)])
632
540
        transform.set_executability(None, wiz_id)
633
541
        transform.apply()
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')
 
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')
652
548
 
653
549
    def test_unable_create_symlink(self):
654
550
        def tt_helper():
678
574
        create.apply()
679
575
        conflicts,root = self.get_transform()
680
576
        # set up duplicate entry, duplicate id
681
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
 
577
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
682
578
                                         'dorothy-id')
683
579
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
684
580
        oz = conflicts.trans_id_tree_file_id('oz-id')
708
604
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
709
605
        raw_conflicts = resolve_conflicts(tt)
710
606
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
711
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
 
607
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
712
608
                                   'dorothy', None, 'dorothy-id')
713
609
        self.assertEqual(cooked_conflicts[0], duplicate)
714
 
        duplicate_id = DuplicateID('Unversioned existing file',
 
610
        duplicate_id = DuplicateID('Unversioned existing file', 
715
611
                                   'dorothy.moved', 'dorothy', None,
716
612
                                   'dorothy-id')
717
613
        self.assertEqual(cooked_conflicts[1], duplicate_id)
725
621
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
726
622
                                               'oz-id')
727
623
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
728
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
 
624
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
729
625
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
730
626
        self.assertEqual(cooked_conflicts[4], deleted_parent)
731
627
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
757
653
                                         ' versioned, but has versioned'
758
654
                                         ' children.  Versioned directory.')
759
655
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
760
 
                                         ' oz/emeraldcity. Cancelled move.')
 
656
                                         ' oz/emeraldcity.  Cancelled move.')
761
657
 
762
658
    def prepare_wrong_parent_kind(self):
763
659
        tt, root = self.get_transform()
834
730
        create.apply()
835
731
        transform, root = self.get_transform()
836
732
        transform.adjust_root_path('oldroot', fun)
837
 
        new_root = transform.trans_id_tree_path('')
 
733
        new_root=transform.trans_id_tree_path('')
838
734
        transform.version_file('new-root', new_root)
839
735
        transform.apply()
840
736
 
854
750
 
855
751
    def test_set_executability_order(self):
856
752
        """Ensure that executability behaves the same, no matter what order.
857
 
 
 
753
        
858
754
        - create file and set executability simultaneously
859
755
        - create file and set executability afterward
860
756
        - unsetting the executability of a file whose executability has not been
1514
1410
        # textual merge
1515
1411
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1516
1412
        # three-way text conflict
1517
 
        self.assertEqual(this.wt.get_file('b').read(),
 
1413
        self.assertEqual(this.wt.get_file('b').read(), 
1518
1414
                         conflict_text('b', 'b2'))
1519
1415
        # OTHER wins
1520
1416
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1524
1420
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1525
1421
        # No change
1526
1422
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1527
 
        # Correct correct results when THIS == OTHER
 
1423
        # Correct correct results when THIS == OTHER 
1528
1424
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1529
1425
        # Text conflict when THIS & OTHER are text and BASE is dir
1530
 
        self.assertEqual(this.wt.get_file('h').read(),
 
1426
        self.assertEqual(this.wt.get_file('h').read(), 
1531
1427
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1532
1428
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1533
1429
                         '1\n2\n3\n4\n')
1534
1430
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1535
1431
                         'h\ni\nj\nk\n')
1536
1432
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1537
 
        self.assertEqual(this.wt.get_file('i').read(),
 
1433
        self.assertEqual(this.wt.get_file('i').read(), 
1538
1434
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1539
1435
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1540
1436
                         '1\n2\n3\n4\n')
1564
1460
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1565
1461
            tg.tt.new_file('c', tg.root, 'c', 'c')
1566
1462
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1567
 
        targets = ((base, 'base-e', 'base-f', None, None),
1568
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
 
1463
        targets = ((base, 'base-e', 'base-f', None, None), 
 
1464
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
1569
1465
                   (other, 'other-e', None, 'other-g', 'other-h'))
1570
1466
        for tg, e_target, f_target, g_target, h_target in targets:
1571
 
            for link, target in (('e', e_target), ('f', f_target),
 
1467
            for link, target in (('e', e_target), ('f', f_target), 
1572
1468
                                 ('g', g_target), ('h', h_target)):
1573
1469
                if target is not None:
1574
1470
                    tg.tt.new_symlink(link, tg.root, target, link)
1600
1496
        base = TransformGroup("BASE", root_id)
1601
1497
        this = TransformGroup("THIS", root_id)
1602
1498
        other = TransformGroup("OTHER", root_id)
1603
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
 
1499
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1604
1500
                                   for t in [base, this, other]]
1605
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
 
1501
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1606
1502
                                   for t in [base, this, other]]
1607
1503
        base.tt.new_directory('c', base_a, 'c')
1608
1504
        this.tt.new_directory('c1', this_a, 'c')
1633
1529
        base = TransformGroup("BASE", root_id)
1634
1530
        this = TransformGroup("THIS", root_id)
1635
1531
        other = TransformGroup("OTHER", root_id)
1636
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
 
1532
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1637
1533
                                   for t in [base, this, other]]
1638
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
 
1534
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1639
1535
                                   for t in [base, this, other]]
1640
1536
 
1641
1537
        base.tt.new_file('g', base_a, 'g', 'g')
1732
1628
        os.symlink('foo', 'target2/symlink')
1733
1629
        build_tree(source.basis_tree(), target)
1734
1630
        self.assertEqual([], target.conflicts())
1735
 
 
 
1631
        
1736
1632
    def test_directory_conflict_handling(self):
1737
1633
        """Ensure that when building trees, conflict handling is done"""
1738
1634
        source = self.make_branch_and_tree('source')
1794
1690
        target = self.make_branch_and_tree('target')
1795
1691
        self.build_tree(['target/name'])
1796
1692
        target.add('name')
1797
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
 
1693
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
1798
1694
            build_tree, source.basis_tree(), target)
1799
1695
 
1800
1696
    def test_build_tree_rename_count(self):
1944
1840
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1945
1841
        self.assertTrue(source.is_executable('file1-id'))
1946
1842
 
1947
 
    def install_rot13_content_filter(self, pattern):
1948
 
        # We could use
1949
 
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
1950
 
        # below, but that looks a bit... hard to read even if it's exactly
1951
 
        # the same thing.
1952
 
        original_registry = filters._reset_registry()
1953
 
        def restore_registry():
1954
 
            filters._reset_registry(original_registry)
1955
 
        self.addCleanup(restore_registry)
1956
 
        def rot13(chunks, context=None):
1957
 
            return [''.join(chunks).encode('rot13')]
1958
 
        rot13filter = filters.ContentFilter(rot13, rot13)
1959
 
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1960
 
        os.mkdir(self.test_home_dir + '/.bazaar')
1961
 
        rules_filename = self.test_home_dir + '/.bazaar/rules'
1962
 
        f = open(rules_filename, 'wb')
1963
 
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
1964
 
        f.close()
1965
 
        def uninstall_rules():
1966
 
            os.remove(rules_filename)
1967
 
            rules.reset_rules()
1968
 
        self.addCleanup(uninstall_rules)
1969
 
        rules.reset_rules()
1970
 
 
1971
 
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
1972
 
        """build_tree will not hardlink files that have content filtering rules
1973
 
        applied to them (but will still hardlink other files from the same tree
1974
 
        if it can).
1975
 
        """
1976
 
        self.requireFeature(HardlinkFeature)
1977
 
        self.install_rot13_content_filter('file1')
1978
 
        source = self.create_ab_tree()
1979
 
        target = self.make_branch_and_tree('target')
1980
 
        revision_tree = source.basis_tree()
1981
 
        revision_tree.lock_read()
1982
 
        self.addCleanup(revision_tree.unlock)
1983
 
        build_tree(revision_tree, target, source, hardlink=True)
1984
 
        target.lock_read()
1985
 
        self.addCleanup(target.unlock)
1986
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1987
 
        source_stat = os.stat('source/file1')
1988
 
        target_stat = os.stat('target/file1')
1989
 
        self.assertNotEqual(source_stat, target_stat)
1990
 
        source_stat = os.stat('source/file2')
1991
 
        target_stat = os.stat('target/file2')
1992
 
        self.assertEqualStat(source_stat, target_stat)
1993
 
 
1994
1843
    def test_case_insensitive_build_tree_inventory(self):
1995
 
        if (tests.CaseInsensitiveFilesystemFeature.available()
1996
 
            or tests.CaseInsCasePresFilenameFeature.available()):
1997
 
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1998
1844
        source = self.make_branch_and_tree('source')
1999
1845
        self.build_tree(['source/file', 'source/FILE'])
2000
1846
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
2008
1854
        self.assertEqual('FILE', target.id2path('upper-id'))
2009
1855
 
2010
1856
 
2011
 
class TestCommitTransform(tests.TestCaseWithTransport):
2012
 
 
2013
 
    def get_branch(self):
2014
 
        tree = self.make_branch_and_tree('tree')
2015
 
        tree.lock_write()
2016
 
        self.addCleanup(tree.unlock)
2017
 
        tree.commit('empty commit')
2018
 
        return tree.branch
2019
 
 
2020
 
    def get_branch_and_transform(self):
2021
 
        branch = self.get_branch()
2022
 
        tt = TransformPreview(branch.basis_tree())
2023
 
        self.addCleanup(tt.finalize)
2024
 
        return branch, tt
2025
 
 
2026
 
    def test_commit_wrong_basis(self):
2027
 
        branch = self.get_branch()
2028
 
        basis = branch.repository.revision_tree(
2029
 
            _mod_revision.NULL_REVISION)
2030
 
        tt = TransformPreview(basis)
2031
 
        self.addCleanup(tt.finalize)
2032
 
        e = self.assertRaises(ValueError, tt.commit, branch, '')
2033
 
        self.assertEqual('TreeTransform not based on branch basis: null:',
2034
 
                         str(e))
2035
 
 
2036
 
    def test_empy_commit(self):
2037
 
        branch, tt = self.get_branch_and_transform()
2038
 
        rev = tt.commit(branch, 'my message')
2039
 
        self.assertEqual(2, branch.revno())
2040
 
        repo = branch.repository
2041
 
        self.assertEqual('my message', repo.get_revision(rev).message)
2042
 
 
2043
 
    def test_merge_parents(self):
2044
 
        branch, tt = self.get_branch_and_transform()
2045
 
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2046
 
        self.assertEqual(['rev1b', 'rev1c'],
2047
 
                         branch.basis_tree().get_parent_ids()[1:])
2048
 
 
2049
 
    def test_first_commit(self):
2050
 
        branch = self.make_branch('branch')
2051
 
        branch.lock_write()
2052
 
        self.addCleanup(branch.unlock)
2053
 
        tt = TransformPreview(branch.basis_tree())
2054
 
        self.addCleanup(tt.finalize)
2055
 
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2056
 
        rev = tt.commit(branch, 'my message')
2057
 
        self.assertEqual([], branch.basis_tree().get_parent_ids())
2058
 
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2059
 
                            branch.last_revision())
2060
 
 
2061
 
    def test_first_commit_with_merge_parents(self):
2062
 
        branch = self.make_branch('branch')
2063
 
        branch.lock_write()
2064
 
        self.addCleanup(branch.unlock)
2065
 
        tt = TransformPreview(branch.basis_tree())
2066
 
        self.addCleanup(tt.finalize)
2067
 
        e = self.assertRaises(ValueError, tt.commit, branch,
2068
 
                          'my message', ['rev1b-id'])
2069
 
        self.assertEqual('Cannot supply merge parents for first commit.',
2070
 
                         str(e))
2071
 
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2072
 
 
2073
 
    def test_add_files(self):
2074
 
        branch, tt = self.get_branch_and_transform()
2075
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2076
 
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2077
 
        if SymlinkFeature.available():
2078
 
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2079
 
        rev = tt.commit(branch, 'message')
2080
 
        tree = branch.basis_tree()
2081
 
        self.assertEqual('file', tree.id2path('file-id'))
2082
 
        self.assertEqual('contents', tree.get_file_text('file-id'))
2083
 
        self.assertEqual('dir', tree.id2path('dir-id'))
2084
 
        if SymlinkFeature.available():
2085
 
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2086
 
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2087
 
 
2088
 
    def test_add_unversioned(self):
2089
 
        branch, tt = self.get_branch_and_transform()
2090
 
        tt.new_file('file', tt.root, 'contents')
2091
 
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2092
 
                          'message', strict=True)
2093
 
 
2094
 
    def test_modify_strict(self):
2095
 
        branch, tt = self.get_branch_and_transform()
2096
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2097
 
        tt.commit(branch, 'message', strict=True)
2098
 
        tt = TransformPreview(branch.basis_tree())
2099
 
        self.addCleanup(tt.finalize)
2100
 
        trans_id = tt.trans_id_file_id('file-id')
2101
 
        tt.delete_contents(trans_id)
2102
 
        tt.create_file('contents', trans_id)
2103
 
        tt.commit(branch, 'message', strict=True)
2104
 
 
2105
 
    def test_commit_malformed(self):
2106
 
        """Committing a malformed transform should raise an exception.
2107
 
 
2108
 
        In this case, we are adding a file without adding its parent.
2109
 
        """
2110
 
        branch, tt = self.get_branch_and_transform()
2111
 
        parent_id = tt.trans_id_file_id('parent-id')
2112
 
        tt.new_file('file', parent_id, 'contents', 'file-id')
2113
 
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2114
 
                          'message')
2115
 
 
2116
 
    def test_commit_rich_revision_data(self):
2117
 
        branch, tt = self.get_branch_and_transform()
2118
 
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
2119
 
                           committer='me <me@example.com>',
2120
 
                           revprops={'foo': 'bar'}, revision_id='revid-1',
2121
 
                           authors=['Author1 <author1@example.com>',
2122
 
                              'Author2 <author2@example.com>',
2123
 
                               ])
2124
 
        self.assertEqual('revid-1', rev_id)
2125
 
        revision = branch.repository.get_revision(rev_id)
2126
 
        self.assertEqual(1, revision.timestamp)
2127
 
        self.assertEqual(43201, revision.timezone)
2128
 
        self.assertEqual('me <me@example.com>', revision.committer)
2129
 
        self.assertEqual(['Author1 <author1@example.com>',
2130
 
                          'Author2 <author2@example.com>'],
2131
 
                         revision.get_apparent_authors())
2132
 
        del revision.properties['authors']
2133
 
        self.assertEqual({'foo': 'bar',
2134
 
                          'branch-nick': 'tree'},
2135
 
                         revision.properties)
2136
 
 
2137
 
    def test_no_explicit_revprops(self):
2138
 
        branch, tt = self.get_branch_and_transform()
2139
 
        rev_id = tt.commit(branch, 'message', authors=[
2140
 
            'Author1 <author1@example.com>',
2141
 
            'Author2 <author2@example.com>', ])
2142
 
        revision = branch.repository.get_revision(rev_id)
2143
 
        self.assertEqual(['Author1 <author1@example.com>',
2144
 
                          'Author2 <author2@example.com>'],
2145
 
                         revision.get_apparent_authors())
2146
 
        self.assertEqual('tree', revision.properties['branch-nick'])
2147
 
 
2148
 
 
2149
1857
class MockTransform(object):
2150
1858
 
2151
1859
    def has_named_child(self, by_parent, parent_id, name):
2318
2026
    def create_tree(self):
2319
2027
        tree = self.make_branch_and_tree('.')
2320
2028
        self.build_tree_contents([('a', 'content 1')])
2321
 
        tree.set_root_id('TREE_ROOT')
2322
2029
        tree.add('a', 'a-id')
2323
2030
        tree.commit('rev1', rev_id='rev1')
2324
2031
        return tree.branch.repository.revision_tree('rev1')
2425
2132
    def test_ignore_pb(self):
2426
2133
        # pb could be supported, but TT.iter_changes doesn't support it.
2427
2134
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2428
 
        preview_tree.iter_changes(revision_tree)
 
2135
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2429
2136
 
2430
2137
    def test_kind(self):
2431
2138
        revision_tree = self.create_tree()
2446
2153
        self.assertEqual(os.stat(limbo_path).st_mtime,
2447
2154
                         preview_tree.get_file_mtime('file-id'))
2448
2155
 
2449
 
    def test_get_file_mtime_renamed(self):
2450
 
        work_tree = self.make_branch_and_tree('tree')
2451
 
        self.build_tree(['tree/file'])
2452
 
        work_tree.add('file', 'file-id')
2453
 
        preview = TransformPreview(work_tree)
2454
 
        self.addCleanup(preview.finalize)
2455
 
        file_trans_id = preview.trans_id_tree_file_id('file-id')
2456
 
        preview.adjust_path('renamed', preview.root, file_trans_id)
2457
 
        preview_tree = preview.get_preview_tree()
2458
 
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2459
 
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
2460
 
 
2461
2156
    def test_get_file(self):
2462
2157
        preview = self.get_empty_preview()
2463
2158
        preview.new_file('file', preview.root, 'contents', 'file-id')
2611
2306
        self.assertEqual(('missing', None, None, None), summary)
2612
2307
 
2613
2308
    def test_file_content_summary_executable(self):
 
2309
        if not osutils.supports_executable():
 
2310
            raise TestNotApplicable()
2614
2311
        preview = self.get_empty_preview()
2615
2312
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2616
2313
        preview.set_executability(True, path_id)
2625
2322
        self.assertIs(None, summary[3])
2626
2323
 
2627
2324
    def test_change_executability(self):
 
2325
        if not osutils.supports_executable():
 
2326
            raise TestNotApplicable()
2628
2327
        tree = self.make_branch_and_tree('tree')
2629
2328
        self.build_tree(['tree/path'])
2630
2329
        tree.add('path')
2644
2343
        # size must be known
2645
2344
        self.assertEqual(len('contents'), summary[1])
2646
2345
        # not executable
2647
 
        self.assertEqual(False, summary[2])
 
2346
        if osutils.supports_executable():
 
2347
            self.assertEqual(False, summary[2])
 
2348
        else:
 
2349
            self.assertEqual(None, summary[2])
2648
2350
        # will not have hash (not cheap to determine)
2649
2351
        self.assertIs(None, summary[3])
2650
2352
 
2791
2493
 
2792
2494
    def test_walkdirs(self):
2793
2495
        preview = self.get_empty_preview()
2794
 
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2795
 
        # FIXME: new_directory should mark root.
2796
 
        preview.fixup_new_roots()
 
2496
        preview.version_file('tree-root', preview.root)
2797
2497
        preview_tree = preview.get_preview_tree()
2798
2498
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2799
2499
                                         'a-id')
2830
2530
        self.addCleanup(work_tree.unlock)
2831
2531
        preview = TransformPreview(work_tree)
2832
2532
        self.addCleanup(preview.finalize)
 
2533
        preview_tree = preview.get_preview_tree()
2833
2534
        file_trans_id = preview.trans_id_file_id('file-id')
2834
2535
        preview.delete_contents(file_trans_id)
2835
2536
        preview.create_file('a\nb\n', file_trans_id)
2836
 
        preview_tree = preview.get_preview_tree()
2837
 
        merger = Merger.from_revision_ids(None, preview_tree,
 
2537
        pb = progress.DummyProgress()
 
2538
        merger = Merger.from_revision_ids(pb, preview_tree,
2838
2539
                                          child_tree.branch.last_revision(),
2839
2540
                                          other_branch=child_tree.branch,
2840
2541
                                          tree_branch=work_tree.branch)
2846
2547
 
2847
2548
    def test_merge_preview_into_workingtree(self):
2848
2549
        tree = self.make_branch_and_tree('tree')
2849
 
        tree.set_root_id('TREE_ROOT')
2850
2550
        tt = TransformPreview(tree)
2851
2551
        self.addCleanup(tt.finalize)
2852
2552
        tt.new_file('name', tt.root, 'content', 'file-id')
2853
2553
        tree2 = self.make_branch_and_tree('tree2')
2854
 
        tree2.set_root_id('TREE_ROOT')
 
2554
        pb = progress.DummyProgress()
2855
2555
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2856
 
                                         None, tree.basis_tree())
 
2556
                                         pb, tree.basis_tree())
2857
2557
        merger.merge_type = Merge3Merger
2858
2558
        merger.do_merge()
2859
2559
 
2869
2569
        tt.create_file('baz', trans_id)
2870
2570
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2871
2571
        self.build_tree_contents([('tree2/foo', 'qux')])
2872
 
        pb = None
 
2572
        pb = progress.DummyProgress()
2873
2573
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2874
2574
                                         pb, tree.basis_tree())
2875
2575
        merger.merge_type = Merge3Merger
2885
2585
                                                           'tree/foo'))
2886
2586
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2887
2587
 
2888
 
    def test_commit_preview_tree(self):
2889
 
        tree = self.make_branch_and_tree('tree')
2890
 
        rev_id = tree.commit('rev1')
2891
 
        tree.branch.lock_write()
2892
 
        self.addCleanup(tree.branch.unlock)
2893
 
        tt = TransformPreview(tree)
2894
 
        tt.new_file('file', tt.root, 'contents', 'file_id')
2895
 
        self.addCleanup(tt.finalize)
2896
 
        preview = tt.get_preview_tree()
2897
 
        preview.set_parent_ids([rev_id])
2898
 
        builder = tree.branch.get_commit_builder([rev_id])
2899
 
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2900
 
        builder.finish_inventory()
2901
 
        rev2_id = builder.commit('rev2')
2902
 
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2903
 
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2904
 
 
2905
 
    def test_ascii_limbo_paths(self):
2906
 
        self.requireFeature(tests.UnicodeFilenameFeature)
2907
 
        branch = self.make_branch('any')
2908
 
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2909
 
        tt = TransformPreview(tree)
2910
 
        self.addCleanup(tt.finalize)
2911
 
        foo_id = tt.new_directory('', ROOT_PARENT)
2912
 
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2913
 
        limbo_path = tt._limbo_name(bar_id)
2914
 
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2915
 
 
2916
2588
 
2917
2589
class FakeSerializer(object):
2918
2590
    """Serializer implementation that simply returns the input.
3013
2685
        self.assertSerializesTo(self.symlink_creation_records(), tt)
3014
2686
 
3015
2687
    def test_deserialize_symlink_creation(self):
3016
 
        self.requireFeature(tests.SymlinkFeature)
3017
2688
        tt = self.get_preview()
3018
2689
        tt.deserialize(iter(self.symlink_creation_records()))
3019
 
        abspath = tt._limbo_name('new-1')
3020
 
        foo_content = osutils.readlink(abspath)
 
2690
        # XXX readlink should be returning unicode, not utf-8
 
2691
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
3021
2692
        self.assertEqual(u'bar\u1234', foo_content)
3022
2693
 
3023
2694
    def make_destruction_preview(self):