/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 breezy/tests/test_transform.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2012, 2016 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 codecs
 
18
import errno
 
19
from io import BytesIO, StringIO
17
20
import os
18
 
from StringIO import StringIO
19
21
import sys
20
22
import time
21
23
 
22
 
from bzrlib import (
 
24
from .. import (
23
25
    bencode,
24
26
    errors,
25
27
    filters,
26
 
    generate_ids,
27
28
    osutils,
28
29
    revision as _mod_revision,
29
30
    rules,
30
31
    tests,
 
32
    trace,
 
33
    transform,
31
34
    urlutils,
32
35
    )
33
 
from bzrlib.bzrdir import BzrDir
34
 
from bzrlib.conflicts import (
 
36
from ..bzr import (
 
37
    generate_ids,
 
38
    )
 
39
from ..conflicts import (
35
40
    DeletingParent,
36
41
    DuplicateEntry,
37
42
    DuplicateID,
40
45
    ParentLoop,
41
46
    UnversionedParent,
42
47
)
43
 
from bzrlib.diff import show_diff_trees
44
 
from bzrlib.errors import (
 
48
from ..controldir import ControlDir
 
49
from ..diff import show_diff_trees
 
50
from ..errors import (
45
51
    DuplicateKey,
46
52
    ExistingLimbo,
47
53
    ExistingPendingDeletion,
49
55
    ImmortalPendingDeletion,
50
56
    LockError,
51
57
    MalformedTransform,
52
 
    NoSuchFile,
53
58
    ReusingTransform,
54
59
)
55
 
from bzrlib.osutils import (
 
60
from ..osutils import (
56
61
    file_kind,
57
62
    pathjoin,
58
63
)
59
 
from bzrlib.merge import Merge3Merger, Merger
60
 
from bzrlib.tests import (
 
64
from ..merge import Merge3Merger, Merger
 
65
from ..mutabletree import MutableTree
 
66
from ..sixish import (
 
67
    BytesIO,
 
68
    PY3,
 
69
    text_type,
 
70
    )
 
71
from . import (
 
72
    features,
 
73
    TestCaseInTempDir,
 
74
    TestSkipped,
 
75
    )
 
76
from .features import (
61
77
    HardlinkFeature,
62
78
    SymlinkFeature,
63
 
    TestCase,
64
 
    TestCaseInTempDir,
65
 
    TestSkipped,
66
 
)
67
 
from bzrlib.transform import (
 
79
    )
 
80
from ..transform import (
68
81
    build_tree,
69
82
    create_from_tree,
70
83
    cook_conflicts,
71
84
    _FileMover,
72
85
    FinalPaths,
73
 
    get_backup_name,
74
86
    resolve_conflicts,
75
87
    resolve_checkout,
76
88
    ROOT_PARENT,
83
95
 
84
96
    def setUp(self):
85
97
        super(TestTreeTransform, self).setUp()
86
 
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
 
98
        self.wt = self.make_branch_and_tree('.', format='development-subtree')
87
99
        os.chdir('..')
88
100
 
89
101
    def get_transform(self):
91
103
        self.addCleanup(transform.finalize)
92
104
        return transform, transform.root
93
105
 
 
106
    def get_transform_for_sha1_test(self):
 
107
        trans, root = self.get_transform()
 
108
        self.wt.lock_tree_write()
 
109
        self.addCleanup(self.wt.unlock)
 
110
        contents = [b'just some content\n']
 
111
        sha1 = osutils.sha_strings(contents)
 
112
        # Roll back the clock
 
113
        trans._creation_mtime = time.time() - 20.0
 
114
        return trans, root, contents, sha1
 
115
 
94
116
    def test_existing_limbo(self):
95
117
        transform, root = self.get_transform()
96
118
        limbo_name = transform._limbodir
123
145
        imaginary_id = transform.trans_id_tree_path('imaginary')
124
146
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
125
147
        self.assertEqual(imaginary_id, imaginary_id2)
126
 
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
127
 
        self.assertEqual(transform.final_kind(root), 'directory')
128
 
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
 
148
        self.assertEqual(root, transform.get_tree_parent(imaginary_id))
 
149
        self.assertEqual('directory', transform.final_kind(root))
 
150
        self.assertEqual(self.wt.path2id(''), transform.final_file_id(root))
129
151
        trans_id = transform.create_path('name', root)
130
152
        self.assertIs(transform.final_file_id(trans_id), None)
131
 
        self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
132
 
        transform.create_file('contents', trans_id)
 
153
        self.assertIs(None, transform.final_kind(trans_id))
 
154
        transform.create_file([b'contents'], trans_id)
133
155
        transform.set_executability(True, trans_id)
134
 
        transform.version_file('my_pretties', trans_id)
 
156
        transform.version_file(b'my_pretties', trans_id)
135
157
        self.assertRaises(DuplicateKey, transform.version_file,
136
 
                          'my_pretties', trans_id)
137
 
        self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
 
158
                          b'my_pretties', trans_id)
 
159
        self.assertEqual(transform.final_file_id(trans_id), b'my_pretties')
138
160
        self.assertEqual(transform.final_parent(trans_id), root)
139
161
        self.assertIs(transform.final_parent(root), ROOT_PARENT)
140
162
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
141
163
        oz_id = transform.create_path('oz', root)
142
164
        transform.create_directory(oz_id)
143
 
        transform.version_file('ozzie', oz_id)
 
165
        transform.version_file(b'ozzie', oz_id)
144
166
        trans_id2 = transform.create_path('name2', root)
145
 
        transform.create_file('contents', trans_id2)
 
167
        transform.create_file([b'contents'], trans_id2)
146
168
        transform.set_executability(False, trans_id2)
147
 
        transform.version_file('my_pretties2', trans_id2)
 
169
        transform.version_file(b'my_pretties2', trans_id2)
148
170
        modified_paths = transform.apply().modified_paths
149
 
        self.assertEqual('contents', self.wt.get_file_byname('name').read())
150
 
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
151
 
        self.assertIs(self.wt.is_executable('my_pretties'), True)
152
 
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
 
171
        with self.wt.get_file('name') as f:
 
172
            self.assertEqual(b'contents', f.read())
 
173
        self.assertEqual(self.wt.path2id('name'), b'my_pretties')
 
174
        self.assertIs(self.wt.is_executable('name'), True)
 
175
        self.assertIs(self.wt.is_executable('name2'), False)
153
176
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
154
177
        self.assertEqual(len(modified_paths), 3)
155
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in
156
 
                          ('ozzie', 'my_pretties', 'my_pretties2')]
 
178
        tree_mod_paths = [self.wt.abspath(self.wt.id2path(f)) for f in
 
179
                          (b'ozzie', b'my_pretties', b'my_pretties2')]
157
180
        self.assertSubset(tree_mod_paths, modified_paths)
158
181
        # is it safe to finalize repeatedly?
159
182
        transform.finalize()
160
183
        transform.finalize()
161
184
 
 
185
    def test_apply_informs_tree_of_observed_sha1(self):
 
186
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
 
187
        trans_id = trans.new_file('file1', root, contents, file_id=b'file1-id',
 
188
                                  sha1=sha1)
 
189
        calls = []
 
190
        orig = self.wt._observed_sha1
 
191
 
 
192
        def _observed_sha1(*args):
 
193
            calls.append(args)
 
194
            orig(*args)
 
195
        self.wt._observed_sha1 = _observed_sha1
 
196
        trans.apply()
 
197
        self.assertEqual([('file1', trans._observed_sha1s[trans_id])],
 
198
                         calls)
 
199
 
 
200
    def test_create_file_caches_sha1(self):
 
201
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
 
202
        trans_id = trans.create_path('file1', root)
 
203
        trans.create_file(contents, trans_id, sha1=sha1)
 
204
        st_val = osutils.lstat(trans._limbo_name(trans_id))
 
205
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
 
206
        self.assertEqual(o_sha1, sha1)
 
207
        self.assertEqualStat(o_st_val, st_val)
 
208
 
 
209
    def test__apply_insertions_updates_sha1(self):
 
210
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
 
211
        trans_id = trans.create_path('file1', root)
 
212
        trans.create_file(contents, trans_id, sha1=sha1)
 
213
        st_val = osutils.lstat(trans._limbo_name(trans_id))
 
214
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
 
215
        self.assertEqual(o_sha1, sha1)
 
216
        self.assertEqualStat(o_st_val, st_val)
 
217
        creation_mtime = trans._creation_mtime + 10.0
 
218
        # We fake a time difference from when the file was created until now it
 
219
        # is being renamed by using os.utime. Note that the change we actually
 
220
        # want to see is the real ctime change from 'os.rename()', but as long
 
221
        # as we observe a new stat value, we should be fine.
 
222
        os.utime(trans._limbo_name(trans_id), (creation_mtime, creation_mtime))
 
223
        trans.apply()
 
224
        new_st_val = osutils.lstat(self.wt.abspath('file1'))
 
225
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
 
226
        self.assertEqual(o_sha1, sha1)
 
227
        self.assertEqualStat(o_st_val, new_st_val)
 
228
        self.assertNotEqual(st_val.st_mtime, new_st_val.st_mtime)
 
229
 
 
230
    def test_new_file_caches_sha1(self):
 
231
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
 
232
        trans_id = trans.new_file('file1', root, contents, file_id=b'file1-id',
 
233
                                  sha1=sha1)
 
234
        st_val = osutils.lstat(trans._limbo_name(trans_id))
 
235
        o_sha1, o_st_val = trans._observed_sha1s[trans_id]
 
236
        self.assertEqual(o_sha1, sha1)
 
237
        self.assertEqualStat(o_st_val, st_val)
 
238
 
 
239
    def test_cancel_creation_removes_observed_sha1(self):
 
240
        trans, root, contents, sha1 = self.get_transform_for_sha1_test()
 
241
        trans_id = trans.new_file('file1', root, contents, file_id=b'file1-id',
 
242
                                  sha1=sha1)
 
243
        self.assertTrue(trans_id in trans._observed_sha1s)
 
244
        trans.cancel_creation(trans_id)
 
245
        self.assertFalse(trans_id in trans._observed_sha1s)
 
246
 
162
247
    def test_create_files_same_timestamp(self):
163
248
        transform, root = self.get_transform()
164
249
        self.wt.lock_tree_write()
166
251
        # Roll back the clock, so that we know everything is being set to the
167
252
        # exact time
168
253
        transform._creation_mtime = creation_mtime = time.time() - 20.0
169
 
        transform.create_file('content-one',
 
254
        transform.create_file([b'content-one'],
170
255
                              transform.create_path('one', root))
171
 
        time.sleep(1) # *ugly*
172
 
        transform.create_file('content-two',
 
256
        time.sleep(1)  # *ugly*
 
257
        transform.create_file([b'content-two'],
173
258
                              transform.create_path('two', root))
174
259
        transform.apply()
175
 
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
 
260
        fo, st1 = self.wt.get_file_with_stat('one', filtered=False)
176
261
        fo.close()
177
 
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
 
262
        fo, st2 = self.wt.get_file_with_stat('two', filtered=False)
178
263
        fo.close()
179
264
        # We only guarantee 2s resolution
180
 
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
 
265
        self.assertTrue(
 
266
            abs(creation_mtime - st1.st_mtime) < 2.0,
181
267
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
182
268
        # But if we have more than that, all files should get the same result
183
269
        self.assertEqual(st1.st_mtime, st2.st_mtime)
184
270
 
185
271
    def test_change_root_id(self):
186
272
        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')
 
273
        self.assertNotEqual(b'new-root-id', self.wt.path2id(''))
 
274
        transform.new_directory('', ROOT_PARENT, b'new-root-id')
189
275
        transform.delete_contents(root)
190
276
        transform.unversion_file(root)
191
277
        transform.fixup_new_roots()
192
278
        transform.apply()
193
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
279
        self.assertEqual(b'new-root-id', self.wt.path2id(''))
194
280
 
195
281
    def test_change_root_id_add_files(self):
196
282
        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')
 
283
        self.assertNotEqual(b'new-root-id', self.wt.path2id(''))
 
284
        new_trans_id = transform.new_directory('', ROOT_PARENT, b'new-root-id')
 
285
        transform.new_file('file', new_trans_id, [b'new-contents\n'],
 
286
                           b'new-file-id')
201
287
        transform.delete_contents(root)
202
288
        transform.unversion_file(root)
203
289
        transform.fixup_new_roots()
204
290
        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'))
 
291
        self.assertEqual(b'new-root-id', self.wt.path2id(''))
 
292
        self.assertEqual(b'new-file-id', self.wt.path2id('file'))
 
293
        self.assertFileEqual(b'new-contents\n', self.wt.abspath('file'))
208
294
 
209
295
    def test_add_two_roots(self):
210
296
        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)
 
297
        transform.new_directory('', ROOT_PARENT, b'new-root-id')
 
298
        transform.new_directory('', ROOT_PARENT, b'alt-root-id')
 
299
        self.assertRaises(ValueError, transform.fixup_new_roots)
 
300
 
 
301
    def test_retain_existing_root(self):
 
302
        tt, root = self.get_transform()
 
303
        with tt:
 
304
            tt.new_directory('', ROOT_PARENT, b'new-root-id')
 
305
            tt.fixup_new_roots()
 
306
            self.assertNotEqual(b'new-root-id', tt.final_file_id(tt.root))
 
307
 
 
308
    def test_retain_existing_root_added_file(self):
 
309
        tt, root = self.get_transform()
 
310
        new_trans_id = tt.new_directory('', ROOT_PARENT, b'new-root-id')
 
311
        child = tt.new_directory('child', new_trans_id, b'child-id')
 
312
        tt.fixup_new_roots()
 
313
        self.assertEqual(tt.root, tt.final_parent(child))
 
314
 
 
315
    def test_add_unversioned_root(self):
 
316
        transform, root = self.get_transform()
 
317
        transform.new_directory('', ROOT_PARENT, None)
 
318
        transform.delete_contents(transform.root)
 
319
        transform.fixup_new_roots()
 
320
        self.assertNotIn(transform.root, transform._new_id)
 
321
 
 
322
    def test_remove_root_fixup(self):
 
323
        transform, root = self.get_transform()
 
324
        old_root_id = self.wt.path2id('')
 
325
        self.assertNotEqual(b'new-root-id', old_root_id)
 
326
        transform.delete_contents(root)
 
327
        transform.unversion_file(root)
 
328
        transform.fixup_new_roots()
 
329
        transform.apply()
 
330
        self.assertEqual(old_root_id, self.wt.path2id(''))
 
331
 
 
332
        transform, root = self.get_transform()
 
333
        transform.new_directory('', ROOT_PARENT, b'new-root-id')
 
334
        transform.new_directory('', ROOT_PARENT, b'alt-root-id')
 
335
        self.assertRaises(ValueError, transform.fixup_new_roots)
 
336
 
 
337
    def test_fixup_new_roots_permits_empty_tree(self):
 
338
        transform, root = self.get_transform()
 
339
        transform.delete_contents(root)
 
340
        transform.unversion_file(root)
 
341
        transform.fixup_new_roots()
 
342
        self.assertIs(None, transform.final_kind(root))
 
343
        self.assertIs(None, transform.final_file_id(root))
 
344
 
 
345
    def test_apply_retains_root_directory(self):
 
346
        # Do not attempt to delete the physical root directory, because that
 
347
        # is impossible.
 
348
        transform, root = self.get_transform()
 
349
        with transform:
 
350
            transform.delete_contents(root)
 
351
            e = self.assertRaises(AssertionError, self.assertRaises,
 
352
                                  errors.TransformRenameFailed,
 
353
                                  transform.apply)
 
354
        self.assertContainsRe('TransformRenameFailed not raised', str(e))
 
355
 
 
356
    def test_apply_retains_file_id(self):
 
357
        transform, root = self.get_transform()
 
358
        old_root_id = transform.tree_file_id(root)
 
359
        transform.unversion_file(root)
 
360
        transform.apply()
 
361
        self.assertEqual(old_root_id, self.wt.path2id(''))
214
362
 
215
363
    def test_hardlink(self):
216
364
        self.requireFeature(HardlinkFeature)
217
365
        transform, root = self.get_transform()
218
 
        transform.new_file('file1', root, 'contents')
 
366
        transform.new_file('file1', root, [b'contents'])
219
367
        transform.apply()
220
368
        target = self.make_branch_and_tree('target')
221
369
        target_transform = TreeTransform(target)
222
370
        trans_id = target_transform.create_path('file1', target_transform.root)
223
371
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
224
372
        target_transform.apply()
225
 
        self.failUnlessExists('target/file1')
 
373
        self.assertPathExists('target/file1')
226
374
        source_stat = os.stat(self.wt.abspath('file1'))
227
375
        target_stat = os.stat('target/file1')
228
376
        self.assertEqual(source_stat, target_stat)
231
379
        transform, root = self.get_transform()
232
380
        self.wt.lock_tree_write()
233
381
        self.addCleanup(self.wt.unlock)
234
 
        trans_id = transform.new_file('name', root, 'contents',
235
 
                                      'my_pretties', True)
236
 
        oz = transform.new_directory('oz', root, 'oz-id')
237
 
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
238
 
        toto = transform.new_file('toto', dorothy, 'toto-contents',
239
 
                                  'toto-id', False)
 
382
        transform.new_file('name', root, [b'contents'], b'my_pretties', True)
 
383
        oz = transform.new_directory('oz', root, b'oz-id')
 
384
        dorothy = transform.new_directory('dorothy', oz, b'dorothy-id')
 
385
        transform.new_file('toto', dorothy, [b'toto-contents'], b'toto-id',
 
386
                           False)
240
387
 
241
388
        self.assertEqual(len(transform.find_conflicts()), 0)
242
389
        transform.apply()
243
390
        self.assertRaises(ReusingTransform, transform.find_conflicts)
244
 
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
245
 
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
246
 
        self.assertIs(self.wt.is_executable('my_pretties'), True)
247
 
        self.assertEqual(self.wt.path2id('oz'), 'oz-id')
248
 
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
249
 
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
 
391
        with open(self.wt.abspath('name'), 'r') as f:
 
392
            self.assertEqual('contents', f.read())
 
393
        self.assertEqual(self.wt.path2id('name'), b'my_pretties')
 
394
        self.assertIs(self.wt.is_executable('name'), True)
 
395
        self.assertEqual(self.wt.path2id('oz'), b'oz-id')
 
396
        self.assertEqual(self.wt.path2id('oz/dorothy'), b'dorothy-id')
 
397
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), b'toto-id')
250
398
 
251
 
        self.assertEqual('toto-contents',
252
 
                         self.wt.get_file_byname('oz/dorothy/toto').read())
253
 
        self.assertIs(self.wt.is_executable('toto-id'), False)
 
399
        self.assertEqual(b'toto-contents',
 
400
                         self.wt.get_file('oz/dorothy/toto').read())
 
401
        self.assertIs(self.wt.is_executable('oz/dorothy/toto'), False)
254
402
 
255
403
    def test_tree_reference(self):
256
404
        transform, root = self.get_transform()
257
405
        tree = transform._tree
258
 
        trans_id = transform.new_directory('reference', root, 'subtree-id')
259
 
        transform.set_tree_reference('subtree-revision', trans_id)
 
406
        trans_id = transform.new_directory('reference', root, b'subtree-id')
 
407
        transform.set_tree_reference(b'subtree-revision', trans_id)
260
408
        transform.apply()
261
409
        tree.lock_read()
262
410
        self.addCleanup(tree.unlock)
263
 
        self.assertEqual('subtree-revision',
264
 
                         tree.inventory['subtree-id'].reference_revision)
 
411
        self.assertEqual(
 
412
            b'subtree-revision',
 
413
            tree.root_inventory.get_entry(b'subtree-id').reference_revision)
265
414
 
266
415
    def test_conflicts(self):
267
416
        transform, root = self.get_transform()
268
 
        trans_id = transform.new_file('name', root, 'contents',
269
 
                                      'my_pretties')
 
417
        trans_id = transform.new_file('name', root, [b'contents'],
 
418
                                      b'my_pretties')
270
419
        self.assertEqual(len(transform.find_conflicts()), 0)
271
 
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
 
420
        trans_id2 = transform.new_file('name', root, [b'Crontents'], b'toto')
272
421
        self.assertEqual(transform.find_conflicts(),
273
422
                         [('duplicate', trans_id, trans_id2, 'name')])
274
423
        self.assertRaises(MalformedTransform, transform.apply)
288
437
        self.assertEqual(transform.find_conflicts(),
289
438
                         [('unversioned parent', lion_id),
290
439
                          ('missing parent', lion_id)])
291
 
        transform.version_file("Courage", lion_id)
 
440
        transform.version_file(b"Courage", lion_id)
292
441
        self.assertEqual(transform.find_conflicts(),
293
442
                         [('missing parent', lion_id),
294
443
                          ('versioning no contents', lion_id)])
295
444
        transform.adjust_path('name2', root, trans_id2)
296
445
        self.assertEqual(transform.find_conflicts(),
297
446
                         [('versioning no contents', lion_id)])
298
 
        transform.create_file('Contents, okay?', lion_id)
 
447
        transform.create_file([b'Contents, okay?'], lion_id)
299
448
        transform.adjust_path('name2', trans_id2, trans_id2)
300
449
        self.assertEqual(transform.find_conflicts(),
301
450
                         [('parent loop', trans_id2),
305
454
        transform.set_executability(True, oz_id)
306
455
        self.assertEqual(transform.find_conflicts(),
307
456
                         [('unversioned executability', oz_id)])
308
 
        transform.version_file('oz-id', oz_id)
 
457
        transform.version_file(b'oz-id', oz_id)
309
458
        self.assertEqual(transform.find_conflicts(),
310
459
                         [('non-file executability', oz_id)])
311
460
        transform.set_executability(None, oz_id)
312
 
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
 
461
        tip_id = transform.new_file('tip', oz_id, [b'ozma'], b'tip-id')
313
462
        transform.apply()
314
 
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
315
 
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
 
463
        self.assertEqual(self.wt.path2id('name'), b'my_pretties')
 
464
        with open(self.wt.abspath('name'), 'rb') as f:
 
465
            self.assertEqual(b'contents', f.read())
316
466
        transform2, root = self.get_transform()
317
 
        oz_id = transform2.trans_id_tree_file_id('oz-id')
318
 
        newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
 
467
        oz_id = transform2.trans_id_tree_path('oz')
 
468
        newtip = transform2.new_file('tip', oz_id, [b'other'], b'tip-id')
319
469
        result = transform2.find_conflicts()
320
470
        fp = FinalPaths(transform2)
321
 
        self.assert_('oz/tip' in transform2._tree_path_ids)
 
471
        self.assertTrue('oz/tip' in transform2._tree_path_ids)
322
472
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
323
473
        self.assertEqual(len(result), 2)
324
474
        self.assertEqual((result[0][0], result[0][1]),
328
478
        transform2.finalize()
329
479
        transform3 = TreeTransform(self.wt)
330
480
        self.addCleanup(transform3.finalize)
331
 
        oz_id = transform3.trans_id_tree_file_id('oz-id')
 
481
        oz_id = transform3.trans_id_tree_path('oz')
332
482
        transform3.delete_contents(oz_id)
333
483
        self.assertEqual(transform3.find_conflicts(),
334
484
                         [('missing parent', oz_id)])
335
485
        root_id = transform3.root
336
 
        tip_id = transform3.trans_id_tree_file_id('tip-id')
 
486
        tip_id = transform3.trans_id_tree_path('oz/tip')
337
487
        transform3.adjust_path('tip', root_id, tip_id)
338
488
        transform3.apply()
339
489
 
345
495
        tree.case_sensitive = True
346
496
        transform = TreeTransform(tree)
347
497
        self.addCleanup(transform.finalize)
348
 
        transform.new_file('file', transform.root, 'content')
349
 
        transform.new_file('FiLe', transform.root, 'content')
 
498
        transform.new_file('file', transform.root, [b'content'])
 
499
        transform.new_file('FiLe', transform.root, [b'content'])
350
500
        result = transform.find_conflicts()
351
501
        self.assertEqual([], result)
352
502
        transform.finalize()
355
505
        tree.case_sensitive = False
356
506
        transform = TreeTransform(tree)
357
507
        self.addCleanup(transform.finalize)
358
 
        transform.new_file('file', transform.root, 'content')
359
 
        transform.new_file('FiLe', transform.root, 'content')
 
508
        transform.new_file('file', transform.root, [b'content'])
 
509
        transform.new_file('FiLe', transform.root, [b'content'])
360
510
        result = transform.find_conflicts()
361
511
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
362
512
 
369
519
        tree.case_sensitive = True
370
520
        transform = TreeTransform(tree)
371
521
        self.addCleanup(transform.finalize)
372
 
        transform.new_file('file', transform.root, 'content')
 
522
        transform.new_file('file', transform.root, [b'content'])
373
523
        result = transform.find_conflicts()
374
524
        self.assertEqual([], result)
375
525
        transform.finalize()
378
528
        tree.case_sensitive = False
379
529
        transform = TreeTransform(tree)
380
530
        self.addCleanup(transform.finalize)
381
 
        transform.new_file('file', transform.root, 'content')
 
531
        transform.new_file('file', transform.root, [b'content'])
382
532
        result = transform.find_conflicts()
383
533
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
384
534
 
390
540
        tree.case_sensitive = False
391
541
        transform = TreeTransform(tree)
392
542
        self.addCleanup(transform.finalize)
393
 
        transform.new_file('file', transform.root, 'content')
394
 
        transform.new_file('FiLe', transform.root, 'content')
 
543
        transform.new_file('file', transform.root, [b'content'])
 
544
        transform.new_file('FiLe', transform.root, [b'content'])
395
545
        resolve_conflicts(transform)
396
546
        transform.apply()
397
 
        self.failUnlessExists('tree/file')
398
 
        self.failUnlessExists('tree/FiLe.moved')
 
547
        self.assertPathExists('tree/file')
 
548
        self.assertPathExists('tree/FiLe.moved')
399
549
 
400
550
    def test_resolve_checkout_case_conflict(self):
401
551
        tree = self.make_branch_and_tree('tree')
405
555
        tree.case_sensitive = False
406
556
        transform = TreeTransform(tree)
407
557
        self.addCleanup(transform.finalize)
408
 
        transform.new_file('file', transform.root, 'content')
409
 
        transform.new_file('FiLe', transform.root, 'content')
 
558
        transform.new_file('file', transform.root, [b'content'])
 
559
        transform.new_file('FiLe', transform.root, [b'content'])
410
560
        resolve_conflicts(transform,
411
561
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
412
562
        transform.apply()
413
 
        self.failUnlessExists('tree/file')
414
 
        self.failUnlessExists('tree/FiLe.moved')
 
563
        self.assertPathExists('tree/file')
 
564
        self.assertPathExists('tree/FiLe.moved')
415
565
 
416
566
    def test_apply_case_conflict(self):
417
567
        """Ensure that a transform with case conflicts can always be applied"""
418
568
        tree = self.make_branch_and_tree('tree')
419
569
        transform = TreeTransform(tree)
420
570
        self.addCleanup(transform.finalize)
421
 
        transform.new_file('file', transform.root, 'content')
422
 
        transform.new_file('FiLe', transform.root, 'content')
 
571
        transform.new_file('file', transform.root, [b'content'])
 
572
        transform.new_file('FiLe', transform.root, [b'content'])
423
573
        dir = transform.new_directory('dir', transform.root)
424
 
        transform.new_file('dirfile', dir, 'content')
425
 
        transform.new_file('dirFiLe', dir, 'content')
 
574
        transform.new_file('dirfile', dir, [b'content'])
 
575
        transform.new_file('dirFiLe', dir, [b'content'])
426
576
        resolve_conflicts(transform)
427
577
        transform.apply()
428
 
        self.failUnlessExists('tree/file')
 
578
        self.assertPathExists('tree/file')
429
579
        if not os.path.exists('tree/FiLe.moved'):
430
 
            self.failUnlessExists('tree/FiLe')
431
 
        self.failUnlessExists('tree/dir/dirfile')
 
580
            self.assertPathExists('tree/FiLe')
 
581
        self.assertPathExists('tree/dir/dirfile')
432
582
        if not os.path.exists('tree/dir/dirFiLe.moved'):
433
 
            self.failUnlessExists('tree/dir/dirFiLe')
 
583
            self.assertPathExists('tree/dir/dirFiLe')
434
584
 
435
585
    def test_case_insensitive_limbo(self):
436
586
        tree = self.make_branch_and_tree('tree')
440
590
        transform = TreeTransform(tree)
441
591
        self.addCleanup(transform.finalize)
442
592
        dir = transform.new_directory('dir', transform.root)
443
 
        first = transform.new_file('file', dir, 'content')
444
 
        second = transform.new_file('FiLe', dir, 'content')
 
593
        first = transform.new_file('file', dir, [b'content'])
 
594
        second = transform.new_file('FiLe', dir, [b'content'])
445
595
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
446
596
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
447
597
 
459
609
 
460
610
    def test_add_del(self):
461
611
        start, root = self.get_transform()
462
 
        start.new_directory('a', root, 'a')
 
612
        start.new_directory('a', root, b'a')
463
613
        start.apply()
464
614
        transform, root = self.get_transform()
465
 
        transform.delete_versioned(transform.trans_id_tree_file_id('a'))
466
 
        transform.new_directory('a', root, 'a')
 
615
        transform.delete_versioned(transform.trans_id_tree_path('a'))
 
616
        transform.new_directory('a', root, b'a')
467
617
        transform.apply()
468
618
 
469
619
    def test_unversioning(self):
470
620
        create_tree, root = self.get_transform()
471
 
        parent_id = create_tree.new_directory('parent', root, 'parent-id')
472
 
        create_tree.new_file('child', parent_id, 'child', 'child-id')
 
621
        parent_id = create_tree.new_directory('parent', root, b'parent-id')
 
622
        create_tree.new_file('child', parent_id, [b'child'], b'child-id')
473
623
        create_tree.apply()
474
624
        unversion = TreeTransform(self.wt)
475
625
        self.addCleanup(unversion.finalize)
477
627
        unversion.unversion_file(parent)
478
628
        self.assertEqual(unversion.find_conflicts(),
479
629
                         [('unversioned parent', parent_id)])
480
 
        file_id = unversion.trans_id_tree_file_id('child-id')
 
630
        file_id = unversion.trans_id_tree_path('parent/child')
481
631
        unversion.unversion_file(file_id)
482
632
        unversion.apply()
483
633
 
485
635
        create_tree, root = self.get_transform()
486
636
        # prepare tree
487
637
        root = create_tree.root
488
 
        create_tree.new_file('name1', root, 'hello1', 'name1')
489
 
        create_tree.new_file('name2', root, 'hello2', 'name2')
490
 
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
491
 
        create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
492
 
        create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
493
 
        create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
 
638
        create_tree.new_file('name1', root, [b'hello1'], b'name1')
 
639
        create_tree.new_file('name2', root, [b'hello2'], b'name2')
 
640
        ddir = create_tree.new_directory('dying_directory', root, b'ddir')
 
641
        create_tree.new_file('dying_file', ddir, [b'goodbye1'], b'dfile')
 
642
        create_tree.new_file('moving_file', ddir, [b'later1'], b'mfile')
 
643
        create_tree.new_file('moving_file2', root, [b'later2'], b'mfile2')
494
644
        create_tree.apply()
495
645
 
496
 
        mangle_tree,root = self.get_transform()
 
646
        mangle_tree, root = self.get_transform()
497
647
        root = mangle_tree.root
498
 
        #swap names
499
 
        name1 = mangle_tree.trans_id_tree_file_id('name1')
500
 
        name2 = mangle_tree.trans_id_tree_file_id('name2')
 
648
        # swap names
 
649
        name1 = mangle_tree.trans_id_tree_path('name1')
 
650
        name2 = mangle_tree.trans_id_tree_path('name2')
501
651
        mangle_tree.adjust_path('name2', root, name1)
502
652
        mangle_tree.adjust_path('name1', root, name2)
503
653
 
504
 
        #tests for deleting parent directories
505
 
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
 
654
        # tests for deleting parent directories
 
655
        ddir = mangle_tree.trans_id_tree_path('dying_directory')
506
656
        mangle_tree.delete_contents(ddir)
507
 
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
 
657
        dfile = mangle_tree.trans_id_tree_path('dying_directory/dying_file')
508
658
        mangle_tree.delete_versioned(dfile)
509
659
        mangle_tree.unversion_file(dfile)
510
 
        mfile = mangle_tree.trans_id_tree_file_id('mfile')
 
660
        mfile = mangle_tree.trans_id_tree_path('dying_directory/moving_file')
511
661
        mangle_tree.adjust_path('mfile', root, mfile)
512
662
 
513
 
        #tests for adding parent directories
514
 
        newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
515
 
        mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
 
663
        # tests for adding parent directories
 
664
        newdir = mangle_tree.new_directory('new_directory', root, b'newdir')
 
665
        mfile2 = mangle_tree.trans_id_tree_path('moving_file2')
516
666
        mangle_tree.adjust_path('mfile2', newdir, mfile2)
517
 
        mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
518
 
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
 
667
        mangle_tree.new_file('newfile', newdir, [b'hello3'], b'dfile')
 
668
        self.assertEqual(mangle_tree.final_file_id(mfile2), b'mfile2')
519
669
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
520
 
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
 
670
        self.assertEqual(mangle_tree.final_file_id(mfile2), b'mfile2')
521
671
        mangle_tree.apply()
522
 
        self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
523
 
        self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
524
 
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
 
672
        with open(self.wt.abspath('name1'), 'r') as f:
 
673
            self.assertEqual(f.read(), 'hello2')
 
674
        with open(self.wt.abspath('name2'), 'r') as f:
 
675
            self.assertEqual(f.read(), 'hello1')
 
676
        mfile2_path = self.wt.abspath(pathjoin('new_directory', 'mfile2'))
525
677
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
526
 
        self.assertEqual(file(mfile2_path).read(), 'later2')
527
 
        self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
528
 
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
529
 
        newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
530
 
        self.assertEqual(file(newfile_path).read(), 'hello3')
531
 
        self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
 
678
        with open(mfile2_path, 'r') as f:
 
679
            self.assertEqual(f.read(), 'later2')
 
680
        self.assertEqual(self.wt.id2path(b'mfile2'), 'new_directory/mfile2')
 
681
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), b'mfile2')
 
682
        newfile_path = self.wt.abspath(pathjoin('new_directory', 'newfile'))
 
683
        with open(newfile_path, 'r') as f:
 
684
            self.assertEqual(f.read(), 'hello3')
 
685
        self.assertEqual(self.wt.path2id('dying_directory'), b'ddir')
532
686
        self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
533
 
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
 
687
        mfile2_path = self.wt.abspath(pathjoin('new_directory', 'mfile2'))
534
688
 
535
689
    def test_both_rename(self):
536
 
        create_tree,root = self.get_transform()
537
 
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
538
 
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
 
690
        create_tree, root = self.get_transform()
 
691
        newdir = create_tree.new_directory('selftest', root, b'selftest-id')
 
692
        create_tree.new_file('blackbox.py', newdir, [
 
693
                             b'hello1'], b'blackbox-id')
539
694
        create_tree.apply()
540
 
        mangle_tree,root = self.get_transform()
541
 
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
542
 
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
 
695
        mangle_tree, root = self.get_transform()
 
696
        selftest = mangle_tree.trans_id_tree_path('selftest')
 
697
        blackbox = mangle_tree.trans_id_tree_path('selftest/blackbox.py')
543
698
        mangle_tree.adjust_path('test', root, selftest)
544
699
        mangle_tree.adjust_path('test_too_much', root, selftest)
545
700
        mangle_tree.set_executability(True, blackbox)
546
701
        mangle_tree.apply()
547
702
 
548
703
    def test_both_rename2(self):
549
 
        create_tree,root = self.get_transform()
550
 
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
551
 
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
552
 
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
553
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
554
 
                             'test_too_much-id')
 
704
        create_tree, root = self.get_transform()
 
705
        breezy = create_tree.new_directory('breezy', root, b'breezy-id')
 
706
        tests = create_tree.new_directory('tests', breezy, b'tests-id')
 
707
        blackbox = create_tree.new_directory('blackbox', tests, b'blackbox-id')
 
708
        create_tree.new_file('test_too_much.py', blackbox, [b'hello1'],
 
709
                             b'test_too_much-id')
555
710
        create_tree.apply()
556
 
        mangle_tree,root = self.get_transform()
557
 
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
558
 
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
559
 
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
560
 
        mangle_tree.adjust_path('selftest', bzrlib, tests)
 
711
        mangle_tree, root = self.get_transform()
 
712
        breezy = mangle_tree.trans_id_tree_path('breezy')
 
713
        tests = mangle_tree.trans_id_tree_path('breezy/tests')
 
714
        test_too_much = mangle_tree.trans_id_tree_path(
 
715
            'breezy/tests/blackbox/test_too_much.py')
 
716
        mangle_tree.adjust_path('selftest', breezy, tests)
561
717
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
562
718
        mangle_tree.set_executability(True, test_too_much)
563
719
        mangle_tree.apply()
564
720
 
565
721
    def test_both_rename3(self):
566
 
        create_tree,root = self.get_transform()
567
 
        tests = create_tree.new_directory('tests', root, 'tests-id')
568
 
        create_tree.new_file('test_too_much.py', tests, 'hello1',
569
 
                             'test_too_much-id')
 
722
        create_tree, root = self.get_transform()
 
723
        tests = create_tree.new_directory('tests', root, b'tests-id')
 
724
        create_tree.new_file('test_too_much.py', tests, [b'hello1'],
 
725
                             b'test_too_much-id')
570
726
        create_tree.apply()
571
 
        mangle_tree,root = self.get_transform()
572
 
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
573
 
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
 
727
        mangle_tree, root = self.get_transform()
 
728
        tests = mangle_tree.trans_id_tree_path('tests')
 
729
        test_too_much = mangle_tree.trans_id_tree_path(
 
730
            'tests/test_too_much.py')
574
731
        mangle_tree.adjust_path('selftest', root, tests)
575
732
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
576
733
        mangle_tree.set_executability(True, test_too_much)
580
737
        create_tree, root = self.get_transform()
581
738
        # prepare tree
582
739
        root = create_tree.root
583
 
        create_tree.new_file('name1', root, 'hello1', 'name1')
 
740
        create_tree.new_file('name1', root, [b'hello1'], b'name1')
584
741
        create_tree.apply()
585
742
        delete_contents, root = self.get_transform()
586
 
        file = delete_contents.trans_id_tree_file_id('name1')
 
743
        file = delete_contents.trans_id_tree_path('name1')
587
744
        delete_contents.delete_contents(file)
588
745
        delete_contents.apply()
589
746
        move_id, root = self.get_transform()
590
 
        name1 = move_id.trans_id_tree_file_id('name1')
591
 
        newdir = move_id.new_directory('dir', root, 'newdir')
 
747
        name1 = move_id.trans_id_tree_path('name1')
 
748
        newdir = move_id.new_directory('dir', root, b'newdir')
592
749
        move_id.adjust_path('name2', newdir, name1)
593
750
        move_id.apply()
594
751
 
596
753
        create_tree, root = self.get_transform()
597
754
        # prepare tree
598
755
        root = create_tree.root
599
 
        create_tree.new_file('name1', root, 'hello1', 'name1')
 
756
        create_tree.new_file('name1', root, [b'hello1'], b'name1')
600
757
        create_tree.apply()
601
758
        delete_contents = TreeTransform(self.wt)
602
759
        self.addCleanup(delete_contents.finalize)
603
 
        file = delete_contents.trans_id_tree_file_id('name1')
 
760
        file = delete_contents.trans_id_tree_path('name1')
604
761
        delete_contents.delete_contents(file)
605
762
        delete_contents.apply()
606
763
        delete_contents.finalize()
607
764
        replace = TreeTransform(self.wt)
608
765
        self.addCleanup(replace.finalize)
609
 
        name2 = replace.new_file('name2', root, 'hello2', 'name1')
 
766
        name2 = replace.new_file('name2', root, [b'hello2'], b'name1')
610
767
        conflicts = replace.find_conflicts()
611
 
        name1 = replace.trans_id_tree_file_id('name1')
 
768
        name1 = replace.trans_id_tree_path('name1')
612
769
        self.assertEqual(conflicts, [('duplicate id', name1, name2)])
613
770
        resolve_conflicts(replace)
614
771
        replace.apply()
615
772
 
616
 
    def _test_symlinks(self, link_name1,link_target1,
 
773
    def _test_symlinks(self, link_name1, link_target1,
617
774
                       link_name2, link_target2):
618
775
 
619
 
        def ozpath(p): return 'oz/' + p
 
776
        def ozpath(p):
 
777
            return 'oz/' + p
620
778
 
621
779
        self.requireFeature(SymlinkFeature)
622
780
        transform, root = self.get_transform()
623
 
        oz_id = transform.new_directory('oz', root, 'oz-id')
624
 
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
625
 
                                       'wizard-id')
 
781
        oz_id = transform.new_directory('oz', root, b'oz-id')
 
782
        transform.new_symlink(link_name1, oz_id, link_target1, b'wizard-id')
626
783
        wiz_id = transform.create_path(link_name2, oz_id)
627
784
        transform.create_symlink(link_target2, wiz_id)
628
 
        transform.version_file('wiz-id2', wiz_id)
 
785
        transform.version_file(b'wiz-id2', wiz_id)
629
786
        transform.set_executability(True, wiz_id)
630
787
        self.assertEqual(transform.find_conflicts(),
631
788
                         [('non-file executability', wiz_id)])
632
789
        transform.set_executability(None, wiz_id)
633
790
        transform.apply()
634
 
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
 
791
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), b'wizard-id')
635
792
        self.assertEqual('symlink',
636
793
                         file_kind(self.wt.abspath(ozpath(link_name1))))
637
794
        self.assertEqual(link_target2,
644
801
                            'wizard2', 'behind_curtain')
645
802
 
646
803
    def test_symlinks_unicode(self):
647
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
804
        self.requireFeature(features.UnicodeFilenameFeature)
648
805
        self._test_symlinks(u'\N{Euro Sign}wizard',
649
806
                            u'wizard-targ\N{Euro Sign}t',
650
807
                            u'\N{Euro Sign}wizard2',
651
808
                            u'b\N{Euro Sign}hind_curtain')
652
809
 
653
 
    def test_unable_create_symlink(self):
 
810
    def test_unsupported_symlink_no_conflict(self):
654
811
        def tt_helper():
655
812
            wt = self.make_branch_and_tree('.')
656
 
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
657
 
            try:
658
 
                tt.new_symlink('foo', tt.root, 'bar')
659
 
                tt.apply()
660
 
            finally:
661
 
                wt.unlock()
 
813
            tt = TreeTransform(wt)
 
814
            self.addCleanup(tt.finalize)
 
815
            tt.new_symlink('foo', tt.root, 'bar')
 
816
            result = tt.find_conflicts()
 
817
            self.assertEqual([], result)
662
818
        os_symlink = getattr(os, 'symlink', None)
663
819
        os.symlink = None
664
820
        try:
665
 
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
666
 
            self.assertEquals(
667
 
                "Unable to create symlink 'foo' on this platform",
668
 
                str(err))
 
821
            tt_helper()
669
822
        finally:
670
823
            if os_symlink:
671
824
                os.symlink = os_symlink
672
825
 
673
826
    def get_conflicted(self):
674
 
        create,root = self.get_transform()
675
 
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
676
 
        oz = create.new_directory('oz', root, 'oz-id')
677
 
        create.new_directory('emeraldcity', oz, 'emerald-id')
 
827
        create, root = self.get_transform()
 
828
        create.new_file('dorothy', root, [b'dorothy'], b'dorothy-id')
 
829
        oz = create.new_directory('oz', root, b'oz-id')
 
830
        create.new_directory('emeraldcity', oz, b'emerald-id')
678
831
        create.apply()
679
 
        conflicts,root = self.get_transform()
 
832
        conflicts, root = self.get_transform()
680
833
        # set up duplicate entry, duplicate id
681
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
682
 
                                         'dorothy-id')
683
 
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
684
 
        oz = conflicts.trans_id_tree_file_id('oz-id')
 
834
        new_dorothy = conflicts.new_file('dorothy', root, [b'dorothy'],
 
835
                                         b'dorothy-id')
 
836
        old_dorothy = conflicts.trans_id_tree_path('dorothy')
 
837
        oz = conflicts.trans_id_tree_path('oz')
685
838
        # set up DeletedParent parent conflict
686
839
        conflicts.delete_versioned(oz)
687
 
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
 
840
        emerald = conflicts.trans_id_tree_path('oz/emeraldcity')
688
841
        # set up MissingParent conflict
689
 
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
 
842
        munchkincity = conflicts.trans_id_file_id(b'munchkincity-id')
690
843
        conflicts.adjust_path('munchkincity', root, munchkincity)
691
 
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
 
844
        conflicts.new_directory('auntem', munchkincity, b'auntem-id')
692
845
        # set up parent loop
693
846
        conflicts.adjust_path('emeraldcity', emerald, emerald)
694
847
        return conflicts, emerald, oz, old_dorothy, new_dorothy
700
853
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
701
854
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
702
855
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
703
 
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
 
856
        self.assertEqual(conflicts.final_file_id(new_dorothy), b'dorothy-id')
704
857
        self.assertEqual(conflicts.final_parent(emerald), oz)
705
858
        conflicts.apply()
706
859
 
709
862
        raw_conflicts = resolve_conflicts(tt)
710
863
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
711
864
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
712
 
                                   'dorothy', None, 'dorothy-id')
 
865
                                   'dorothy', None, b'dorothy-id')
