~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Jelmer Vernooij
  • Date: 2009-04-10 15:58:09 UTC
  • mto: This revision was merged to the branch mainline in revision 4284.
  • Revision ID: jelmer@samba.org-20090410155809-kdibzcjvp7pdb83f
Fix missing import.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
import errno
18
17
import os
 
18
import stat
19
19
from StringIO import StringIO
20
20
import sys
21
 
import time
22
21
 
23
22
from bzrlib import (
24
 
    bencode,
25
23
    errors,
26
 
    filters,
27
24
    generate_ids,
28
25
    osutils,
 
26
    progress,
29
27
    revision as _mod_revision,
30
 
    rules,
31
28
    symbol_versioning,
32
29
    tests,
33
 
    trace,
34
 
    transform,
35
30
    urlutils,
36
31
    )
37
32
from bzrlib.bzrdir import BzrDir
38
 
from bzrlib.conflicts import (
39
 
    DeletingParent,
40
 
    DuplicateEntry,
41
 
    DuplicateID,
42
 
    MissingParent,
43
 
    NonDirectoryParent,
44
 
    ParentLoop,
45
 
    UnversionedParent,
46
 
)
 
33
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
 
34
                              UnversionedParent, ParentLoop, DeletingParent,
 
35
                              NonDirectoryParent)
47
36
from bzrlib.diff import show_diff_trees
48
 
from bzrlib.errors import (
49
 
    DuplicateKey,
50
 
    ExistingLimbo,
51
 
    ExistingPendingDeletion,
52
 
    ImmortalLimbo,
53
 
    ImmortalPendingDeletion,
54
 
    LockError,
55
 
    MalformedTransform,
56
 
    ReusingTransform,
57
 
)
58
 
from bzrlib.osutils import (
59
 
    file_kind,
60
 
    pathjoin,
61
 
)
 
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
62
43
from bzrlib.merge import Merge3Merger, Merger
63
44
from bzrlib.tests import (
64
 
    features,
65
45
    HardlinkFeature,
66
46
    SymlinkFeature,
 
47
    TestCase,
67
48
    TestCaseInTempDir,
68
49
    TestSkipped,
69
 
)
70
 
from bzrlib.transform import (
71
 
    build_tree,
72
 
    create_from_tree,
73
 
    cook_conflicts,
74
 
    _FileMover,
75
 
    FinalPaths,
76
 
    resolve_conflicts,
77
 
    resolve_checkout,
78
 
    ROOT_PARENT,
79
 
    TransformPreview,
80
 
    TreeTransform,
81
 
)
 
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
82
57
 
83
58
 
84
59
class TestTreeTransform(tests.TestCaseWithTransport):
93
68
        self.addCleanup(transform.finalize)
94
69
        return transform, transform.root
95
70
 
96
 
    def get_transform_for_sha1_test(self):
97
 
        trans, root = self.get_transform()
98
 
        self.wt.lock_tree_write()
99
 
        self.addCleanup(self.wt.unlock)
100
 
        contents = ['just some content\n']
101
 
        sha1 = osutils.sha_strings(contents)
102
 
        # Roll back the clock
103
 
        trans._creation_mtime = time.time() - 20.0
104
 
        return trans, root, contents, sha1
105
 
 
106
71
    def test_existing_limbo(self):
107
72
        transform, root = self.get_transform()
108
73
        limbo_name = transform._limbodir
135
100
        imaginary_id = transform.trans_id_tree_path('imaginary')
136
101
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
137
102
        self.assertEqual(imaginary_id, imaginary_id2)
138
 
        self.assertEqual(root, transform.get_tree_parent(imaginary_id))
139
 
        self.assertEqual('directory', transform.final_kind(root))
140
 
        self.assertEqual(self.wt.get_root_id(), transform.final_file_id(root))
 
103
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
 
104
        self.assertEqual(transform.final_kind(root), 'directory')
 
105
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
141
106
        trans_id = transform.create_path('name', root)
142
107
        self.assertIs(transform.final_file_id(trans_id), None)
143
 
        self.assertIs(None, transform.final_kind(trans_id))
 
108
        self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
144
109
        transform.create_file('contents', trans_id)
145
110
        transform.set_executability(True, trans_id)
146
111
        transform.version_file('my_pretties', trans_id)
171
136
        transform.finalize()
172
137
        transform.finalize()
173
138
 
174
 
    def test_apply_informs_tree_of_observed_sha1(self):
175
 
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
176
 
        trans_id = trans.new_file('file1', root, contents, file_id='file1-id',
177
 
                                  sha1=sha1)
178
 
        calls = []
179
 
        orig = self.wt._observed_sha1
180
 
        def _observed_sha1(*args):
181
 
            calls.append(args)
182
 
            orig(*args)
183
 
        self.wt._observed_sha1 = _observed_sha1
184
 
        trans.apply()
185
 
        self.assertEqual([(None, 'file1', trans._observed_sha1s[trans_id])],
186
 
                         calls)
187
 
 
188
 
    def test_create_file_caches_sha1(self):
189
 
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
190
 
        trans_id = trans.create_path('file1', root)
191
 
        trans.create_file(contents, trans_id, sha1=sha1)
192
 
        st_val = osutils.lstat(trans._limbo_name(trans_id))
193
 
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
194
 
        self.assertEqual(o_sha1, sha1)
195
 
        self.assertEqualStat(o_st_val, st_val)
196
 
 
197
 
    def test__apply_insertions_updates_sha1(self):
198
 
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
199
 
        trans_id = trans.create_path('file1', root)
200
 
        trans.create_file(contents, trans_id, sha1=sha1)
201
 
        st_val = osutils.lstat(trans._limbo_name(trans_id))
202
 
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
203
 
        self.assertEqual(o_sha1, sha1)
204
 
        self.assertEqualStat(o_st_val, st_val)
205
 
        creation_mtime = trans._creation_mtime + 10.0
206
 
        # We fake a time difference from when the file was created until now it
207
 
        # is being renamed by using os.utime. Note that the change we actually
208
 
        # want to see is the real ctime change from 'os.rename()', but as long
209
 
        # as we observe a new stat value, we should be fine.
210
 
        os.utime(trans._limbo_name(trans_id), (creation_mtime, creation_mtime))
211
 
        trans.apply()
212
 
        new_st_val = osutils.lstat(self.wt.abspath('file1'))
213
 
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
214
 
        self.assertEqual(o_sha1, sha1)
215
 
        self.assertEqualStat(o_st_val, new_st_val)
216
 
        self.assertNotEqual(st_val.st_mtime, new_st_val.st_mtime)
217
 
 
218
 
    def test_new_file_caches_sha1(self):
219
 
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
220
 
        trans_id = trans.new_file('file1', root, contents, file_id='file1-id',
221
 
                                  sha1=sha1)
222
 
        st_val = osutils.lstat(trans._limbo_name(trans_id))
223
 
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
224
 
        self.assertEqual(o_sha1, sha1)
225
 
        self.assertEqualStat(o_st_val, st_val)
226
 
 
227
 
    def test_cancel_creation_removes_observed_sha1(self):
228
 
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
229
 
        trans_id = trans.new_file('file1', root, contents, file_id='file1-id',
230
 
                                  sha1=sha1)
231
 
        self.assertTrue(trans_id in trans._observed_sha1s)
232
 
        trans.cancel_creation(trans_id)
233
 
        self.assertFalse(trans_id in trans._observed_sha1s)
234
 
 
235
 
    def test_create_files_same_timestamp(self):
236
 
        transform, root = self.get_transform()
237
 
        self.wt.lock_tree_write()
238
 
        self.addCleanup(self.wt.unlock)
239
 
        # Roll back the clock, so that we know everything is being set to the
240
 
        # exact time
241
 
        transform._creation_mtime = creation_mtime = time.time() - 20.0
242
 
        transform.create_file('content-one',
243
 
                              transform.create_path('one', root))
244
 
        time.sleep(1) # *ugly*
245
 
        transform.create_file('content-two',
246
 
                              transform.create_path('two', root))
247
 
        transform.apply()
248
 
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
249
 
        fo.close()
250
 
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
251
 
        fo.close()
252
 
        # We only guarantee 2s resolution
253
 
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
254
 
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
255
 
        # But if we have more than that, all files should get the same result
256
 
        self.assertEqual(st1.st_mtime, st2.st_mtime)
257
 
 
258
 
    def test_change_root_id(self):
259
 
        transform, root = self.get_transform()
260
 
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
261
 
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
262
 
        transform.delete_contents(root)
263
 
        transform.unversion_file(root)
264
 
        transform.fixup_new_roots()
265
 
        transform.apply()
266
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
267
 
 
268
 
    def test_change_root_id_add_files(self):
269
 
        transform, root = self.get_transform()
270
 
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
271
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
272
 
        transform.new_file('file', new_trans_id, ['new-contents\n'],
273
 
                           'new-file-id')
274
 
        transform.delete_contents(root)
275
 
        transform.unversion_file(root)
276
 
        transform.fixup_new_roots()
277
 
        transform.apply()
278
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
279
 
        self.assertEqual('new-file-id', self.wt.path2id('file'))
280
 
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
281
 
 
282
 
    def test_add_two_roots(self):
283
 
        transform, root = self.get_transform()
