~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: John Ferlito
  • Date: 2009-09-02 04:31:45 UTC
  • mto: (4665.7.1 serve-init)
  • mto: This revision was merged to the branch mainline in revision 4913.
  • Revision ID: johnf@inodes.org-20090902043145-gxdsfw03ilcwbyn5
Add a debian init script for bzr --serve

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
18
import stat
20
20
import sys
21
21
 
22
22
from bzrlib import (
 
23
    bencode,
23
24
    errors,
24
25
    generate_ids,
25
26
    osutils,
35
36
                              NonDirectoryParent)
36
37
from bzrlib.diff import show_diff_trees
37
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, CantMoveRoot, 
 
39
                           ReusingTransform, CantMoveRoot,
39
40
                           PathsNotVersionedError, ExistingLimbo,
40
41
                           ExistingPendingDeletion, ImmortalLimbo,
41
42
                           ImmortalPendingDeletion, LockError)
42
43
from bzrlib.osutils import file_kind, pathjoin
43
 
from bzrlib.merge import Merge3Merger
 
44
from bzrlib.merge import Merge3Merger, Merger
44
45
from bzrlib.tests import (
45
 
    CaseInsensitiveFilesystemFeature,
46
46
    HardlinkFeature,
47
47
    SymlinkFeature,
48
48
    TestCase,
49
49
    TestCaseInTempDir,
50
50
    TestSkipped,
51
51
    )
52
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
53
 
                              resolve_conflicts, cook_conflicts, 
 
52
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
 
53
                              resolve_conflicts, cook_conflicts,
54
54
                              build_tree, get_backup_name,
55
55
                              _FileMover, resolve_checkout,
56
 
                              TransformPreview)
 
56
                              TransformPreview, create_from_tree)
 
57
 
57
58
 
58
59
class TestTreeTransform(tests.TestCaseWithTransport):
59
60
 
128
129
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
129
130
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
130
131
        self.assertEqual(len(modified_paths), 3)
131
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
132
        tree_mod_paths = [self.wt.id2abspath(f) for f in
132
133
                          ('ozzie', 'my_pretties', 'my_pretties2')]
133
134
        self.assertSubset(tree_mod_paths, modified_paths)
134
135
        # is it safe to finalize repeatedly?
154
155
        transform, root = self.get_transform()
155
156
        self.wt.lock_tree_write()
156
157
        self.addCleanup(self.wt.unlock)