713
866
        self.assertEqual(cooked_conflicts[0], duplicate)
714
867
        duplicate_id = DuplicateID('Unversioned existing file',
715
868
                                   'dorothy.moved', 'dorothy', None,
716
 
                                   'dorothy-id')
 
869
                                   b'dorothy-id')
717
870
        self.assertEqual(cooked_conflicts[1], duplicate_id)
718
871
        missing_parent = MissingParent('Created directory', 'munchkincity',
719
 
                                       'munchkincity-id')
720
 
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
 
872
                                       b'munchkincity-id')
 
873
        deleted_parent = DeletingParent('Not deleting', 'oz', b'oz-id')
721
874
        self.assertEqual(cooked_conflicts[2], missing_parent)
722
875
        unversioned_parent = UnversionedParent('Versioned directory',
723
876
                                               'munchkincity',
724
 
                                               'munchkincity-id')
 
877
                                               b'munchkincity-id')
725
878
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
726
 
                                               'oz-id')
 
879
                                                b'oz-id')
727
880
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
728
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
729
 
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
 
881
        parent_loop = ParentLoop(
 
882
            'Cancelled move', 'oz/emeraldcity',
 
883
            'oz/emeraldcity', b'emerald-id', b'emerald-id')
730
884
        self.assertEqual(cooked_conflicts[4], deleted_parent)
731
885
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
732
886
        self.assertEqual(cooked_conflicts[6], parent_loop)
738
892
        raw_conflicts = resolve_conflicts(tt)
739
893
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
740
894
        tt.finalize()
741
 
        conflicts_s = [str(c) for c in cooked_conflicts]
 
895
        conflicts_s = [text_type(c) for c in cooked_conflicts]
742
896
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
743
897
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
744
898
                                         'Moved existing file to '
751
905
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
752
906
                                         ' versioned, but has versioned'
753
907
                                         ' children.  Versioned directory.')
754
 
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
755
 
                                         " is not empty.  Not deleting.")
 
908
        self.assertEqualDiff(
 
909
            conflicts_s[4], "Conflict: can't delete oz because it"
 
910
                            " is not empty.  Not deleting.")
756
911
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
757
912
                                         ' versioned, but has versioned'
758
913
                                         ' children.  Versioned directory.')
761
916
 
762
917
    def prepare_wrong_parent_kind(self):
763
918
        tt, root = self.get_transform()
764
 
        tt.new_file('parent', root, 'contents', 'parent-id')
 
919
        tt.new_file('parent', root, [b'contents'], b'parent-id')
765
920
        tt.apply()
766
921
        tt, root = self.get_transform()
767
 
        parent_id = tt.trans_id_file_id('parent-id')
768
 
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
922
        parent_id = tt.trans_id_file_id(b'parent-id')
 
923
        tt.new_file('child,', parent_id, [b'contents2'], b'file-id')
769
924
        return tt
770
925
 
771
926
    def test_find_conflicts_wrong_parent_kind(self):
775
930
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
776
931
        tt = self.prepare_wrong_parent_kind()
777
932
        raw_conflicts = resolve_conflicts(tt)
778
 
        self.assertEqual(set([('non-directory parent', 'Created directory',
779
 
                         'new-3')]), raw_conflicts)
 
933
        self.assertEqual({('non-directory parent', 'Created directory',
 
934
                           'new-3')}, raw_conflicts)
780
935
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
781
936
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
782
 
        'parent-id')], cooked_conflicts)
 
937
                                             b'parent-id')], cooked_conflicts)
783
938
        tt.apply()
784
 
        self.assertEqual(None, self.wt.path2id('parent'))
785
 
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
939
        self.assertFalse(self.wt.is_versioned('parent'))
 
940
        self.assertEqual(b'parent-id', self.wt.path2id('parent.new'))
786
941
 
787
942
    def test_resolve_conflicts_wrong_new_parent_kind(self):
788
943
        tt, root = self.get_transform()
789
 
        parent_id = tt.new_directory('parent', root, 'parent-id')
790
 
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
944
        parent_id = tt.new_directory('parent', root, b'parent-id')
 
945
        tt.new_file('child,', parent_id, [b'contents2'], b'file-id')
791
946
        tt.apply()
792
947
        tt, root = self.get_transform()
793
 
        parent_id = tt.trans_id_file_id('parent-id')
 
948
        parent_id = tt.trans_id_file_id(b'parent-id')
794
949
        tt.delete_contents(parent_id)
795
 
        tt.create_file('contents', parent_id)
 
950
        tt.create_file([b'contents'], parent_id)
796
951
        raw_conflicts = resolve_conflicts(tt)
797
 
        self.assertEqual(set([('non-directory parent', 'Created directory',
798
 
                         'new-3')]), raw_conflicts)
 
952
        self.assertEqual({('non-directory parent', 'Created directory',
 
953
                           'new-3')}, raw_conflicts)
799
954
        tt.apply()
800
 
        self.assertEqual(None, self.wt.path2id('parent'))
801
 
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
955
        self.assertFalse(self.wt.is_versioned('parent'))
 
956
        self.assertEqual(b'parent-id', self.wt.path2id('parent.new'))
802
957
 
803
958
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
804
959
        tt, root = self.get_transform()
805
960
        parent_id = tt.new_directory('parent', root)
806
 
        tt.new_file('child,', parent_id, 'contents2')
 
961
        tt.new_file('child,', parent_id, [b'contents2'])
807
962
        tt.apply()
808
963
        tt, root = self.get_transform()
809
964
        parent_id = tt.trans_id_tree_path('parent')
810
965
        tt.delete_contents(parent_id)
811
 
        tt.create_file('contents', parent_id)
 
966
        tt.create_file([b'contents'], parent_id)
812
967
        resolve_conflicts(tt)
813
968
        tt.apply()
814
 
        self.assertIs(None, self.wt.path2id('parent'))
815
 
        self.assertIs(None, self.wt.path2id('parent.new'))
 
969
        self.assertFalse(self.wt.is_versioned('parent'))
 
970
        self.assertFalse(self.wt.is_versioned('parent.new'))
 
971
 
 
972
    def test_resolve_conflicts_missing_parent(self):
 
973
        wt = self.make_branch_and_tree('.')
 
974
        tt = TreeTransform(wt)
 
975
        self.addCleanup(tt.finalize)
 
976
        parent = tt.trans_id_file_id(b'parent-id')
 
977
        tt.new_file('file', parent, [b'Contents'])
 
978
        raw_conflicts = resolve_conflicts(tt)
 
979
        # Since the directory doesn't exist it's seen as 'missing'.  So
 
980
        # 'resolve_conflicts' create a conflict asking for it to be created.
 
981
        self.assertLength(1, raw_conflicts)
 
982
        self.assertEqual(('missing parent', 'Created directory', 'new-1'),
 
983
                         raw_conflicts.pop())
 
984
        # apply fail since the missing directory doesn't exist
 
985
        self.assertRaises(errors.NoFinalPath, tt.apply)
816
986
 
817
987
    def test_moving_versioned_directories(self):
818
988
        create, root = self.get_transform()
819
 
        kansas = create.new_directory('kansas', root, 'kansas-id')
820
 
        create.new_directory('house', kansas, 'house-id')
821
 
        create.new_directory('oz', root, 'oz-id')
 
989
        kansas = create.new_directory('kansas', root, b'kansas-id')
 
990
        create.new_directory('house', kansas, b'house-id')
 
991
        create.new_directory('oz', root, b'oz-id')
822
992
        create.apply()
823
993
        cyclone, root = self.get_transform()
824
 
        oz = cyclone.trans_id_tree_file_id('oz-id')
825
 
        house = cyclone.trans_id_tree_file_id('house-id')
 
994
        oz = cyclone.trans_id_tree_path('oz')
 
995
        house = cyclone.trans_id_tree_path('house')
826
996
        cyclone.adjust_path('house', oz, house)
827
997
        cyclone.apply()
828
998
 
829
999
    def test_moving_root(self):
830
1000
        create, root = self.get_transform()
831
 
        fun = create.new_directory('fun', root, 'fun-id')
832
 
        create.new_directory('sun', root, 'sun-id')
833
 
        create.new_directory('moon', root, 'moon')
 