284
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
285
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
286
 
        self.assertRaises(ValueError, transform.fixup_new_roots)
287
 
 
288
 
    def test_add_unversioned_root(self):
289
 
        transform, root = self.get_transform()
290
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, None)
291
 
        transform.fixup_new_roots()
292
 
        self.assertNotIn(transform.root, transform._new_id)
293
 
 
294
 
    def test_remove_root_fixup(self):
295
 
        transform, root = self.get_transform()
296
 
        old_root_id = self.wt.get_root_id()
297
 
        self.assertNotEqual('new-root-id', old_root_id)
298
 
        transform.delete_contents(root)
299
 
        transform.unversion_file(root)
300
 
        transform.fixup_new_roots()
301
 
        transform.apply()
302
 
        self.assertEqual(old_root_id, self.wt.get_root_id())
303
 
 
304
 
        transform, root = self.get_transform()
305
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
306
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
307
 
        self.assertRaises(ValueError, transform.fixup_new_roots)
308
 
 
309
 
    def test_apply_retains_root_directory(self):
310
 
        # Do not attempt to delete the physical root directory, because that
311
 
        # is impossible.
312
 
        transform, root = self.get_transform()
313
 
        with transform:
314
 
            transform.delete_contents(root)
315
 
            e = self.assertRaises(AssertionError, self.assertRaises,
316
 
                                  errors.TransformRenameFailed,
317
 
                                  transform.apply)
318
 
        self.assertContainsRe('TransformRenameFailed not raised', str(e))
319
 
 
320
139
    def test_hardlink(self):
321
140
        self.requireFeature(HardlinkFeature)
322
141
        transform, root = self.get_transform()
327
146
        trans_id = target_transform.create_path('file1', target_transform.root)
328
147
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
329
148
        target_transform.apply()
330
 
        self.assertPathExists('target/file1')
 
149
        self.failUnlessExists('target/file1')
331
150
        source_stat = os.stat(self.wt.abspath('file1'))
332
151
        target_stat = os.stat('target/file1')
333
152
        self.assertEqual(source_stat, target_stat)
499
318
        transform.new_file('FiLe', transform.root, 'content')
500
319
        resolve_conflicts(transform)
501
320
        transform.apply()
502
 
        self.assertPathExists('tree/file')
503
 
        self.assertPathExists('tree/FiLe.moved')
 
321
        self.failUnlessExists('tree/file')
 
322
        self.failUnlessExists('tree/FiLe.moved')
504
323
 
505
324
    def test_resolve_checkout_case_conflict(self):
506
325
        tree = self.make_branch_and_tree('tree')
515
334
        resolve_conflicts(transform,
516
335
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
517
336
        transform.apply()
518
 
        self.assertPathExists('tree/file')
519
 
        self.assertPathExists('tree/FiLe.moved')
 
337
        self.failUnlessExists('tree/file')
 
338
        self.failUnlessExists('tree/FiLe.moved')
520
339
 
521
340
    def test_apply_case_conflict(self):
522
341
        """Ensure that a transform with case conflicts can always be applied"""
530
349
        transform.new_file('dirFiLe', dir, 'content')
531
350
        resolve_conflicts(transform)
532
351
        transform.apply()
533
 
        self.assertPathExists('tree/file')
 
352
        self.failUnlessExists('tree/file')
534
353
        if not os.path.exists('tree/FiLe.moved'):
535
 
            self.assertPathExists('tree/FiLe')
536
 
        self.assertPathExists('tree/dir/dirfile')
 
354
            self.failUnlessExists('tree/FiLe')
 
355
        self.failUnlessExists('tree/dir/dirfile')
537
356
        if not os.path.exists('tree/dir/dirFiLe.moved'):
538
 
            self.assertPathExists('tree/dir/dirFiLe')
 
357
            self.failUnlessExists('tree/dir/dirFiLe')
539
358
 
540
359
    def test_case_insensitive_limbo(self):
541
360
        tree = self.make_branch_and_tree('tree')
550
369
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
551
370
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
552
371
 
553
 
    def test_adjust_path_updates_child_limbo_names(self):
554
 
        tree = self.make_branch_and_tree('tree')
555
 
        transform = TreeTransform(tree)
556
 
        self.addCleanup(transform.finalize)
557
 
        foo_id = transform.new_directory('foo', transform.root)
558
 
        bar_id = transform.new_directory('bar', foo_id)
559
 
        baz_id = transform.new_directory('baz', bar_id)
560
 
        qux_id = transform.new_directory('qux', baz_id)
561
 
        transform.adjust_path('quxx', foo_id, bar_id)
562
 
        self.assertStartsWith(transform._limbo_name(qux_id),
563
 
                              transform._limbo_name(bar_id))
564
 
 
565
372
    def test_add_del(self):
566
373
        start, root = self.get_transform()
567
374
        start.new_directory('a', root, 'a')
718
525
        resolve_conflicts(replace)
719
526
        replace.apply()
720
527
 
721
 
    def _test_symlinks(self, link_name1,link_target1,
722
 
                       link_name2, link_target2):
723
 
 
724
 
        def ozpath(p): return 'oz/' + p
725
 
 
 
528
    def test_symlinks(self):
726
529
        self.requireFeature(SymlinkFeature)
727
 
        transform, root = self.get_transform()
 
530
        transform,root = self.get_transform()
728
531
        oz_id = transform.new_directory('oz', root, 'oz-id')
729
 
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
 
532
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target',
730
533
                                       'wizard-id')
731
 
        wiz_id = transform.create_path(link_name2, oz_id)
732
 
        transform.create_symlink(link_target2, wiz_id)
 
534
        wiz_id = transform.create_path('wizard2', oz_id)
 
535
        transform.create_symlink('behind_curtain', wiz_id)
733
536
        transform.version_file('wiz-id2', wiz_id)
734
537
        transform.set_executability(True, wiz_id)
735
538
        self.assertEqual(transform.find_conflicts(),
736
539
                         [('non-file executability', wiz_id)])
737
540
        transform.set_executability(None, wiz_id)
738
541
        transform.apply()
739
 
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
740
 
        self.assertEqual('symlink',
741
 
                         file_kind(self.wt.abspath(ozpath(link_name1))))
742
 
        self.assertEqual(link_target2,
743
 
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
744
 
        self.assertEqual(link_target1,
745
 
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
746
 
 
747
 
    def test_symlinks(self):
748
 
        self._test_symlinks('wizard', 'wizard-target',
749
 
                            'wizard2', 'behind_curtain')
750
 
 
751
 
    def test_symlinks_unicode(self):
752
 
        self.requireFeature(tests.UnicodeFilenameFeature)
753
 
        self._test_symlinks(u'\N{Euro Sign}wizard',
754
 
                            u'wizard-targ\N{Euro Sign}t',
755
 
                            u'\N{Euro Sign}wizard2',
756
 
                            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')
757
548
 
758
549
    def test_unable_create_symlink(self):
759
550
        def tt_helper():
843
634
        raw_conflicts = resolve_conflicts(tt)
844
635
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
845
636
        tt.finalize()
846
 
        conflicts_s = [unicode(c) for c in cooked_conflicts]
 
637
        conflicts_s = [str(c) for c in cooked_conflicts]
847
638
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
848
639
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
849
640
                                         'Moved existing file to '
862
653
                                         ' versioned, but has versioned'
863
654
                                         ' children.  Versioned directory.')
864
655
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
865
 
                                         ' oz/emeraldcity. Cancelled move.')
 
656
                                         ' oz/emeraldcity.  Cancelled move.')
866
657
 
867
658
    def prepare_wrong_parent_kind(self):
868
659
        tt, root = self.get_transform()
919
710
        self.assertIs(None, self.wt.path2id('parent'))
920
711
        self.assertIs(None, self.wt.path2id('parent.new'))
921
712
 
922
 
    def test_resolve_conflicts_missing_parent(self):
923
 
        wt = self.make_branch_and_tree('.')
924
 
        tt = TreeTransform(wt)
925
 
        self.addCleanup(tt.finalize)
926
 
        parent = tt.trans_id_file_id('parent-id')
927
 
        tt.new_file('file', parent, 'Contents')
928
 
        raw_conflicts = resolve_conflicts(tt)
929
 
        # Since the directory doesn't exist it's seen as 'missing'.  So
930
 
        # 'resolve_conflicts' create a conflict asking for it to be created.
931
 
        self.assertLength(1, raw_conflicts)
932
 
        self.assertEqual(('missing parent', 'Created directory', 'new-1'),
933
 
                         raw_conflicts.pop())
934
 
        # apply fail since the missing directory doesn't exist
935
 
        self.assertRaises(errors.NoFinalPath, tt.apply)
936
 
 
937
713
    def test_moving_versioned_directories(self):
938
714
        create, root = self.get_transform()
939
715
        kansas = create.new_directory('kansas', root, 'kansas-id')
954
730
        create.apply()
955
731
        transform, root = self.get_transform()
956
732
        transform.adjust_root_path('oldroot', fun)
957
 
        new_root = transform.trans_id_tree_path('')
 
733
        new_root=transform.trans_id_tree_path('')
958
734
        transform.version_file('new-root', new_root)
959
735
        transform.apply()
960
736
 
972
748
        rename.set_executability(True, myfile)