157
 
        trans_id = transform.new_file('name', root, 'contents', 
 
158
        trans_id = transform.new_file('name', root, 'contents',
158
159
                                      'my_pretties', True)
159
160
        oz = transform.new_directory('oz', root, 'oz-id')
160
161
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
161
 
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
162
        toto = transform.new_file('toto', dorothy, 'toto-contents',
162
163
                                  'toto-id', False)
163
164
 
164
165
        self.assertEqual(len(transform.find_conflicts()), 0)
188
189
 
189
190
    def test_conflicts(self):
190
191
        transform, root = self.get_transform()
191
 
        trans_id = transform.new_file('name', root, 'contents', 
 
192
        trans_id = transform.new_file('name', root, 'contents',
192
193
                                      'my_pretties')
193
194
        self.assertEqual(len(transform.find_conflicts()), 0)
194
195
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
195
 
        self.assertEqual(transform.find_conflicts(), 
 
196
        self.assertEqual(transform.find_conflicts(),
196
197
                         [('duplicate', trans_id, trans_id2, 'name')])
197
198
        self.assertRaises(MalformedTransform, transform.apply)
198
199
        transform.adjust_path('name', trans_id, trans_id2)
199
 
        self.assertEqual(transform.find_conflicts(), 
 
200
        self.assertEqual(transform.find_conflicts(),
200
201
                         [('non-directory parent', trans_id)])
201
202
        tinman_id = transform.trans_id_tree_path('tinman')
202
203
        transform.adjust_path('name', tinman_id, trans_id2)
203
 
        self.assertEqual(transform.find_conflicts(), 
204
 
                         [('unversioned parent', tinman_id), 
 
204
        self.assertEqual(transform.find_conflicts(),
 
205
                         [('unversioned parent', tinman_id),
205
206
                          ('missing parent', tinman_id)])
206
207
        lion_id = transform.create_path('lion', root)
207
 
        self.assertEqual(transform.find_conflicts(), 
208
 
                         [('unversioned parent', tinman_id), 
 
208
        self.assertEqual(transform.find_conflicts(),
 
209
                         [('unversioned parent', tinman_id),
209
210
                          ('missing parent', tinman_id)])
210
211
        transform.adjust_path('name', lion_id, trans_id2)
211
 
        self.assertEqual(transform.find_conflicts(), 
 
212
        self.assertEqual(transform.find_conflicts(),
212
213
                         [('unversioned parent', lion_id),
213
214
                          ('missing parent', lion_id)])
214
215
        transform.version_file("Courage", lion_id)
215
 
        self.assertEqual(transform.find_conflicts(), 
216
 
                         [('missing parent', lion_id), 
 
216
        self.assertEqual(transform.find_conflicts(),
 
217
                         [('missing parent', lion_id),
217
218
                          ('versioning no contents', lion_id)])
218
219
        transform.adjust_path('name2', root, trans_id2)
219
 
        self.assertEqual(transform.find_conflicts(), 
 
220
        self.assertEqual(transform.find_conflicts(),
220
221
                         [('versioning no contents', lion_id)])
221
222
        transform.create_file('Contents, okay?', lion_id)
222
223
        transform.adjust_path('name2', trans_id2, trans_id2)
223
 
        self.assertEqual(transform.find_conflicts(), 
224
 
                         [('parent loop', trans_id2), 
 
224
        self.assertEqual(transform.find_conflicts(),
 
225
                         [('parent loop', trans_id2),
225
226
                          ('non-directory parent', trans_id2)])
226
227
        transform.adjust_path('name2', root, trans_id2)
227
228
        oz_id = transform.new_directory('oz', root)
228
229
        transform.set_executability(True, oz_id)
229
 
        self.assertEqual(transform.find_conflicts(), 
 
230
        self.assertEqual(transform.find_conflicts(),
230
231
                         [('unversioned executability', oz_id)])
231
232
        transform.version_file('oz-id', oz_id)
232
 
        self.assertEqual(transform.find_conflicts(), 
 
233
        self.assertEqual(transform.find_conflicts(),
233
234
                         [('non-file executability', oz_id)])
234
235
        transform.set_executability(None, oz_id)
235
236
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
244
245
        self.assert_('oz/tip' in transform2._tree_path_ids)
245
246
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
246
247
        self.assertEqual(len(result), 2)
247
 
        self.assertEqual((result[0][0], result[0][1]), 
 
248
        self.assertEqual((result[0][0], result[0][1]),
248
249
                         ('duplicate', newtip))
249
 
        self.assertEqual((result[1][0], result[1][2]), 
 
250
        self.assertEqual((result[1][0], result[1][2]),
250
251
                         ('duplicate id', newtip))
251
252
        transform2.finalize()
252
253
        transform3 = TreeTransform(self.wt)
253
254
        self.addCleanup(transform3.finalize)
254
255
        oz_id = transform3.trans_id_tree_file_id('oz-id')
255
256
        transform3.delete_contents(oz_id)
256
 
        self.assertEqual(transform3.find_conflicts(), 
 
257
        self.assertEqual(transform3.find_conflicts(),
257
258
                         [('missing parent', oz_id)])
258
259
        root_id = transform3.root
259
260
        tip_id = transform3.trans_id_tree_file_id('tip-id')
386
387
        self.addCleanup(unversion.finalize)
387
388
        parent = unversion.trans_id_tree_path('parent')
388
389
        unversion.unversion_file(parent)
389
 
        self.assertEqual(unversion.find_conflicts(), 
 
390
        self.assertEqual(unversion.find_conflicts(),
390
391
                         [('unversioned parent', parent_id)])
391
392
        file_id = unversion.trans_id_tree_file_id('child-id')
392
393
        unversion.unversion_file(file_id)
412
413
        mangle_tree.adjust_path('name2', root, name1)
413
414
        mangle_tree.adjust_path('name1', root, name2)
414
415
 
415
 
        #tests for deleting parent directories 
 
416
        #tests for deleting parent directories
416
417
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
417
418
        mangle_tree.delete_contents(ddir)
418
419
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
447
448
        create_tree,root = self.get_transform()
448
449
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
449
450
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
450
 
        create_tree.apply()        
 
451
        create_tree.apply()
451
452
        mangle_tree,root = self.get_transform()
452
453
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
453
454
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
461
462
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
462
463
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
463
464
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
464
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
465
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
465
466
                             'test_too_much-id')
466
 
        create_tree.apply()        
 
467
        create_tree.apply()
467
468
        mangle_tree,root = self.get_transform()
468
469
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
469
470
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
470
471
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
471
472
        mangle_tree.adjust_path('selftest', bzrlib, tests)
472
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
473
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
473
474
        mangle_tree.set_executability(True, test_too_much)
474
475
        mangle_tree.apply()
475
476
 
476
477
    def test_both_rename3(self):
477
478
        create_tree,root = self.get_transform()
478
479
        tests = create_tree.new_directory('tests', root, 'tests-id')
479
 
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
480
        create_tree.new_file('test_too_much.py', tests, 'hello1',
480
481
                             'test_too_much-id')
481
 
        create_tree.apply()        
 
482
        create_tree.apply()
482
483
        mangle_tree,root = self.get_transform()
483
484
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
484
485
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
485
486
        mangle_tree.adjust_path('selftest', root, tests)
486
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
487
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
487
488
        mangle_tree.set_executability(True, test_too_much)
488
489
        mangle_tree.apply()
489
490
 
502
503
        newdir = move_id.new_directory('dir', root, 'newdir')
503
504
        move_id.adjust_path('name2', newdir, name1)
504
505
        move_id.apply()
505
 
        
 
506
 
506
507
    def test_replace_dangling_ie(self):
507
508
        create_tree, root = self.get_transform()
508
509
        # prepare tree
524
525
        resolve_conflicts(replace)
525
526
        replace.apply()
526
527
 
527
 
    def test_symlinks(self):
 
528
    def _test_symlinks(self, link_name1,link_target1,
 
529
                       link_name2, link_target2):
 
530
 
 
531
        def ozpath(p): return 'oz/' + p
 
532
 
528
533
        self.requireFeature(SymlinkFeature)
529
 
        transform,root = self.get_transform()
 
534
        transform, root = self.get_transform()
530
535
        oz_id = transform.new_directory('oz', root, 'oz-id')
531
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
536
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
532
537
                                       'wizard-id')
533
 
        wiz_id = transform.create_path('wizard2', oz_id)
534
 
        transform.create_symlink('behind_curtain', wiz_id)
535
 
        transform.version_file('wiz-id2', wiz_id)            
 
538
        wiz_id = transform.create_path(link_name2, oz_id)
 
539
        transform.create_symlink(link_target2, wiz_id)
 
540
        transform.version_file('wiz-id2', wiz_id)
536
541
        transform.set_executability(True, wiz_id)
537
 
        self.assertEqual(transform.find_conflicts(), 
 
542
        self.assertEqual(transform.find_conflicts(),
538
543
                         [('non-file executability', wiz_id)])
539
544
        transform.set_executability(None, wiz_id)
540
545
        transform.apply()
541
 
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
542
 
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
543
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
544
 
                         'behind_curtain')
545
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
546
 
                         'wizard-target')
 
546
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
 
547
        self.assertEqual('symlink',
 
548
                         file_kind(self.wt.abspath(ozpath(link_name1))))
 
549
        self.assertEqual(link_target2,
 
550
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
 
551
        self.assertEqual(link_target1,
 
552
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
 
553
 
 
554
    def test_symlinks(self):
 
555
        self._test_symlinks('wizard', 'wizard-target',
 
556
                            'wizard2', 'behind_curtain')
 
557
 
 
558
    def test_symlinks_unicode(self):
 
559
        self.requireFeature(tests.UnicodeFilenameFeature)
 
560
        self._test_symlinks(u'\N{Euro Sign}wizard',
 
561
                            u'wizard-targ\N{Euro Sign}t',
 
562
                            u'\N{Euro Sign}wizard2',
 
563
                            u'b\N{Euro Sign}hind_curtain')
547
564
 
548
565
    def test_unable_create_symlink(self):
549
566
        def tt_helper():
573
590
        create.apply()
574
591
        conflicts,root = self.get_transform()
575
592
        # set up duplicate entry, duplicate id
576
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
593
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
577
594
                                         'dorothy-id')
578
595
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
579
596
        oz = conflicts.trans_id_tree_file_id('oz-id')
603
620
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
604
621
        raw_conflicts = resolve_conflicts(tt)
605
622
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
606
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
623
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
607
624
                                   'dorothy', None, 'dorothy-id')
608
625
        self.assertEqual(cooked_conflicts[0], duplicate)
609
 
        duplicate_id = DuplicateID('Unversioned existing file', 
 
626
        duplicate_id = DuplicateID('Unversioned existing file',
610
627
                                   'dorothy.moved', 'dorothy', None,
611
628
                                   'dorothy-id')
612
629
        self.assertEqual(cooked_conflicts[1], duplicate_id)
620
637
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
621
638
                                               'oz-id')
622
639
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
623
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
640
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
624
641
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
625
642
        self.assertEqual(cooked_conflicts[4], deleted_parent)
626
643
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
749
766
 
750
767
    def test_set_executability_order(self):
751
768
        """Ensure that executability behaves the same, no matter what order.
752
 
        
 
769
 
753
770
        - create file and set executability simultaneously
754
771
        - create file and set executability afterward
755
772
        - unsetting the executability of a file whose executability has not been
1162
1179
        transform.cancel_creation(parent)
1163
1180
        transform.finalize()
1164
1181
 
1165
 
    def test_case_insensitive_clash(self):
1166
 
        self.requireFeature(CaseInsensitiveFilesystemFeature)
 
1182
    def test_rollback_on_directory_clash(self):
1167
1183
        def tt_helper():
1168
1184
            wt = self.make_branch_and_tree('.')
1169
1185
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1170
1186
            try:
1171
 
                tt.new_file('foo', tt.root, 'bar')
1172
 
                tt.new_file('Foo', tt.root, 'spam')
 
1187
                foo = tt.new_directory('foo', tt.root)
 
1188
                tt.new_file('bar', foo, 'foobar')
 
1189
                baz = tt.new_directory('baz', tt.root)
 
1190
                tt.new_file('qux', baz, 'quux')
 
1191
                # Ask for a rename 'foo' -> 'baz'
 
1192
                tt.adjust_path('baz', tt.root, foo)
1173
1193
                # Lie to tt that we've already resolved all conflicts.
1174
1194
                tt.apply(no_conflicts=True)
1175
1195
            except:
1176
1196
                wt.unlock()
1177
1197
                raise
 
1198
        # The rename will fail because the target directory is not empty (but
 
1199
        # raises FileExists anyway).
1178
1200
        err = self.assertRaises(errors.FileExists, tt_helper)
1179
1201
        self.assertContainsRe(str(err),
1180
 
            "^File exists: .+/foo")
 
1202
            "^File exists: .+/baz")
1181
1203
 
1182
1204
    def test_two_directories_clash(self):
1183
1205
        def tt_helper():
1186
1208
            try:
1187
1209
                foo_1 = tt.new_directory('foo', tt.root)
1188
1210
                tt.new_directory('bar', foo_1)
 
1211
                # Adding the same directory with a different content
1189
1212
                foo_2 = tt.new_directory('foo', tt.root)
1190
1213
                tt.new_directory('baz', foo_2)
1191
1214
                # Lie to tt that we've already resolved all conflicts.
1204
1227
            try:
1205
1228
                foo_1 = tt.new_directory('foo', tt.root)
1206
1229
                tt.new_directory('bar', foo_1)
 
1230
                # Adding the same directory with a different content
1207
1231
                foo_2 = tt.new_directory('foo', tt.root)
1208
1232
                tt.new_directory('baz', foo_2)
1209
1233
                # Lie to tt that we've already resolved all conflicts.
1306
1330
        transform.cancel_creation(trans_id)
1307
1331
        transform.apply()
1308
1332
 
 
1333
    def test_create_from_tree(self):
 
1334
        tree1 = self.make_branch_and_tree('tree1')
 
1335
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
 
1336
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
 
1337
        tree2 = self.make_branch_and_tree('tree2')
 
1338
        tt = TreeTransform(tree2)
 
1339
        foo_trans_id = tt.create_path('foo', tt.root)
 
1340
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1341
        bar_trans_id = tt.create_path('bar', tt.root)
 
1342
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
 
1343
        tt.apply()
 
1344
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
 
1345
        self.assertFileEqual('baz', 'tree2/bar')
 
1346
 
 
1347
    def test_create_from_tree_bytes(self):
 
1348
        """Provided lines are used instead of tree content."""
 
1349
        tree1 = self.make_branch_and_tree('tree1')
 
1350
        self.build_tree_contents([('tree1/foo', 'bar'),])
 
1351
        tree1.add('foo', 'foo-id')
 
1352
        tree2 = self.make_branch_and_tree('tree2')
 
1353
        tt = TreeTransform(tree2)
 
1354
        foo_trans_id = tt.create_path('foo', tt.root)
 
1355
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
 
1356
        tt.apply()
 
1357
        self.assertFileEqual('qux', 'tree2/foo')
 
1358
 
 
1359
    def test_create_from_tree_symlink(self):
 
1360
        self.requireFeature(SymlinkFeature)
 
1361
        tree1 = self.make_branch_and_tree('tree1')
 
1362
        os.symlink('bar', 'tree1/foo')
 
1363
        tree1.add('foo', 'foo-id')
 
1364
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
 
1365
        foo_trans_id = tt.create_path('foo', tt.root)
 
1366
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1367
        tt.apply()
 
1368
        self.assertEqual('bar', os.readlink('tree2/foo'))
 
1369
 
1309
1370
 
1310
1371
class TransformGroup(object):
1311
1372
 
1365
1426
        # textual merge
1366
1427
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1367
1428
        # three-way text conflict
1368
 
        self.assertEqual(this.wt.get_file('b').read(), 
 
1429
        self.assertEqual(this.wt.get_file('b').read(),
1369
1430
                         conflict_text('b', 'b2'))
1370
1431
        # OTHER wins
1371
1432
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1375
1436
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1376
1437
        # No change
1377
1438
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1378
 
        # Correct correct results when THIS == OTHER 
 
1439
        # Correct correct results when THIS == OTHER
1379
1440
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1380
1441
        # Text conflict when THIS & OTHER are text and BASE is dir
1381
 
        self.assertEqual(this.wt.get_file('h').read(), 
 
1442
        self.assertEqual(this.wt.get_file('h').read(),
1382
1443
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1383
1444
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1384
1445
                         '1\n2\n3\n4\n')
1385
1446
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1386
1447
                         'h\ni\nj\nk\n')
1387
1448
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1388
 
        self.assertEqual(this.wt.get_file('i').read(), 
 
1449
        self.assertEqual(this.wt.get_file('i').read(),
1389
1450
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1390
1451
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1391
1452
                         '1\n2\n3\n4\n')
1415
1476
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1416
1477
            tg.tt.new_file('c', tg.root, 'c', 'c')
1417
1478
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1418
 
        targets = ((base, 'base-e', 'base-f', None, None), 
1419
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
 
1479
        targets = ((base, 'base-e', 'base-f', None, None),
 
1480
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1420
1481
                   (other, 'other-e', None, 'other-g', 'other-h'))
1421
1482
        for tg, e_target, f_target, g_target, h_target in targets:
1422
 
            for link, target in (('e', e_target), ('f', f_target), 
 
1483
            for link, target in (('e', e_target), ('f', f_target),
1423
1484
                                 ('g', g_target), ('h', h_target)):
1424
1485
                if target is not None:
1425
1486
                    tg.tt.new_symlink(link, tg.root, target, link)
1451
1512
        base = TransformGroup("BASE", root_id)
1452
1513
        this = TransformGroup("THIS", root_id)
1453
1514
        other = TransformGroup("OTHER", root_id)
1454
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1515
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1455
1516
                                   for t in [base, this, other]]
1456
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1517
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1457
1518
                                   for t in [base, this, other]]
1458
1519
        base.tt.new_directory('c', base_a, 'c')
1459
1520
        this.tt.new_directory('c1', this_a, 'c')
1484
1545
        base = TransformGroup("BASE", root_id)
1485
1546
        this = TransformGroup("THIS", root_id)
1486
1547
        other = TransformGroup("OTHER", root_id)
1487
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1548
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1488
1549
                                   for t in [base, this, other]]
1489
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1550
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1490
1551
                                   for t in [base, this, other]]
1491
1552
 
1492
1553
        base.tt.new_file('g', base_a, 'g', 'g')
1583
1644
        os.symlink('foo', 'target2/symlink')
1584
1645
        build_tree(source.basis_tree(), target)
1585
1646
        self.assertEqual([], target.conflicts())
1586
 
        
 
1647
 
1587
1648
    def test_directory_conflict_handling(self):
1588
1649
        """Ensure that when building trees, conflict handling is done"""
1589
1650
        source = self.make_branch_and_tree('source')
1645
1706
        target = self.make_branch_and_tree('target')
1646
1707
        self.build_tree(['target/name'])
1647
1708
        target.add('name')
1648
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1709
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1649
1710
            build_tree, source.basis_tree(), target)
1650
1711
 
1651
1712
    def test_build_tree_rename_count(self):
1796
1857
        self.assertTrue(source.is_executable('file1-id'))
1797
1858
 
1798
1859
    def test_case_insensitive_build_tree_inventory(self):
 
1860
        if (tests.CaseInsensitiveFilesystemFeature.available()
 
1861
            or tests.CaseInsCasePresFilenameFeature.available()):
 
1862
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1799
1863
        source = self.make_branch_and_tree('source')
1800
1864
        self.build_tree(['source/file', 'source/FILE'])
1801
1865
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1809
1873
        self.assertEqual('FILE', target.id2path('upper-id'))
1810
1874
 
1811
1875
 
 
1876
class TestCommitTransform(tests.TestCaseWithTransport):
 
1877
 
 
1878
    def get_branch(self):
 
1879
        tree = self.make_branch_and_tree('tree')
 
1880
        tree.lock_write()
 
1881
        self.addCleanup(tree.unlock)
 
1882
        tree.commit('empty commit')
 
1883
        return tree.branch
 
1884
 
 
1885
    def get_branch_and_transform(self):
 
1886
        branch = self.get_branch()
 
1887
        tt = TransformPreview(branch.basis_tree())
 
1888
        self.addCleanup(tt.finalize)
 
1889
        return branch, tt
 
1890
 
 
1891
    def test_commit_wrong_basis(self):
 
1892
        branch = self.get_branch()
 
1893
        basis = branch.repository.revision_tree(
 
1894
            _mod_revision.NULL_REVISION)
 
1895
        tt = TransformPreview(basis)
 
1896
        self.addCleanup(tt.finalize)
 
1897
        e = self.assertRaises(ValueError, tt.commit, branch, '')
 
1898
        self.assertEqual('TreeTransform not based on branch basis: null:',
 
1899
                         str(e))
 
1900
 
 
1901
    def test_empy_commit(self):
 
1902
        branch, tt = self.get_branch_and_transform()
 
1903
        rev = tt.commit(branch, 'my message')
 
1904
        self.assertEqual(2, branch.revno())
 
1905
        repo = branch.repository
 
1906
        self.assertEqual('my message', repo.get_revision(rev).message)
 
1907
 
 
1908
    def test_merge_parents(self):
 
1909
        branch, tt = self.get_branch_and_transform()
 
1910
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
 
1911
        self.assertEqual(['rev1b', 'rev1c'],
 
1912
                         branch.basis_tree().get_parent_ids()[1:])
 
1913
 
 
1914
    def test_first_commit(self):
 
1915
        branch = self.make_branch('branch')
 
1916
        branch.lock_write()
 
1917
        self.addCleanup(branch.unlock)
 
1918
        tt = TransformPreview(branch.basis_tree())
 
1919
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
 
1920
        rev = tt.commit(branch, 'my message')
 
1921
        self.assertEqual([], branch.basis_tree().get_parent_ids())
 
1922
        self.assertNotEqual(_mod_revision.NULL_REVISION,
 
1923
                            branch.last_revision())
 
1924
 
 
1925
    def test_first_commit_with_merge_parents(self):
 
1926
        branch = self.make_branch('branch')
 
1927
        branch.lock_write()
 
1928
        self.addCleanup(branch.unlock)
 
1929
        tt = TransformPreview(branch.basis_tree())
 
1930
        e = self.assertRaises(ValueError, tt.commit, branch,
 
1931
                          'my message', ['rev1b-id'])
 
1932
        self.assertEqual('Cannot supply merge parents for first commit.',
 
1933
                         str(e))
 
1934
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
 
1935
 
 
1936
    def test_add_files(self):
 
1937
        branch, tt = self.get_branch_and_transform()
 
1938
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
1939
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
 
1940
        tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
 
1941
        rev = tt.commit(branch, 'message')
 
1942
        tree = branch.basis_tree()
 
1943
        self.assertEqual('file', tree.id2path('file-id'))
 
1944
        self.assertEqual('contents', tree.get_file_text('file-id'))
 
1945
        self.assertEqual('dir', tree.id2path('dir-id'))
 
1946
        self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
 
1947
        self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
1948
 
 
1949
    def test_add_unversioned(self):
 
1950
        branch, tt = self.get_branch_and_transform()
 
1951
        tt.new_file('file', tt.root, 'contents')
 
1952
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
 
1953
                          'message', strict=True)
 
1954
 
 
1955
    def test_modify_strict(self):
 
1956
        branch, tt = self.get_branch_and_transform()
 
1957
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
1958
        tt.commit(branch, 'message', strict=True)
 
1959
        tt = TransformPreview(branch.basis_tree())
 
1960
        trans_id = tt.trans_id_file_id('file-id')
 
1961
        tt.delete_contents(trans_id)
 
1962
        tt.create_file('contents', trans_id)
 
1963
        tt.commit(branch, 'message', strict=True)
 
1964
 
 
1965
    def test_commit_malformed(self):
 
1966
        """Committing a malformed transform should raise an exception.
 
1967
 
 
1968
        In this case, we are adding a file without adding its parent.
 
1969
        """
 
1970
        branch, tt = self.get_branch_and_transform()
 
1971
        parent_id = tt.trans_id_file_id('parent-id')
 
1972
        tt.new_file('file', parent_id, 'contents', 'file-id')
 
1973
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
 
1974
                          'message')
 
1975
 
 
1976
 
1812
1977
class MockTransform(object):
1813
1978
 
1814
1979
    def has_named_child(self, by_parent, parent_id, name):
1969
2134
        resolve_conflicts(tt)
1970
2135
 
1971
2136
 
 
2137
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
 
2138
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
 
2139
                  (False, False))
 
2140
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
 
2141
              ('', ''), ('directory', 'directory'), (False, None))
 
2142
 
 
2143
 
1972
2144
class TestTransformPreview(tests.TestCaseWithTransport):
1973
2145
 
1974
2146
    def create_tree(self):
1975
2147
        tree = self.make_branch_and_tree('.')
1976
2148
        self.build_tree_contents([('a', 'content 1')])
 
2149
        tree.set_root_id('TREE_ROOT')
1977
2150
        tree.add('a', 'a-id')
1978
2151
        tree.commit('rev1', rev_id='rev1')
1979
2152
        return tree.branch.repository.revision_tree('rev1')
2046
2219
                          (False, False))],
2047
2220
                          list(preview_tree.iter_changes(revision_tree)))
2048
2221
 
2049
 
    def test_wrong_tree_value_error(self):
 
2222
    def test_include_unchanged_succeeds(self):
2050
2223
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2051
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2052
 
                              preview_tree)
2053
 
        self.assertEqual('from_tree must be transform source tree.', str(e))
 
2224
        changes = preview_tree.iter_changes(revision_tree,
 
2225
                                            include_unchanged=True)
 
2226
        root = revision_tree.inventory.root.file_id
2054
2227
 
2055
 
    def test_include_unchanged_value_error(self):
2056
 
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2057
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2058
 
                              revision_tree, include_unchanged=True)
2059
 
        self.assertEqual('include_unchanged is not supported', str(e))
 
2228
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2060
2229
 
2061
2230
    def test_specific_files(self):
2062
2231
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2063
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2064
 
                              revision_tree, specific_files=['pete'])
2065
 
        self.assertEqual('specific_files is not supported', str(e))
 
2232
        changes = preview_tree.iter_changes(revision_tree,
 
2233
                                            specific_files=[''])
 
2234
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2066
2235
 
2067
 
    def test_want_unversioned_value_error(self):
 
2236
    def test_want_unversioned(self):
2068
2237
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2069
 
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
2070
 
                              revision_tree, want_unversioned=True)
2071
 
        self.assertEqual('want_unversioned is not supported', str(e))
 
2238
        changes = preview_tree.iter_changes(revision_tree,
 
2239
                                            want_unversioned=True)
 
2240
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2072
2241
 
2073
2242
    def test_ignore_extra_trees_no_specific_files(self):
2074
2243
        # extra_trees is harmless without specific_files, so we'll silently
2452
2621
        expected = [(('', 'tree-root'),
2453
2622
                    [('a', 'a', 'file', None, 'a-id', 'file')])]
2454
2623
        self.assertEqual(expected, list(preview_tree.walkdirs()))
 
2624
 
 
2625
    def test_extras(self):
 
2626
        work_tree = self.make_branch_and_tree('tree')
 
2627
        self.build_tree(['tree/removed-file', 'tree/existing-file',
 
2628
                         'tree/not-removed-file'])
 
2629
        work_tree.add(['removed-file', 'not-removed-file'])
 
2630
        preview = TransformPreview(work_tree)
 
2631
        self.addCleanup(preview.finalize)
 
2632
        preview.new_file('new-file', preview.root, 'contents')
 
2633
        preview.new_file('new-versioned-file', preview.root, 'contents',
 
2634
                         'new-versioned-id')
 
2635
        tree = preview.get_preview_tree()
 
2636
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
 
2637
        self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
 
2638
                         set(tree.extras()))
 
2639
 
 
2640
    def test_merge_into_preview(self):
 
2641
        work_tree = self.make_branch_and_tree('tree')
 
2642
        self.build_tree_contents([('tree/file','b\n')])
 
2643
        work_tree.add('file', 'file-id')
 
2644
        work_tree.commit('first commit')
 
2645
        child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
 
2646
        self.build_tree_contents([('child/file','b\nc\n')])
 
2647
        child_tree.commit('child commit')
 
2648
        child_tree.lock_write()
 
2649
        self.addCleanup(child_tree.unlock)
 
2650
        work_tree.lock_write()
 
2651
        self.addCleanup(work_tree.unlock)
 
2652
        preview = TransformPreview(work_tree)
 
2653
        self.addCleanup(preview.finalize)
 
2654
        preview_tree = preview.get_preview_tree()
 
2655
        file_trans_id = preview.trans_id_file_id('file-id')
 
2656
        preview.delete_contents(file_trans_id)
 
2657
        preview.create_file('a\nb\n', file_trans_id)
 
2658
        pb = progress.DummyProgress()
 
2659
        merger = Merger.from_revision_ids(pb, preview_tree,
 
2660
                                          child_tree.branch.last_revision(),
 
2661
                                          other_branch=child_tree.branch,
 
2662
                                          tree_branch=work_tree.branch)
 
2663
        merger.merge_type = Merge3Merger
 
2664
        tt = merger.make_merger().make_preview_transform()
 
2665
        self.addCleanup(tt.finalize)
 
2666
        final_tree = tt.get_preview_tree()
 
2667
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
 
2668
 
 
2669
    def test_merge_preview_into_workingtree(self):
 
2670
        tree = self.make_branch_and_tree('tree')
 
2671
        tree.set_root_id('TREE_ROOT')
 
2672
        tt = TransformPreview(tree)
 
2673
        self.addCleanup(tt.finalize)
 
2674
        tt.new_file('name', tt.root, 'content', 'file-id')
 
2675
        tree2 = self.make_branch_and_tree('tree2')
 
2676
        tree2.set_root_id('TREE_ROOT')
 
2677
        pb = progress.DummyProgress()
 
2678
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2679
                                         pb, tree.basis_tree())
 
2680
        merger.merge_type = Merge3Merger
 
2681
        merger.do_merge()
 
2682
 
 
2683
    def test_merge_preview_into_workingtree_handles_conflicts(self):
 
2684
        tree = self.make_branch_and_tree('tree')
 
2685
        self.build_tree_contents([('tree/foo', 'bar')])
 
2686
        tree.add('foo', 'foo-id')
 
2687
        tree.commit('foo')
 
2688
        tt = TransformPreview(tree)
 
2689
        self.addCleanup(tt.finalize)
 
2690
        trans_id = tt.trans_id_file_id('foo-id')
 
2691
        tt.delete_contents(trans_id)
 
2692
        tt.create_file('baz', trans_id)
 
2693
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
2694
        self.build_tree_contents([('tree2/foo', 'qux')])
 
2695
        pb = progress.DummyProgress()
 
2696
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2697
                                         pb, tree.basis_tree())
 
2698
        merger.merge_type = Merge3Merger
 
2699
        merger.do_merge()
 
2700
 
 
2701
    def test_is_executable(self):
 
2702
        tree = self.make_branch_and_tree('tree')
 
2703
        preview = TransformPreview(tree)
 
2704
        self.addCleanup(preview.finalize)
 
2705
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
 
2706
        preview_tree = preview.get_preview_tree()
 
2707
        self.assertEqual(False, preview_tree.is_executable('baz-id',
 
2708
                                                           'tree/foo'))
 
2709
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
 
2710
 
 
2711
    def test_commit_preview_tree(self):
 
2712
        tree = self.make_branch_and_tree('tree')
 
2713
        rev_id = tree.commit('rev1')
 
2714
        tree.branch.lock_write()
 
2715
        self.addCleanup(tree.branch.unlock)
 
2716
        tt = TransformPreview(tree)
 
2717
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
2718
        self.addCleanup(tt.finalize)
 
2719
        preview = tt.get_preview_tree()
 
2720
        preview.set_parent_ids([rev_id])
 
2721
        builder = tree.branch.get_commit_builder([rev_id])
 
2722
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
2723
        builder.finish_inventory()
 
2724
        rev2_id = builder.commit('rev2')
 
2725
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
2726
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
2727
 
 
2728
 
 
2729
class FakeSerializer(object):
 
2730
    """Serializer implementation that simply returns the input.
 
2731
 
 
2732
    The input is returned in the order used by pack.ContainerPushParser.
 
2733
    """
 
2734
    @staticmethod
 
2735
    def bytes_record(bytes, names):
 
2736
        return names, bytes
 
2737
 
 
2738
 
 
2739
class TestSerializeTransform(tests.TestCaseWithTransport):
 
2740
 
 
2741
    _test_needs_features = [tests.UnicodeFilenameFeature]
 
2742
 
 
2743
    def get_preview(self, tree=None):
 
2744
        if tree is None:
 
2745
            tree = self.make_branch_and_tree('tree')
 
2746
        tt = TransformPreview(tree)
 
2747
        self.addCleanup(tt.finalize)
 
2748
        return tt
 
2749
 
 
2750
    def assertSerializesTo(self, expected, tt):
 
2751
        records = list(tt.serialize(FakeSerializer()))
 
2752
        self.assertEqual(expected, records)
 
2753
 
 
2754
    @staticmethod
 
2755
    def default_attribs():
 
2756
        return {
 
2757
            '_id_number': 1,
 
2758
            '_new_name': {},
 
2759
            '_new_parent': {},
 
2760
            '_new_executability': {},
 
2761
            '_new_id': {},
 
2762
            '_tree_path_ids': {'': 'new-0'},
 
2763
            '_removed_id': [],
 
2764
            '_removed_contents': [],
 
2765
            '_non_present_ids': {},
 
2766
            }
 
2767
 
 
2768
    def make_records(self, attribs, contents):
 
2769
        records = [
 
2770
            (((('attribs'),),), bencode.bencode(attribs))]
 
2771
        records.extend([(((n, k),), c) for n, k, c in contents])
 
2772
        return records
 
2773
 
 
2774
    def creation_records(self):
 
2775
        attribs = self.default_attribs()
 
2776
        attribs['_id_number'] = 3
 
2777
        attribs['_new_name'] = {
 
2778
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
 
2779
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
 
2780
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
 
2781
        attribs['_new_executability'] = {'new-1': 1}
 
2782
        contents = [
 
2783
            ('new-1', 'file', 'i 1\nbar\n'),
 
2784
            ('new-2', 'directory', ''),
 
2785
            ]
 
2786
        return self.make_records(attribs, contents)
 
2787
 
 
2788
    def test_serialize_creation(self):
 
2789
        tt = self.get_preview()
 
2790
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
 
2791
        tt.new_directory('qux', tt.root, 'quxx')
 
2792
        self.assertSerializesTo(self.creation_records(), tt)
 
2793
 
 
2794
    def test_deserialize_creation(self):
 
2795
        tt = self.get_preview()
 
2796
        tt.deserialize(iter(self.creation_records()))
 
2797
        self.assertEqual(3, tt._id_number)
 
2798
        self.assertEqual({'new-1': u'foo\u1234',
 
2799
                          'new-2': 'qux'}, tt._new_name)
 
2800
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
 
2801
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
 
2802
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
 
2803
        self.assertEqual({'new-1': True}, tt._new_executability)
 
2804
        self.assertEqual({'new-1': 'file',
 
2805
                          'new-2': 'directory'}, tt._new_contents)
 
2806
        foo_limbo = open(tt._limbo_name('new-1'), 'rb')
 
2807
        try:
 
2808
            foo_content = foo_limbo.read()
 
2809
        finally:
 
2810
            foo_limbo.close()
 
2811
        self.assertEqual('bar', foo_content)
 
2812
 
 
2813
    def symlink_creation_records(self):
 
2814
        attribs = self.default_attribs()
 
2815
        attribs['_id_number'] = 2
 
2816
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
 
2817
        attribs['_new_parent'] = {'new-1': 'new-0'}
 
2818
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
 
2819
        return self.make_records(attribs, contents)
 
2820
 
 
2821
    def test_serialize_symlink_creation(self):
 
2822
        self.requireFeature(tests.SymlinkFeature)
 
2823
        tt = self.get_preview()
 
2824
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
 
2825
        self.assertSerializesTo(self.symlink_creation_records(), tt)
 
2826
 
 
2827
    def test_deserialize_symlink_creation(self):
 
2828
        self.requireFeature(tests.SymlinkFeature)
 
2829
        tt = self.get_preview()
 
2830
        tt.deserialize(iter(self.symlink_creation_records()))
 
2831
        abspath = tt._limbo_name('new-1')
 
2832
        foo_content = osutils.readlink(abspath)
 
2833
        self.assertEqual(u'bar\u1234', foo_content)
 
2834
 
 
2835
    def make_destruction_preview(self):
 
2836
        tree = self.make_branch_and_tree('.')
 
2837
        self.build_tree([u'foo\u1234', 'bar'])
 
2838
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
 
2839
        return self.get_preview(tree)
 
2840
 
 
2841
    def destruction_records(self):
 
2842
        attribs = self.default_attribs()
 
2843
        attribs['_id_number'] = 3
 
2844
        attribs['_removed_id'] = ['new-1']
 
2845
        attribs['_removed_contents'] = ['new-2']
 
2846
        attribs['_tree_path_ids'] = {
 
2847
            '': 'new-0',
 
2848
            u'foo\u1234'.encode('utf-8'): 'new-1',
 
2849
            'bar': 'new-2',
 
2850
            }
 
2851
        return self.make_records(attribs, [])
 
2852
 
 
2853
    def test_serialize_destruction(self):
 
2854
        tt = self.make_destruction_preview()
 
2855
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
 
2856
        tt.unversion_file(foo_trans_id)
 
2857
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
 
2858
        tt.delete_contents(bar_trans_id)
 
2859
        self.assertSerializesTo(self.destruction_records(), tt)
 
2860
 
 
2861
    def test_deserialize_destruction(self):
 
2862
        tt = self.make_destruction_preview()
 
2863
        tt.deserialize(iter(self.destruction_records()))
 
2864
        self.assertEqual({u'foo\u1234': 'new-1',
 
2865
                          'bar': 'new-2',
 
2866
                          '': tt.root}, tt._tree_path_ids)
 
2867
        self.assertEqual({'new-1': u'foo\u1234',
 
2868
                          'new-2': 'bar',
 
2869
                          tt.root: ''}, tt._tree_id_paths)
 
2870
        self.assertEqual(set(['new-1']), tt._removed_id)
 
2871
        self.assertEqual(set(['new-2']), tt._removed_contents)
 
2872
 
 
2873
    def missing_records(self):
 
2874
        attribs = self.default_attribs()
 
2875
        attribs['_id_number'] = 2
 
2876
        attribs['_non_present_ids'] = {
 
2877
            'boo': 'new-1',}
 
2878
        return self.make_records(attribs, [])
 
2879
 
 
2880
    def test_serialize_missing(self):
 
2881
        tt = self.get_preview()
 
2882
        boo_trans_id = tt.trans_id_file_id('boo')
 
2883
        self.assertSerializesTo(self.missing_records(), tt)
 
2884
 
 
2885
    def test_deserialize_missing(self):
 
2886
        tt = self.get_preview()
 
2887
        tt.deserialize(iter(self.missing_records()))
 
2888
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
 
2889
 
 
2890
    def make_modification_preview(self):
 
2891
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
2892
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
2893
        tree = self.make_branch_and_tree('tree')
 
2894
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
2895
        tree.add('file', 'file-id')
 
2896
        return self.get_preview(tree), LINES_TWO
 
2897
 
 
2898
    def modification_records(self):
 
2899
        attribs = self.default_attribs()
 
2900
        attribs['_id_number'] = 2
 
2901
        attribs['_tree_path_ids'] = {
 
2902
            'file': 'new-1',
 
2903
            '': 'new-0',}
 
2904
        attribs['_removed_contents'] = ['new-1']
 
2905
        contents = [('new-1', 'file',
 
2906
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
 
2907
        return self.make_records(attribs, contents)
 
2908
 
 
2909
    def test_serialize_modification(self):
 
2910
        tt, LINES = self.make_modification_preview()
 
2911
        trans_id = tt.trans_id_file_id('file-id')
 
2912
        tt.delete_contents(trans_id)
 
2913
        tt.create_file(LINES, trans_id)
 
2914
        self.assertSerializesTo(self.modification_records(), tt)
 
2915
 
 
2916
    def test_deserialize_modification(self):
 
2917
        tt, LINES = self.make_modification_preview()
 
2918
        tt.deserialize(iter(self.modification_records()))
 
2919
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
2920
 
 
2921
    def make_kind_change_preview(self):
 
2922
        LINES = 'a\nb\nc\nd\n'
 
2923
        tree = self.make_branch_and_tree('tree')
 
2924
        self.build_tree(['tree/foo/'])
 
2925
        tree.add('foo', 'foo-id')
 
2926
        return self.get_preview(tree), LINES
 
2927
 
 
2928
    def kind_change_records(self):
 
2929
        attribs = self.default_attribs()
 
2930
        attribs['_id_number'] = 2
 
2931
        attribs['_tree_path_ids'] = {
 
2932
            'foo': 'new-1',
 
2933
            '': 'new-0',}
 
2934
        attribs['_removed_contents'] = ['new-1']
 
2935
        contents = [('new-1', 'file',
 
2936
                     'i 4\na\nb\nc\nd\n\n')]
 
2937
        return self.make_records(attribs, contents)
 
2938
 
 
2939
    def test_serialize_kind_change(self):
 
2940
        tt, LINES = self.make_kind_change_preview()
 
2941
        trans_id = tt.trans_id_file_id('foo-id')
 
2942
        tt.delete_contents(trans_id)
 
2943
        tt.create_file(LINES, trans_id)
 
2944
        self.assertSerializesTo(self.kind_change_records(), tt)
 
2945
 
 
2946
    def test_deserialize_kind_change(self):
 
2947
        tt, LINES = self.make_kind_change_preview()
 
2948
        tt.deserialize(iter(self.kind_change_records()))
 
2949
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
2950
 
 
2951
    def make_add_contents_preview(self):
 
2952
        LINES = 'a\nb\nc\nd\n'
 
2953
        tree = self.make_branch_and_tree('tree')
 
2954
        self.build_tree(['tree/foo'])
 
2955
        tree.add('foo')
 
2956
        os.unlink('tree/foo')
 
2957
        return self.get_preview(tree), LINES
 
2958
 
 
2959
    def add_contents_records(self):
 
2960
        attribs = self.default_attribs()
 
2961
        attribs['_id_number'] = 2
 
2962
        attribs['_tree_path_ids'] = {
 
2963
            'foo': 'new-1',
 
2964
            '': 'new-0',}
 
2965
        contents = [('new-1', 'file',
 
2966
                     'i 4\na\nb\nc\nd\n\n')]
 
2967
        return self.make_records(attribs, contents)
 
2968
 
 
2969
    def test_serialize_add_contents(self):
 
2970
        tt, LINES = self.make_add_contents_preview()
 
2971
        trans_id = tt.trans_id_tree_path('foo')
 
2972
        tt.create_file(LINES, trans_id)
 
2973
        self.assertSerializesTo(self.add_contents_records(), tt)
 
2974
 
 
2975
    def test_deserialize_add_contents(self):
 
2976
        tt, LINES = self.make_add_contents_preview()
 
2977
        tt.deserialize(iter(self.add_contents_records()))
 
2978
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
2979
 
 
2980
    def test_get_parents_lines(self):
 
2981
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
2982
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
2983
        tree = self.make_branch_and_tree('tree')
 
2984
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
2985
        tree.add('file', 'file-id')
 
2986
        tt = self.get_preview(tree)
 
2987
        trans_id = tt.trans_id_tree_path('file')
 
2988
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
 
2989
            tt._get_parents_lines(trans_id))
 
2990
 
 
2991
    def test_get_parents_texts(self):
 
2992
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
2993
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
2994
        tree = self.make_branch_and_tree('tree')
 
2995
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
2996
        tree.add('file', 'file-id')
 
2997
        tt = self.get_preview(tree)
 
2998
        trans_id = tt.trans_id_tree_path('file')
 
2999
        self.assertEqual((LINES_ONE,),
 
3000
            tt._get_parents_texts(trans_id))