1001
        fun = create.new_directory('fun', root, b'fun-id')
 
1002
        create.new_directory('sun', root, b'sun-id')
 
1003
        create.new_directory('moon', root, b'moon')
834
1004
        create.apply()
835
1005
        transform, root = self.get_transform()
836
1006
        transform.adjust_root_path('oldroot', fun)
837
1007
        new_root = transform.trans_id_tree_path('')
838
 
        transform.version_file('new-root', new_root)
 
1008
        transform.version_file(b'new-root', new_root)
839
1009
        transform.apply()
840
1010
 
841
1011
    def test_renames(self):
842
1012
        create, root = self.get_transform()
843
 
        old = create.new_directory('old-parent', root, 'old-id')
844
 
        intermediate = create.new_directory('intermediate', old, 'im-id')
845
 
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
846
 
                                 'myfile-id')
 
1013
        old = create.new_directory('old-parent', root, b'old-id')
 
1014
        intermediate = create.new_directory('intermediate', old, b'im-id')
 
1015
        myfile = create.new_file('myfile', intermediate, [b'myfile-text'],
 
1016
                                 b'myfile-id')
847
1017
        create.apply()
848
1018
        rename, root = self.get_transform()
849
 
        old = rename.trans_id_file_id('old-id')
 
1019
        old = rename.trans_id_file_id(b'old-id')
850
1020
        rename.adjust_path('new', root, old)
851
 
        myfile = rename.trans_id_file_id('myfile-id')
 
1021
        myfile = rename.trans_id_file_id(b'myfile-id')
852
1022
        rename.set_executability(True, myfile)
853
1023
        rename.apply()
854
1024
 
 
1025
    def test_rename_fails(self):
 
1026
        self.requireFeature(features.not_running_as_root)
 
1027
        # see https://bugs.launchpad.net/bzr/+bug/491763
 
1028
        create, root_id = self.get_transform()
 
1029
        create.new_directory('first-dir', root_id, b'first-id')
 
1030
        create.new_file('myfile', root_id, [b'myfile-text'], b'myfile-id')
 
1031
        create.apply()
 
1032
        if os.name == "posix" and sys.platform != "cygwin":
 
1033
            # posix filesystems fail on renaming if the readonly bit is set
 
1034
            osutils.make_readonly(self.wt.abspath('first-dir'))
 
1035
        elif os.name == "nt":
 
1036
            # windows filesystems fail on renaming open files
 
1037
            self.addCleanup(open(self.wt.abspath('myfile')).close)
 
1038
        else:
 
1039
            self.skipTest("Can't force a permissions error on rename")
 
1040
        # now transform to rename
 
1041
        rename_transform, root_id = self.get_transform()
 
1042
        file_trans_id = rename_transform.trans_id_file_id(b'myfile-id')
 
1043
        dir_id = rename_transform.trans_id_file_id(b'first-id')
 
1044
        rename_transform.adjust_path('newname', dir_id, file_trans_id)
 
1045
        e = self.assertRaises(errors.TransformRenameFailed,
 
1046
                              rename_transform.apply)
 
1047
        # On nix looks like:
 
1048
        # "Failed to rename .../work/.bzr/checkout/limbo/new-1
 
1049
        # to .../first-dir/newname: [Errno 13] Permission denied"
 
1050
        # On windows looks like:
 
1051
        # "Failed to rename .../work/myfile to
 
1052
        # .../work/.bzr/checkout/limbo/new-1: [Errno 13] Permission denied"
 
1053
        # This test isn't concerned with exactly what the error looks like,
 
1054
        # and the strerror will vary across OS and locales, but the assert
 
1055
        # that the exeception attributes are what we expect
 
1056
        self.assertEqual(e.errno, errno.EACCES)
 
1057
        if os.name == "posix":
 
1058
            self.assertEndsWith(e.to_path, "/first-dir/newname")
 
1059
        else:
 
1060
            self.assertEqual(os.path.basename(e.from_path), "myfile")
 
1061
 
855
1062
    def test_set_executability_order(self):
856
1063
        """Ensure that executability behaves the same, no matter what order.
857
1064
 
858
1065
        - create file and set executability simultaneously
859
1066
        - create file and set executability afterward
860
 
        - unsetting the executability of a file whose executability has not been
 
1067
        - unsetting the executability of a file whose executability has not
 
1068
          been
861
1069
        declared should throw an exception (this may happen when a
862
1070
        merge attempts to create a file with a duplicate ID)
863
1071
        """
865
1073
        wt = transform._tree
866
1074
        wt.lock_read()
867
1075
        self.addCleanup(wt.unlock)
868
 
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
869
 
                           True)
 
1076
        transform.new_file('set_on_creation', root, [b'Set on creation'],
 
1077
                           b'soc', True)
870
1078
        sac = transform.new_file('set_after_creation', root,
871
 
                                 'Set after creation', 'sac')
 
1079
                                 [b'Set after creation'], b'sac')
872
1080
        transform.set_executability(True, sac)
873
 
        uws = transform.new_file('unset_without_set', root, 'Unset badly',
874
 
                                 'uws')
 
1081
        uws = transform.new_file('unset_without_set', root, [b'Unset badly'],
 
1082
                                 b'uws')
875
1083
        self.assertRaises(KeyError, transform.set_executability, None, uws)
876
1084
        transform.apply()
877
 
        self.assertTrue(wt.is_executable('soc'))
878
 
        self.assertTrue(wt.is_executable('sac'))
 
1085
        self.assertTrue(wt.is_executable('set_on_creation'))
 
1086
        self.assertTrue(wt.is_executable('set_after_creation'))
879
1087
 
880
1088
    def test_preserve_mode(self):
881
1089
        """File mode is preserved when replacing content"""
882
1090
        if sys.platform == 'win32':
883
1091
            raise TestSkipped('chmod has no effect on win32')
884
1092
        transform, root = self.get_transform()
885
 
        transform.new_file('file1', root, 'contents', 'file1-id', True)
 
1093
        transform.new_file('file1', root, [b'contents'], b'file1-id', True)
886
1094
        transform.apply()
887
1095
        self.wt.lock_write()
888
1096
        self.addCleanup(self.wt.unlock)
889
 
        self.assertTrue(self.wt.is_executable('file1-id'))
 
1097
        self.assertTrue(self.wt.is_executable('file1'))
890
1098
        transform, root = self.get_transform()
891
 
        file1_id = transform.trans_id_tree_file_id('file1-id')
 
1099
        file1_id = transform.trans_id_tree_path('file1')
892
1100
        transform.delete_contents(file1_id)
893
 
        transform.create_file('contents2', file1_id)
 
1101
        transform.create_file([b'contents2'], file1_id)
894
1102
        transform.apply()
895
 
        self.assertTrue(self.wt.is_executable('file1-id'))
 
1103
        self.assertTrue(self.wt.is_executable('file1'))
896
1104
 
897
1105
    def test__set_mode_stats_correctly(self):
898
1106
        """_set_mode stats to determine file mode."""
901
1109
 
902
1110
        stat_paths = []
903
1111
        real_stat = os.stat
 
1112
 
904
1113
        def instrumented_stat(path):
905
1114
            stat_paths.append(path)
906
1115
            return real_stat(path)
907
1116
 
908
1117
        transform, root = self.get_transform()
909
1118
 
910
 
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
911
 
                                     file_id='bar-id-1', executable=False)
 
1119
        bar1_id = transform.new_file('bar', root, [b'bar contents 1\n'],
 
1120
                                     file_id=b'bar-id-1', executable=False)
912
1121
        transform.apply()
913
1122
 
914
1123
        transform, root = self.get_transform()
916
1125
        bar2_id = transform.trans_id_tree_path('bar2')
917
1126
        try:
918
1127
            os.stat = instrumented_stat
919
 
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
 
1128
            transform.create_file([b'bar2 contents\n'],
 
1129
                                  bar2_id, mode_id=bar1_id)
920
1130
        finally:
921
1131
            os.stat = real_stat
922
1132
            transform.finalize()
925
1135
        self.assertEqual([bar1_abspath], stat_paths)
926
1136
 
927
1137
    def test_iter_changes(self):
928
 
        self.wt.set_root_id('eert_toor')
 
1138
        self.wt.set_root_id(b'eert_toor')
929
1139
        transform, root = self.get_transform()
930
 
        transform.new_file('old', root, 'blah', 'id-1', True)
 
1140
        transform.new_file('old', root, [b'blah'], b'id-1', True)
931
1141
        transform.apply()
932
1142
        transform, root = self.get_transform()
933
1143
        try:
934
1144
            self.assertEqual([], list(transform.iter_changes()))
935
 
            old = transform.trans_id_tree_file_id('id-1')
 
1145
            old = transform.trans_id_tree_path('old')
936
1146
            transform.unversion_file(old)
937
 
            self.assertEqual([('id-1', ('old', None), False, (True, False),
938
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
939
 
                (True, True))], list(transform.iter_changes()))
940
 
            transform.new_directory('new', root, 'id-1')
941
 
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
942
 
                ('eert_toor', 'eert_toor'), ('old', 'new'),
943
 
                ('file', 'directory'),
944
 
                (True, False))], list(transform.iter_changes()))
 
1147
            self.assertEqual([(b'id-1', ('old', None), False, (True, False),
 
1148
                               (b'eert_toor', b'eert_toor'),
 
1149
                               ('old', 'old'), ('file', 'file'),
 
1150
                               (True, True), False)],
 
1151
                             list(transform.iter_changes()))
 
1152
            transform.new_directory('new', root, b'id-1')
 
1153
            self.assertEqual([(b'id-1', ('old', 'new'), True, (True, True),
 
1154
                               (b'eert_toor', b'eert_toor'), ('old', 'new'),
 
1155
                               ('file', 'directory'),
 
1156
                               (True, False), False)],
 
1157
                             list(transform.iter_changes()))
945
1158
        finally:
946
1159
            transform.finalize()
947
1160
 
948
1161
    def test_iter_changes_new(self):
949
 
        self.wt.set_root_id('eert_toor')
 
1162
        self.wt.set_root_id(b'eert_toor')
950
1163
        transform, root = self.get_transform()
951
 
        transform.new_file('old', root, 'blah')
 
1164
        transform.new_file('old', root, [b'blah'])
952
1165
        transform.apply()
953
1166
        transform, root = self.get_transform()
954
1167
        try:
955
1168
            old = transform.trans_id_tree_path('old')
956
 
            transform.version_file('id-1', old)
957
 
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
958
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
959
 
                (False, False))], list(transform.iter_changes()))
 
1169
            transform.version_file(b'id-1', old)
 
1170
            self.assertEqual([(b'id-1', (None, 'old'), False, (False, True),
 
1171
                               (b'eert_toor', b'eert_toor'),
 
1172
                               ('old', 'old'), ('file', 'file'),
 
1173
                               (False, False), False)],
 
1174
                             list(transform.iter_changes()))
960
1175
        finally:
961
1176
            transform.finalize()
962
1177
 
963
1178
    def test_iter_changes_modifications(self):
964
 
        self.wt.set_root_id('eert_toor')
 
1179
        self.wt.set_root_id(b'eert_toor')
965
1180
        transform, root = self.get_transform()
966
 
        transform.new_file('old', root, 'blah', 'id-1')
967
 
        transform.new_file('new', root, 'blah')
968
 
        transform.new_directory('subdir', root, 'subdir-id')
 
1181
        transform.new_file('old', root, [b'blah'], b'id-1')
 
1182
        transform.new_file('new', root, [b'blah'])
 
1183
        transform.new_directory('subdir', root, b'subdir-id')
969
1184
        transform.apply()
970
1185
        transform, root = self.get_transform()
971
1186
        try:
972
1187
            old = transform.trans_id_tree_path('old')
973
 
            subdir = transform.trans_id_tree_file_id('subdir-id')
 
1188
            subdir = transform.trans_id_tree_path('subdir')
974
1189
            new = transform.trans_id_tree_path('new')
975
1190
            self.assertEqual([], list(transform.iter_changes()))
976
1191
 
977
 
            #content deletion
 
1192
            # content deletion
978
1193
            transform.delete_contents(old)
979
 
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
980
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
981
 
                (False, False))], list(transform.iter_changes()))
 
1194
            self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True),
 
1195
                               (b'eert_toor', b'eert_toor'),
 
1196
                               ('old', 'old'), ('file', None),
 
1197
                               (False, False), False)],
 
1198
                             list(transform.iter_changes()))
982
1199
 
983
 
            #content change
984
 
            transform.create_file('blah', old)
985
 
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
986
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
987
 
                (False, False))], list(transform.iter_changes()))
 
1200
            # content change
 
1201
            transform.create_file([b'blah'], old)
 
1202
            self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True),
 
1203
                               (b'eert_toor', b'eert_toor'),
 
1204
                               ('old', 'old'), ('file', 'file'),
 
1205
                               (False, False), False)],
 
1206
                             list(transform.iter_changes()))
988
1207
            transform.cancel_deletion(old)
989
 
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
990
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
991
 
                (False, False))], list(transform.iter_changes()))
 
1208
            self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True),
 
1209
                               (b'eert_toor', b'eert_toor'),
 
1210
                               ('old', 'old'), ('file', 'file'),
 
1211
                               (False, False), False)],
 
1212
                             list(transform.iter_changes()))
992
1213
            transform.cancel_creation(old)
993
1214
 
994
1215
            # move file_id to a different file
995
1216
            self.assertEqual([], list(transform.iter_changes()))
996
1217
            transform.unversion_file(old)
997
 
            transform.version_file('id-1', new)
 
1218
            transform.version_file(b'id-1', new)
998
1219
            transform.adjust_path('old', root, new)
999
 
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1000
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1001
 
                (False, False))], list(transform.iter_changes()))
 
1220
            self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True),
 
1221
                               (b'eert_toor', b'eert_toor'),
 
1222
                               ('old', 'old'), ('file', 'file'),
 
1223
                               (False, False), False)],
 
1224
                             list(transform.iter_changes()))
1002
1225
            transform.cancel_versioning(new)
1003
1226
            transform._removed_id = set()
1004
1227
 
1005
 
            #execute bit
 
1228
            # execute bit
1006
1229
            self.assertEqual([], list(transform.iter_changes()))
1007
1230
            transform.set_executability(True, old)
1008
 
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
1009
 
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1010
 
                (False, True))], list(transform.iter_changes()))
 
1231
            self.assertEqual([(b'id-1', ('old', 'old'), False, (True, True),
 
1232
                               (b'eert_toor', b'eert_toor'),
 
1233
                               ('old', 'old'), ('file', 'file'),
 
1234
                               (False, True), False)],
 
1235
                             list(transform.iter_changes()))
1011
1236
            transform.set_executability(None, old)
1012
1237
 
1013
1238
            # filename
1014
1239
            self.assertEqual([], list(transform.iter_changes()))
1015
1240
            transform.adjust_path('new', root, old)
1016
1241
            transform._new_parent = {}
1017
 
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
1018
 
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
1019
 
                (False, False))], list(transform.iter_changes()))
 
1242
            self.assertEqual([(b'id-1', ('old', 'new'), False, (True, True),
 
1243
                               (b'eert_toor', b'eert_toor'),
 
1244
                               ('old', 'new'), ('file', 'file'),
 
1245
                               (False, False), False)],
 
1246
                             list(transform.iter_changes()))
1020
1247
            transform._new_name = {}
1021
1248
 
1022
1249
            # parent directory
1023
1250
            self.assertEqual([], list(transform.iter_changes()))
1024
1251
            transform.adjust_path('new', subdir, old)
1025
1252
            transform._new_name = {}
1026
 
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
1027
 
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
1028
 
                ('file', 'file'), (False, False))],
1029
 
                list(transform.iter_changes()))
 
1253
            self.assertEqual([(b'id-1', ('old', 'subdir/old'), False,
 
1254
                               (True, True), (b'eert_toor',
 
1255
                                              b'subdir-id'), ('old', 'old'),
 
1256
                               ('file', 'file'), (False, False), False)],
 
1257
                             list(transform.iter_changes()))
1030
1258
            transform._new_path = {}
1031
1259
 
1032
1260
        finally:
1033
1261
            transform.finalize()
1034
1262
 
1035
1263
    def test_iter_changes_modified_bleed(self):
1036
 
        self.wt.set_root_id('eert_toor')
 
1264
        self.wt.set_root_id(b'eert_toor')
1037
1265
        """Modified flag should not bleed from one change to another"""
1038
1266
        # unfortunately, we have no guarantee that file1 (which is modified)
1039
1267
        # will be applied before file2.  And if it's applied after file2, it
1040
1268
        # obviously can't bleed into file2's change output.  But for now, it
1041
1269
        # works.
1042
1270
        transform, root = self.get_transform()
1043
 
        transform.new_file('file1', root, 'blah', 'id-1')
1044
 
        transform.new_file('file2', root, 'blah', 'id-2')
 
1271
        transform.new_file('file1', root, [b'blah'], b'id-1')
 
1272
        transform.new_file('file2', root, [b'blah'], b'id-2')
1045
1273
        transform.apply()
1046
1274
        transform, root = self.get_transform()
1047
1275
        try:
1048
 
            transform.delete_contents(transform.trans_id_file_id('id-1'))
 
1276
            transform.delete_contents(transform.trans_id_file_id(b'id-1'))
1049
1277
            transform.set_executability(True,
1050
 
            transform.trans_id_file_id('id-2'))
1051
 
            self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
1052
 
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
1053
 
                ('file', None), (False, False)),
1054
 
                ('id-2', (u'file2', u'file2'), False, (True, True),
1055
 
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
1056
 
                ('file', 'file'), (False, True))],
 
1278
                                        transform.trans_id_file_id(b'id-2'))
 
1279
            self.assertEqual(
 
1280
                [(b'id-1', (u'file1', u'file1'), True, (True, True),
 
1281
                 (b'eert_toor', b'eert_toor'), ('file1', u'file1'),
 
1282
                 ('file', None), (False, False), False),
 
1283
                 (b'id-2', (u'file2', u'file2'), False, (True, True),
 
1284
                 (b'eert_toor', b'eert_toor'), ('file2', u'file2'),
 
1285
                 ('file', 'file'), (False, True), False)],
1057
1286
                list(transform.iter_changes()))
1058
1287
        finally:
1059
1288
            transform.finalize()
1060
1289
 
1061
1290
    def test_iter_changes_move_missing(self):
1062
1291
        """Test moving ids with no files around"""
1063
 
        self.wt.set_root_id('toor_eert')
 
1292
        self.wt.set_root_id(b'toor_eert')
1064
1293
        # Need two steps because versioning a non-existant file is a conflict.
1065
1294
        transform, root = self.get_transform()
1066
 
        transform.new_directory('floater', root, 'floater-id')
 
1295
        transform.new_directory('floater', root, b'floater-id')
1067
1296
        transform.apply()
1068
1297
        transform, root = self.get_transform()
1069
1298
        transform.delete_contents(transform.trans_id_tree_path('floater'))
1072
1301
        floater = transform.trans_id_tree_path('floater')
1073
1302
        try:
1074
1303
            transform.adjust_path('flitter', root, floater)
1075
 
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
1076
 
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
1077
 
            (None, None), (False, False))], list(transform.iter_changes()))
 
1304
            self.assertEqual([(b'floater-id', ('floater', 'flitter'), False,
 
1305
                               (True, True),
 
1306
                               (b'toor_eert', b'toor_eert'),
 
1307
                               ('floater', 'flitter'),
 
1308
                               (None, None), (False, False), False)],
 
1309
                             list(transform.iter_changes()))
1078
1310
        finally:
1079
1311
            transform.finalize()
1080
1312
 
1081
1313
    def test_iter_changes_pointless(self):
1082
1314
        """Ensure that no-ops are not treated as modifications"""
1083
 
        self.wt.set_root_id('eert_toor')
 
1315
        self.wt.set_root_id(b'eert_toor')
1084
1316
        transform, root = self.get_transform()
1085
 
        transform.new_file('old', root, 'blah', 'id-1')
1086
 
        transform.new_directory('subdir', root, 'subdir-id')
 
1317
        transform.new_file('old', root, [b'blah'], b'id-1')
 
1318
        transform.new_directory('subdir', root, b'subdir-id')
1087
1319
        transform.apply()
1088
1320
        transform, root = self.get_transform()
1089
1321
        try:
1090
1322
            old = transform.trans_id_tree_path('old')
1091
 
            subdir = transform.trans_id_tree_file_id('subdir-id')
 
1323
            subdir = transform.trans_id_tree_path('subdir')
1092
1324
            self.assertEqual([], list(transform.iter_changes()))
1093
1325
            transform.delete_contents(subdir)
1094
1326
            transform.create_directory(subdir)
1095
1327
            transform.set_executability(False, old)
1096
1328
            transform.unversion_file(old)
1097
 
            transform.version_file('id-1', old)
 
1329
            transform.version_file(b'id-1', old)
1098
1330
            transform.adjust_path('old', root, old)
1099
1331
            self.assertEqual([], list(transform.iter_changes()))
1100
1332
        finally:
1102
1334
 
1103
1335
    def test_rename_count(self):
1104
1336
        transform, root = self.get_transform()
1105
 
        transform.new_file('name1', root, 'contents')
 
1337
        transform.new_file('name1', root, [b'contents'])
1106
1338
        self.assertEqual(transform.rename_count, 0)
1107
1339
        transform.apply()
1108
1340
        self.assertEqual(transform.rename_count, 1)
1125
1357
        """
1126
1358
        transform, root = self.get_transform()
1127
1359
        parent1 = transform.new_directory('parent1', root)
1128
 
        child1 = transform.new_file('child1', parent1, 'contents')
 
1360
        child1 = transform.new_file('child1', parent1, [b'contents'])
1129
1361
        parent2 = transform.new_directory('parent2', root)
1130
1362
        transform.adjust_path('child1', parent2, child1)
1131
1363
        transform.apply()
1132
 
        self.failIfExists(self.wt.abspath('parent1/child1'))
1133
 
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
 
1364
        self.assertPathDoesNotExist(self.wt.abspath('parent1/child1'))
 
1365
        self.assertPathExists(self.wt.abspath('parent2/child1'))
1134
1366
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1135
1367
        # no rename for child1 (counting only renames during apply)
1136
 
        self.failUnlessEqual(2, transform.rename_count)
 
1368
        self.assertEqual(2, transform.rename_count)
1137
1369
 
1138
1370
    def test_cancel_parent(self):
1139
1371
        """Cancelling a parent doesn't cause deletion of a non-empty directory