973
749
        rename.apply()
974
750
 
975
 
    def test_rename_fails(self):
976
 
        self.requireFeature(features.not_running_as_root)
977
 
        # see https://bugs.launchpad.net/bzr/+bug/491763
978
 
        create, root_id = self.get_transform()
979
 
        first_dir = create.new_directory('first-dir', root_id, 'first-id')
980
 
        myfile = create.new_file('myfile', root_id, 'myfile-text',
981
 
                                 'myfile-id')
982
 
        create.apply()
983
 
        if os.name == "posix" and sys.platform != "cygwin":
984
 
            # posix filesystems fail on renaming if the readonly bit is set
985
 
            osutils.make_readonly(self.wt.abspath('first-dir'))
986
 
        elif os.name == "nt":
987
 
            # windows filesystems fail on renaming open files
988
 
            self.addCleanup(file(self.wt.abspath('myfile')).close)
989
 
        else:
990
 
            self.skip("Don't know how to force a permissions error on rename")
991
 
        # now transform to rename
992
 
        rename_transform, root_id = self.get_transform()
993
 
        file_trans_id = rename_transform.trans_id_file_id('myfile-id')
994
 
        dir_id = rename_transform.trans_id_file_id('first-id')
995
 
        rename_transform.adjust_path('newname', dir_id, file_trans_id)
996
 
        e = self.assertRaises(errors.TransformRenameFailed,
997
 
            rename_transform.apply)
998
 
        # On nix looks like: 
999
 
        # "Failed to rename .../work/.bzr/checkout/limbo/new-1
1000
 
        # to .../first-dir/newname: [Errno 13] Permission denied"
1001
 
        # On windows looks like:
1002
 
        # "Failed to rename .../work/myfile to 
1003
 
        # .../work/.bzr/checkout/limbo/new-1: [Errno 13] Permission denied"
1004
 
        # This test isn't concerned with exactly what the error looks like,
1005
 
        # and the strerror will vary across OS and locales, but the assert
1006
 
        # that the exeception attributes are what we expect
1007
 
        self.assertEqual(e.errno, errno.EACCES)
1008
 
        if os.name == "posix":
1009
 
            self.assertEndsWith(e.to_path, "/first-dir/newname")
1010
 
        else:
1011
 
            self.assertEqual(os.path.basename(e.from_path), "myfile")
1012
 
 
1013
751
    def test_set_executability_order(self):
1014
752
        """Ensure that executability behaves the same, no matter what order.
1015
753
 
1287
1025
        parent2 = transform.new_directory('parent2', root)
1288
1026
        transform.adjust_path('child1', parent2, child1)
1289
1027
        transform.apply()
1290
 
        self.assertPathDoesNotExist(self.wt.abspath('parent1/child1'))
1291
 
        self.assertPathExists(self.wt.abspath('parent2/child1'))
 
1028
        self.failIfExists(self.wt.abspath('parent1/child1'))
 
1029
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1292
1030
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1293
1031
        # no rename for child1 (counting only renames during apply)
1294
 
        self.assertEqual(2, transform.rename_count)
 
1032
        self.failUnlessEqual(2, transform.rename_count)
1295
1033
 
1296
1034
    def test_cancel_parent(self):
1297
1035
        """Cancelling a parent doesn't cause deletion of a non-empty directory
1320
1058
        parent2 = transform.new_directory('parent2', root)
1321
1059
        transform.adjust_path('child1', parent2, child1)
1322
1060
        transform.apply()
1323
 
        self.assertPathDoesNotExist(self.wt.abspath('parent1'))
1324
 
        self.assertPathExists(self.wt.abspath('parent2/child1'))
 
1061
        self.failIfExists(self.wt.abspath('parent1'))
 
1062
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1325
1063
        # rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1326
 
        self.assertEqual(2, transform.rename_count)
 
1064
        self.failUnlessEqual(2, transform.rename_count)
1327
1065
 
1328
1066
    def test_adjust_and_cancel(self):
1329
1067
        """Make sure adjust_path keeps track of limbo children properly"""
1362
1100
        child = transform.new_directory('child', parent)
1363
1101
        transform.adjust_path('parent', root, parent)
1364
1102
        transform.apply()
1365
 
        self.assertPathExists(self.wt.abspath('parent/child'))
 
1103
        self.failUnlessExists(self.wt.abspath('parent/child'))
1366
1104
        self.assertEqual(1, transform.rename_count)
1367
1105
 
1368
1106
    def test_reuse_name(self):
1499
1237
        tt.create_file(["aa\n"], bar_trans_id)
1500
1238
        tt.version_file("bar-1", bar_trans_id)
1501
1239
        tt.apply()
1502
 
        self.assertPathExists("foo/bar")
 
1240
        self.failUnlessExists("foo/bar")
1503
1241
        wt.lock_read()
1504
1242
        try:
1505
1243
            self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1522
1260
        tt.delete_contents(foo_trans_id)
1523
1261
        tt.create_symlink("bar", foo_trans_id)
1524
1262
        tt.apply()
1525
 
        self.assertPathExists("foo")
 
1263
        self.failUnlessExists("foo")
1526
1264
        wt.lock_read()
1527
1265
        self.addCleanup(wt.unlock)
1528
1266
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1541
1279
        tt.delete_versioned(bar_trans_id)
1542
1280
        tt.create_file(["aa\n"], foo_trans_id)
1543
1281
        tt.apply()
1544
 
        self.assertPathExists("foo")
 
1282
        self.failUnlessExists("foo")
1545
1283
        wt.lock_read()
1546
1284
        self.addCleanup(wt.unlock)
1547
1285
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1562
1300
        self.build_tree(['baz'])
1563
1301
        tt.create_hardlink("baz", foo_trans_id)
1564
1302
        tt.apply()
1565
 
        self.assertPathExists("foo")
1566
 
        self.assertPathExists("baz")
 
1303
        self.failUnlessExists("foo")
 
1304
        self.failUnlessExists("baz")
1567
1305
        wt.lock_read()
1568
1306
        self.addCleanup(wt.unlock)
1569
1307
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1631
1369
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
1632
1370
 
1633
1371
 
1634
 
class TestInventoryAltered(tests.TestCaseWithTransport):
1635
 
 
1636
 
    def test_inventory_altered_unchanged(self):
1637
 
        tree = self.make_branch_and_tree('tree')
1638
 
        self.build_tree(['tree/foo'])
1639
 
        tree.add('foo', 'foo-id')
1640
 
        with TransformPreview(tree) as tt:
1641
 
            self.assertEqual([], tt._inventory_altered())
1642
 
 
1643
 
    def test_inventory_altered_changed_parent_id(self):
1644
 
        tree = self.make_branch_and_tree('tree')
1645
 
        self.build_tree(['tree/foo'])
1646
 
        tree.add('foo', 'foo-id')
1647
 
        with TransformPreview(tree) as tt:
1648
 
            tt.unversion_file(tt.root)
1649
 
            tt.version_file('new-id', tt.root)
1650
 
            foo_trans_id = tt.trans_id_tree_file_id('foo-id')
1651
 
            foo_tuple = ('foo', foo_trans_id)
1652
 
            root_tuple = ('', tt.root)
1653
 
            self.assertEqual([root_tuple, foo_tuple], tt._inventory_altered())
1654
 
 
1655
 
    def test_inventory_altered_noop_changed_parent_id(self):
1656
 
        tree = self.make_branch_and_tree('tree')
1657
 
        self.build_tree(['tree/foo'])
1658
 
        tree.add('foo', 'foo-id')
1659
 
        with TransformPreview(tree) as tt:
1660
 
            tt.unversion_file(tt.root)
1661
 
            tt.version_file(tree.get_root_id(), tt.root)
1662
 
            foo_trans_id = tt.trans_id_tree_file_id('foo-id')
1663
 
            self.assertEqual([], tt._inventory_altered())
1664
 
 
1665
 
 
1666
1372
class TestTransformMerge(TestCaseInTempDir):
1667
1373
 
1668
1374
    def test_text_merge(self):
1878
1584
        tree.add_reference(subtree)
1879
1585
        tree.commit('a revision')
1880
1586
        tree.branch.create_checkout('target')
1881
 
        self.assertPathExists('target')
1882
 
        self.assertPathExists('target/subtree')
 
1587
        self.failUnlessExists('target')
 
1588
        self.failUnlessExists('target/subtree')
1883
1589
 
1884
1590
    def test_file_conflict_handling(self):
1885
1591
        """Ensure that when building trees, conflict handling is done"""
1932
1638
        source.commit('added file')
1933
1639
        build_tree(source.basis_tree(), target)
1934
1640
        self.assertEqual([], target.conflicts())
1935
 
        self.assertPathExists('target/dir1/file')
 
1641
        self.failUnlessExists('target/dir1/file')
1936
1642
 
1937
1643
        # Ensure contents are merged
1938
1644
        target = self.make_branch_and_tree('target2')
1939
1645
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1940
1646
        build_tree(source.basis_tree(), target)
1941
1647
        self.assertEqual([], target.conflicts())
1942
 
        self.assertPathExists('target2/dir1/file2')
1943
 
        self.assertPathExists('target2/dir1/file')
 
1648
        self.failUnlessExists('target2/dir1/file2')
 