1144
1376
        """
1145
1377
        transform, root = self.get_transform()
1146
1378
        parent1 = transform.new_directory('parent1', root)
1147
 
        child1 = transform.new_file('child1', parent1, 'contents')
1148
 
        child2 = transform.new_file('child2', parent1, 'contents')
 
1379
        child1 = transform.new_file('child1', parent1, [b'contents'])
 
1380
        child2 = transform.new_file('child2', parent1, [b'contents'])
1149
1381
        try:
1150
1382
            transform.cancel_creation(parent1)
1151
1383
        except OSError:
1162
1394
        parent2 = transform.new_directory('parent2', root)
1163
1395
        transform.adjust_path('child1', parent2, child1)
1164
1396
        transform.apply()
1165
 
        self.failIfExists(self.wt.abspath('parent1'))
1166
 
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
 
1397
        self.assertPathDoesNotExist(self.wt.abspath('parent1'))
 
1398
        self.assertPathExists(self.wt.abspath('parent2/child1'))
1167
1399
        # rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1168
 
        self.failUnlessEqual(2, transform.rename_count)
 
1400
        self.assertEqual(2, transform.rename_count)
1169
1401
 
1170
1402
    def test_adjust_and_cancel(self):
1171
1403
        """Make sure adjust_path keeps track of limbo children properly"""
1172
1404
        transform, root = self.get_transform()
1173
1405
        parent1 = transform.new_directory('parent1', root)
1174
 
        child1 = transform.new_file('child1', parent1, 'contents')
 
1406
        child1 = transform.new_file('child1', parent1, [b'contents'])
1175
1407
        parent2 = transform.new_directory('parent2', root)
1176
1408
        transform.adjust_path('child1', parent2, child1)
1177
1409
        transform.cancel_creation(child1)
1186
1418
    def test_noname_contents(self):
1187
1419
        """TreeTransform should permit deferring naming files."""
1188
1420
        transform, root = self.get_transform()
1189
 
        parent = transform.trans_id_file_id('parent-id')
 
1421
        parent = transform.trans_id_file_id(b'parent-id')
1190
1422
        try:
1191
1423
            transform.create_directory(parent)
1192
1424
        except KeyError:
1196
1428
    def test_noname_contents_nested(self):
1197
1429
        """TreeTransform should permit deferring naming files."""
1198
1430
        transform, root = self.get_transform()
1199
 
        parent = transform.trans_id_file_id('parent-id')
 
1431
        parent = transform.trans_id_file_id(b'parent-id')
1200
1432
        try:
1201
1433
            transform.create_directory(parent)
1202
1434
        except KeyError:
1203
1435
            self.fail("Can't handle contents with no name")
1204
 
        child = transform.new_directory('child', parent)
 
1436
        transform.new_directory('child', parent)
1205
1437
        transform.adjust_path('parent', root, parent)
1206
1438
        transform.apply()
1207
 
        self.failUnlessExists(self.wt.abspath('parent/child'))
 
1439
        self.assertPathExists(self.wt.abspath('parent/child'))
1208
1440
        self.assertEqual(1, transform.rename_count)
1209
1441
 
1210
1442
    def test_reuse_name(self):
1211
1443
        """Avoid reusing the same limbo name for different files"""
1212
1444
        transform, root = self.get_transform()
1213
1445
        parent = transform.new_directory('parent', root)
1214
 
        child1 = transform.new_directory('child', parent)
 
1446
        transform.new_directory('child', parent)
1215
1447
        try:
1216
1448
            child2 = transform.new_directory('child', parent)
1217
1449
        except OSError:
1231
1463
        parent = transform.new_directory('parent', root)
1232
1464
        child1 = transform.new_directory('child', parent)
1233
1465
        transform.adjust_path('child1', parent, child1)
1234
 
        child2 = transform.new_directory('child', parent)
 
1466
        transform.new_directory('child', parent)
1235
1467
        transform.apply()
1236
1468
        # limbo/new-1 => parent
1237
1469
        self.assertEqual(1, transform.rename_count)
1243
1475
        child1 = transform.new_directory('child1', parent2)
1244
1476
        transform.cancel_creation(parent2)
1245
1477
        transform.create_directory(parent2)
1246
 
        child2 = transform.new_directory('child1', parent2)
 
1478
        transform.new_directory('child1', parent2)
1247
1479
        transform.adjust_path('child2', parent2, child1)
1248
1480
        transform.apply()
1249
1481
        # limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1253
1485
        """Finalize must be done in child-to-parent order"""
1254
1486
        transform, root = self.get_transform()
1255
1487
        parent = transform.new_directory('parent', root)
1256
 
        child = transform.new_directory('child', parent)
 
1488
        transform.new_directory('child', parent)
1257
1489
        try:
1258
1490
            transform.finalize()
1259
1491
        except OSError:
1273
1505
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1274
1506
            try:
1275
1507
                foo = tt.new_directory('foo', tt.root)
1276
 
                tt.new_file('bar', foo, 'foobar')
 
1508
                tt.new_file('bar', foo, [b'foobar'])
1277
1509
                baz = tt.new_directory('baz', tt.root)
1278
 
                tt.new_file('qux', baz, 'quux')
 
1510
                tt.new_file('qux', baz, [b'quux'])
1279
1511
                # Ask for a rename 'foo' -> 'baz'
1280
1512
                tt.adjust_path('baz', tt.root, foo)
1281
1513
                # Lie to tt that we've already resolved all conflicts.
1282
1514
                tt.apply(no_conflicts=True)
1283
 
            except:
 
1515
            except BaseException:
1284
1516
                wt.unlock()
1285
1517
                raise
1286
1518
        # The rename will fail because the target directory is not empty (but
1287
1519
        # raises FileExists anyway).
1288
1520
        err = self.assertRaises(errors.FileExists, tt_helper)
1289
 
        self.assertContainsRe(str(err),
1290
 
            "^File exists: .+/baz")
 
1521
        self.assertEndsWith(err.path, "/baz")
1291
1522
 
1292
1523
    def test_two_directories_clash(self):
1293
1524
        def tt_helper():
1301
1532
                tt.new_directory('baz', foo_2)
1302
1533
                # Lie to tt that we've already resolved all conflicts.
1303
1534
                tt.apply(no_conflicts=True)
1304
 
            except:
 
1535
            except BaseException:
1305
1536
                wt.unlock()
1306
1537
                raise
1307
1538
        err = self.assertRaises(errors.FileExists, tt_helper)
1308
 
        self.assertContainsRe(str(err),
1309
 
            "^File exists: .+/foo")
 
1539
        self.assertEndsWith(err.path, "/foo")
1310
1540
 
1311
1541
    def test_two_directories_clash_finalize(self):
1312
1542
        def tt_helper():
1320
1550
                tt.new_directory('baz', foo_2)
1321
1551
                # Lie to tt that we've already resolved all conflicts.
1322
1552
                tt.apply(no_conflicts=True)
1323
 
            except:
 
1553
            except BaseException:
1324
1554
                tt.finalize()
1325
1555
                raise
1326
1556
        err = self.assertRaises(errors.FileExists, tt_helper)
1327
 
        self.assertContainsRe(str(err),
1328
 
            "^File exists: .+/foo")
 
1557
        self.assertEndsWith(err.path, "/foo")
1329
1558
 
1330
1559
    def test_file_to_directory(self):
1331
1560
        wt = self.make_branch_and_tree('.')
1338
1567
        tt.delete_contents(foo_trans_id)
1339
1568
        tt.create_directory(foo_trans_id)
1340
1569
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1341
 
        tt.create_file(["aa\n"], bar_trans_id)
1342
 
        tt.version_file("bar-1", bar_trans_id)
 
1570
        tt.create_file([b"aa\n"], bar_trans_id)
 
1571
        tt.version_file(b"bar-1", bar_trans_id)
1343
1572
        tt.apply()
1344
 
        self.failUnlessExists("foo/bar")
 
1573
        self.assertPathExists("foo/bar")
1345
1574
        wt.lock_read()
1346
1575
        try:
1347
 
            self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1348
 
                    "directory")
 
1576
            self.assertEqual(wt.kind("foo"), "directory")
1349
1577
        finally:
1350
1578
            wt.unlock()
1351
1579
        wt.commit("two")
1364
1592
        tt.delete_contents(foo_trans_id)
1365
1593
        tt.create_symlink("bar", foo_trans_id)
1366
1594
        tt.apply()
1367
 
        self.failUnlessExists("foo")
 
1595
        self.assertPathExists("foo")
1368
1596
        wt.lock_read()
1369
1597
        self.addCleanup(wt.unlock)
1370
 
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1371
 
                "symlink")
 
1598
        self.assertEqual(wt.kind("foo"), "symlink")
 
1599
 
 
1600
    def test_file_to_symlink_unsupported(self):
 
1601
        wt = self.make_branch_and_tree('.')
 
1602
        self.build_tree(['foo'])
 
1603
        wt.add(['foo'])
 
1604
        wt.commit("one")
 
1605
        self.overrideAttr(osutils, 'supports_symlinks', lambda p: False)
 
1606
        tt = TreeTransform(wt)
 
1607
        self.addCleanup(tt.finalize)
 
1608
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1609
        tt.delete_contents(foo_trans_id)
 
1610
        log = BytesIO()
 
1611
        trace.push_log_file(log)
 
1612
        tt.create_symlink("bar", foo_trans_id)
 
1613
        tt.apply()
 
1614
        self.assertContainsRe(
 
1615
            log.getvalue(),
 
1616
            b'Unable to create symlink "foo" on this filesystem')
1372
1617
 
1373
1618
    def test_dir_to_file(self):
1374
1619
        wt = self.make_branch_and_tree('.')
1381
1626
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1382
1627
        tt.delete_contents(foo_trans_id)
1383
1628
        tt.delete_versioned(bar_trans_id)
1384
 
        tt.create_file(["aa\n"], foo_trans_id)
 
1629
        tt.create_file([b"aa\n"], foo_trans_id)
1385
1630
        tt.apply()
1386
 
        self.failUnlessExists("foo")
 
1631
        self.assertPathExists("foo")
1387
1632
        wt.lock_read()
1388
1633
        self.addCleanup(wt.unlock)
1389
 
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1390
 
                "file")
 
1634
        self.assertEqual(wt.kind("foo"), "file")
1391
1635
 
1392
1636
    def test_dir_to_hardlink(self):
1393
1637
        self.requireFeature(HardlinkFeature)
1404
1648
        self.build_tree(['baz'])
1405
1649
        tt.create_hardlink("baz", foo_trans_id)
1406
1650
        tt.apply()
1407
 
        self.failUnlessExists("foo")
1408
 
        self.failUnlessExists("baz")
 
1651
        self.assertPathExists("foo")
 
1652
        self.assertPathExists("baz")
1409
1653
        wt.lock_read()
1410
1654
        self.addCleanup(wt.unlock)
1411
 
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1412
 
                "file")
 
1655
        self.assertEqual(wt.kind("foo"), "file")
1413
1656
 
1414
1657
    def test_no_final_path(self):
1415
1658
        transform, root = self.get_transform()
1416
 
        trans_id = transform.trans_id_file_id('foo')
1417
 
        transform.create_file('bar', trans_id)
 
1659
        trans_id = transform.trans_id_file_id(b'foo')
 
1660
        transform.create_file([b'bar'], trans_id)
1418
1661
        transform.cancel_creation(trans_id)
1419
1662
        transform.apply()
1420
1663
 
1421
1664
    def test_create_from_tree(self):
1422
1665
        tree1 = self.make_branch_and_tree('tree1')
1423
 
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1424
 
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
 
1666
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', b'baz')])
 
1667
        tree1.add(['foo', 'bar'], [b'foo-id', b'bar-id'])
1425
1668
        tree2 = self.make_branch_and_tree('tree2')
1426
1669
        tt = TreeTransform(tree2)
1427
1670
        foo_trans_id = tt.create_path('foo', tt.root)
1428
 
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1671
        create_from_tree(tt, foo_trans_id, tree1, 'foo')
1429
1672
        bar_trans_id = tt.create_path('bar', tt.root)
1430
 
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
 
1673
        create_from_tree(tt, bar_trans_id, tree1, 'bar')
1431
1674
        tt.apply()
1432
1675
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1433
 
        self.assertFileEqual('baz', 'tree2/bar')
 
1676
        self.assertFileEqual(b'baz', 'tree2/bar')
1434
1677
 
1435
1678
    def test_create_from_tree_bytes(self):
1436
1679
        """Provided lines are used instead of tree content."""
1437
1680
        tree1 = self.make_branch_and_tree('tree1')
1438
 
        self.build_tree_contents([('tree1/foo', 'bar'),])
1439
 
        tree1.add('foo', 'foo-id')
 
1681
        self.build_tree_contents([('tree1/foo', b'bar'), ])
 
1682
        tree1.add('foo', b'foo-id')
1440
1683
        tree2 = self.make_branch_and_tree('tree2')
1441
1684
        tt = TreeTransform(tree2)
1442
1685
        foo_trans_id = tt.create_path('foo', tt.root)
1443
 
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
 
1686
        create_from_tree(tt, foo_trans_id, tree1, 'foo', chunks=[b'qux'])
1444
1687
        tt.apply()
1445
 
        self.assertFileEqual('qux', 'tree2/foo')
 
1688
        self.assertFileEqual(b'qux', 'tree2/foo')
1446
1689
 
1447
1690
    def test_create_from_tree_symlink(self):
1448
1691
        self.requireFeature(SymlinkFeature)
1449
1692
        tree1 = self.make_branch_and_tree('tree1')
1450
1693
        os.symlink('bar', 'tree1/foo')
1451
 
        tree1.add('foo', 'foo-id')
 
1694
        tree1.add('foo', b'foo-id')
1452
1695
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
1453
1696
        foo_trans_id = tt.create_path('foo', tt.root)
1454
 
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1697
        create_from_tree(tt, foo_trans_id, tree1, 'foo')
1455
1698
        tt.apply()
1456
1699
        self.assertEqual('bar', os.readlink('tree2/foo'))
1457
1700
 
1461
1704
    def __init__(self, dirname, root_id):
1462
1705
        self.name = dirname
1463
1706
        os.mkdir(dirname)
1464
 
        self.wt = BzrDir.create_standalone_workingtree(dirname)
 
1707
        self.wt = ControlDir.create_standalone_workingtree(dirname)
1465
1708
        self.wt.set_root_id(root_id)
1466
1709
        self.b = self.wt.branch
1467
1710
        self.tt = TreeTransform(self.wt)
1468
 
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
 
1711
        self.root = self.tt.trans_id_tree_path('')
1469
1712
 
1470
1713
 
1471
1714
def conflict_text(tree, merge):
1472
 
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
1473
 
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
 
1715
    template = b'%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
 
1716
    return template % (b'<' * 7, tree, b'=' * 7, merge, b'>' * 7)
 
1717
 
 
1718
 
 
1719
class TestInventoryAltered(tests.TestCaseWithTransport):
 
1720
 
 
1721
    def test_inventory_altered_unchanged(self):
 
1722
        tree = self.make_branch_and_tree('tree')
 
1723
        self.build_tree(['tree/foo'])
 
1724
        tree.add('foo', b'foo-id')
 
1725
        with TransformPreview(tree) as tt:
 
1726
            self.assertEqual([], tt._inventory_altered())
 
1727
 
 
1728
    def test_inventory_altered_changed_parent_id(self):
 
1729
        tree = self.make_branch_and_tree('tree')
 
1730
        self.build_tree(['tree/foo'])
 
1731
        tree.add('foo', b'foo-id')
 
1732
        with TransformPreview(tree) as tt:
 
1733
            tt.unversion_file(tt.root)
 
1734
            tt.version_file(b'new-id', tt.root)
 
1735
            foo_trans_id = tt.trans_id_tree_path('foo')
 
1736
            foo_tuple = ('foo', foo_trans_id)
 
1737
            root_tuple = ('', tt.root)
 
1738
            self.assertEqual([root_tuple, foo_tuple], tt._inventory_altered())
 
1739
 
 
1740
    def test_inventory_altered_noop_changed_parent_id(self):
 
1741
        tree = self.make_branch_and_tree('tree')
 
1742
        self.build_tree(['tree/foo'])
 
1743
        tree.add('foo', b'foo-id')
 
1744
        with TransformPreview(tree) as tt:
 
1745
            tt.unversion_file(tt.root)
 
1746
            tt.version_file(tree.path2id(''), tt.root)
 
1747
            tt.trans_id_tree_path('foo')
 
1748
            self.assertEqual([], tt._inventory_altered())
1474
1749
 
1475
1750
 
1476
1751
class TestTransformMerge(TestCaseInTempDir):
1478
1753
    def test_text_merge(self):
1479
1754
        root_id = generate_ids.gen_root_id()
1480
1755
        base = TransformGroup("base", root_id)
1481
 
        base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
1482
 
        base.tt.new_file('b', base.root, 'b1', 'b')
1483
 
        base.tt.new_file('c', base.root, 'c', 'c')
1484
 
        base.tt.new_file('d', base.root, 'd', 'd')
1485
 
        base.tt.new_file('e', base.root, 'e', 'e')
1486
 
        base.tt.new_file('f', base.root, 'f', 'f')
1487
 
        base.tt.new_directory('g', base.root, 'g')
1488
 
        base.tt.new_directory('h', base.root, 'h')
 
1756
        base.tt.new_file('a', base.root, [b'a\nb\nc\nd\be\n'], b'a')
 
1757
        base.tt.new_file('b', base.root, [b'b1'], b'b')
 
1758
        base.tt.new_file('c', base.root, [b'c'], b'c')
 
1759
        base.tt.new_file('d', base.root, [b'd'], b'd')
 
1760
        base.tt.new_file('e', base.root, [b'e'], b'e')
 
1761
        base.tt.new_file('f', base.root, [b'f'], b'f')
 
1762
        base.tt.new_directory('g', base.root, b'g')
 
1763
        base.tt.new_directory('h', base.root, b'h')
1489
1764
        base.tt.apply()
1490
1765
        other = TransformGroup("other", root_id)
1491
 
        other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
1492
 
        other.tt.new_file('b', other.root, 'b2', 'b')
1493
 
        other.tt.new_file('c', other.root, 'c2', 'c')
1494
 
        other.tt.new_file('d', other.root, 'd', 'd')
1495
 
        other.tt.new_file('e', other.root, 'e2', 'e')
1496
 
        other.tt.new_file('f', other.root, 'f', 'f')
1497
 
        other.tt.new_file('g', other.root, 'g', 'g')
1498
 
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1499
 
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
 
1766
        other.tt.new_file('a', other.root, [b'y\nb\nc\nd\be\n'], b'a')
 
1767
        other.tt.new_file('b', other.root, [b'b2'], b'b')
 
1768
        other.tt.new_file('c', other.root, [b'c2'], b'c')
 
1769
        other.tt.new_file('d', other.root, [b'd'], b'd')
 
1770
        other.tt.new_file('e', other.root, [b'e2'], b'e')
 
1771
        other.tt.new_file('f', other.root, [b'f'], b'f')
 
1772
        other.tt.new_file('g', other.root, [b'g'], b'g')
 
1773
        other.tt.new_file('h', other.root, [b'h\ni\nj\nk\n'], b'h')
 
1774
        other.tt.new_file('i', other.root, [b'h\ni\nj\nk\n'], b'i')
1500
1775
        other.tt.apply()
1501
1776
        this = TransformGroup("this", root_id)
1502
 
        this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
1503
 
        this.tt.new_file('b', this.root, 'b', 'b')
1504
 
        this.tt.new_file('c', this.root, 'c', 'c')
1505
 
        this.tt.new_file('d', this.root, 'd2', 'd')
1506
 
        this.tt.new_file('e', this.root, 'e2', 'e')
1507
 
        this.tt.new_file('f', this.root, 'f', 'f')
1508
 
        this.tt.new_file('g', this.root, 'g', 'g')
1509
 
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1510
 
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
 
1777
        this.tt.new_file('a', this.root, [b'a\nb\nc\nd\bz\n'], b'a')
 
1778
        this.tt.new_file('b', this.root, [b'b'], b'b')
 
1779
        this.tt.new_file('c', this.root, [b'c'], b'c')
 
1780
        this.tt.new_file('d', this.root, [b'd2'], b'd')
 
1781
        this.tt.new_file('e', this.root, [b'e2'], b'e')
 
1782
        this.tt.new_file('f', this.root, [b'f'], b'f')
 
1783
        this.tt.new_file('g', this.root, [b'g'], b'g')
 
1784
        this.tt.new_file('h', this.root, [b'1\n2\n3\n4\n'], b'h')
 
1785
        this.tt.new_file('i', this.root, [b'1\n2\n3\n4\n'], b'i')
1511
1786
        this.tt.apply()
1512
1787
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1513
1788
 
1514
1789
        # textual merge
1515
 
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
 
1790
        with this.wt.get_file(this.wt.id2path(b'a')) as f:
 
1791
            self.assertEqual(f.read(), b'y\nb\nc\nd\bz\n')
1516
1792
        # three-way text conflict
1517
 
        self.assertEqual(this.wt.get_file('b').read(),
1518
 
                         conflict_text('b', 'b2'))
 
1793
        with this.wt.get_file(this.wt.id2path(b'b')) as f:
 
1794
            self.assertEqual(f.read(), conflict_text(b'b', b'b2'))
1519
1795
        # OTHER wins
1520
 
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
 
1796
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'c')).read(), b'c2')
1521
1797
        # THIS wins
1522
 
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
 
1798
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'd')).read(), b'd2')
1523
1799
        # Ambigious clean merge
1524
 
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
 
1800
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'e')).read(), b'e2')
1525
1801
        # No change
1526
 
        self.assertEqual(this.wt.get_file('f').read(), 'f')
 
1802
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'f')).read(), b'f')
1527
1803
        # Correct correct results when THIS == OTHER
1528
 
        self.assertEqual(this.wt.get_file('g').read(), 'g')
 
1804
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'g')).read(), b'g')
1529
1805
        # Text conflict when THIS & OTHER are text and BASE is dir
1530
 
        self.assertEqual(this.wt.get_file('h').read(),
1531
 
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1532
 
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1533
 
                         '1\n2\n3\n4\n')
1534
 
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1535
 
                         'h\ni\nj\nk\n')
 
1806
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'h')).read(),
 
1807
                         conflict_text(b'1\n2\n3\n4\n', b'h\ni\nj\nk\n'))
 
1808
        self.assertEqual(this.wt.get_file('h.THIS').read(),
 
1809
                         b'1\n2\n3\n4\n')
 
1810
        self.assertEqual(this.wt.get_file('h.OTHER').read(),
 
1811
                         b'h\ni\nj\nk\n')
1536
1812
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1537
 
        self.assertEqual(this.wt.get_file('i').read(),
1538
 
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1539
 
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1540
 
                         '1\n2\n3\n4\n')
1541
 
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
1542
 
                         'h\ni\nj\nk\n')
 
1813
        self.assertEqual(this.wt.get_file(this.wt.id2path(b'i')).read(),
 
1814
                         conflict_text(b'1\n2\n3\n4\n', b'h\ni\nj\nk\n'))
 
1815
        self.assertEqual(this.wt.get_file('i.THIS').read(),
 
1816
                         b'1\n2\n3\n4\n')
 
1817
        self.assertEqual(this.wt.get_file('i.OTHER').read(),
 
1818
                         b'h\ni\nj\nk\n')
1543
1819
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1544
1820
        modified = ['a', 'b', 'c', 'h', 'i']
1545
1821
        merge_modified = this.wt.merge_modified()
1546
1822
        self.assertSubset(merge_modified, modified)
1547
1823
        self.assertEqual(len(merge_modified), len(modified))
1548
 
        file(this.wt.id2abspath('a'), 'wb').write('booga')
 
1824
        with open(this.wt.abspath(this.wt.id2path(b'a')), 'wb') as f:
 
1825
            f.write(b'booga')
1549
1826
        modified.pop(0)
1550
1827
        merge_modified = this.wt.merge_modified()
1551
1828
        self.assertSubset(merge_modified, modified)
1560
1837
        this = TransformGroup("THIS", root_id)
1561
1838
        other = TransformGroup("OTHER", root_id)
1562
1839
        for tg in this, base, other:
1563
 
            tg.tt.new_directory('a', tg.root, 'a')
1564
 
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1565
 
            tg.tt.new_file('c', tg.root, 'c', 'c')
1566
 
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
 
1840
            tg.tt.new_directory('a', tg.root, b'a')
 
1841
            tg.tt.new_symlink('b', tg.root, 'b', b'b')
 
1842
            tg.tt.new_file('c', tg.root, [b'c'], b'c')
 
1843
            tg.tt.new_symlink('d', tg.root, tg.name, b'd')
1567
1844
        targets = ((base, 'base-e', 'base-f', None, None),
1568
1845
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1569
1846
                   (other, 'other-e', None, 'other-g', 'other-h'))
1571
1848
            for link, target in (('e', e_target), ('f', f_target),
1572
1849
                                 ('g', g_target), ('h', h_target)):
1573
1850
                if target is not None:
1574
 
                    tg.tt.new_symlink(link, tg.root, target, link)
 
1851
                    tg.tt.new_symlink(link, tg.root, target,
 
1852
                                      link.encode('ascii'))
1575
1853
 
1576
1854
        for tg in this, base, other:
1577
1855
            tg.tt.apply()
1580
1858
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
1581
1859
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
1582
1860
        for suffix in ('THIS', 'BASE', 'OTHER'):
1583
 
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
 
1861
            self.assertEqual(os.readlink(
 
1862
                this.wt.abspath('d.' + suffix)), suffix)
1584
1863
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1585
 
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
1586
 
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
 
1864
        self.assertEqual(this.wt.id2path(b'd'), 'd.OTHER')
 
1865
        self.assertEqual(this.wt.id2path(b'f'), 'f.THIS')
1587
1866
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
1588
1867
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
1589
1868
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
1600
1879
        base = TransformGroup("BASE", root_id)
1601
1880
        this = TransformGroup("THIS", root_id)
1602
1881
        other = TransformGroup("OTHER", root_id)
1603
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1604
 
                                   for t in [base, this, other]]
1605
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1606
 
                                   for t in [base, this, other]]
1607
 
        base.tt.new_directory('c', base_a, 'c')
1608
 
        this.tt.new_directory('c1', this_a, 'c')
1609
 
        other.tt.new_directory('c', other_b, 'c')
1610
 
 
1611
 
        base.tt.new_directory('d', base_a, 'd')
1612
 
        this.tt.new_directory('d1', this_b, 'd')
1613
 
        other.tt.new_directory('d', other_a, 'd')
1614
 
 
1615
 
        base.tt.new_directory('e', base_a, 'e')
1616
 
        this.tt.new_directory('e', this_a, 'e')
1617
 
        other.tt.new_directory('e1', other_b, 'e')
1618
 
 
1619
 
        base.tt.new_directory('f', base_a, 'f')
1620
 
        this.tt.new_directory('f1', this_b, 'f')
1621
 
        other.tt.new_directory('f1', other_b, 'f')
 
1882
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, b'a')
 
1883
                                   for t in [base, this, other]]
 
1884
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, b'b')
 
1885
                                   for t in [base, this, other]]
 
1886
        base.tt.new_directory('c', base_a, b'c')
 
1887
        this.tt.new_directory('c1', this_a, b'c')
 
1888
        other.tt.new_directory('c', other_b, b'c')
 
1889
 
 
1890
        base.tt.new_directory('d', base_a, b'd')
 
1891
        this.tt.new_directory('d1', this_b, b'd')
 
1892
        other.tt.new_directory('d', other_a, b'd')
 
1893
 
 
1894
        base.tt.new_directory('e', base_a, b'e')
 
1895
        this.tt.new_directory('e', this_a, b'e')
 
1896
        other.tt.new_directory('e1', other_b, b'e')
 
1897
 
 
1898
        base.tt.new_directory('f', base_a, b'f')
 
1899
        this.tt.new_directory('f1', this_b, b'f')
 
1900
        other.tt.new_directory('f1', other_b, b'f')
1622
1901
 
1623
1902
        for tg in [this, base, other]:
1624
1903
            tg.tt.apply()
1625
1904
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1626
 
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
1627
 
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
1628
 
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
1629
 
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
 
1905
        self.assertEqual(this.wt.id2path(b'c'), pathjoin('b/c1'))
 
1906
        self.assertEqual(this.wt.id2path(b'd'), pathjoin('b/d1'))
 
1907
        self.assertEqual(this.wt.id2path(b'e'), pathjoin('b/e1'))
 
1908
        self.assertEqual(this.wt.id2path(b'f'), pathjoin('b/f1'))
1630
1909
 
1631
1910
    def test_filename_merge_conflicts(self):
1632
1911
        root_id = generate_ids.gen_root_id()
1633
1912
        base = TransformGroup("BASE", root_id)
1634
1913
        this = TransformGroup("THIS", root_id)
1635
1914
        other = TransformGroup("OTHER", root_id)
1636
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1637
 
                                   for t in [base, this, other]]
1638
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1639
 
                                   for t in [base, this, other]]
1640
 
 
1641
 
        base.tt.new_file('g', base_a, 'g', 'g')
1642
 
        other.tt.new_file('g1', other_b, 'g1', 'g')
1643
 
 
1644
 
        base.tt.new_file('h', base_a, 'h', 'h')
1645
 
        this.tt.new_file('h1', this_b, 'h1', 'h')
1646
 
 
1647
 
        base.tt.new_file('i', base.root, 'i', 'i')
1648
 
        other.tt.new_directory('i1', this_b, 'i')
 
1915
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, b'a')
 
1916
                                   for t in [base, this, other]]
 
1917
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, b'b')
 
1918
                                   for t in [base, this, other]]
 
1919
 
 
1920
        base.tt.new_file('g', base_a, [b'g'], b'g')
 
1921
        other.tt.new_file('g1', other_b, [b'g1'], b'g')
 
1922
 
 
1923
        base.tt.new_file('h', base_a, [b'h'], b'h')
 
1924
        this.tt.new_file('h1', this_b, [b'h1'], b'h')
 
1925
 
 
1926
        base.tt.new_file('i', base.root, [b'i'], b'i')
 
1927
        other.tt.new_directory('i1', this_b, b'i')
1649
1928
 
1650
1929
        for tg in [this, base, other]:
1651
1930
            tg.tt.apply()
1652
1931
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1653
1932
 
1654
 
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
 
1933
        self.assertEqual(this.wt.id2path(b'g'), pathjoin('b/g1.OTHER'))
1655
1934
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
1656
1935
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1657
 
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
 
1936
        self.assertEqual(this.wt.id2path(b'h'), pathjoin('b/h1.THIS'))
1658
1937
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
1659
1938
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1660
 
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
 
1939
        self.assertEqual(this.wt.id2path(b'i'), pathjoin('b/i1.OTHER'))
1661
1940
 
1662
1941
 
1663
1942
class TestBuildTree(tests.TestCaseWithTransport):
1665
1944
    def test_build_tree_with_symlinks(self):
1666
1945
        self.requireFeature(SymlinkFeature)
1667
1946
        os.mkdir('a')
1668
 
        a = BzrDir.create_standalone_workingtree('a')
 
1947
        a = ControlDir.create_standalone_workingtree('a')
1669
1948
        os.mkdir('a/foo')
1670
 
        file('a/foo/bar', 'wb').write('contents')
 
1949
        with open('a/foo/bar', 'wb') as f:
 
1950
            f.write(b'contents')
1671
1951
        os.symlink('a/foo/bar', 'a/foo/baz')
1672
1952
        a.add(['foo', 'foo/bar', 'foo/baz'])
1673
1953
        a.commit('initial commit')
1674
 
        b = BzrDir.create_standalone_workingtree('b')
 
1954
        b = ControlDir.create_standalone_workingtree('b')
1675
1955
        basis = a.basis_tree()
1676
1956
        basis.lock_read()
1677
1957
        self.addCleanup(basis.unlock)
1678
1958
        build_tree(basis, b)
1679
1959
        self.assertIs(os.path.isdir('b/foo'), True)
1680
 
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
 
1960
        with open('b/foo/bar', 'rb') as f:
 
1961
            self.assertEqual(f.read(), b"contents")
1681
1962
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1682
1963
 
1683
1964
    def test_build_with_references(self):
1684
1965
        tree = self.make_branch_and_tree('source',
1685
 
            format='dirstate-with-subtree')
 
1966
                                         format='development-subtree')
1686
1967
        subtree = self.make_branch_and_tree('source/subtree',
1687
 
            format='dirstate-with-subtree')
 
1968
                                            format='development-subtree')
1688
1969
        tree.add_reference(subtree)
1689
1970
        tree.commit('a revision')
1690
1971
        tree.branch.create_checkout('target')
1691
 
        self.failUnlessExists('target')
1692
 
        self.failUnlessExists('target/subtree')
 
1972
        self.assertPathExists('target')
 
1973
        self.assertPathExists('target/subtree')
1693
1974
 
1694
1975
    def test_file_conflict_handling(self):
1695
1976
        """Ensure that when building trees, conflict handling is done"""
1696
1977
        source = self.make_branch_and_tree('source')
1697
1978
        target = self.make_branch_and_tree('target')
1698
1979
        self.build_tree(['source/file', 'target/file'])
1699
 
        source.add('file', 'new-file')
 
1980
        source.add('file', b'new-file')
1700
1981
        source.commit('added file')
1701
1982
        build_tree(source.basis_tree(), target)
1702
 
        self.assertEqual([DuplicateEntry('Moved existing file to',
1703
 
                          'file.moved', 'file', None, 'new-file')],
1704
 
                         target.conflicts())
 
1983
        self.assertEqual(
 
1984
            [DuplicateEntry('Moved existing file to', 'file.moved',
 
1985
                            'file', None, 'new-file')],
 
1986
            target.conflicts())
1705
1987
        target2 = self.make_branch_and_tree('target2')
1706
 
        target_file = file('target2/file', 'wb')
1707
 
        try:
1708
 
            source_file = file('source/file', 'rb')
1709
 
            try:
1710
 
                target_file.write(source_file.read())
1711
 
            finally:
1712
 
                source_file.close()
1713
 
        finally:
1714
 
            target_file.close()
 
1988
        with open('target2/file', 'wb') as target_file, \
 
1989
                open('source/file', 'rb') as source_file:
 
1990
            target_file.write(source_file.read())
1715
1991
        build_tree(source.basis_tree(), target2)
1716
1992
        self.assertEqual([], target2.conflicts())
1717
1993
 
1720
1996
        self.requireFeature(SymlinkFeature)
1721
1997
        source = self.make_branch_and_tree('source')
1722
1998
        os.symlink('foo', 'source/symlink')
1723
 
        source.add('symlink', 'new-symlink')
 
1999
        source.add('symlink', b'new-symlink')
1724
2000
        source.commit('added file')
1725
2001
        target = self.make_branch_and_tree('target')
1726
2002
        os.symlink('bar', 'target/symlink')
1727
2003
        build_tree(source.basis_tree(), target)
1728
 
        self.assertEqual([DuplicateEntry('Moved existing file to',
1729
 
            'symlink.moved', 'symlink', None, 'new-symlink')],
 
2004
        self.assertEqual(
 
2005
            [DuplicateEntry('Moved existing file to', 'symlink.moved',
 
2006
                            'symlink', None, 'new-symlink')],
1730
2007
            target.conflicts())
1731
2008
        target = self.make_branch_and_tree('target2')
1732
2009
        os.symlink('foo', 'target2/symlink')
1738
2015
        source = self.make_branch_and_tree('source')
1739
2016
        target = self.make_branch_and_tree('target')
1740
2017
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1741
 
        source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
 
2018
        source.add(['dir1', 'dir1/file'], [b'new-dir1', b'new-file'])
1742
2019
        source.commit('added file')
1743
2020
        build_tree(source.basis_tree(), target)
1744
2021
        self.assertEqual([], target.conflicts())
1745
 
        self.failUnlessExists('target/dir1/file')
 
2022
        self.assertPathExists('target/dir1/file')
1746
2023
 
1747
2024
        # Ensure contents are merged
1748
2025
        target = self.make_branch_and_tree('target2')
1749
2026
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1750
2027
        build_tree(source.basis_tree(), target)
1751
2028
        self.assertEqual([], target.conflicts())
1752
 
        self.failUnlessExists('target2/dir1/file2')
1753
 
        self.failUnlessExists('target2/dir1/file')
 
2029
        self.assertPathExists('target2/dir1/file2')
 
2030
        self.assertPathExists('target2/dir1/file')
1754
2031
 
1755
2032
        # Ensure new contents are suppressed for existing branches
1756
2033
        target = self.make_branch_and_tree('target3')
1757
2034
        self.make_branch('target3/dir1')
1758
2035
        self.build_tree(['target3/dir1/file2'])
1759
2036
        build_tree(source.basis_tree(), target)
1760
 
        self.failIfExists('target3/dir1/file')
1761
 
        self.failUnlessExists('target3/dir1/file2')
1762
 
        self.failUnlessExists('target3/dir1.diverted/file')
1763
 
        self.assertEqual([DuplicateEntry('Diverted to',
1764
 
            'dir1.diverted', 'dir1', 'new-dir1', None)],
 
2037
        self.assertPathDoesNotExist('target3/dir1/file')
 
2038
        self.assertPathExists('target3/dir1/file2')
 
2039
        self.assertPathExists('target3/dir1.diverted/file')
 
2040
        self.assertEqual(
 
2041
            [DuplicateEntry('Diverted to', 'dir1.diverted',
 
2042
                            'dir1', 'new-dir1', None)],
1765
2043
            target.conflicts())
1766
2044
 
1767
2045
        target = self.make_branch_and_tree('target4')
1768
2046
        self.build_tree(['target4/dir1/'])
1769
2047
        self.make_branch('target4/dir1/file')
1770
2048
        build_tree(source.basis_tree(), target)
1771
 
        self.failUnlessExists('target4/dir1/file')
 
2049
        self.assertPathExists('target4/dir1/file')
1772
2050
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1773
 
        self.failUnlessExists('target4/dir1/file.diverted')
1774
 
        self.assertEqual([DuplicateEntry('Diverted to',
1775
 
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
 
2051
        self.assertPathExists('target4/dir1/file.diverted')
 
2052
        self.assertEqual(
 
2053
            [DuplicateEntry('Diverted to', 'dir1/file.diverted',
 
2054
                            'dir1/file', 'new-file', None)],
1776
2055
            target.conflicts())
1777
2056
 
1778
2057
    def test_mixed_conflict_handling(self):
1780
2059
        source = self.make_branch_and_tree('source')
1781
2060
        target = self.make_branch_and_tree('target')
1782
2061
        self.build_tree(['source/name', 'target/name/'])
1783
 
        source.add('name', 'new-name')
 
2062
        source.add('name', b'new-name')
1784
2063
        source.commit('added file')
1785
2064
        build_tree(source.basis_tree(), target)
1786
 
        self.assertEqual([DuplicateEntry('Moved existing file to',
1787
 
            'name.moved', 'name', None, 'new-name')], target.conflicts())
 
2065
        self.assertEqual(
 
2066
            [DuplicateEntry('Moved existing file to',
 
2067
                            'name.moved', 'name', None, 'new-name')],
 
2068
            target.conflicts())
1788
2069
 
1789
2070
    def test_raises_in_populated(self):
1790
2071
        source = self.make_branch_and_tree('source')
1795
2076
        self.build_tree(['target/name'])
1796
2077
        target.add('name')
1797
2078
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1798
 
            build_tree, source.basis_tree(), target)
 
2079
                          build_tree, source.basis_tree(), target)
1799
2080
 
1800
2081
    def test_build_tree_rename_count(self):
1801
2082
        source = self.make_branch_and_tree('source')
1817
2098
    def create_ab_tree(self):
1818
2099
        """Create a committed test tree with two files"""
1819
2100
        source = self.make_branch_and_tree('source')
1820
 
        self.build_tree_contents([('source/file1', 'A')])
1821
 
        self.build_tree_contents([('source/file2', 'B')])
1822
 
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
2101
        self.build_tree_contents([('source/file1', b'A')])
 
2102
        self.build_tree_contents([('source/file2', b'B')])
 
2103
        source.add(['file1', 'file2'], [b'file1-id', b'file2-id'])
1823
2104
        source.commit('commit files')
1824
2105
        source.lock_write()
1825
2106
        self.addCleanup(source.unlock)
1827
2108
 
1828
2109
    def test_build_tree_accelerator_tree(self):
1829
2110
        source = self.create_ab_tree()
1830
 
        self.build_tree_contents([('source/file2', 'C')])
 
2111
        self.build_tree_contents([('source/file2', b'C')])
1831
2112
        calls = []
1832
2113
        real_source_get_file = source.get_file
1833
 
        def get_file(file_id, path=None):
1834
 
            calls.append(file_id)
1835
 
            return real_source_get_file(file_id, path)
 
2114
 
 
2115
        def get_file(path):
 
2116
            calls.append(path)
 
2117
            return real_source_get_file(path)
1836
2118
        source.get_file = get_file
1837
2119
        target = self.make_branch_and_tree('target')
1838
2120
        revision_tree = source.basis_tree()
1839
2121
        revision_tree.lock_read()
1840
2122
        self.addCleanup(revision_tree.unlock)
1841
2123
        build_tree(revision_tree, target, source)
1842
 
        self.assertEqual(['file1-id'], calls)
 
2124
        self.assertEqual(['file1'], calls)
1843
2125
        target.lock_read()
1844
2126
        self.addCleanup(target.unlock)
1845
2127
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1846
2128
 
 
2129
    def test_build_tree_accelerator_tree_observes_sha1(self):
 
2130
        source = self.create_ab_tree()
 
2131
        sha1 = osutils.sha_string(b'A')
 
2132
        target = self.make_branch_and_tree('target')
 
2133
        target.lock_write()
 
2134
        self.addCleanup(target.unlock)
 
2135
        state = target.current_dirstate()
 
2136
        state._cutoff_time = time.time() + 60
 
2137
        build_tree(source.basis_tree(), target, source)
 
2138
        entry = state._get_entry(0, path_utf8=b'file1')
 
2139
        self.assertEqual(sha1, entry[1][0][1])
 
2140
 
1847
2141
    def test_build_tree_accelerator_tree_missing_file(self):
1848
2142
        source = self.create_ab_tree()
1849
2143
        os.unlink('source/file1')
1860
2154
    def test_build_tree_accelerator_wrong_kind(self):
1861
2155
        self.requireFeature(SymlinkFeature)
1862
2156
        source = self.make_branch_and_tree('source')
1863
 
        self.build_tree_contents([('source/file1', '')])
1864
 
        self.build_tree_contents([('source/file2', '')])
1865
 
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
2157
        self.build_tree_contents([('source/file1', b'')])
 
2158
        self.build_tree_contents([('source/file2', b'')])
 
2159
        source.add(['file1', 'file2'], [b'file1-id', b'file2-id'])
1866
2160
        source.commit('commit files')
1867
2161
        os.unlink('source/file2')
1868
 
        self.build_tree_contents([('source/file2/', 'C')])
 
2162
        self.build_tree_contents([('source/file2/', b'C')])
1869
2163
        os.unlink('source/file1')
1870
2164
        os.symlink('file2', 'source/file1')
1871
2165
        calls = []
1872
2166
        real_source_get_file = source.get_file
1873
 
        def get_file(file_id, path=None):
1874
 
            calls.append(file_id)
1875
 
            return real_source_get_file(file_id, path)
 
2167
 
 
2168
        def get_file(path):
 
2169
            calls.append(path)
 
2170
            return real_source_get_file(path)
1876
2171
        source.get_file = get_file
1877
2172
        target = self.make_branch_and_tree('target')
1878
2173
        revision_tree = source.basis_tree()
1911
2206
 
1912
2207
    def test_build_tree_accelerator_tree_moved(self):
1913
2208
        source = self.make_branch_and_tree('source')
1914
 
        self.build_tree_contents([('source/file1', 'A')])
1915
 
        source.add(['file1'], ['file1-id'])
 
2209
        self.build_tree_contents([('source/file1', b'A')])
 
2210
        source.add(['file1'], [b'file1-id'])
1916
2211
        source.commit('commit files')
1917
2212
        source.rename_one('file1', 'file2')
1918
2213
        source.lock_read()
1930
2225
        self.requireFeature(HardlinkFeature)
1931
2226
        source = self.create_ab_tree()
1932
2227
        tt = TreeTransform(source)
1933
 
        trans_id = tt.trans_id_tree_file_id('file1-id')
 
2228
        trans_id = tt.trans_id_tree_path('file1')
1934
2229
        tt.set_executability(True, trans_id)
1935
2230
        tt.apply()
1936
 
        self.assertTrue(source.is_executable('file1-id'))
 
2231
        self.assertTrue(source.is_executable('file1'))
1937
2232
        target = self.make_branch_and_tree('target')
1938
2233
        revision_tree = source.basis_tree()
1939
2234
        revision_tree.lock_read()
1942
2237
        target.lock_read()
1943
2238
        self.addCleanup(target.unlock)
1944
2239
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1945
 
        self.assertTrue(source.is_executable('file1-id'))
 
2240
        self.assertTrue(source.is_executable('file1'))
1946
2241
 
1947
2242
    def install_rot13_content_filter(self, pattern):
1948
2243
        # We could use
1950
2245
        # below, but that looks a bit... hard to read even if it's exactly
1951
2246
        # the same thing.
1952
2247
        original_registry = filters._reset_registry()
 
2248
 
1953
2249
        def restore_registry():
1954
2250
            filters._reset_registry(original_registry)
1955
2251
        self.addCleanup(restore_registry)
 
2252
 
1956
2253
        def rot13(chunks, context=None):
1957
 
            return [''.join(chunks).encode('rot13')]
 
2254
            return [
 
2255
                codecs.encode(chunk.decode('ascii'), 'rot13').encode('ascii')
 
2256
                for chunk in chunks]
1958
2257
        rot13filter = filters.ContentFilter(rot13, rot13)
1959
 
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
 
2258
        filters.filter_stacks_registry.register(
 
2259
            'rot13', {'yes': [rot13filter]}.get)
1960
2260
        os.mkdir(self.test_home_dir + '/.bazaar')
1961
2261
        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()
 
2262
        with open(rules_filename, 'wb') as f:
 
2263
            f.write(b'[name %s]\nrot13=yes\n' % (pattern,))
 
2264
 
1965
2265
        def uninstall_rules():
1966
2266
            os.remove(rules_filename)
1967
2267
            rules.reset_rules()
1974
2274
        if it can).
1975
2275
        """
1976
2276
        self.requireFeature(HardlinkFeature)
1977
 
        self.install_rot13_content_filter('file1')
 
2277
        self.install_rot13_content_filter(b'file1')
1978
2278
        source = self.create_ab_tree()
1979
2279
        target = self.make_branch_and_tree('target')
1980
2280
        revision_tree = source.basis_tree()
1992
2292
        self.assertEqualStat(source_stat, target_stat)
1993
2293
 
1994
2294
    def test_case_insensitive_build_tree_inventory(self):
1995
 
        if (tests.CaseInsensitiveFilesystemFeature.available()
1996
 
            or tests.CaseInsCasePresFilenameFeature.available()):
 
2295
        if (features.CaseInsensitiveFilesystemFeature.available()
 
2296
                or features.CaseInsCasePresFilenameFeature.available()):
1997
2297
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1998
2298
        source = self.make_branch_and_tree('source')
1999
2299
        self.build_tree(['source/file', 'source/FILE'])
2000
 
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
 
2300
        source.add(['file', 'FILE'], [b'lower-id', b'upper-id'])
2001
2301
        source.commit('added files')
2002
2302
        # Don't try this at home, kids!
2003
2303
        # Force the tree to report that it is case insensitive
2004
2304
        target = self.make_branch_and_tree('target')
2005
2305
        target.case_sensitive = False
2006
2306
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
2007
 
        self.assertEqual('file.moved', target.id2path('lower-id'))
2008
 
        self.assertEqual('FILE', target.id2path('upper-id'))
 
2307
        self.assertEqual('file.moved', target.id2path(b'lower-id'))
 
2308
        self.assertEqual('FILE', target.id2path(b'upper-id'))
 
2309
 
 
2310
    def test_build_tree_observes_sha(self):
 
2311
        source = self.make_branch_and_tree('source')
 
2312
        self.build_tree(['source/file1', 'source/dir/', 'source/dir/file2'])
 
2313
        source.add(['file1', 'dir', 'dir/file2'],
 
2314
                   [b'file1-id', b'dir-id', b'file2-id'])
 
2315
        source.commit('new files')
 
2316
        target = self.make_branch_and_tree('target')
 
2317
        target.lock_write()
 
2318
        self.addCleanup(target.unlock)
 
2319
        # We make use of the fact that DirState caches its cutoff time. So we
 
2320
        # set the 'safe' time to one minute in the future.
 
2321
        state = target.current_dirstate()
 
2322
        state._cutoff_time = time.time() + 60
 
2323
        build_tree(source.basis_tree(), target)
 
2324
        entry1_sha = osutils.sha_file_by_name('source/file1')
 
2325
        entry2_sha = osutils.sha_file_by_name('source/dir/file2')
 
2326
        # entry[1] is the state information, entry[1][0] is the state of the
 
2327
        # working tree, entry[1][0][1] is the sha value for the current working
 
2328
        # tree
 
2329
        entry1 = state._get_entry(0, path_utf8=b'file1')
 
2330
        self.assertEqual(entry1_sha, entry1[1][0][1])
 
2331
        # The 'size' field must also be set.
 
2332
        self.assertEqual(25, entry1[1][0][2])
 
2333
        entry1_state = entry1[1][0]
 
2334
        entry2 = state._get_entry(0, path_utf8=b'dir/file2')
 
2335
        self.assertEqual(entry2_sha, entry2[1][0][1])
 
2336
        self.assertEqual(29, entry2[1][0][2])
 
2337
        entry2_state = entry2[1][0]
 
2338
        # Now, make sure that we don't have to re-read the content. The
 
2339
        # packed_stat should match exactly.
 
2340
        self.assertEqual(entry1_sha, target.get_file_sha1('file1'))
 
2341
        self.assertEqual(entry2_sha, target.get_file_sha1('dir/file2'))
 
2342
        self.assertEqual(entry1_state, entry1[1][0])
 
2343
        self.assertEqual(entry2_state, entry2[1][0])
2009
2344
 
2010
2345
 
2011
2346
class TestCommitTransform(tests.TestCaseWithTransport):
2042
2377
 
2043
2378
    def test_merge_parents(self):
2044
2379
        branch, tt = self.get_branch_and_transform()
2045
 
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2046
 
        self.assertEqual(['rev1b', 'rev1c'],
 
2380
        tt.commit(branch, 'my message', [b'rev1b', b'rev1c'])
 
2381
        self.assertEqual([b'rev1b', b'rev1c'],
2047
2382
                         branch.basis_tree().get_parent_ids()[1:])
2048
2383
 
2049
2384
    def test_first_commit(self):
2052
2387
        self.addCleanup(branch.unlock)
2053
2388
        tt = TransformPreview(branch.basis_tree())
2054
2389
        self.addCleanup(tt.finalize)
2055
 
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2056
 
        rev = tt.commit(branch, 'my message')
 
2390
        tt.new_directory('', ROOT_PARENT, b'TREE_ROOT')
 
2391
        tt.commit(branch, 'my message')
2057
2392
        self.assertEqual([], branch.basis_tree().get_parent_ids())
2058
2393
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2059
2394
                            branch.last_revision())
2065
2400
        tt = TransformPreview(branch.basis_tree())
2066
2401
        self.addCleanup(tt.finalize)
2067
2402
        e = self.assertRaises(ValueError, tt.commit, branch,
2068
 
                          'my message', ['rev1b-id'])
 
2403
                              'my message', [b'rev1b-id'])
2069
2404
        self.assertEqual('Cannot supply merge parents for first commit.',
2070
2405
                         str(e))
2071
2406
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2072
2407
 
2073
2408
    def test_add_files(self):
2074
2409
        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')
 
2410
        tt.new_file('file', tt.root, [b'contents'], b'file-id')
 
2411
        trans_id = tt.new_directory('dir', tt.root, b'dir-id')
2077
2412
        if SymlinkFeature.available():
2078
 
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2079
 
        rev = tt.commit(branch, 'message')
 
2413
            tt.new_symlink('symlink', trans_id, 'target', b'symlink-id')
 
2414
        tt.commit(branch, 'message')
2080
2415
        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'))
 
2416
        self.assertEqual('file', tree.id2path(b'file-id'))
 
2417
        self.assertEqual(b'contents', tree.get_file_text('file'))
 
2418
        self.assertEqual('dir', tree.id2path(b'dir-id'))
2084
2419
        if SymlinkFeature.available():
2085
 
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2086
 
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
2420
            self.assertEqual('dir/symlink', tree.id2path(b'symlink-id'))
 
2421
            self.assertEqual('target', tree.get_symlink_target('dir/symlink'))
2087
2422
 
2088
2423
    def test_add_unversioned(self):
2089
2424
        branch, tt = self.get_branch_and_transform()
2090
 
        tt.new_file('file', tt.root, 'contents')
 
2425
        tt.new_file('file', tt.root, [b'contents'])
2091
2426
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2092
2427
                          'message', strict=True)
2093
2428
 
2094
2429
    def test_modify_strict(self):
2095
2430
        branch, tt = self.get_branch_and_transform()
2096
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2431
        tt.new_file('file', tt.root, [b'contents'], b'file-id')
2097
2432
        tt.commit(branch, 'message', strict=True)
2098
2433
        tt = TransformPreview(branch.basis_tree())
2099
2434
        self.addCleanup(tt.finalize)
2100
 
        trans_id = tt.trans_id_file_id('file-id')
 
2435
        trans_id = tt.trans_id_file_id(b'file-id')
2101
2436
        tt.delete_contents(trans_id)
2102
 
        tt.create_file('contents', trans_id)
 
2437
        tt.create_file([b'contents'], trans_id)
2103
2438
        tt.commit(branch, 'message', strict=True)
2104
2439
 
2105
2440
    def test_commit_malformed(self):
2108
2443
        In this case, we are adding a file without adding its parent.