1649
        self.failUnlessExists('target2/dir1/file')
1944
1650
 
1945
1651
        # Ensure new contents are suppressed for existing branches
1946
1652
        target = self.make_branch_and_tree('target3')
1947
1653
        self.make_branch('target3/dir1')
1948
1654
        self.build_tree(['target3/dir1/file2'])
1949
1655
        build_tree(source.basis_tree(), target)
1950
 
        self.assertPathDoesNotExist('target3/dir1/file')
1951
 
        self.assertPathExists('target3/dir1/file2')
1952
 
        self.assertPathExists('target3/dir1.diverted/file')
 
1656
        self.failIfExists('target3/dir1/file')
 
1657
        self.failUnlessExists('target3/dir1/file2')
 
1658
        self.failUnlessExists('target3/dir1.diverted/file')
1953
1659
        self.assertEqual([DuplicateEntry('Diverted to',
1954
1660
            'dir1.diverted', 'dir1', 'new-dir1', None)],
1955
1661
            target.conflicts())
1958
1664
        self.build_tree(['target4/dir1/'])
1959
1665
        self.make_branch('target4/dir1/file')
1960
1666
        build_tree(source.basis_tree(), target)
1961
 
        self.assertPathExists('target4/dir1/file')
 
1667
        self.failUnlessExists('target4/dir1/file')
1962
1668
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1963
 
        self.assertPathExists('target4/dir1/file.diverted')
 
1669
        self.failUnlessExists('target4/dir1/file.diverted')
1964
1670
        self.assertEqual([DuplicateEntry('Diverted to',
1965
1671
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1966
1672
            target.conflicts())
2034
1740
        self.addCleanup(target.unlock)
2035
1741
        self.assertEqual([], list(target.iter_changes(revision_tree)))
2036
1742
 
2037
 
    def test_build_tree_accelerator_tree_observes_sha1(self):
2038
 
        source = self.create_ab_tree()
2039
 
        sha1 = osutils.sha_string('A')
2040
 
        target = self.make_branch_and_tree('target')
2041
 
        target.lock_write()
2042
 
        self.addCleanup(target.unlock)
2043
 
        state = target.current_dirstate()
2044
 
        state._cutoff_time = time.time() + 60
2045
 
        build_tree(source.basis_tree(), target, source)
2046
 
        entry = state._get_entry(0, path_utf8='file1')
2047
 
        self.assertEqual(sha1, entry[1][0][1])
2048
 
 
2049
1743
    def test_build_tree_accelerator_tree_missing_file(self):
2050
1744
        source = self.create_ab_tree()
2051
1745
        os.unlink('source/file1')
2146
1840
        self.assertEqual([], list(target.iter_changes(revision_tree)))
2147
1841
        self.assertTrue(source.is_executable('file1-id'))
2148
1842
 
2149
 
    def install_rot13_content_filter(self, pattern):
2150
 
        # We could use
2151
 
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
2152
 
        # below, but that looks a bit... hard to read even if it's exactly
2153
 
        # the same thing.
2154
 
        original_registry = filters._reset_registry()
2155
 
        def restore_registry():
2156
 
            filters._reset_registry(original_registry)
2157
 
        self.addCleanup(restore_registry)
2158
 
        def rot13(chunks, context=None):
2159
 
            return [''.join(chunks).encode('rot13')]
2160
 
        rot13filter = filters.ContentFilter(rot13, rot13)
2161
 
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
2162
 
        os.mkdir(self.test_home_dir + '/.bazaar')
2163
 
        rules_filename = self.test_home_dir + '/.bazaar/rules'
2164
 
        f = open(rules_filename, 'wb')
2165
 
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
2166
 
        f.close()
2167
 
        def uninstall_rules():
2168
 
            os.remove(rules_filename)
2169
 
            rules.reset_rules()
2170
 
        self.addCleanup(uninstall_rules)
2171
 
        rules.reset_rules()
2172
 
 
2173
 
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
2174
 
        """build_tree will not hardlink files that have content filtering rules
2175
 
        applied to them (but will still hardlink other files from the same tree
2176
 
        if it can).
2177
 
        """
2178
 
        self.requireFeature(HardlinkFeature)
2179
 
        self.install_rot13_content_filter('file1')
2180
 
        source = self.create_ab_tree()
2181
 
        target = self.make_branch_and_tree('target')
2182
 
        revision_tree = source.basis_tree()
2183
 
        revision_tree.lock_read()
2184
 
        self.addCleanup(revision_tree.unlock)
2185
 
        build_tree(revision_tree, target, source, hardlink=True)
2186
 
        target.lock_read()
2187
 
        self.addCleanup(target.unlock)
2188
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
2189
 
        source_stat = os.stat('source/file1')
2190
 
        target_stat = os.stat('target/file1')
2191
 
        self.assertNotEqual(source_stat, target_stat)
2192
 
        source_stat = os.stat('source/file2')
2193
 
        target_stat = os.stat('target/file2')
2194
 
        self.assertEqualStat(source_stat, target_stat)
2195
 
 
2196
1843
    def test_case_insensitive_build_tree_inventory(self):
2197
 
        if (tests.CaseInsensitiveFilesystemFeature.available()
2198
 
            or tests.CaseInsCasePresFilenameFeature.available()):
2199
 
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
2200
1844
        source = self.make_branch_and_tree('source')
2201
1845
        self.build_tree(['source/file', 'source/FILE'])
2202
1846
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
2209
1853
        self.assertEqual('file.moved', target.id2path('lower-id'))
2210
1854
        self.assertEqual('FILE', target.id2path('upper-id'))
2211
1855
 
2212
 
    def test_build_tree_observes_sha(self):
2213
 
        source = self.make_branch_and_tree('source')
2214
 
        self.build_tree(['source/file1', 'source/dir/', 'source/dir/file2'])
2215
 
        source.add(['file1', 'dir', 'dir/file2'],
2216
 
                   ['file1-id', 'dir-id', 'file2-id'])
2217
 
        source.commit('new files')
2218
 
        target = self.make_branch_and_tree('target')
2219
 
        target.lock_write()
2220
 
        self.addCleanup(target.unlock)
2221
 
        # We make use of the fact that DirState caches its cutoff time. So we
2222
 
        # set the 'safe' time to one minute in the future.
2223
 
        state = target.current_dirstate()
2224
 
        state._cutoff_time = time.time() + 60
2225
 
        build_tree(source.basis_tree(), target)
2226
 
        entry1_sha = osutils.sha_file_by_name('source/file1')
2227
 
        entry2_sha = osutils.sha_file_by_name('source/dir/file2')
2228
 
        # entry[1] is the state information, entry[1][0] is the state of the
2229
 
        # working tree, entry[1][0][1] is the sha value for the current working
2230
 
        # tree
2231
 
        entry1 = state._get_entry(0, path_utf8='file1')
2232
 
        self.assertEqual(entry1_sha, entry1[1][0][1])
2233
 
        # The 'size' field must also be set.
2234
 
        self.assertEqual(25, entry1[1][0][2])
2235
 
        entry1_state = entry1[1][0]
2236
 
        entry2 = state._get_entry(0, path_utf8='dir/file2')
2237
 
        self.assertEqual(entry2_sha, entry2[1][0][1])
2238
 
        self.assertEqual(29, entry2[1][0][2])
2239
 
        entry2_state = entry2[1][0]
2240
 
        # Now, make sure that we don't have to re-read the content. The
2241
 
        # packed_stat should match exactly.
2242
 
        self.assertEqual(entry1_sha, target.get_file_sha1('file1-id', 'file1'))
2243
 
        self.assertEqual(entry2_sha,
2244
 
                         target.get_file_sha1('file2-id', 'dir/file2'))
2245
 
        self.assertEqual(entry1_state, entry1[1][0])
2246
 
        self.assertEqual(entry2_state, entry2[1][0])
2247
 
 
2248
 
 
2249
 
class TestCommitTransform(tests.TestCaseWithTransport):
2250
 
 
2251
 
    def get_branch(self):
2252
 
        tree = self.make_branch_and_tree('tree')
2253
 
        tree.lock_write()
2254
 
        self.addCleanup(tree.unlock)
2255
 
        tree.commit('empty commit')
2256
 
        return tree.branch
2257
 
 
2258
 
    def get_branch_and_transform(self):
2259
 
        branch = self.get_branch()
2260
 
        tt = TransformPreview(branch.basis_tree())
2261
 
        self.addCleanup(tt.finalize)
2262
 
        return branch, tt
2263
 
 
2264
 
    def test_commit_wrong_basis(self):
2265
 
        branch = self.get_branch()
2266
 
        basis = branch.repository.revision_tree(
2267
 
            _mod_revision.NULL_REVISION)
2268
 
        tt = TransformPreview(basis)
2269
 
        self.addCleanup(tt.finalize)
2270
 
        e = self.assertRaises(ValueError, tt.commit, branch, '')
2271
 
        self.assertEqual('TreeTransform not based on branch basis: null:',
2272
 
                         str(e))
2273
 
 
2274
 
    def test_empy_commit(self):
2275
 
        branch, tt = self.get_branch_and_transform()
2276
 
        rev = tt.commit(branch, 'my message')
2277
 
        self.assertEqual(2, branch.revno())
2278
 
        repo = branch.repository
2279
 
        self.assertEqual('my message', repo.get_revision(rev).message)
2280
 
 
2281
 
    def test_merge_parents(self):
2282
 
        branch, tt = self.get_branch_and_transform()
2283
 
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2284
 
        self.assertEqual(['rev1b', 'rev1c'],
2285
 
                         branch.basis_tree().get_parent_ids()[1:])
2286
 
 
2287
 
    def test_first_commit(self):
2288
 
        branch = self.make_branch('branch')
2289
 
        branch.lock_write()
2290
 
        self.addCleanup(branch.unlock)
2291
 
        tt = TransformPreview(branch.basis_tree())
2292
 
        self.addCleanup(tt.finalize)
2293
 
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2294
 
        rev = tt.commit(branch, 'my message')
2295
 
        self.assertEqual([], branch.basis_tree().get_parent_ids())
2296
 
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2297
 
                            branch.last_revision())
2298
 
 
2299
 
    def test_first_commit_with_merge_parents(self):
2300
 
        branch = self.make_branch('branch')
2301
 
        branch.lock_write()
2302
 
        self.addCleanup(branch.unlock)
2303
 
        tt = TransformPreview(branch.basis_tree())
2304
 
        self.addCleanup(tt.finalize)
2305
 
        e = self.assertRaises(ValueError, tt.commit, branch,
2306
 
                          'my message', ['rev1b-id'])
2307
 
        self.assertEqual('Cannot supply merge parents for first commit.',
2308
 
                         str(e))
2309
 
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2310
 
 
2311
 
    def test_add_files(self):
2312
 
        branch, tt = self.get_branch_and_transform()
2313
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2314
 
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2315
 
        if SymlinkFeature.available():
2316
 
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2317
 
        rev = tt.commit(branch, 'message')
2318
 
        tree = branch.basis_tree()
2319
 
        self.assertEqual('file', tree.id2path('file-id'))
2320
 
        self.assertEqual('contents', tree.get_file_text('file-id'))
2321
 
        self.assertEqual('dir', tree.id2path('dir-id'))
2322
 
        if SymlinkFeature.available():
2323
 
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2324
 
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2325
 
 
2326
 
    def test_add_unversioned(self):
2327
 
        branch, tt = self.get_branch_and_transform()
2328
 
        tt.new_file('file', tt.root, 'contents')
2329
 
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2330
 
                          'message', strict=True)