2109
2444
        """
2110
2445
        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')
 
2446
        parent_id = tt.trans_id_file_id(b'parent-id')
 
2447
        tt.new_file('file', parent_id, [b'contents'], b'file-id')
2113
2448
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2114
2449
                          'message')
2115
2450
 
2117
2452
        branch, tt = self.get_branch_and_transform()
2118
2453
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
2119
2454
                           committer='me <me@example.com>',
2120
 
                           revprops={'foo': 'bar'}, revision_id='revid-1',
 
2455
                           revprops={u'foo': 'bar'}, revision_id=b'revid-1',
2121
2456
                           authors=['Author1 <author1@example.com>',
2122
 
                              'Author2 <author2@example.com>',
2123
 
                               ])
2124
 
        self.assertEqual('revid-1', rev_id)
 
2457
                                    'Author2 <author2@example.com>',
 
2458
                                    ])
 
2459
        self.assertEqual(b'revid-1', rev_id)
2125
2460
        revision = branch.repository.get_revision(rev_id)
2126
2461
        self.assertEqual(1, revision.timestamp)
2127
2462
        self.assertEqual(43201, revision.timezone)
2146
2481
        self.assertEqual('tree', revision.properties['branch-nick'])
2147
2482
 
2148
2483
 
2149
 
class MockTransform(object):
2150
 
 
2151
 
    def has_named_child(self, by_parent, parent_id, name):
2152
 
        for child_id in by_parent[parent_id]:
2153
 
            if child_id == '0':
2154
 
                if name == "name~":
2155
 
                    return True
2156
 
            elif name == "name.~%s~" % child_id:
2157
 
                return True
2158
 
        return False
2159
 
 
2160
 
 
2161
 
class MockEntry(object):
2162
 
    def __init__(self):
2163
 
        object.__init__(self)
2164
 
        self.name = "name"
2165
 
 
2166
 
 
2167
 
class TestGetBackupName(TestCase):
2168
 
    def test_get_backup_name(self):
2169
 
        tt = MockTransform()
2170
 
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
2171
 
        self.assertEqual(name, 'name.~1~')
2172
 
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
2173
 
        self.assertEqual(name, 'name.~2~')
2174
 
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
2175
 
        self.assertEqual(name, 'name.~1~')
2176
 
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
2177
 
        self.assertEqual(name, 'name.~1~')
2178
 
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
2179
 
        self.assertEqual(name, 'name.~4~')
2180
 
 
2181
 
 
2182
2484
class TestFileMover(tests.TestCaseWithTransport):
2183
2485
 
2184
2486
    def test_file_mover(self):
2185
2487
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2186
2488
        mover = _FileMover()
2187
2489
        mover.rename('a', 'q')
2188
 
        self.failUnlessExists('q')
2189
 
        self.failIfExists('a')
2190
 
        self.failUnlessExists('q/b')
2191
 
        self.failUnlessExists('c')
2192
 
        self.failUnlessExists('c/d')
 
2490
        self.assertPathExists('q')
 
2491
        self.assertPathDoesNotExist('a')
 
2492
        self.assertPathExists('q/b')
 
2493
        self.assertPathExists('c')
 
2494
        self.assertPathExists('c/d')
2193
2495
 
2194
2496
    def test_pre_delete_rollback(self):
2195
2497
        self.build_tree(['a/'])
2196
2498
        mover = _FileMover()
2197
2499
        mover.pre_delete('a', 'q')
2198
 
        self.failUnlessExists('q')
2199
 
        self.failIfExists('a')
 
2500
        self.assertPathExists('q')
 
2501
        self.assertPathDoesNotExist('a')
2200
2502
        mover.rollback()
2201
 
        self.failIfExists('q')
2202
 
        self.failUnlessExists('a')
 
2503
        self.assertPathDoesNotExist('q')
 
2504
        self.assertPathExists('a')
2203
2505
 
2204
2506
    def test_apply_deletions(self):
2205
2507
        self.build_tree(['a/', 'b/'])
2206
2508
        mover = _FileMover()
2207
2509
        mover.pre_delete('a', 'q')
2208
2510
        mover.pre_delete('b', 'r')
2209
 
        self.failUnlessExists('q')
2210
 
        self.failUnlessExists('r')
2211
 
        self.failIfExists('a')
2212
 
        self.failIfExists('b')
 
2511
        self.assertPathExists('q')
 
2512
        self.assertPathExists('r')
 
2513
        self.assertPathDoesNotExist('a')
 
2514
        self.assertPathDoesNotExist('b')
2213
2515
        mover.apply_deletions()
2214
 
        self.failIfExists('q')
2215
 
        self.failIfExists('r')
2216
 
        self.failIfExists('a')
2217
 
        self.failIfExists('b')
 
2516
        self.assertPathDoesNotExist('q')
 
2517
        self.assertPathDoesNotExist('r')
 
2518
        self.assertPathDoesNotExist('a')
 
2519
        self.assertPathDoesNotExist('b')
2218
2520
 
2219
2521
    def test_file_mover_rollback(self):
2220
2522
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2223
2525
        mover.rename('c/e', 'c/d')
2224
2526
        try:
2225
2527
            mover.rename('a', 'c')
2226
 
        except errors.FileExists, e:
 
2528
        except errors.FileExists:
2227
2529
            mover.rollback()
2228
 
        self.failUnlessExists('a')
2229
 
        self.failUnlessExists('c/d')
 
2530
        self.assertPathExists('a')
 
2531
        self.assertPathExists('c/d')
2230
2532
 
2231
2533
 
2232
2534
class Bogus(Exception):
2244
2546
 
2245
2547
        def rename(self, source, target):
2246
2548
            if (self.bad_source is not None and
2247
 
                source.endswith(self.bad_source)):
 
2549
                    source.endswith(self.bad_source)):
2248
2550
                raise Bogus
2249
2551
            elif (self.bad_target is not None and
2250
 
                target.endswith(self.bad_target)):
 
2552
                  target.endswith(self.bad_target)):
2251
2553
                raise Bogus
2252
2554
            else:
2253
2555
                _FileMover.rename(self, source, target)
2262
2564
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2263
2565
        self.assertRaises(Bogus, tt.apply,
2264
2566
                          _mover=self.ExceptionFileMover(bad_source='a'))
2265
 
        self.failUnlessExists('a')
2266
 
        self.failUnlessExists('a/b')
 
2567
        self.assertPathExists('a')
 
2568
        self.assertPathExists('a/b')
2267
2569
        tt.apply()
2268
 
        self.failUnlessExists('c')
2269
 
        self.failUnlessExists('c/d')
 
2570
        self.assertPathExists('c')
 
2571
        self.assertPathExists('c/d')
2270
2572
 
2271
2573
    def test_rollback_rename_into_place(self):
2272
2574
        tree = self.make_branch_and_tree('.')
2278
2580
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2279
2581
        self.assertRaises(Bogus, tt.apply,
2280
2582
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
2281
 
        self.failUnlessExists('a')
2282
 
        self.failUnlessExists('a/b')
 
2583
        self.assertPathExists('a')
 
2584
        self.assertPathExists('a/b')
2283
2585
        tt.apply()
2284
 
        self.failUnlessExists('c')
2285
 
        self.failUnlessExists('c/d')
 
2586
        self.assertPathExists('c')
 
2587
        self.assertPathExists('c/d')
2286
2588
 
2287
2589
    def test_rollback_deletion(self):
2288
2590
        tree = self.make_branch_and_tree('.')
2294
2596
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2295
2597
        self.assertRaises(Bogus, tt.apply,
2296
2598
                          _mover=self.ExceptionFileMover(bad_target='d'))
2297
 
        self.failUnlessExists('a')
2298
 
        self.failUnlessExists('a/b')
2299
 
 
2300
 
    def test_resolve_no_parent(self):
 
2599
        self.assertPathExists('a')
 
2600
        self.assertPathExists('a/b')
 
2601
 
 
2602
 
 
2603
class TestFinalizeRobustness(tests.TestCaseWithTransport):
 
2604
    """Ensure treetransform creation errors can be safely cleaned up after"""
 
2605
 
 
2606
    def _override_globals_in_method(self, instance, method_name, globals):
 
2607
        """Replace method on instance with one with updated globals"""
 
2608
        import types
 
2609
        func = getattr(instance, method_name).__func__
 
2610
        new_globals = dict(func.__globals__)
 
2611
        new_globals.update(globals)
 
2612
        new_func = types.FunctionType(func.__code__, new_globals,
 
2613
                                      func.__name__, func.__defaults__)
 
2614
        if PY3:
 
2615
            setattr(instance, method_name,
 
2616
                    types.MethodType(new_func, instance))
 
2617
        else:
 
2618
            setattr(instance, method_name,
 
2619
                    types.MethodType(new_func, instance, instance.__class__))
 
2620
        self.addCleanup(delattr, instance, method_name)
 
2621
 
 
2622
    @staticmethod
 
2623
    def _fake_open_raises_before(name, mode):
 
2624
        """Like open() but raises before doing anything"""
 
2625
        raise RuntimeError
 
2626
 
 
2627
    @staticmethod
 
2628
    def _fake_open_raises_after(name, mode):
 
2629
        """Like open() but raises after creating file without returning"""
 
2630
        open(name, mode).close()
 
2631
        raise RuntimeError
 
2632
 
 
2633
    def create_transform_and_root_trans_id(self):
 
2634
        """Setup a transform creating a file in limbo"""
 
2635
        tree = self.make_branch_and_tree('.')
 
2636
        tt = TreeTransform(tree)
 
2637
        return tt, tt.create_path("a", tt.root)
 
2638
 
 
2639
    def create_transform_and_subdir_trans_id(self):
 
2640
        """Setup a transform creating a directory containing a file in limbo"""
 
2641
        tree = self.make_branch_and_tree('.')
 
2642
        tt = TreeTransform(tree)
 
2643
        d_trans_id = tt.create_path("d", tt.root)
 
2644
        tt.create_directory(d_trans_id)
 
2645
        f_trans_id = tt.create_path("a", d_trans_id)
 
2646
        tt.adjust_path("a", d_trans_id, f_trans_id)
 
2647
        return tt, f_trans_id
 
2648
 
 
2649
    def test_root_create_file_open_raises_before_creation(self):
 
2650
        tt, trans_id = self.create_transform_and_root_trans_id()
 
2651
        self._override_globals_in_method(
 
2652
            tt, "create_file", {"open": self._fake_open_raises_before})
 
2653
        self.assertRaises(RuntimeError, tt.create_file,
 
2654
                          [b"contents"], trans_id)
 
2655
        path = tt._limbo_name(trans_id)
 
2656
        self.assertPathDoesNotExist(path)
 
2657
        tt.finalize()
 
2658
        self.assertPathDoesNotExist(tt._limbodir)
 
2659
 
 
2660
    def test_root_create_file_open_raises_after_creation(self):
 
2661
        tt, trans_id = self.create_transform_and_root_trans_id()
 
2662
        self._override_globals_in_method(
 
2663
            tt, "create_file", {"open": self._fake_open_raises_after})
 
2664
        self.assertRaises(RuntimeError, tt.create_file,
 
2665
                          [b"contents"], trans_id)
 
2666
        path = tt._limbo_name(trans_id)
 
2667
        self.assertPathExists(path)
 
2668
        tt.finalize()
 
2669
        self.assertPathDoesNotExist(path)
 
2670
        self.assertPathDoesNotExist(tt._limbodir)
 
2671
 
 
2672
    def test_subdir_create_file_open_raises_before_creation(self):
 
2673
        tt, trans_id = self.create_transform_and_subdir_trans_id()
 
2674
        self._override_globals_in_method(
 
2675
            tt, "create_file", {"open": self._fake_open_raises_before})
 
2676
        self.assertRaises(RuntimeError, tt.create_file,
 
2677
                          [b"contents"], trans_id)
 
2678
        path = tt._limbo_name(trans_id)
 
2679
        self.assertPathDoesNotExist(path)
 
2680
        tt.finalize()
 
2681
        self.assertPathDoesNotExist(tt._limbodir)
 
2682
 
 
2683
    def test_subdir_create_file_open_raises_after_creation(self):
 
2684
        tt, trans_id = self.create_transform_and_subdir_trans_id()
 
2685
        self._override_globals_in_method(
 
2686
            tt, "create_file", {"open": self._fake_open_raises_after})
 
2687
        self.assertRaises(RuntimeError, tt.create_file,
 
2688
                          [b"contents"], trans_id)
 
2689
        path = tt._limbo_name(trans_id)
 
2690
        self.assertPathExists(path)
 
2691
        tt.finalize()
 
2692
        self.assertPathDoesNotExist(path)
 
2693
        self.assertPathDoesNotExist(tt._limbodir)
 
2694
 
 
2695
    def test_rename_in_limbo_rename_raises_after_rename(self):
 
2696
        tt, trans_id = self.create_transform_and_root_trans_id()
 
2697
        parent1 = tt.new_directory('parent1', tt.root)
 
2698
        child1 = tt.new_file('child1', parent1, [b'contents'])
 
2699
        parent2 = tt.new_directory('parent2', tt.root)
 
2700
 
 
2701
        class FakeOSModule(object):
 
2702
            def rename(self, old, new):
 
2703
                os.rename(old, new)
 
2704
                raise RuntimeError
 
2705
        self._override_globals_in_method(tt, "_rename_in_limbo",
 
2706
                                         {"os": FakeOSModule()})
 
2707
        self.assertRaises(
 
2708
            RuntimeError, tt.adjust_path, "child1", parent2, child1)
 
2709
        path = osutils.pathjoin(tt._limbo_name(parent2), "child1")
 
2710
        self.assertPathExists(path)
 
2711
        tt.finalize()
 
2712
        self.assertPathDoesNotExist(path)
 
2713
        self.assertPathDoesNotExist(tt._limbodir)
 
2714
 
 
2715
    def test_rename_in_limbo_rename_raises_before_rename(self):
 
2716
        tt, trans_id = self.create_transform_and_root_trans_id()
 
2717
        parent1 = tt.new_directory('parent1', tt.root)
 
2718
        child1 = tt.new_file('child1', parent1, [b'contents'])
 
2719
        parent2 = tt.new_directory('parent2', tt.root)
 
2720
 
 
2721
        class FakeOSModule(object):
 
2722
            def rename(self, old, new):
 
2723
                raise RuntimeError
 
2724
        self._override_globals_in_method(tt, "_rename_in_limbo",
 
2725
                                         {"os": FakeOSModule()})
 
2726
        self.assertRaises(
 
2727
            RuntimeError, tt.adjust_path, "child1", parent2, child1)
 
2728
        path = osutils.pathjoin(tt._limbo_name(parent1), "child1")
 
2729
        self.assertPathExists(path)
 
2730
        tt.finalize()
 
2731
        self.assertPathDoesNotExist(path)
 
2732
        self.assertPathDoesNotExist(tt._limbodir)
 
2733
 
 
2734
 
 
2735
class TestTransformMissingParent(tests.TestCaseWithTransport):
 
2736
 
 
2737
    def make_tt_with_versioned_dir(self):
2301
2738
        wt = self.make_branch_and_tree('.')
 
2739
        self.build_tree(['dir/', ])
 
2740
        wt.add(['dir'], [b'dir-id'])
 
2741
        wt.commit('Create dir')
2302
2742
        tt = TreeTransform(wt)
2303
2743
        self.addCleanup(tt.finalize)
2304
 
        parent = tt.trans_id_file_id('parent-id')
2305
 
        tt.new_file('file', parent, 'Contents')
2306
 
        resolve_conflicts(tt)
2307
 
 
2308
 
 
2309
 
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2310
 
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2311
 
                  (False, False))
2312
 
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2313
 
              ('', ''), ('directory', 'directory'), (False, None))
 
2744
        return wt, tt
 
2745
 
 
2746
    def test_resolve_create_parent_for_versioned_file(self):
 
2747
        wt, tt = self.make_tt_with_versioned_dir()
 
2748
        dir_tid = tt.trans_id_tree_path('dir')
 
2749
        tt.new_file('file', dir_tid, [b'Contents'], file_id=b'file-id')
 
2750
        tt.delete_contents(dir_tid)
 
2751
        tt.unversion_file(dir_tid)
 
2752
        conflicts = resolve_conflicts(tt)
 
2753
        # one conflict for the missing directory, one for the unversioned
 
2754
        # parent
 
2755
        self.assertLength(2, conflicts)
 
2756
 
 
2757
    def test_non_versioned_file_create_conflict(self):
 
2758
        wt, tt = self.make_tt_with_versioned_dir()
 
2759
        dir_tid = tt.trans_id_tree_path('dir')
 
2760
        tt.new_file('file', dir_tid, [b'Contents'])
 
2761
        tt.delete_contents(dir_tid)
 
2762
        tt.unversion_file(dir_tid)
 
2763
        conflicts = resolve_conflicts(tt)
 
2764
        # no conflicts or rather: orphaning 'file' resolve the 'dir' conflict
 
2765
        self.assertLength(1, conflicts)
 
2766
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
 
2767
                         conflicts.pop())
 
2768
 
 
2769
 
 
2770
A_ENTRY = (b'a-id', ('a', 'a'), True, (True, True),
 
2771
           (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
 
2772
           (False, False), False)
 
2773
ROOT_ENTRY = (b'TREE_ROOT', ('', ''), False, (True, True), (None, None),
 
2774
              ('', ''), ('directory', 'directory'), (False, False), False)
2314
2775
 
2315
2776
 
2316
2777
class TestTransformPreview(tests.TestCaseWithTransport):
2317
2778
 
2318
2779
    def create_tree(self):
2319
2780
        tree = self.make_branch_and_tree('.')
2320
 
        self.build_tree_contents([('a', 'content 1')])
2321
 
        tree.set_root_id('TREE_ROOT')
2322
 
        tree.add('a', 'a-id')
2323
 
        tree.commit('rev1', rev_id='rev1')
2324
 
        return tree.branch.repository.revision_tree('rev1')
 
2781
        self.build_tree_contents([('a', b'content 1')])
 
2782
        tree.set_root_id(b'TREE_ROOT')
 
2783
        tree.add('a', b'a-id')
 
2784
        tree.commit('rev1', rev_id=b'rev1')
 
2785
        return tree.branch.repository.revision_tree(b'rev1')
2325
2786
 
2326
2787
    def get_empty_preview(self):
2327
2788
        repository = self.make_repository('repo')
2345
2806
        revision_tree = self.create_tree()
2346
2807
        preview = TransformPreview(revision_tree)
2347
2808
        self.addCleanup(preview.finalize)
2348
 
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
2809
        preview.new_file('file2', preview.root, [b'content B\n'], b'file2-id')
2349
2810
        preview_tree = preview.get_preview_tree()
2350
 
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
2351
 
        self.assertEqual(
2352
 
            preview_tree.get_file('file2-id').read(), 'content B\n')
 
2811
        self.assertEqual(preview_tree.kind('file2'), 'file')
 
2812
        with preview_tree.get_file('file2') as f:
 
2813
            self.assertEqual(f.read(), b'content B\n')
2353
2814
 
2354
2815
    def test_diff_preview_tree(self):
2355
2816
        revision_tree = self.create_tree()
2356
2817
        preview = TransformPreview(revision_tree)
2357
2818
        self.addCleanup(preview.finalize)
2358
 
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
2819
        preview.new_file('file2', preview.root, [b'content B\n'], b'file2-id')
2359
2820
        preview_tree = preview.get_preview_tree()
2360
 
        out = StringIO()
 
2821
        out = BytesIO()
2361
2822
        show_diff_trees(revision_tree, preview_tree, out)
2362
2823
        lines = out.getvalue().splitlines()
2363
 
        self.assertEqual(lines[0], "=== added file 'file2'")
 
2824
        self.assertEqual(lines[0], b"=== added file 'file2'")
2364
2825
        # 3 lines of diff administrivia
2365
 
        self.assertEqual(lines[4], "+content B")
 
2826
        self.assertEqual(lines[4], b"+content B")
 
2827
 
 
2828
    def test_unsupported_symlink_diff(self):
 
2829
        self.requireFeature(SymlinkFeature)
 
2830
        tree = self.make_branch_and_tree('.')
 
2831
        self.build_tree_contents([('a', 'content 1')])
 
2832
        tree.set_root_id(b'TREE_ROOT')
 
2833
        tree.add('a', b'a-id')
 
2834
        os.symlink('a', 'foo')
 
2835
        tree.add('foo', b'foo-id')
 
2836
        tree.commit('rev1', rev_id=b'rev1')
 
2837
        revision_tree = tree.branch.repository.revision_tree(b'rev1')
 
2838
        preview = TransformPreview(revision_tree)
 
2839
        self.addCleanup(preview.finalize)
 
2840
        preview.delete_versioned(preview.trans_id_tree_path('foo'))
 
2841
        preview_tree = preview.get_preview_tree()
 
2842
        out = BytesIO()
 
2843
        log = BytesIO()
 
2844
        trace.push_log_file(log)
 
2845
        os_symlink = getattr(os, 'symlink', None)
 
2846
        os.symlink = None
 
2847
        try:
 
2848
            show_diff_trees(revision_tree, preview_tree, out)
 
2849
            lines = out.getvalue().splitlines()
 
2850
        finally:
 
2851
            os.symlink = os_symlink
 
2852
        self.assertContainsRe(
 
2853
            log.getvalue(),
 
2854
            b'Ignoring "foo" as symlinks are not supported on this filesystem')
2366
2855
 
2367
2856
    def test_transform_conflicts(self):
2368
2857
        revision_tree = self.create_tree()
2369
2858
        preview = TransformPreview(revision_tree)
2370
2859
        self.addCleanup(preview.finalize)
2371
 
        preview.new_file('a', preview.root, 'content 2')
 
2860
        preview.new_file('a', preview.root, [b'content 2'])
2372
2861
        resolve_conflicts(preview)
2373
 
        trans_id = preview.trans_id_file_id('a-id')
 
2862
        trans_id = preview.trans_id_file_id(b'a-id')
2374
2863
        self.assertEqual('a.moved', preview.final_name(trans_id))
2375
2864
 
2376
2865
    def get_tree_and_preview_tree(self):
2377
2866
        revision_tree = self.create_tree()
2378
2867
        preview = TransformPreview(revision_tree)
2379
2868
        self.addCleanup(preview.finalize)
2380
 
        a_trans_id = preview.trans_id_file_id('a-id')
 
2869
        a_trans_id = preview.trans_id_file_id(b'a-id')
2381
2870
        preview.delete_contents(a_trans_id)
2382
 
        preview.create_file('b content', a_trans_id)
 
2871
        preview.create_file([b'b content'], a_trans_id)
2383
2872
        preview_tree = preview.get_preview_tree()
2384
2873
        return revision_tree, preview_tree
2385
2874
 
2386
2875
    def test_iter_changes(self):
2387
2876
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2388
 
        root = revision_tree.inventory.root.file_id
2389
 
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2390
 
                          (root, root), ('a', 'a'), ('file', 'file'),
2391
 
                          (False, False))],
2392
 
                          list(preview_tree.iter_changes(revision_tree)))
 
2877
        root = revision_tree.path2id('')
 
2878
        self.assertEqual([(b'a-id', ('a', 'a'), True, (True, True),
 
2879
                           (root, root), ('a', 'a'), ('file', 'file'),
 
2880
                           (False, False), False)],
 
2881
                         list(preview_tree.iter_changes(revision_tree)))
2393
2882
 
2394
2883
    def test_include_unchanged_succeeds(self):
2395
2884
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2396
2885
        changes = preview_tree.iter_changes(revision_tree,
2397
2886
                                            include_unchanged=True)
2398
 
        root = revision_tree.inventory.root.file_id
2399
 
 
2400
2887
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2401
2888
 
2402
2889
    def test_specific_files(self):
2403
2890
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2404
2891
        changes = preview_tree.iter_changes(revision_tree,
2405
2892
                                            specific_files=[''])
2406
 
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2893
        self.assertEqual([A_ENTRY], list(changes))
2407
2894
 
2408
2895
    def test_want_unversioned(self):
2409
2896
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2410
2897
        changes = preview_tree.iter_changes(revision_tree,
2411
2898
                                            want_unversioned=True)
2412
 
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2899
        self.assertEqual([A_ENTRY], list(changes))
2413
2900
 
2414
2901
    def test_ignore_extra_trees_no_specific_files(self):
2415
2902
        # extra_trees is harmless without specific_files, so we'll silently
2431
2918
        revision_tree = self.create_tree()
2432
2919
        preview = TransformPreview(revision_tree)
2433
2920
        self.addCleanup(preview.finalize)
2434
 
        preview.new_file('file', preview.root, 'contents', 'file-id')
2435
 
        preview.new_directory('directory', preview.root, 'dir-id')
 
2921
        preview.new_file('file', preview.root, [b'contents'], b'file-id')
 
2922
        preview.new_directory('directory', preview.root, b'dir-id')
2436
2923
        preview_tree = preview.get_preview_tree()
2437
 
        self.assertEqual('file', preview_tree.kind('file-id'))
2438
 
        self.assertEqual('directory', preview_tree.kind('dir-id'))
 
2924
        self.assertEqual('file', preview_tree.kind('file'))
 
2925
        self.assertEqual('directory', preview_tree.kind('directory'))
2439
2926
 
2440
2927
    def test_get_file_mtime(self):
2441
2928
        preview = self.get_empty_preview()
2442
 
        file_trans_id = preview.new_file('file', preview.root, 'contents',
2443
 
                                         'file-id')
 
2929
        file_trans_id = preview.new_file('file', preview.root, [b'contents'],
 
2930
                                         b'file-id')
2444
2931
        limbo_path = preview._limbo_name(file_trans_id)
2445
2932
        preview_tree = preview.get_preview_tree()
2446
2933
        self.assertEqual(os.stat(limbo_path).st_mtime,
2447
 
                         preview_tree.get_file_mtime('file-id'))
 
2934
                         preview_tree.get_file_mtime('file'))
2448
2935
 
2449
2936
    def test_get_file_mtime_renamed(self):
2450
2937
        work_tree = self.make_branch_and_tree('tree')
2451
2938
        self.build_tree(['tree/file'])
2452
 
        work_tree.add('file', 'file-id')
 
2939
        work_tree.add('file', b'file-id')
2453
2940
        preview = TransformPreview(work_tree)
2454
2941
        self.addCleanup(preview.finalize)
2455
 
        file_trans_id = preview.trans_id_tree_file_id('file-id')
 
2942
        file_trans_id = preview.trans_id_tree_path('file')
2456
2943
        preview.adjust_path('renamed', preview.root, file_trans_id)
2457
2944
        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')
 
2945
        preview_mtime = preview_tree.get_file_mtime('renamed')
 
2946
        work_mtime = work_tree.get_file_mtime('file')
 
2947
 
 
2948
    def test_get_file_size(self):
 
2949
        work_tree = self.make_branch_and_tree('tree')
 
2950
        self.build_tree_contents([('tree/old', b'old')])
 
2951
        work_tree.add('old', b'old-id')
 
2952
        preview = TransformPreview(work_tree)
 
2953
        self.addCleanup(preview.finalize)
 
2954
        preview.new_file('name', preview.root, [b'contents'], b'new-id',
 
2955
                         'executable')
 
2956
        tree = preview.get_preview_tree()
 
2957
        self.assertEqual(len('old'), tree.get_file_size('old'))
 
2958
        self.assertEqual(len('contents'), tree.get_file_size('name'))
2460
2959
 
2461
2960
    def test_get_file(self):
2462
2961
        preview = self.get_empty_preview()
2463
 
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2962
        preview.new_file('file', preview.root, [b'contents'], b'file-id')
2464
2963
        preview_tree = preview.get_preview_tree()
2465
 
        tree_file = preview_tree.get_file('file-id')
2466
 
        try:
2467
 
            self.assertEqual('contents', tree_file.read())
2468
 
        finally:
2469
 
            tree_file.close()
 
2964
        with preview_tree.get_file('file') as tree_file:
 
2965
            self.assertEqual(b'contents', tree_file.read())
2470
2966
 
2471
2967
    def test_get_symlink_target(self):
2472
2968
        self.requireFeature(SymlinkFeature)
2473
2969
        preview = self.get_empty_preview()
2474
 
        preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
 
2970
        preview.new_symlink('symlink', preview.root, 'target', b'symlink-id')
2475
2971
        preview_tree = preview.get_preview_tree()
2476
2972
        self.assertEqual('target',
2477
 
                         preview_tree.get_symlink_target('symlink-id'))
 
2973
                         preview_tree.get_symlink_target('symlink'))
2478
2974
 
2479
2975
    def test_all_file_ids(self):
2480
2976
        tree = self.make_branch_and_tree('tree')
2481
2977
        self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2482
 
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
 
2978
        tree.add(['a', 'b', 'c'], [b'a-id', b'b-id', b'c-id'])
2483
2979
        preview = TransformPreview(tree)
2484
2980
        self.addCleanup(preview.finalize)
2485
 
        preview.unversion_file(preview.trans_id_file_id('b-id'))
2486
 
        c_trans_id = preview.trans_id_file_id('c-id')
 
2981
        preview.unversion_file(preview.trans_id_file_id(b'b-id'))
 
2982
        c_trans_id = preview.trans_id_file_id(b'c-id')
2487
2983
        preview.unversion_file(c_trans_id)
2488
 
        preview.version_file('c-id', c_trans_id)
 
2984
        preview.version_file(b'c-id', c_trans_id)
2489
2985
        preview_tree = preview.get_preview_tree()
2490
 
        self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
 
2986
        self.assertEqual({b'a-id', b'c-id', tree.path2id('')},
2491
2987
                         preview_tree.all_file_ids())
2492
2988
 
2493
2989
    def test_path2id_deleted_unchanged(self):
2494
2990
        tree = self.make_branch_and_tree('tree')
2495
2991
        self.build_tree(['tree/unchanged', 'tree/deleted'])
2496
 
        tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
 
2992
        tree.add(['unchanged', 'deleted'], [b'unchanged-id', b'deleted-id'])
2497
2993
        preview = TransformPreview(tree)
2498
2994
        self.addCleanup(preview.finalize)
2499
 
        preview.unversion_file(preview.trans_id_file_id('deleted-id'))
 
2995
        preview.unversion_file(preview.trans_id_file_id(b'deleted-id'))
2500
2996
        preview_tree = preview.get_preview_tree()
2501
 
        self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2502
 
        self.assertIs(None, preview_tree.path2id('deleted'))
 
2997
        self.assertEqual(b'unchanged-id', preview_tree.path2id('unchanged'))
 
2998
        self.assertFalse(preview_tree.is_versioned('deleted'))
2503
2999
 
2504
3000
    def test_path2id_created(self):
2505
3001
        tree = self.make_branch_and_tree('tree')
2506
3002
        self.build_tree(['tree/unchanged'])
2507
 
        tree.add(['unchanged'], ['unchanged-id'])
 
3003
        tree.add(['unchanged'], [b'unchanged-id'])
2508
3004
        preview = TransformPreview(tree)
2509
3005
        self.addCleanup(preview.finalize)
2510
 
        preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2511
 
            'contents', 'new-id')
 
3006
        preview.new_file('new', preview.trans_id_file_id(b'unchanged-id'),
 
3007
                         [b'contents'], b'new-id')
2512
3008
        preview_tree = preview.get_preview_tree()
2513
 
        self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
 
3009
        self.assertEqual(b'new-id', preview_tree.path2id('unchanged/new'))
2514
3010
 
2515
3011
    def test_path2id_moved(self):
2516
3012
        tree = self.make_branch_and_tree('tree')
2517
3013
        self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2518
3014
        tree.add(['old_parent', 'old_parent/child'],
2519
 
                 ['old_parent-id', 'child-id'])
 
3015
                 [b'old_parent-id', b'child-id'])
2520
3016
        preview = TransformPreview(tree)
2521
3017
        self.addCleanup(preview.finalize)
2522
3018
        new_parent = preview.new_directory('new_parent', preview.root,
2523
 
                                           'new_parent-id')
 
3019
                                           b'new_parent-id')
2524
3020
        preview.adjust_path('child', new_parent,
2525
 
                            preview.trans_id_file_id('child-id'))
 
3021
                            preview.trans_id_file_id(b'child-id'))
2526
3022
        preview_tree = preview.get_preview_tree()
2527
 
        self.assertIs(None, preview_tree.path2id('old_parent/child'))
2528
 
        self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
 
3023
        self.assertFalse(preview_tree.is_versioned('old_parent/child'))
 
3024
        self.assertEqual(b'child-id', preview_tree.path2id('new_parent/child'))
2529
3025
 
2530
3026
    def test_path2id_renamed_parent(self):
2531
3027
        tree = self.make_branch_and_tree('tree')
2532
3028
        self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2533
3029
        tree.add(['old_name', 'old_name/child'],
2534
 
                 ['parent-id', 'child-id'])
 
3030
                 [b'parent-id', b'child-id'])
2535
3031
        preview = TransformPreview(tree)
2536
3032
        self.addCleanup(preview.finalize)
2537
3033
        preview.adjust_path('new_name', preview.root,
2538
 
                            preview.trans_id_file_id('parent-id'))
 
3034
                            preview.trans_id_file_id(b'parent-id'))
2539
3035
        preview_tree = preview.get_preview_tree()
2540
 
        self.assertIs(None, preview_tree.path2id('old_name/child'))
2541
 
        self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
 
3036
        self.assertFalse(preview_tree.is_versioned('old_name/child'))
 
3037
        self.assertEqual(b'child-id', preview_tree.path2id('new_name/child'))
2542
3038
 
2543
 
    def assertMatchingIterEntries(self, tt, specific_file_ids=None):
 
3039
    def assertMatchingIterEntries(self, tt, specific_files=None):
2544
3040
        preview_tree = tt.get_preview_tree()
2545
3041
        preview_result = list(preview_tree.iter_entries_by_dir(
2546
 
                              specific_file_ids))
 
3042
                              specific_files=specific_files))
2547
3043
        tree = tt._tree
2548
3044
        tt.apply()
2549
 
        actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
 
3045
        actual_result = list(tree.iter_entries_by_dir(
 
3046
            specific_files=specific_files))
2550
3047
        self.assertEqual(actual_result, preview_result)
2551
3048
 
2552
3049
    def test_iter_entries_by_dir_new(self):
2553
3050
        tree = self.make_branch_and_tree('tree')
2554
3051
        tt = TreeTransform(tree)
2555
 
        tt.new_file('new', tt.root, 'contents', 'new-id')
 
3052
        tt.new_file('new', tt.root, [b'contents'], b'new-id')
2556
3053
        self.assertMatchingIterEntries(tt)
2557
3054
 
2558
3055
    def test_iter_entries_by_dir_deleted(self):
2559
3056
        tree = self.make_branch_and_tree('tree')
2560
3057
        self.build_tree(['tree/deleted'])
2561
 
        tree.add('deleted', 'deleted-id')
 
3058
        tree.add('deleted', b'deleted-id')
2562
3059
        tt = TreeTransform(tree)
2563
 
        tt.delete_contents(tt.trans_id_file_id('deleted-id'))
 
3060
        tt.delete_contents(tt.trans_id_file_id(b'deleted-id'))
2564
3061
        self.assertMatchingIterEntries(tt)
2565
3062
 
2566
3063
    def test_iter_entries_by_dir_unversioned(self):
2567
3064
        tree = self.make_branch_and_tree('tree')
2568
3065
        self.build_tree(['tree/removed'])
2569
 
        tree.add('removed', 'removed-id')
 
3066
        tree.add('removed', b'removed-id')
2570
3067
        tt = TreeTransform(tree)
2571
 
        tt.unversion_file(tt.trans_id_file_id('removed-id'))
 
3068
        tt.unversion_file(tt.trans_id_file_id(b'removed-id'))
2572
3069
        self.assertMatchingIterEntries(tt)
2573
3070
 
2574
3071
    def test_iter_entries_by_dir_moved(self):
2575
3072
        tree = self.make_branch_and_tree('tree')
2576
3073
        self.build_tree(['tree/moved', 'tree/new_parent/'])
2577
 
        tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
 
3074
        tree.add(['moved', 'new_parent'], [b'moved-id', b'new_parent-id'])
2578
3075
        tt = TreeTransform(tree)
2579
 
        tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2580
 
                       tt.trans_id_file_id('moved-id'))
 
3076
        tt.adjust_path('moved', tt.trans_id_file_id(b'new_parent-id'),
 
3077
                       tt.trans_id_file_id(b'moved-id'))
2581
3078
        self.assertMatchingIterEntries(tt)
2582
3079
 
2583
 
    def test_iter_entries_by_dir_specific_file_ids(self):
 
3080
    def test_iter_entries_by_dir_specific_files(self):
2584
3081
        tree = self.make_branch_and_tree('tree')
2585
 
        tree.set_root_id('tree-root-id')
 
3082
        tree.set_root_id(b'tree-root-id')
2586
3083
        self.build_tree(['tree/parent/', 'tree/parent/child'])
2587
 
        tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
 
3084
        tree.add(['parent', 'parent/child'], [b'parent-id', b'child-id'])
2588
3085
        tt = TreeTransform(tree)
2589
 
        self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
 
3086
        self.assertMatchingIterEntries(tt, ['', 'parent/child'])
2590
3087
 
2591
3088
    def test_symlink_content_summary(self):
2592
3089
        self.requireFeature(SymlinkFeature)
2593
3090
        preview = self.get_empty_preview()
2594
 
        preview.new_symlink('path', preview.root, 'target', 'path-id')
 
3091
        preview.new_symlink('path', preview.root, 'target', b'path-id')
2595
3092
        summary = preview.get_preview_tree().path_content_summary('path')
2596
3093
        self.assertEqual(('symlink', None, None, 'target'), summary)
2597
3094
 
2612
3109
 
2613
3110
    def test_file_content_summary_executable(self):
2614
3111
        preview = self.get_empty_preview()
2615
 
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
 
3112
        path_id = preview.new_file('path', preview.root, [
 
3113
                                   b'contents'], b'path-id')
2616
3114
        preview.set_executability(True, path_id)
2617
3115
        summary = preview.get_preview_tree().path_content_summary('path')
2618
3116
        self.assertEqual(4, len(summary))
2637
3135
 
2638
3136
    def test_file_content_summary_non_exec(self):
2639
3137
        preview = self.get_empty_preview()
2640
 
        preview.new_file('path', preview.root, 'contents', 'path-id')
 
3138
        preview.new_file('path', preview.root, [b'contents'], b'path-id')
2641
3139
        summary = preview.get_preview_tree().path_content_summary('path')
2642
3140
        self.assertEqual(4, len(summary))
2643
3141
        self.assertEqual('file', summary[0])
2650
3148
 
2651
3149
    def test_dir_content_summary(self):
2652
3150
        preview = self.get_empty_preview()
2653
 
        preview.new_directory('path', preview.root, 'path-id')
 
3151
        preview.new_directory('path', preview.root, b'path-id')
2654
3152
        summary = preview.get_preview_tree().path_content_summary('path')
2655
3153
        self.assertEqual(('directory', None, None, None), summary)
2656
3154
 
2657
3155
    def test_tree_content_summary(self):
2658
3156
        preview = self.get_empty_preview()
2659
 
        path = preview.new_directory('path', preview.root, 'path-id')
2660
 
        preview.set_tree_reference('rev-1', path)
 
3157
        path = preview.new_directory('path', preview.root, b'path-id')
 
3158
        preview.set_tree_reference(b'rev-1', path)
2661
3159
        summary = preview.get_preview_tree().path_content_summary('path')
2662
3160
        self.assertEqual(4, len(summary))
2663
3161
        self.assertEqual('tree-reference', summary[0])
2664
3162
 
2665
3163
    def test_annotate(self):
2666
3164
        tree = self.make_branch_and_tree('tree')
2667
 
        self.build_tree_contents([('tree/file', 'a\n')])
2668
 
        tree.add('file', 'file-id')
2669
 
        tree.commit('a', rev_id='one')
2670
 
        self.build_tree_contents([('tree/file', 'a\nb\n')])
 
3165
        self.build_tree_contents([('tree/file', b'a\n')])
 
3166
        tree.add('file', b'file-id')
 
3167
        tree.commit('a', rev_id=b'one')
 
3168
        self.build_tree_contents([('tree/file', b'a\nb\n')])
2671
3169
        preview = TransformPreview(tree)
2672
3170
        self.addCleanup(preview.finalize)
2673
 
        file_trans_id = preview.trans_id_file_id('file-id')
 
3171
        file_trans_id = preview.trans_id_file_id(b'file-id')
2674
3172
        preview.delete_contents(file_trans_id)
2675
 
        preview.create_file('a\nb\nc\n', file_trans_id)
 
3173
        preview.create_file([b'a\nb\nc\n'], file_trans_id)
2676
3174
        preview_tree = preview.get_preview_tree()
2677
3175
        expected = [
2678
 
            ('one', 'a\n'),
2679
 
            ('me:', 'b\n'),
2680
 
            ('me:', 'c\n'),
 
3176
            (b'one', b'a\n'),
 
3177
            (b'me:', b'b\n'),
 
3178
            (b'me:', b'c\n'),
2681
3179
        ]
2682
 
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
3180
        annotation = preview_tree.annotate_iter(
 
3181
            'file', default_revision=b'me:')
2683
3182
        self.assertEqual(expected, annotation)
2684
3183
 
2685
3184
    def test_annotate_missing(self):
2686
3185
        preview = self.get_empty_preview()
2687
 
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
3186
        preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id')
2688
3187
        preview_tree = preview.get_preview_tree()
2689
3188
        expected = [
2690
 
            ('me:', 'a\n'),
2691
 
            ('me:', 'b\n'),
2692
 
            ('me:', 'c\n'),
2693
 
         ]
2694
 
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
3189
            (b'me:', b'a\n'),
 
3190
            (b'me:', b'b\n'),
 
3191
            (b'me:', b'c\n'),
 
3192
            ]
 
3193
        annotation = preview_tree.annotate_iter(
 
3194
            'file', default_revision=b'me:')
2695
3195
        self.assertEqual(expected, annotation)
2696
3196
 
2697
3197
    def test_annotate_rename(self):
2698
3198
        tree = self.make_branch_and_tree('tree')
2699
 
        self.build_tree_contents([('tree/file', 'a\n')])
2700
 
        tree.add('file', 'file-id')
2701
 
        tree.commit('a', rev_id='one')
 
3199
        self.build_tree_contents([('tree/file', b'a\n')])
 
3200
        tree.add('file', b'file-id')
 
3201
        tree.commit('a', rev_id=b'one')
2702
3202
        preview = TransformPreview(tree)
2703
3203
        self.addCleanup(preview.finalize)
2704
 
        file_trans_id = preview.trans_id_file_id('file-id')
 
3204
        file_trans_id = preview.trans_id_file_id(b'file-id')
2705
3205
        preview.adjust_path('newname', preview.root, file_trans_id)
2706
3206
        preview_tree = preview.get_preview_tree()
2707
3207
        expected = [
2708
 
            ('one', 'a\n'),
 
3208
            (b'one', b'a\n'),
2709
3209
        ]
2710
 
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
3210
        annotation = preview_tree.annotate_iter(
 
3211
            'file', default_revision=b'me:')
2711
3212
        self.assertEqual(expected, annotation)
2712
3213
 
2713
3214
    def test_annotate_deleted(self):
2714
3215
        tree = self.make_branch_and_tree('tree')
2715
 
        self.build_tree_contents([('tree/file', 'a\n')])
2716
 
        tree.add('file', 'file-id')
2717
 
        tree.commit('a', rev_id='one')
2718
 
        self.build_tree_contents([('tree/file', 'a\nb\n')])
 
3216
        self.build_tree_contents([('tree/file', b'a\n')])
 
3217
        tree.add('file', b'file-id')
 
3218
        tree.commit('a', rev_id=b'one')
 
3219
        self.build_tree_contents([('tree/file', b'a\nb\n')])
2719
3220
        preview = TransformPreview(tree)
2720
3221
        self.addCleanup(preview.finalize)
2721
 
        file_trans_id = preview.trans_id_file_id('file-id')
 
3222
        file_trans_id = preview.trans_id_file_id(b'file-id')
2722
3223
        preview.delete_contents(file_trans_id)
2723
3224
        preview_tree = preview.get_preview_tree()
2724
 
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
3225
        annotation = preview_tree.annotate_iter(
 
3226
            'file', default_revision=b'me:')
2725
3227
        self.assertIs(None, annotation)
2726
3228
 
2727
3229
    def test_stored_kind(self):
2728
3230
        preview = self.get_empty_preview()
2729
 
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
3231
        preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id')
2730
3232
        preview_tree = preview.get_preview_tree()
2731
 
        self.assertEqual('file', preview_tree.stored_kind('file-id'))
 
3233
        self.assertEqual('file', preview_tree.stored_kind('file'))
2732
3234
 
2733
3235
    def test_is_executable(self):
2734
3236
        preview = self.get_empty_preview()
2735
 
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2736
 
        preview.set_executability(True, preview.trans_id_file_id('file-id'))
 
3237
        preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id')
 
3238
        preview.set_executability(True, preview.trans_id_file_id(b'file-id'))
2737
3239
        preview_tree = preview.get_preview_tree()
2738
 
        self.assertEqual(True, preview_tree.is_executable('file-id'))
 
3240
        self.assertEqual(True, preview_tree.is_executable('file'))
2739
3241
 
2740
3242
    def test_get_set_parent_ids(self):
2741
3243
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2742
3244
        self.assertEqual([], preview_tree.get_parent_ids())
2743
 
        preview_tree.set_parent_ids(['rev-1'])
2744
 
        self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
 
3245
        preview_tree.set_parent_ids([b'rev-1'])
 
3246
        self.assertEqual([b'rev-1'], preview_tree.get_parent_ids())
2745
3247
 
2746
3248
    def test_plan_file_merge(self):
2747
3249
        work_a = self.make_branch_and_tree('wta')
2748
 
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2749
 
        work_a.add('file', 'file-id')
 
3250
        self.build_tree_contents([('wta/file', b'a\nb\nc\nd\n')])
 
3251
        work_a.add('file', b'file-id')
2750
3252
        base_id = work_a.commit('base version')
2751
 
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
 
3253
        tree_b = work_a.controldir.sprout('wtb').open_workingtree()
2752
3254
        preview = TransformPreview(work_a)
2753
3255
        self.addCleanup(preview.finalize)
2754
 
        trans_id = preview.trans_id_file_id('file-id')
 
3256
        trans_id = preview.trans_id_file_id(b'file-id')
2755
3257
        preview.delete_contents(trans_id)
2756
 
        preview.create_file('b\nc\nd\ne\n', trans_id)
2757
 
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
 
3258
        preview.create_file([b'b\nc\nd\ne\n'], trans_id)
 
3259
        self.build_tree_contents([('wtb/file', b'a\nc\nd\nf\n')])
2758
3260
        tree_a = preview.get_preview_tree()
2759
3261
        tree_a.set_parent_ids([base_id])
2760
3262
        self.assertEqual([
2761
 
            ('killed-a', 'a\n'),
2762
 
            ('killed-b', 'b\n'),
2763
 
            ('unchanged', 'c\n'),
2764
 
            ('unchanged', 'd\n'),
2765
 
            ('new-a', 'e\n'),
2766
 
            ('new-b', 'f\n'),
2767
 
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
 
3263
            ('killed-a', b'a\n'),
 
3264
            ('killed-b', b'b\n'),
 
3265
            ('unchanged', b'c\n'),
 
3266
            ('unchanged', b'd\n'),
 
3267
            ('new-a', b'e\n'),
 
3268
            ('new-b', b'f\n'),
 
3269
        ], list(tree_a.plan_file_merge('file', tree_b)))
2768
3270
 
2769
3271
    def test_plan_file_merge_revision_tree(self):
2770
3272
        work_a = self.make_branch_and_tree('wta')
2771
 
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2772
 
        work_a.add('file', 'file-id')
 
3273
        self.build_tree_contents([('wta/file', b'a\nb\nc\nd\n')])
 
3274
        work_a.add('file', b'file-id')
2773
3275
        base_id = work_a.commit('base version')
2774
 
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
 
3276
        tree_b = work_a.controldir.sprout('wtb').open_workingtree()
2775
3277
        preview = TransformPreview(work_a.basis_tree())
2776
3278
        self.addCleanup(preview.finalize)
2777
 
        trans_id = preview.trans_id_file_id('file-id')
 
3279
        trans_id = preview.trans_id_file_id(b'file-id')
2778
3280
        preview.delete_contents(trans_id)
2779
 
        preview.create_file('b\nc\nd\ne\n', trans_id)
2780
 
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
 
3281
        preview.create_file([b'b\nc\nd\ne\n'], trans_id)
 
3282
        self.build_tree_contents([('wtb/file', b'a\nc\nd\nf\n')])
2781
3283
        tree_a = preview.get_preview_tree()
2782
3284
        tree_a.set_parent_ids([base_id])
2783
3285
        self.assertEqual([
2784
 
            ('killed-a', 'a\n'),
2785
 
            ('killed-b', 'b\n'),
2786
 
            ('unchanged', 'c\n'),
2787
 
            ('unchanged', 'd\n'),
2788
 
            ('new-a', 'e\n'),
2789
 
            ('new-b', 'f\n'),
2790
 
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
 
3286
            ('killed-a', b'a\n'),
 
3287
            ('killed-b', b'b\n'),
 
3288
            ('unchanged', b'c\n'),
 
3289
            ('unchanged', b'd\n'),
 
3290
            ('new-a', b'e\n'),
 
3291
            ('new-b', b'f\n'),
 
3292
        ], list(tree_a.plan_file_merge('file', tree_b)))
2791
3293
 
2792
3294
    def test_walkdirs(self):
2793
3295
        preview = self.get_empty_preview()
2794
 
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
3296
        preview.new_directory('', ROOT_PARENT, b'tree-root')
2795
3297
        # FIXME: new_directory should mark root.
2796
3298
        preview.fixup_new_roots()
2797
3299
        preview_tree = preview.get_preview_tree()
2798
 
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2799
 
                                         'a-id')
2800
 
        expected = [(('', 'tree-root'),
2801
 
                    [('a', 'a', 'file', None, 'a-id', 'file')])]
 
3300
        preview.new_file('a', preview.root, [b'contents'], b'a-id')
 
3301
        expected = [(('', b'tree-root'),
 
3302
                     [('a', 'a', 'file', None, b'a-id', 'file')])]
2802
3303
        self.assertEqual(expected, list(preview_tree.walkdirs()))
2803
3304
 
2804
3305
    def test_extras(self):
2808
3309
        work_tree.add(['removed-file', 'not-removed-file'])
2809
3310
        preview = TransformPreview(work_tree)
2810
3311
        self.addCleanup(preview.finalize)
2811
 
        preview.new_file('new-file', preview.root, 'contents')
2812
 
        preview.new_file('new-versioned-file', preview.root, 'contents',
2813
 
                         'new-versioned-id')
 
3312
        preview.new_file('new-file', preview.root, [b'contents'])
 
3313
        preview.new_file('new-versioned-file', preview.root, [b'contents'],
 
3314
                         b'new-versioned-id')
2814
3315
        tree = preview.get_preview_tree()
2815
3316
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2816
 
        self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
 
3317
        self.assertEqual({'new-file', 'removed-file', 'existing-file'},
2817
3318
                         set(tree.extras()))
2818
3319
 
2819
3320
    def test_merge_into_preview(self):
2820
3321
        work_tree = self.make_branch_and_tree('tree')
2821
 
        self.build_tree_contents([('tree/file','b\n')])
2822
 
        work_tree.add('file', 'file-id')
 
3322
        self.build_tree_contents([('tree/file', b'b\n')])
 
3323
        work_tree.add('file', b'file-id')
2823
3324
        work_tree.commit('first commit')
2824
 
        child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2825
 
        self.build_tree_contents([('child/file','b\nc\n')])
 
3325
        child_tree = work_tree.controldir.sprout('child').open_workingtree()
 
3326
        self.build_tree_contents([('child/file', b'b\nc\n')])
2826
3327
        child_tree.commit('child commit')
2827
3328
        child_tree.lock_write()
2828
3329
        self.addCleanup(child_tree.unlock)
2830
3331
        self.addCleanup(work_tree.unlock)
2831
3332
        preview = TransformPreview(work_tree)
2832
3333
        self.addCleanup(preview.finalize)
2833
 
        file_trans_id = preview.trans_id_file_id('file-id')
 
3334
        file_trans_id = preview.trans_id_file_id(b'file-id')
2834
3335
        preview.delete_contents(file_trans_id)
2835
 
        preview.create_file('a\nb\n', file_trans_id)
 
3336
        preview.create_file([b'a\nb\n'], file_trans_id)
2836
3337
        preview_tree = preview.get_preview_tree()
2837
 
        merger = Merger.from_revision_ids(None, preview_tree,
 
3338
        merger = Merger.from_revision_ids(preview_tree,
2838
3339
                                          child_tree.branch.last_revision(),
2839
3340
                                          other_branch=child_tree.branch,
2840
3341
                                          tree_branch=work_tree.branch)
2842
3343
        tt = merger.make_merger().make_preview_transform()
2843
3344
        self.addCleanup(tt.finalize)
2844
3345
        final_tree = tt.get_preview_tree()
2845
 
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
 
3346
        self.assertEqual(
 
3347
            b'a\nb\nc\n',
 
3348
            final_tree.get_file_text(final_tree.id2path(b'file-id')))
2846
3349
 
2847
3350
    def test_merge_preview_into_workingtree(self):
2848
3351
        tree = self.make_branch_and_tree('tree')
2849
 
        tree.set_root_id('TREE_ROOT')
 
3352
        tree.set_root_id(b'TREE_ROOT')
2850
3353
        tt = TransformPreview(tree)
2851
3354
        self.addCleanup(tt.finalize)
2852
 
        tt.new_file('name', tt.root, 'content', 'file-id')
 
3355
        tt.new_file('name', tt.root, [b'content'], b'file-id')
2853
3356
        tree2 = self.make_branch_and_tree('tree2')
2854
 
        tree2.set_root_id('TREE_ROOT')
 
3357
        tree2.set_root_id(b'TREE_ROOT')
2855
3358
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2856
 
                                         None, tree.basis_tree())
 
3359
                                         tree.basis_tree())
2857
3360
        merger.merge_type = Merge3Merger
2858
3361
        merger.do_merge()
2859
3362
 
2860
3363
    def test_merge_preview_into_workingtree_handles_conflicts(self):
2861
3364
        tree = self.make_branch_and_tree('tree')
2862
 
        self.build_tree_contents([('tree/foo', 'bar')])
2863
 
        tree.add('foo', 'foo-id')
 
3365
        self.build_tree_contents([('tree/foo', b'bar')])
 
3366
        tree.add('foo', b'foo-id')
2864
3367
        tree.commit('foo')
2865
3368
        tt = TransformPreview(tree)
2866
3369
        self.addCleanup(tt.finalize)
2867
 
        trans_id = tt.trans_id_file_id('foo-id')
 
3370
        trans_id = tt.trans_id_file_id(b'foo-id')
2868
3371
        tt.delete_contents(trans_id)
2869
 
        tt.create_file('baz', trans_id)
2870
 
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2871
 
        self.build_tree_contents([('tree2/foo', 'qux')])
2872
 
        pb = None
 
3372
        tt.create_file([b'baz'], trans_id)
 
3373
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
 
3374
        self.build_tree_contents([('tree2/foo', b'qux')])
2873
3375
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2874
 
                                         pb, tree.basis_tree())
 
3376
                                         tree.basis_tree())
2875
3377
        merger.merge_type = Merge3Merger
2876
3378
        merger.do_merge()
2877
3379
 
 
3380
    def test_has_filename(self):
 
3381
        wt = self.make_branch_and_tree('tree')
 
3382
        self.build_tree(['tree/unmodified', 'tree/removed', 'tree/modified'])
 
3383
        tt = TransformPreview(wt)
 
3384
        removed_id = tt.trans_id_tree_path('removed')
 
3385
        tt.delete_contents(removed_id)
 
3386
        tt.new_file('new', tt.root, [b'contents'])
 
3387
        modified_id = tt.trans_id_tree_path('modified')
 
3388
        tt.delete_contents(modified_id)
 
3389
        tt.create_file([b'modified-contents'], modified_id)
 
3390
        self.addCleanup(tt.finalize)
 
3391
        tree = tt.get_preview_tree()
 
3392
        self.assertTrue(tree.has_filename('unmodified'))
 
3393
        self.assertFalse(tree.has_filename('not-present'))
 
3394
        self.assertFalse(tree.has_filename('removed'))
 
3395
        self.assertTrue(tree.has_filename('new'))
 
3396
        self.assertTrue(tree.has_filename('modified'))
 
3397
 
2878
3398
    def test_is_executable(self):
2879
3399
        tree = self.make_branch_and_tree('tree')
2880
3400
        preview = TransformPreview(tree)
2881
3401
        self.addCleanup(preview.finalize)
2882
 
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
 
3402
        preview.new_file('foo', preview.root, [b'bar'], b'baz-id')
2883
3403
        preview_tree = preview.get_preview_tree()
2884
 
        self.assertEqual(False, preview_tree.is_executable('baz-id',
2885
 
                                                           'tree/foo'))
2886
 
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
 
3404
        self.assertEqual(False, preview_tree.is_executable('tree/foo'))
2887
3405
 
2888
3406
    def test_commit_preview_tree(self):
2889
3407
        tree = self.make_branch_and_tree('tree')
2891
3409
        tree.branch.lock_write()
2892
3410
        self.addCleanup(tree.branch.unlock)
2893
3411
        tt = TransformPreview(tree)
2894
 
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
3412
        tt.new_file('file', tt.root, [b'contents'], b'file_id')
2895
3413
        self.addCleanup(tt.finalize)
2896
3414
        preview = tt.get_preview_tree()
2897
3415
        preview.set_parent_ids([rev_id])
2900
3418
        builder.finish_inventory()
2901
3419
        rev2_id = builder.commit('rev2')
2902
3420
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2903
 
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
3421
        self.assertEqual(b'contents', rev2_tree.get_file_text('file'))
2904
3422
 
2905
3423
    def test_ascii_limbo_paths(self):
2906
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
3424
        self.requireFeature(features.UnicodeFilenameFeature)
2907
3425
        branch = self.make_branch('any')
2908
3426
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2909
3427
        tt = TransformPreview(tree)
2910
3428
        self.addCleanup(tt.finalize)
2911
3429
        foo_id = tt.new_directory('', ROOT_PARENT)
2912
 
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
 
3430
        bar_id = tt.new_file(u'\u1234bar', foo_id, [b'contents'])
2913
3431
        limbo_path = tt._limbo_name(bar_id)
2914
 
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
 
3432
        self.assertEqual(limbo_path, limbo_path)
2915
3433
 
2916
3434
 
2917
3435
class FakeSerializer(object):
2926
3444
 
2927
3445
class TestSerializeTransform(tests.TestCaseWithTransport):
2928
3446
 
2929
 
    _test_needs_features = [tests.UnicodeFilenameFeature]
 
3447
    _test_needs_features = [features.UnicodeFilenameFeature]
2930
3448
 
2931
3449
    def get_preview(self, tree=None):
2932
3450
        if tree is None:
2942
3460
    @staticmethod
2943
3461
    def default_attribs():
2944
3462
        return {
2945
 
            '_id_number': 1,
2946
 
            '_new_name': {},
2947
 
            '_new_parent': {},
2948
 
            '_new_executability': {},
2949
 
            '_new_id': {},
2950
 
            '_tree_path_ids': {'': 'new-0'},
2951
 
            '_removed_id': [],
2952
 
            '_removed_contents': [],
2953
 
            '_non_present_ids': {},
 
3463
            b'_id_number': 1,
 
3464
            b'_new_name': {},
 
3465
            b'_new_parent': {},
 
3466
            b'_new_executability': {},
 
3467
            b'_new_id': {},
 
3468
            b'_tree_path_ids': {b'': b'new-0'},
 
3469
            b'_removed_id': [],
 
3470
            b'_removed_contents': [],
 
3471
            b'_non_present_ids': {},
2954
3472
            }
2955
3473
 
2956
3474
    def make_records(self, attribs, contents):
2957
3475
        records = [
2958
 
            (((('attribs'),),), bencode.bencode(attribs))]
 
3476
            ((((b'attribs'),),), bencode.bencode(attribs))]
2959
3477
        records.extend([(((n, k),), c) for n, k, c in contents])
2960
3478
        return records
2961
3479
 
2962
3480
    def creation_records(self):
2963
3481
        attribs = self.default_attribs()
2964
 
        attribs['_id_number'] = 3
2965
 
        attribs['_new_name'] = {
2966
 
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2967
 
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2968
 
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2969
 
        attribs['_new_executability'] = {'new-1': 1}
 
3482
        attribs[b'_id_number'] = 3
 
3483
        attribs[b'_new_name'] = {
 
3484
            b'new-1': u'foo\u1234'.encode('utf-8'), b'new-2': b'qux'}
 
3485
        attribs[b'_new_id'] = {b'new-1': b'baz', b'new-2': b'quxx'}
 
3486
        attribs[b'_new_parent'] = {b'new-1': b'new-0', b'new-2': b'new-0'}
 
3487
        attribs[b'_new_executability'] = {b'new-1': 1}
2970
3488
        contents = [
2971
 
            ('new-1', 'file', 'i 1\nbar\n'),
2972
 
            ('new-2', 'directory', ''),
 
3489
            (b'new-1', b'file', b'i 1\nbar\n'),
 
3490
            (b'new-2', b'directory', b''),
2973
3491
            ]
2974
3492
        return self.make_records(attribs, contents)
2975
3493
 
2976
3494
    def test_serialize_creation(self):
2977
3495
        tt = self.get_preview()
2978
 
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2979
 
        tt.new_directory('qux', tt.root, 'quxx')
 
3496
        tt.new_file(u'foo\u1234', tt.root, [b'bar'], b'baz', True)
 
3497
        tt.new_directory('qux', tt.root, b'quxx')
2980
3498
        self.assertSerializesTo(self.creation_records(), tt)
2981
3499
 
2982
3500
    def test_deserialize_creation(self):
2985
3503
        self.assertEqual(3, tt._id_number)
2986
3504
        self.assertEqual({'new-1': u'foo\u1234',
2987
3505
                          'new-2': 'qux'}, tt._new_name)
2988
 
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
 
3506
        self.assertEqual({'new-1': b'baz', 'new-2': b'quxx'}, tt._new_id)
2989
3507
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2990
 
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
 
3508
        self.assertEqual({b'baz': 'new-1', b'quxx': 'new-2'}, tt._r_new_id)
2991
3509
        self.assertEqual({'new-1': True}, tt._new_executability)
2992
3510
        self.assertEqual({'new-1': 'file',
2993
3511
                          'new-2': 'directory'}, tt._new_contents)
2996
3514
            foo_content = foo_limbo.read()
2997
3515
        finally:
2998
3516
            foo_limbo.close()
2999
 
        self.assertEqual('bar', foo_content)
 
3517
        self.assertEqual(b'bar', foo_content)
3000
3518
 
3001
3519
    def symlink_creation_records(self):
3002
3520
        attribs = self.default_attribs()
3003
 
        attribs['_id_number'] = 2
3004
 
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
3005
 
        attribs['_new_parent'] = {'new-1': 'new-0'}
3006
 
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
 
3521
        attribs[b'_id_number'] = 2
 
3522
        attribs[b'_new_name'] = {b'new-1': u'foo\u1234'.encode('utf-8')}
 
3523
        attribs[b'_new_parent'] = {b'new-1': b'new-0'}
 
3524
        contents = [(b'new-1', b'symlink', u'bar\u1234'.encode('utf-8'))]
3007
3525
        return self.make_records(attribs, contents)
3008
3526
 
3009
3527
    def test_serialize_symlink_creation(self):
3010
 
        self.requireFeature(tests.SymlinkFeature)
 
3528
        self.requireFeature(features.SymlinkFeature)
3011
3529
        tt = self.get_preview()
3012
3530
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
3013
3531
        self.assertSerializesTo(self.symlink_creation_records(), tt)
3014
3532
 
3015
3533
    def test_deserialize_symlink_creation(self):
3016
 
        self.requireFeature(tests.SymlinkFeature)
 
3534
        self.requireFeature(features.SymlinkFeature)
3017
3535
        tt = self.get_preview()
3018
3536
        tt.deserialize(iter(self.symlink_creation_records()))
3019
3537
        abspath = tt._limbo_name('new-1')
3023
3541
    def make_destruction_preview(self):
3024
3542
        tree = self.make_branch_and_tree('.')
3025
3543
        self.build_tree([u'foo\u1234', 'bar'])
3026
 
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
 
3544
        tree.add([u'foo\u1234', 'bar'], [b'foo-id', b'bar-id'])
3027
3545
        return self.get_preview(tree)
3028
3546
 
3029
3547
    def destruction_records(self):
3030
3548
        attribs = self.default_attribs()
3031
 
        attribs['_id_number'] = 3
3032
 
        attribs['_removed_id'] = ['new-1']
3033
 
        attribs['_removed_contents'] = ['new-2']
3034
 
        attribs['_tree_path_ids'] = {
3035
 
            '': 'new-0',
3036
 
            u'foo\u1234'.encode('utf-8'): 'new-1',
3037
 
            'bar': 'new-2',
 
3549
        attribs[b'_id_number'] = 3
 
3550
        attribs[b'_removed_id'] = [b'new-1']
 
3551
        attribs[b'_removed_contents'] = [b'new-2']
 
3552
        attribs[b'_tree_path_ids'] = {
 
3553
            b'': b'new-0',
 
3554
            u'foo\u1234'.encode('utf-8'): b'new-1',
 
3555
            b'bar': b'new-2',
3038
3556
            }
3039
3557
        return self.make_records(attribs, [])
3040
3558
 
3041
3559
    def test_serialize_destruction(self):
3042
3560
        tt = self.make_destruction_preview()
3043
 
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
 
3561
        foo_trans_id = tt.trans_id_tree_path(u'foo\u1234')
3044
3562
        tt.unversion_file(foo_trans_id)
3045
 
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
 
3563
        bar_trans_id = tt.trans_id_tree_path('bar')
3046
3564
        tt.delete_contents(bar_trans_id)
3047
3565
        self.assertSerializesTo(self.destruction_records(), tt)
3048
3566
 
3055
3573
        self.assertEqual({'new-1': u'foo\u1234',
3056
3574
                          'new-2': 'bar',
3057
3575
                          tt.root: ''}, tt._tree_id_paths)
3058
 
        self.assertEqual(set(['new-1']), tt._removed_id)
3059
 
        self.assertEqual(set(['new-2']), tt._removed_contents)
 
3576
        self.assertEqual({'new-1'}, tt._removed_id)
 
3577
        self.assertEqual({'new-2'}, tt._removed_contents)
3060
3578
 
3061
3579
    def missing_records(self):
3062
3580
        attribs = self.default_attribs()
3063
 
        attribs['_id_number'] = 2
3064
 
        attribs['_non_present_ids'] = {
3065
 
            'boo': 'new-1',}
 
3581
        attribs[b'_id_number'] = 2
 
3582
        attribs[b'_non_present_ids'] = {
 
3583
            b'boo': b'new-1', }
3066
3584
        return self.make_records(attribs, [])
3067
3585
 
3068
3586
    def test_serialize_missing(self):
3069
3587
        tt = self.get_preview()
3070
 
        boo_trans_id = tt.trans_id_file_id('boo')
 
3588
        tt.trans_id_file_id(b'boo')
3071
3589
        self.assertSerializesTo(self.missing_records(), tt)
3072
3590
 
3073
3591
    def test_deserialize_missing(self):
3074
3592
        tt = self.get_preview()
3075
3593
        tt.deserialize(iter(self.missing_records()))
3076
 
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
 
3594
        self.assertEqual({b'boo': 'new-1'}, tt._non_present_ids)
3077
3595
 
3078
3596
    def make_modification_preview(self):
3079
 
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3080
 
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3597
        LINES_ONE = b'aa\nbb\ncc\ndd\n'
 
3598
        LINES_TWO = b'z\nbb\nx\ndd\n'
3081
3599
        tree = self.make_branch_and_tree('tree')
3082
3600
        self.build_tree_contents([('tree/file', LINES_ONE)])
3083
 
        tree.add('file', 'file-id')
3084
 
        return self.get_preview(tree), LINES_TWO
 
3601
        tree.add('file', b'file-id')
 
3602
        return self.get_preview(tree), [LINES_TWO]
3085
3603
 
3086
3604
    def modification_records(self):
3087
3605
        attribs = self.default_attribs()
3088
 
        attribs['_id_number'] = 2
3089
 
        attribs['_tree_path_ids'] = {
3090
 
            'file': 'new-1',
3091
 
            '': 'new-0',}
3092
 
        attribs['_removed_contents'] = ['new-1']
3093
 
        contents = [('new-1', 'file',
3094
 
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
 
3606
        attribs[b'_id_number'] = 2
 
3607
        attribs[b'_tree_path_ids'] = {
 
3608
            b'file': b'new-1',
 
3609
            b'': b'new-0', }
 
3610
        attribs[b'_removed_contents'] = [b'new-1']
 
3611
        contents = [(b'new-1', b'file',
 
3612
                     b'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
3095
3613
        return self.make_records(attribs, contents)
3096
3614
 
3097
3615
    def test_serialize_modification(self):
3098
3616
        tt, LINES = self.make_modification_preview()
3099
 
        trans_id = tt.trans_id_file_id('file-id')
 
3617
        trans_id = tt.trans_id_file_id(b'file-id')
3100
3618
        tt.delete_contents(trans_id)
3101
3619
        tt.create_file(LINES, trans_id)
3102
3620
        self.assertSerializesTo(self.modification_records(), tt)
3104
3622
    def test_deserialize_modification(self):
3105
3623
        tt, LINES = self.make_modification_preview()
3106
3624
        tt.deserialize(iter(self.modification_records()))
3107
 
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3625
        self.assertFileEqual(b''.join(LINES), tt._limbo_name('new-1'))
3108
3626
 
3109
3627
    def make_kind_change_preview(self):
3110
 
        LINES = 'a\nb\nc\nd\n'
 
3628
        LINES = b'a\nb\nc\nd\n'
3111
3629
        tree = self.make_branch_and_tree('tree')
3112
3630
        self.build_tree(['tree/foo/'])
3113
 
        tree.add('foo', 'foo-id')
3114
 
        return self.get_preview(tree), LINES
 
3631
        tree.add('foo', b'foo-id')
 
3632
        return self.get_preview(tree), [LINES]
3115
3633
 
3116
3634
    def kind_change_records(self):
3117
3635
        attribs = self.default_attribs()
3118
 
        attribs['_id_number'] = 2
3119
 
        attribs['_tree_path_ids'] = {
3120
 
            'foo': 'new-1',
3121
 
            '': 'new-0',}
3122
 
        attribs['_removed_contents'] = ['new-1']
3123
 
        contents = [('new-1', 'file',
3124
 
                     'i 4\na\nb\nc\nd\n\n')]
 
3636
        attribs[b'_id_number'] = 2
 
3637
        attribs[b'_tree_path_ids'] = {
 
3638
            b'foo': b'new-1',
 
3639
            b'': b'new-0', }
 
3640
        attribs[b'_removed_contents'] = [b'new-1']
 
3641
        contents = [(b'new-1', b'file',
 
3642
                     b'i 4\na\nb\nc\nd\n\n')]
3125
3643
        return self.make_records(attribs, contents)
3126
3644
 
3127
3645
    def test_serialize_kind_change(self):
3128
3646
        tt, LINES = self.make_kind_change_preview()
3129
 
        trans_id = tt.trans_id_file_id('foo-id')
 
3647
        trans_id = tt.trans_id_file_id(b'foo-id')
3130
3648
        tt.delete_contents(trans_id)
3131
3649
        tt.create_file(LINES, trans_id)
3132
3650
        self.assertSerializesTo(self.kind_change_records(), tt)
3134
3652
    def test_deserialize_kind_change(self):
3135
3653
        tt, LINES = self.make_kind_change_preview()
3136
3654
        tt.deserialize(iter(self.kind_change_records()))
3137
 
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3655
        self.assertFileEqual(b''.join(LINES), tt._limbo_name('new-1'))
3138
3656
 
3139
3657
    def make_add_contents_preview(self):
3140
 
        LINES = 'a\nb\nc\nd\n'
 
3658
        LINES = b'a\nb\nc\nd\n'
3141
3659
        tree = self.make_branch_and_tree('tree')
3142
3660
        self.build_tree(['tree/foo'])
3143
3661
        tree.add('foo')
3146
3664
 
3147
3665
    def add_contents_records(self):
3148
3666
        attribs = self.default_attribs()
3149
 
        attribs['_id_number'] = 2
3150
 
        attribs['_tree_path_ids'] = {
3151
 
            'foo': 'new-1',
3152
 
            '': 'new-0',}
3153
 
        contents = [('new-1', 'file',
3154
 
                     'i 4\na\nb\nc\nd\n\n')]
 
3667
        attribs[b'_id_number'] = 2
 
3668
        attribs[b'_tree_path_ids'] = {
 
3669
            b'foo': b'new-1',
 
3670
            b'': b'new-0', }
 
3671
        contents = [(b'new-1', b'file',
 
3672
                     b'i 4\na\nb\nc\nd\n\n')]
3155
3673
        return self.make_records(attribs, contents)
3156
3674
 
3157
3675
    def test_serialize_add_contents(self):
3158
3676
        tt, LINES = self.make_add_contents_preview()
3159
3677
        trans_id = tt.trans_id_tree_path('foo')
3160
 
        tt.create_file(LINES, trans_id)
 
3678
        tt.create_file([LINES], trans_id)
3161
3679
        self.assertSerializesTo(self.add_contents_records(), tt)
3162
3680
 
3163
3681
    def test_deserialize_add_contents(self):
3166
3684
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3167
3685
 
3168
3686
    def test_get_parents_lines(self):
3169
 
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3170
 
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3687
        LINES_ONE = b'aa\nbb\ncc\ndd\n'
3171
3688
        tree = self.make_branch_and_tree('tree')
3172
3689
        self.build_tree_contents([('tree/file', LINES_ONE)])
3173
 
        tree.add('file', 'file-id')
 
3690
        tree.add('file', b'file-id')
3174
3691
        tt = self.get_preview(tree)
3175
3692
        trans_id = tt.trans_id_tree_path('file')
3176
 
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3177
 
            tt._get_parents_lines(trans_id))
 
3693
        self.assertEqual(([b'aa\n', b'bb\n', b'cc\n', b'dd\n'],),
 
3694
                         tt._get_parents_lines(trans_id))
3178
3695
 
3179
3696
    def test_get_parents_texts(self):
3180
 
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3181
 
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3697
        LINES_ONE = b'aa\nbb\ncc\ndd\n'
3182
3698
        tree = self.make_branch_and_tree('tree')
3183
3699
        self.build_tree_contents([('tree/file', LINES_ONE)])
3184
 
        tree.add('file', 'file-id')
 
3700
        tree.add('file', b'file-id')
3185
3701
        tt = self.get_preview(tree)
3186
3702
        trans_id = tt.trans_id_tree_path('file')
3187
3703
        self.assertEqual((LINES_ONE,),
3188
 
            tt._get_parents_texts(trans_id))
 
3704
                         tt._get_parents_texts(trans_id))
 
3705
 
 
3706
 
 
3707
class TestOrphan(tests.TestCaseWithTransport):
 
3708
 
 
3709
    def test_no_orphan_for_transform_preview(self):
 
3710
        tree = self.make_branch_and_tree('tree')
 
3711
        tt = transform.TransformPreview(tree)
 
3712
        self.addCleanup(tt.finalize)
 
3713
        self.assertRaises(NotImplementedError, tt.new_orphan, 'foo', 'bar')
 
3714
 
 
3715
    def _set_orphan_policy(self, wt, policy):
 
3716
        wt.branch.get_config_stack().set('transform.orphan_policy',
 
3717
                                         policy)
 
3718
 
 
3719
    def _prepare_orphan(self, wt):
 
3720
        self.build_tree(['dir/', 'dir/file', 'dir/foo'])
 
3721
        wt.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
 
3722
        wt.commit('add dir and file ignoring foo')
 
3723
        tt = transform.TreeTransform(wt)
 
3724
        self.addCleanup(tt.finalize)
 
3725
        # dir and bar are deleted
 
3726
        dir_tid = tt.trans_id_tree_path('dir')
 
3727
        file_tid = tt.trans_id_tree_path('dir/file')
 
3728
        orphan_tid = tt.trans_id_tree_path('dir/foo')
 
3729
        tt.delete_contents(file_tid)
 
3730
        tt.unversion_file(file_tid)
 
3731
        tt.delete_contents(dir_tid)
 
3732
        tt.unversion_file(dir_tid)
 
3733
        # There should be a conflict because dir still contain foo
 
3734
        raw_conflicts = tt.find_conflicts()
 
3735
        self.assertLength(1, raw_conflicts)
 
3736
        self.assertEqual(('missing parent', 'new-1'), raw_conflicts[0])
 
3737
        return tt, orphan_tid
 
3738
 
 
3739
    def test_new_orphan_created(self):
 
3740
        wt = self.make_branch_and_tree('.')
 
3741
        self._set_orphan_policy(wt, 'move')
 
3742
        tt, orphan_tid = self._prepare_orphan(wt)
 
3743
        warnings = []
 
3744
 
 
3745
        def warning(*args):
 
3746
            warnings.append(args[0] % args[1:])
 
3747
        self.overrideAttr(trace, 'warning', warning)
 
3748
        remaining_conflicts = resolve_conflicts(tt)
 
3749
        self.assertEqual(['dir/foo has been orphaned in brz-orphans'],
 
3750
                         warnings)
 
3751
        # Yeah for resolved conflicts !
 
3752
        self.assertLength(0, remaining_conflicts)
 
3753
        # We have a new orphan
 
3754
        self.assertEqual('foo.~1~', tt.final_name(orphan_tid))
 
3755
        self.assertEqual('brz-orphans',
 
3756
                         tt.final_name(tt.final_parent(orphan_tid)))
 
3757
 
 
3758
    def test_never_orphan(self):
 
3759
        wt = self.make_branch_and_tree('.')
 
3760
        self._set_orphan_policy(wt, 'conflict')
 
3761
        tt, orphan_tid = self._prepare_orphan(wt)
 
3762
        remaining_conflicts = resolve_conflicts(tt)
 
3763
        self.assertLength(1, remaining_conflicts)
 
3764
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
 
3765
                         remaining_conflicts.pop())
 
3766
 
 
3767
    def test_orphan_error(self):
 
3768
        def bogus_orphan(tt, orphan_id, parent_id):
 
3769
            raise transform.OrphaningError(tt.final_name(orphan_id),
 
3770
                                           tt.final_name(parent_id))
 
3771
        transform.orphaning_registry.register('bogus', bogus_orphan,
 
3772
                                              'Raise an error when orphaning')
 
3773
        wt = self.make_branch_and_tree('.')
 
3774
        self._set_orphan_policy(wt, 'bogus')
 
3775
        tt, orphan_tid = self._prepare_orphan(wt)
 
3776
        remaining_conflicts = resolve_conflicts(tt)
 
3777
        self.assertLength(1, remaining_conflicts)
 
3778
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
 
3779
                         remaining_conflicts.pop())
 
3780
 
 
3781
    def test_unknown_orphan_policy(self):
 
3782
        wt = self.make_branch_and_tree('.')
 
3783
        # Set a fictional policy nobody ever implemented
 
3784
        self._set_orphan_policy(wt, 'donttouchmypreciouuus')
 
3785
        tt, orphan_tid = self._prepare_orphan(wt)
 
3786
        warnings = []
 
3787
 
 
3788
        def warning(*args):
 
3789
            warnings.append(args[0] % args[1:])
 
3790
        self.overrideAttr(trace, 'warning', warning)
 
3791
        remaining_conflicts = resolve_conflicts(tt)
 
3792
        # We fallback to the default policy which create a conflict
 
3793
        self.assertLength(1, remaining_conflicts)
 
3794
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
 
3795
                         remaining_conflicts.pop())
 
3796
        self.assertLength(1, warnings)
 
3797
        self.assertStartsWith(warnings[0], 'Value "donttouchmypreciouuus" ')
 
3798
 
 
3799
 
 
3800
class TestTransformHooks(tests.TestCaseWithTransport):
 
3801
 
 
3802
    def setUp(self):
 
3803
        super(TestTransformHooks, self).setUp()
 
3804
        self.wt = self.make_branch_and_tree('.')
 
3805
        os.chdir('..')
 
3806
 
 
3807
    def get_transform(self):
 
3808
        transform = TreeTransform(self.wt)
 
3809
        self.addCleanup(transform.finalize)
 
3810
        return transform, transform.root
 
3811
 
 
3812
    def test_pre_commit_hooks(self):
 
3813
        calls = []
 
3814
 
 
3815
        def record_pre_transform(tree, tt):
 
3816
            calls.append((tree, tt))
 
3817
        MutableTree.hooks.install_named_hook(
 
3818
            'pre_transform', record_pre_transform, "Pre transform")
 
3819
        transform, root = self.get_transform()
 
3820
        old_root_id = transform.tree_file_id(root)
 
3821
        transform.apply()
 
3822
        self.assertEqual(old_root_id, self.wt.path2id(''))
 
3823
        self.assertEqual([(self.wt, transform)], calls)
 
3824
 
 
3825
    def test_post_commit_hooks(self):
 
3826
        calls = []
 
3827
 
 
3828
        def record_post_transform(tree, tt):
 
3829
            calls.append((tree, tt))
 
3830
        MutableTree.hooks.install_named_hook(
 
3831
            'post_transform', record_post_transform, "Post transform")
 
3832
        transform, root = self.get_transform()
 
3833
        old_root_id = transform.tree_file_id(root)
 
3834
        transform.apply()
 
3835
        self.assertEqual(old_root_id, self.wt.path2id(''))
 
3836
        self.assertEqual([(self.wt, transform)], calls)
 
3837
 
 
3838
 
 
3839
class TestLinkTree(tests.TestCaseWithTransport):
 
3840
 
 
3841
    _test_needs_features = [HardlinkFeature]
 
3842
 
 
3843
    def setUp(self):
 
3844
        tests.TestCaseWithTransport.setUp(self)
 
3845
        self.parent_tree = self.make_branch_and_tree('parent')
 
3846
        self.parent_tree.lock_write()
 
3847
        self.addCleanup(self.parent_tree.unlock)
 
3848
        self.build_tree_contents([('parent/foo', b'bar')])
 
3849
        self.parent_tree.add('foo')
 
3850
        self.parent_tree.commit('added foo')
 
3851
        child_controldir = self.parent_tree.controldir.sprout('child')
 
3852
        self.child_tree = child_controldir.open_workingtree()
 
3853
 
 
3854
    def hardlinked(self):
 
3855
        parent_stat = os.lstat(self.parent_tree.abspath('foo'))
 
3856
        child_stat = os.lstat(self.child_tree.abspath('foo'))
 
3857
        return parent_stat.st_ino == child_stat.st_ino
 
3858
 
 
3859
    def test_link_fails_if_modified(self):
 
3860
        """If the file to be linked has modified text, don't link."""
 
3861
        self.build_tree_contents([('child/foo', b'baz')])
 
3862
        transform.link_tree(self.child_tree, self.parent_tree)
 
3863
        self.assertFalse(self.hardlinked())
 
3864
 
 
3865
    def test_link_fails_if_execute_bit_changed(self):
 
3866
        """If the file to be linked has modified execute bit, don't link."""
 
3867
        tt = TreeTransform(self.child_tree)
 
3868
        try:
 
3869
            trans_id = tt.trans_id_tree_path('foo')
 
3870
            tt.set_executability(True, trans_id)
 
3871
            tt.apply()
 
3872
        finally:
 
3873
            tt.finalize()
 
3874
        transform.link_tree(self.child_tree, self.parent_tree)
 
3875
        self.assertFalse(self.hardlinked())
 
3876
 
 
3877
    def test_link_succeeds_if_unmodified(self):
 
3878
        """If the file to be linked is unmodified, link"""
 
3879
        transform.link_tree(self.child_tree, self.parent_tree)
 
3880
        self.assertTrue(self.hardlinked())