2331
 
 
2332
 
    def test_modify_strict(self):
2333
 
        branch, tt = self.get_branch_and_transform()
2334
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2335
 
        tt.commit(branch, 'message', strict=True)
2336
 
        tt = TransformPreview(branch.basis_tree())
2337
 
        self.addCleanup(tt.finalize)
2338
 
        trans_id = tt.trans_id_file_id('file-id')
2339
 
        tt.delete_contents(trans_id)
2340
 
        tt.create_file('contents', trans_id)
2341
 
        tt.commit(branch, 'message', strict=True)
2342
 
 
2343
 
    def test_commit_malformed(self):
2344
 
        """Committing a malformed transform should raise an exception.
2345
 
 
2346
 
        In this case, we are adding a file without adding its parent.
2347
 
        """
2348
 
        branch, tt = self.get_branch_and_transform()
2349
 
        parent_id = tt.trans_id_file_id('parent-id')
2350
 
        tt.new_file('file', parent_id, 'contents', 'file-id')
2351
 
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2352
 
                          'message')
2353
 
 
2354
 
    def test_commit_rich_revision_data(self):
2355
 
        branch, tt = self.get_branch_and_transform()
2356
 
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
2357
 
                           committer='me <me@example.com>',
2358
 
                           revprops={'foo': 'bar'}, revision_id='revid-1',
2359
 
                           authors=['Author1 <author1@example.com>',
2360
 
                              'Author2 <author2@example.com>',
2361
 
                               ])
2362
 
        self.assertEqual('revid-1', rev_id)
2363
 
        revision = branch.repository.get_revision(rev_id)
2364
 
        self.assertEqual(1, revision.timestamp)
2365
 
        self.assertEqual(43201, revision.timezone)
2366
 
        self.assertEqual('me <me@example.com>', revision.committer)
2367
 
        self.assertEqual(['Author1 <author1@example.com>',
2368
 
                          'Author2 <author2@example.com>'],
2369
 
                         revision.get_apparent_authors())
2370
 
        del revision.properties['authors']
2371
 
        self.assertEqual({'foo': 'bar',
2372
 
                          'branch-nick': 'tree'},
2373
 
                         revision.properties)
2374
 
 
2375
 
    def test_no_explicit_revprops(self):
2376
 
        branch, tt = self.get_branch_and_transform()
2377
 
        rev_id = tt.commit(branch, 'message', authors=[
2378
 
            'Author1 <author1@example.com>',
2379
 
            'Author2 <author2@example.com>', ])
2380
 
        revision = branch.repository.get_revision(rev_id)
2381
 
        self.assertEqual(['Author1 <author1@example.com>',
2382
 
                          'Author2 <author2@example.com>'],
2383
 
                         revision.get_apparent_authors())
2384
 
        self.assertEqual('tree', revision.properties['branch-nick'])
2385
 
 
2386
 
 
2387
 
class TestBackupName(tests.TestCase):
2388
 
 
2389
 
    def test_deprecations(self):
2390
 
        class MockTransform(object):
2391
 
 
2392
 
            def has_named_child(self, by_parent, parent_id, name):
2393
 
                return name in by_parent.get(parent_id, [])
2394
 
 
2395
 
        class MockEntry(object):
2396
 
 
2397
 
            def __init__(self):
2398
 
                object.__init__(self)
2399
 
                self.name = "name"
2400
 
 
 
1856
 
 
1857
class MockTransform(object):
 
1858
 
 
1859
    def has_named_child(self, by_parent, parent_id, name):
 
1860
        for child_id in by_parent[parent_id]:
 
1861
            if child_id == '0':
 
1862
                if name == "name~":
 
1863
                    return True
 
1864
            elif name == "name.~%s~" % child_id:
 
1865
                return True
 
1866
        return False
 
1867
 
 
1868
 
 
1869
class MockEntry(object):
 
1870
    def __init__(self):
 
1871
        object.__init__(self)
 
1872
        self.name = "name"
 
1873
 
 
1874
 
 
1875
class TestGetBackupName(TestCase):
 
1876
    def test_get_backup_name(self):
2401
1877
        tt = MockTransform()
2402
 
        name1 = self.applyDeprecated(
2403
 
            symbol_versioning.deprecated_in((2, 3, 0)),
2404
 
            transform.get_backup_name, MockEntry(), {'a':[]}, 'a', tt)
2405
 
        self.assertEqual('name.~1~', name1)
2406
 
        name2 = self.applyDeprecated(
2407
 
            symbol_versioning.deprecated_in((2, 3, 0)),
2408
 
            transform._get_backup_name, 'name', {'a':['name.~1~']}, 'a', tt)
2409
 
        self.assertEqual('name.~2~', name2)
 
1878
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
 
1879
        self.assertEqual(name, 'name.~1~')
 
1880
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
 
1881
        self.assertEqual(name, 'name.~2~')
 
1882
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
 
1883
        self.assertEqual(name, 'name.~1~')
 
1884
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
 
1885
        self.assertEqual(name, 'name.~1~')
 
1886
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
 
1887
        self.assertEqual(name, 'name.~4~')
2410
1888
 
2411
1889
 
2412
1890
class TestFileMover(tests.TestCaseWithTransport):
2415
1893
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2416
1894
        mover = _FileMover()
2417
1895
        mover.rename('a', 'q')
2418
 
        self.assertPathExists('q')
2419
 
        self.assertPathDoesNotExist('a')
2420
 
        self.assertPathExists('q/b')
2421
 
        self.assertPathExists('c')
2422
 
        self.assertPathExists('c/d')
 
1896
        self.failUnlessExists('q')
 
1897
        self.failIfExists('a')
 
1898
        self.failUnlessExists('q/b')
 
1899
        self.failUnlessExists('c')
 
1900
        self.failUnlessExists('c/d')
2423
1901
 
2424
1902
    def test_pre_delete_rollback(self):
2425
1903
        self.build_tree(['a/'])
2426
1904
        mover = _FileMover()
2427
1905
        mover.pre_delete('a', 'q')
2428
 
        self.assertPathExists('q')
2429
 
        self.assertPathDoesNotExist('a')
 
1906
        self.failUnlessExists('q')
 
1907
        self.failIfExists('a')
2430
1908
        mover.rollback()
2431
 
        self.assertPathDoesNotExist('q')
2432
 
        self.assertPathExists('a')
 
1909
        self.failIfExists('q')
 
1910
        self.failUnlessExists('a')
2433
1911
 
2434
1912
    def test_apply_deletions(self):
2435
1913
        self.build_tree(['a/', 'b/'])
2436
1914
        mover = _FileMover()
2437
1915
        mover.pre_delete('a', 'q')
2438
1916
        mover.pre_delete('b', 'r')
2439
 
        self.assertPathExists('q')
2440
 
        self.assertPathExists('r')
2441
 
        self.assertPathDoesNotExist('a')
2442
 
        self.assertPathDoesNotExist('b')
 
1917
        self.failUnlessExists('q')
 
1918
        self.failUnlessExists('r')
 
1919
        self.failIfExists('a')
 
1920
        self.failIfExists('b')
2443
1921
        mover.apply_deletions()
2444
 
        self.assertPathDoesNotExist('q')
2445
 
        self.assertPathDoesNotExist('r')
2446
 
        self.assertPathDoesNotExist('a')
2447
 
        self.assertPathDoesNotExist('b')
 
1922
        self.failIfExists('q')
 
1923
        self.failIfExists('r')
 
1924
        self.failIfExists('a')
 
1925
        self.failIfExists('b')
2448
1926
 
2449
1927
    def test_file_mover_rollback(self):
2450
1928
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2455
1933
            mover.rename('a', 'c')
2456
1934
        except errors.FileExists, e:
2457
1935
            mover.rollback()
2458
 
        self.assertPathExists('a')
2459
 
        self.assertPathExists('c/d')
 
1936
        self.failUnlessExists('a')
 
1937
        self.failUnlessExists('c/d')
2460
1938
 
2461
1939
 
2462
1940
class Bogus(Exception):
2492
1970
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2493
1971
        self.assertRaises(Bogus, tt.apply,
2494
1972
                          _mover=self.ExceptionFileMover(bad_source='a'))
2495
 
        self.assertPathExists('a')
2496
 
        self.assertPathExists('a/b')
 
1973
        self.failUnlessExists('a')
 
1974
        self.failUnlessExists('a/b')
2497
1975
        tt.apply()
2498
 
        self.assertPathExists('c')
2499
 
        self.assertPathExists('c/d')
 
1976
        self.failUnlessExists('c')
 
1977
        self.failUnlessExists('c/d')
2500
1978
 
2501
1979
    def test_rollback_rename_into_place(self):
2502
1980
        tree = self.make_branch_and_tree('.')
2508
1986
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2509
1987
        self.assertRaises(Bogus, tt.apply,
2510
1988
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
2511
 
        self.assertPathExists('a')
2512
 
        self.assertPathExists('a/b')
 
1989
        self.failUnlessExists('a')
 
1990
        self.failUnlessExists('a/b')
2513
1991
        tt.apply()
2514
 
        self.assertPathExists('c')
2515
 
        self.assertPathExists('c/d')
 
1992
        self.failUnlessExists('c')
 
1993
        self.failUnlessExists('c/d')
2516
1994
 
2517
1995
    def test_rollback_deletion(self):
2518
1996
        tree = self.make_branch_and_tree('.')
2524
2002
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2525
2003
        self.assertRaises(Bogus, tt.apply,
2526
2004
                          _mover=self.ExceptionFileMover(bad_target='d'))
2527
 
        self.assertPathExists('a')
2528
 
        self.assertPathExists('a/b')
2529
 
 
2530
 
 
2531
 
class TestFinalizeRobustness(tests.TestCaseWithTransport):
2532
 
    """Ensure treetransform creation errors can be safely cleaned up after"""
2533
 
 
2534
 
    def _override_globals_in_method(self, instance, method_name, globals):
2535
 
        """Replace method on instance with one with updated globals"""
2536
 
        import types
2537
 
        func = getattr(instance, method_name).im_func
2538
 
        new_globals = dict(func.func_globals)
2539
 
        new_globals.update(globals)
2540
 
        new_func = types.FunctionType(func.func_code, new_globals,
2541
 
            func.func_name, func.func_defaults)
2542
 
        setattr(instance, method_name,
2543
 
            types.MethodType(new_func, instance, instance.__class__))
2544
 
        self.addCleanup(delattr, instance, method_name)
2545
 
 
2546
 
    @staticmethod
2547
 
    def _fake_open_raises_before(name, mode):
2548
 
        """Like open() but raises before doing anything"""
2549
 
        raise RuntimeError
2550
 
 
2551
 
    @staticmethod
2552
 
    def _fake_open_raises_after(name, mode):
2553
 
        """Like open() but raises after creating file without returning"""
2554
 
        open(name, mode).close()
2555
 
        raise RuntimeError
2556
 
 
2557
 
    def create_transform_and_root_trans_id(self):
2558
 
        """Setup a transform creating a file in limbo"""
2559
 
        tree = self.make_branch_and_tree('.')
2560
 
        tt = TreeTransform(tree)
2561
 
        return tt, tt.create_path("a", tt.root)
2562
 
 
2563
 
    def create_transform_and_subdir_trans_id(self):
2564
 
        """Setup a transform creating a directory containing a file in limbo"""
2565
 
        tree = self.make_branch_and_tree('.')
2566
 
        tt = TreeTransform(tree)
2567
 
        d_trans_id = tt.create_path("d", tt.root)
2568
 
        tt.create_directory(d_trans_id)
2569
 
        f_trans_id = tt.create_path("a", d_trans_id)
2570
 
        tt.adjust_path("a", d_trans_id, f_trans_id)
2571
 
        return tt, f_trans_id
2572
 
 
2573
 
    def test_root_create_file_open_raises_before_creation(self):
2574
 
        tt, trans_id = self.create_transform_and_root_trans_id()
2575
 
        self._override_globals_in_method(tt, "create_file",
2576
 
            {"open": self._fake_open_raises_before})
2577
 
        self.assertRaises(RuntimeError, tt.create_file, ["contents"], trans_id)
2578
 
        path = tt._limbo_name(trans_id)
2579
 
        self.assertPathDoesNotExist(path)
2580
 
        tt.finalize()
2581
 
        self.assertPathDoesNotExist(tt._limbodir)
2582
 
 
2583
 
    def test_root_create_file_open_raises_after_creation(self):
2584
 
        tt, trans_id = self.create_transform_and_root_trans_id()
2585
 
        self._override_globals_in_method(tt, "create_file",
2586
 
            {"open": self._fake_open_raises_after})
2587
 
        self.assertRaises(RuntimeError, tt.create_file, ["contents"], trans_id)
2588
 
        path = tt._limbo_name(trans_id)
2589
 
        self.assertPathExists(path)
2590
 
        tt.finalize()
2591
 
        self.assertPathDoesNotExist(path)
2592
 
        self.assertPathDoesNotExist(tt._limbodir)
2593
 
 
2594
 
    def test_subdir_create_file_open_raises_before_creation(self):
2595
 
        tt, trans_id = self.create_transform_and_subdir_trans_id()
2596
 
        self._override_globals_in_method(tt, "create_file",
2597
 
            {"open": self._fake_open_raises_before})
2598
 
        self.assertRaises(RuntimeError, tt.create_file, ["contents"], trans_id)
2599
 
        path = tt._limbo_name(trans_id)
2600
 
        self.assertPathDoesNotExist(path)
2601
 
        tt.finalize()
2602
 
        self.assertPathDoesNotExist(tt._limbodir)
2603
 
 
2604
 
    def test_subdir_create_file_open_raises_after_creation(self):
2605
 
        tt, trans_id = self.create_transform_and_subdir_trans_id()
2606
 
        self._override_globals_in_method(tt, "create_file",
2607
 
            {"open": self._fake_open_raises_after})
2608
 
        self.assertRaises(RuntimeError, tt.create_file, ["contents"], trans_id)
2609
 
        path = tt._limbo_name(trans_id)
2610
 
        self.assertPathExists(path)
2611
 
        tt.finalize()
2612
 
        self.assertPathDoesNotExist(path)
2613
 
        self.assertPathDoesNotExist(tt._limbodir)
2614
 
 
2615
 
    def test_rename_in_limbo_rename_raises_after_rename(self):
2616
 
        tt, trans_id = self.create_transform_and_root_trans_id()
2617
 
        parent1 = tt.new_directory('parent1', tt.root)
2618
 
        child1 = tt.new_file('child1', parent1, 'contents')
2619
 
        parent2 = tt.new_directory('parent2', tt.root)
2620
 
 
2621
 
        class FakeOSModule(object):
2622
 
            def rename(self, old, new):
2623
 
                os.rename(old, new)
2624
 
                raise RuntimeError
2625
 
        self._override_globals_in_method(tt, "_rename_in_limbo",
2626
 
            {"os": FakeOSModule()})
2627
 
        self.assertRaises(
2628
 
            RuntimeError, tt.adjust_path, "child1", parent2, child1)
2629
 
        path = osutils.pathjoin(tt._limbo_name(parent2), "child1")
2630
 
        self.assertPathExists(path)
2631
 
        tt.finalize()
2632
 
        self.assertPathDoesNotExist(path)
2633
 
        self.assertPathDoesNotExist(tt._limbodir)
2634
 
 
2635
 
    def test_rename_in_limbo_rename_raises_before_rename(self):
2636
 
        tt, trans_id = self.create_transform_and_root_trans_id()
2637
 
        parent1 = tt.new_directory('parent1', tt.root)
2638
 
        child1 = tt.new_file('child1', parent1, 'contents')
2639
 
        parent2 = tt.new_directory('parent2', tt.root)
2640
 
 
2641
 
        class FakeOSModule(object):
2642
 
            def rename(self, old, new):
2643
 
                raise RuntimeError
2644
 
        self._override_globals_in_method(tt, "_rename_in_limbo",
2645
 
            {"os": FakeOSModule()})
2646
 
        self.assertRaises(
2647
 
            RuntimeError, tt.adjust_path, "child1", parent2, child1)
2648
 
        path = osutils.pathjoin(tt._limbo_name(parent1), "child1")
2649
 
        self.assertPathExists(path)
2650
 
        tt.finalize()
2651
 
        self.assertPathDoesNotExist(path)
2652
 
        self.assertPathDoesNotExist(tt._limbodir)
2653
 
 
2654
 
 
2655
 
class TestTransformMissingParent(tests.TestCaseWithTransport):
2656
 
 
2657
 
    def make_tt_with_versioned_dir(self):
 
2005
        self.failUnlessExists('a')
 
2006
        self.failUnlessExists('a/b')
 
2007
 
 
2008
    def test_resolve_no_parent(self):
2658
2009
        wt = self.make_branch_and_tree('.')
2659
 
        self.build_tree(['dir/',])
2660
 
        wt.add(['dir'], ['dir-id'])
2661
 
        wt.commit('Create dir')
2662
2010
        tt = TreeTransform(wt)
2663
2011
        self.addCleanup(tt.finalize)
2664
 
        return wt, tt
2665
 
 
2666
 
    def test_resolve_create_parent_for_versioned_file(self):
2667
 
        wt, tt = self.make_tt_with_versioned_dir()
2668
 
        dir_tid = tt.trans_id_tree_file_id('dir-id')
2669
 
        file_tid = tt.new_file('file', dir_tid, 'Contents', file_id='file-id')
2670
 
        tt.delete_contents(dir_tid)
2671
 
        tt.unversion_file(dir_tid)
2672
 
        conflicts = resolve_conflicts(tt)
2673
 
        # one conflict for the missing directory, one for the unversioned
2674
 
        # parent
2675
 
        self.assertLength(2, conflicts)
2676
 
 
2677
 
    def test_non_versioned_file_create_conflict(self):
2678
 
        wt, tt = self.make_tt_with_versioned_dir()
2679
 
        dir_tid = tt.trans_id_tree_file_id('dir-id')
2680
 
        tt.new_file('file', dir_tid, 'Contents')
2681
 
        tt.delete_contents(dir_tid)
2682
 
        tt.unversion_file(dir_tid)
2683
 
        conflicts = resolve_conflicts(tt)
2684
 
        # no conflicts or rather: orphaning 'file' resolve the 'dir' conflict
2685
 
        self.assertLength(1, conflicts)
2686
 
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
2687
 
                         conflicts.pop())
 
2012
        parent = tt.trans_id_file_id('parent-id')
 
2013
        tt.new_file('file', parent, 'Contents')
 
2014
        resolve_conflicts(tt)
2688
2015
 
2689
2016
 
2690
2017
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2691
2018
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2692
2019
                  (False, False))
2693
2020
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2694
 
              ('', ''), ('directory', 'directory'), (False, False))
 
2021
              ('', ''), ('directory', 'directory'), (False, None))
2695
2022
 
2696
2023
 
2697
2024
class TestTransformPreview(tests.TestCaseWithTransport):
2699
2026
    def create_tree(self):
2700
2027
        tree = self.make_branch_and_tree('.')
2701
2028
        self.build_tree_contents([('a', 'content 1')])
2702
 
        tree.set_root_id('TREE_ROOT')
2703
2029
        tree.add('a', 'a-id')
2704
2030
        tree.commit('rev1', rev_id='rev1')
2705
2031
        return tree.branch.repository.revision_tree('rev1')
2784
2110
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2785
2111
        changes = preview_tree.iter_changes(revision_tree,
2786
2112
                                            specific_files=[''])
2787
 
        self.assertEqual([A_ENTRY], list(changes))
 
2113
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2788
2114
 
2789
2115
    def test_want_unversioned(self):
2790
2116
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2791
2117
        changes = preview_tree.iter_changes(revision_tree,
2792
2118
                                            want_unversioned=True)
2793
 
        self.assertEqual([A_ENTRY], list(changes))
 
2119
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2794
2120
 
2795
2121
    def test_ignore_extra_trees_no_specific_files(self):
2796
2122
        # extra_trees is harmless without specific_files, so we'll silently
2806
2132
    def test_ignore_pb(self):
2807
2133
        # pb could be supported, but TT.iter_changes doesn't support it.
2808
2134
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2809
 
        preview_tree.iter_changes(revision_tree)
 
2135
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2810
2136
 
2811
2137
    def test_kind(self):
2812
2138
        revision_tree = self.create_tree()
2827
2153
        self.assertEqual(os.stat(limbo_path).st_mtime,
2828
2154
                         preview_tree.get_file_mtime('file-id'))
2829
2155
 
2830
 
    def test_get_file_mtime_renamed(self):
2831
 
        work_tree = self.make_branch_and_tree('tree')
2832
 
        self.build_tree(['tree/file'])
2833
 
        work_tree.add('file', 'file-id')
2834
 
        preview = TransformPreview(work_tree)
2835
 
        self.addCleanup(preview.finalize)
2836
 
        file_trans_id = preview.trans_id_tree_file_id('file-id')
2837
 
        preview.adjust_path('renamed', preview.root, file_trans_id)
2838
 
        preview_tree = preview.get_preview_tree()
2839
 
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2840
 
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
2841
 
 
2842
 
    def test_get_file_size(self):
2843
 
        work_tree = self.make_branch_and_tree('tree')
2844
 
        self.build_tree_contents([('tree/old', 'old')])
2845
 
        work_tree.add('old', 'old-id')
2846
 
        preview = TransformPreview(work_tree)
2847
 
        self.addCleanup(preview.finalize)
2848
 
        new_id = preview.new_file('name', preview.root, 'contents', 'new-id',
2849
 
                                  'executable')
2850
 
        tree = preview.get_preview_tree()
2851
 
        self.assertEqual(len('old'), tree.get_file_size('old-id'))
2852
 
        self.assertEqual(len('contents'), tree.get_file_size('new-id'))
2853
 
 
2854
2156
    def test_get_file(self):
2855
2157
        preview = self.get_empty_preview()
2856
2158
        preview.new_file('file', preview.root, 'contents', 'file-id')
3004
2306
        self.assertEqual(('missing', None, None, None), summary)
3005
2307
 
3006
2308
    def test_file_content_summary_executable(self):
 
2309
        if not osutils.supports_executable():
 
2310
            raise TestNotApplicable()
3007
2311
        preview = self.get_empty_preview()
3008
2312
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
3009
2313
        preview.set_executability(True, path_id)
3018
2322
        self.assertIs(None, summary[3])
3019
2323
 
3020
2324
    def test_change_executability(self):
 
2325
        if not osutils.supports_executable():
 
2326
            raise TestNotApplicable()
3021
2327
        tree = self.make_branch_and_tree('tree')
3022
2328
        self.build_tree(['tree/path'])
3023
2329
        tree.add('path')
3037
2343
        # size must be known
3038
2344
        self.assertEqual(len('contents'), summary[1])
3039
2345
        # not executable
3040
 
        self.assertEqual(False, summary[2])
 
2346
        if osutils.supports_executable():
 
2347
            self.assertEqual(False, summary[2])
 
2348
        else:
 
2349
            self.assertEqual(None, summary[2])
3041
2350
        # will not have hash (not cheap to determine)
3042
2351
        self.assertIs(None, summary[3])
3043
2352
 
3184
2493
 
3185
2494
    def test_walkdirs(self):
3186
2495
        preview = self.get_empty_preview()
3187
 
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
3188
 
        # FIXME: new_directory should mark root.
3189
 
        preview.fixup_new_roots()
 
2496
        preview.version_file('tree-root', preview.root)
3190
2497
        preview_tree = preview.get_preview_tree()
3191
2498
        file_trans_id = preview.new_file('a', preview.root, 'contents',
3192
2499
                                         'a-id')
3223
2530
        self.addCleanup(work_tree.unlock)
3224
2531
        preview = TransformPreview(work_tree)
3225
2532
        self.addCleanup(preview.finalize)
 
2533
        preview_tree = preview.get_preview_tree()
3226
2534
        file_trans_id = preview.trans_id_file_id('file-id')
3227
2535
        preview.delete_contents(file_trans_id)
3228
2536
        preview.create_file('a\nb\n', file_trans_id)
3229
 
        preview_tree = preview.get_preview_tree()
3230
 
        merger = Merger.from_revision_ids(None, preview_tree,
 
2537
        pb = progress.DummyProgress()
 
2538
        merger = Merger.from_revision_ids(pb, preview_tree,
3231
2539
                                          child_tree.branch.last_revision(),
3232
2540
                                          other_branch=child_tree.branch,
3233
2541
                                          tree_branch=work_tree.branch)
3239
2547
 
3240
2548
    def test_merge_preview_into_workingtree(self):
3241
2549
        tree = self.make_branch_and_tree('tree')
3242
 
        tree.set_root_id('TREE_ROOT')
3243
2550
        tt = TransformPreview(tree)
3244
2551
        self.addCleanup(tt.finalize)
3245
2552
        tt.new_file('name', tt.root, 'content', 'file-id')
3246
2553
        tree2 = self.make_branch_and_tree('tree2')
3247
 
        tree2.set_root_id('TREE_ROOT')
 
2554
        pb = progress.DummyProgress()
3248
2555
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
3249
 
                                         None, tree.basis_tree())
 
2556
                                         pb, tree.basis_tree())
3250
2557
        merger.merge_type = Merge3Merger
3251
2558
        merger.do_merge()
3252
2559
 
3262
2569
        tt.create_file('baz', trans_id)
3263
2570
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
3264
2571
        self.build_tree_contents([('tree2/foo', 'qux')])
3265
 
        pb = None
 
2572
        pb = progress.DummyProgress()
3266
2573
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
3267
2574
                                         pb, tree.basis_tree())
3268
2575
        merger.merge_type = Merge3Merger
3269
2576
        merger.do_merge()
3270
2577
 
3271
 
    def test_has_filename(self):
3272
 
        wt = self.make_branch_and_tree('tree')
3273
 
        self.build_tree(['tree/unmodified', 'tree/removed', 'tree/modified'])
3274
 
        tt = TransformPreview(wt)
3275
 
        removed_id = tt.trans_id_tree_path('removed')
3276
 
        tt.delete_contents(removed_id)
3277
 
        tt.new_file('new', tt.root, 'contents')
3278
 
        modified_id = tt.trans_id_tree_path('modified')
3279
 
        tt.delete_contents(modified_id)
3280
 
        tt.create_file('modified-contents', modified_id)
3281
 
        self.addCleanup(tt.finalize)
3282
 
        tree = tt.get_preview_tree()
3283
 
        self.assertTrue(tree.has_filename('unmodified'))
3284
 
        self.assertFalse(tree.has_filename('not-present'))
3285
 
        self.assertFalse(tree.has_filename('removed'))
3286
 
        self.assertTrue(tree.has_filename('new'))
3287
 
        self.assertTrue(tree.has_filename('modified'))
3288
 
 
3289
2578
    def test_is_executable(self):
3290
2579
        tree = self.make_branch_and_tree('tree')
3291
2580
        preview = TransformPreview(tree)
3296
2585
                                                           'tree/foo'))
3297
2586
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
3298
2587
 
3299
 
    def test_commit_preview_tree(self):
3300
 
        tree = self.make_branch_and_tree('tree')
3301
 
        rev_id = tree.commit('rev1')
3302
 
        tree.branch.lock_write()
3303
 
        self.addCleanup(tree.branch.unlock)
3304
 
        tt = TransformPreview(tree)
3305
 
        tt.new_file('file', tt.root, 'contents', 'file_id')
3306
 
        self.addCleanup(tt.finalize)
3307
 
        preview = tt.get_preview_tree()
3308
 
        preview.set_parent_ids([rev_id])
3309
 
        builder = tree.branch.get_commit_builder([rev_id])
3310
 
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
3311
 
        builder.finish_inventory()
3312
 
        rev2_id = builder.commit('rev2')
3313
 
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
3314
 
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
3315
 
 
3316
 
    def test_ascii_limbo_paths(self):
3317
 
        self.requireFeature(tests.UnicodeFilenameFeature)
3318
 
        branch = self.make_branch('any')
3319
 
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
3320
 
        tt = TransformPreview(tree)
3321
 
        self.addCleanup(tt.finalize)
3322
 
        foo_id = tt.new_directory('', ROOT_PARENT)
3323
 
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
3324
 
        limbo_path = tt._limbo_name(bar_id)
3325
 
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
3326
 
 
3327
2588
 
3328
2589
class FakeSerializer(object):
3329
2590
    """Serializer implementation that simply returns the input.
3424
2685
        self.assertSerializesTo(self.symlink_creation_records(), tt)
3425
2686
 
3426
2687
    def test_deserialize_symlink_creation(self):
3427
 
        self.requireFeature(tests.SymlinkFeature)
3428
2688
        tt = self.get_preview()
3429
2689
        tt.deserialize(iter(self.symlink_creation_records()))
3430
 
        abspath = tt._limbo_name('new-1')
3431
 
        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')
3432
2692
        self.assertEqual(u'bar\u1234', foo_content)
3433
2693
 
3434
2694
    def make_destruction_preview(self):
3597
2857
        trans_id = tt.trans_id_tree_path('file')
3598
2858
        self.assertEqual((LINES_ONE,),
3599
2859
            tt._get_parents_texts(trans_id))
3600
 
 
3601
 
 
3602
 
class TestOrphan(tests.TestCaseWithTransport):
3603
 
 
3604
 
    def test_no_orphan_for_transform_preview(self):
3605
 
        tree = self.make_branch_and_tree('tree')
3606
 
        tt = transform.TransformPreview(tree)
3607
 
        self.addCleanup(tt.finalize)
3608
 
        self.assertRaises(NotImplementedError, tt.new_orphan, 'foo', 'bar')
3609
 
 
3610
 
    def _set_orphan_policy(self, wt, policy):
3611
 
        wt.branch.get_config().set_user_option('bzr.transform.orphan_policy',
3612
 
                                               policy)
3613
 
 
3614
 
    def _prepare_orphan(self, wt):
3615
 
        self.build_tree(['dir/', 'dir/file', 'dir/foo'])
3616
 
        wt.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
3617
 
        wt.commit('add dir and file ignoring foo')
3618
 
        tt = transform.TreeTransform(wt)
3619
 
        self.addCleanup(tt.finalize)
3620
 
        # dir and bar are deleted
3621
 
        dir_tid = tt.trans_id_tree_path('dir')
3622
 
        file_tid = tt.trans_id_tree_path('dir/file')
3623
 
        orphan_tid = tt.trans_id_tree_path('dir/foo')
3624
 
        tt.delete_contents(file_tid)
3625
 
        tt.unversion_file(file_tid)
3626
 
        tt.delete_contents(dir_tid)
3627
 
        tt.unversion_file(dir_tid)
3628
 
        # There should be a conflict because dir still contain foo
3629
 
        raw_conflicts = tt.find_conflicts()
3630
 
        self.assertLength(1, raw_conflicts)
3631
 
        self.assertEqual(('missing parent', 'new-1'), raw_conflicts[0])
3632
 
        return tt, orphan_tid
3633
 
 
3634
 
    def test_new_orphan_created(self):
3635
 
        wt = self.make_branch_and_tree('.')
3636
 
        self._set_orphan_policy(wt, 'move')
3637
 
        tt, orphan_tid = self._prepare_orphan(wt)
3638
 
        warnings = []
3639
 
        def warning(*args):
3640
 
            warnings.append(args[0] % args[1:])
3641
 
        self.overrideAttr(trace, 'warning', warning)
3642
 
        remaining_conflicts = resolve_conflicts(tt)
3643
 
        self.assertEquals(['dir/foo has been orphaned in bzr-orphans'],
3644
 
                          warnings)
3645
 
        # Yeah for resolved conflicts !
3646
 
        self.assertLength(0, remaining_conflicts)
3647
 
        # We have a new orphan
3648
 
        self.assertEquals('foo.~1~', tt.final_name(orphan_tid))
3649
 
        self.assertEquals('bzr-orphans',
3650
 
                          tt.final_name(tt.final_parent(orphan_tid)))
3651
 
 
3652
 
    def test_never_orphan(self):
3653
 
        wt = self.make_branch_and_tree('.')
3654
 
        self._set_orphan_policy(wt, 'conflict')
3655
 
        tt, orphan_tid = self._prepare_orphan(wt)
3656
 
        remaining_conflicts = resolve_conflicts(tt)
3657
 
        self.assertLength(1, remaining_conflicts)
3658
 
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3659
 
                         remaining_conflicts.pop())
3660
 
 
3661
 
    def test_orphan_error(self):
3662
 
        def bogus_orphan(tt, orphan_id, parent_id):
3663
 
            raise transform.OrphaningError(tt.final_name(orphan_id),
3664
 
                                           tt.final_name(parent_id))
3665
 
        transform.orphaning_registry.register('bogus', bogus_orphan,
3666
 
                                              'Raise an error when orphaning')
3667
 
        wt = self.make_branch_and_tree('.')
3668
 
        self._set_orphan_policy(wt, 'bogus')
3669
 
        tt, orphan_tid = self._prepare_orphan(wt)
3670
 
        remaining_conflicts = resolve_conflicts(tt)
3671
 
        self.assertLength(1, remaining_conflicts)
3672
 
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3673
 
                         remaining_conflicts.pop())
3674
 
 
3675
 
    def test_unknown_orphan_policy(self):
3676
 
        wt = self.make_branch_and_tree('.')
3677
 
        # Set a fictional policy nobody ever implemented
3678
 
        self._set_orphan_policy(wt, 'donttouchmypreciouuus')
3679
 
        tt, orphan_tid = self._prepare_orphan(wt)
3680
 
        warnings = []
3681
 
        def warning(*args):
3682
 
            warnings.append(args[0] % args[1:])
3683
 
        self.overrideAttr(trace, 'warning', warning)
3684
 
        remaining_conflicts = resolve_conflicts(tt)
3685
 
        # We fallback to the default policy which create a conflict
3686
 
        self.assertLength(1, remaining_conflicts)
3687
 
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3688
 
                         remaining_conflicts.pop())
3689
 
        self.assertLength(1, warnings)
3690
 
        self.assertStartsWith(warnings[0], 'donttouchmypreciouuus')