/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Aaron Bentley
  • Date: 2008-11-03 19:21:41 UTC
  • mto: (0.16.100 shelf-ui)
  • mto: This revision was merged to the branch mainline in revision 3820.
  • Revision ID: aaron@aaronbentley.com-20081103192141-nsgid8m3wbtwe73y
Re-format GPL notifications

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
 
18
import stat
18
19
from StringIO import StringIO
19
20
import sys
20
 
import time
21
21
 
22
22
from bzrlib import (
23
 
    bencode,
24
23
    errors,
25
 
    filters,
26
24
    generate_ids,
27
25
    osutils,
28
26
    progress,
29
27
    revision as _mod_revision,
30
 
    rules,
 
28
    symbol_versioning,
31
29
    tests,
32
30
    urlutils,
33
31
    )
37
35
                              NonDirectoryParent)
38
36
from bzrlib.diff import show_diff_trees
39
37
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
40
 
                           ReusingTransform, CantMoveRoot,
 
38
                           ReusingTransform, CantMoveRoot, 
41
39
                           PathsNotVersionedError, ExistingLimbo,
42
40
                           ExistingPendingDeletion, ImmortalLimbo,
43
41
                           ImmortalPendingDeletion, LockError)
44
42
from bzrlib.osutils import file_kind, pathjoin
45
43
from bzrlib.merge import Merge3Merger, Merger
46
44
from bzrlib.tests import (
 
45
    CaseInsensitiveFilesystemFeature,
47
46
    HardlinkFeature,
48
47
    SymlinkFeature,
49
48
    TestCase,
50
49
    TestCaseInTempDir,
51
50
    TestSkipped,
52
51
    )
53
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
54
 
                              resolve_conflicts, cook_conflicts,
 
52
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
 
53
                              resolve_conflicts, cook_conflicts, 
55
54
                              build_tree, get_backup_name,
56
55
                              _FileMover, resolve_checkout,
57
56
                              TransformPreview, create_from_tree)
 
57
from bzrlib.util import bencode
58
58
 
59
59
 
60
60
class TestTreeTransform(tests.TestCaseWithTransport):
130
130
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
131
131
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
132
132
        self.assertEqual(len(modified_paths), 3)
133
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in
 
133
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
134
134
                          ('ozzie', 'my_pretties', 'my_pretties2')]
135
135
        self.assertSubset(tree_mod_paths, modified_paths)
136
136
        # is it safe to finalize repeatedly?
137
137
        transform.finalize()
138
138
        transform.finalize()
139
139
 
140
 
    def test_create_files_same_timestamp(self):
141
 
        transform, root = self.get_transform()
142
 
        self.wt.lock_tree_write()
143
 
        self.addCleanup(self.wt.unlock)
144
 
        # Roll back the clock, so that we know everything is being set to the
145
 
        # exact time
146
 
        transform._creation_mtime = creation_mtime = time.time() - 20.0
147
 
        transform.create_file('content-one',
148
 
                              transform.create_path('one', root))
149
 
        time.sleep(1) # *ugly*
150
 
        transform.create_file('content-two',
151
 
                              transform.create_path('two', root))
152
 
        transform.apply()
153
 
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
154
 
        fo.close()
155
 
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
156
 
        fo.close()
157
 
        # We only guarantee 2s resolution
158
 
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
159
 
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
160
 
        # But if we have more than that, all files should get the same result
161
 
        self.assertEqual(st1.st_mtime, st2.st_mtime)
162
 
 
163
 
    def test_change_root_id(self):
164
 
        transform, root = self.get_transform()
165
 
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
166
 
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
167
 
        transform.delete_contents(root)
168
 
        transform.unversion_file(root)
169
 
        transform.fixup_new_roots()
170
 
        transform.apply()
171
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
172
 
 
173
 
    def test_change_root_id_add_files(self):
174
 
        transform, root = self.get_transform()
175
 
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
176
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
177
 
        transform.new_file('file', new_trans_id, ['new-contents\n'],
178
 
                           'new-file-id')
179
 
        transform.delete_contents(root)
180
 
        transform.unversion_file(root)
181
 
        transform.fixup_new_roots()
182
 
        transform.apply()
183
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
184
 
        self.assertEqual('new-file-id', self.wt.path2id('file'))
185
 
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
186
 
 
187
 
    def test_add_two_roots(self):
188
 
        transform, root = self.get_transform()
189
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
190
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
191
 
        self.assertRaises(ValueError, transform.fixup_new_roots)
192
 
 
193
140
    def test_hardlink(self):
194
141
        self.requireFeature(HardlinkFeature)
195
142
        transform, root = self.get_transform()
209
156
        transform, root = self.get_transform()
210
157
        self.wt.lock_tree_write()
211
158
        self.addCleanup(self.wt.unlock)
212
 
        trans_id = transform.new_file('name', root, 'contents',
 
159
        trans_id = transform.new_file('name', root, 'contents', 
213
160
                                      'my_pretties', True)
214
161
        oz = transform.new_directory('oz', root, 'oz-id')
215
162
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
216
 
        toto = transform.new_file('toto', dorothy, 'toto-contents',
 
163
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
217
164
                                  'toto-id', False)
218
165
 
219
166
        self.assertEqual(len(transform.find_conflicts()), 0)
243
190
 
244
191
    def test_conflicts(self):
245
192
        transform, root = self.get_transform()
246
 
        trans_id = transform.new_file('name', root, 'contents',
 
193
        trans_id = transform.new_file('name', root, 'contents', 
247
194
                                      'my_pretties')
248
195
        self.assertEqual(len(transform.find_conflicts()), 0)
249
196
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
250
 
        self.assertEqual(transform.find_conflicts(),
 
197
        self.assertEqual(transform.find_conflicts(), 
251
198
                         [('duplicate', trans_id, trans_id2, 'name')])
252
199
        self.assertRaises(MalformedTransform, transform.apply)
253
200
        transform.adjust_path('name', trans_id, trans_id2)
254
 
        self.assertEqual(transform.find_conflicts(),
 
201
        self.assertEqual(transform.find_conflicts(), 
255
202
                         [('non-directory parent', trans_id)])
256
203
        tinman_id = transform.trans_id_tree_path('tinman')
257
204
        transform.adjust_path('name', tinman_id, trans_id2)
258
 
        self.assertEqual(transform.find_conflicts(),
259
 
                         [('unversioned parent', tinman_id),
 
205
        self.assertEqual(transform.find_conflicts(), 
 
206
                         [('unversioned parent', tinman_id), 
260
207
                          ('missing parent', tinman_id)])
261
208
        lion_id = transform.create_path('lion', root)
262
 
        self.assertEqual(transform.find_conflicts(),
263
 
                         [('unversioned parent', tinman_id),
 
209
        self.assertEqual(transform.find_conflicts(), 
 
210
                         [('unversioned parent', tinman_id), 
264
211
                          ('missing parent', tinman_id)])
265
212
        transform.adjust_path('name', lion_id, trans_id2)
266
 
        self.assertEqual(transform.find_conflicts(),
 
213
        self.assertEqual(transform.find_conflicts(), 
267
214
                         [('unversioned parent', lion_id),
268
215
                          ('missing parent', lion_id)])
269
216
        transform.version_file("Courage", lion_id)
270
 
        self.assertEqual(transform.find_conflicts(),
271
 
                         [('missing parent', lion_id),
 
217
        self.assertEqual(transform.find_conflicts(), 
 
218
                         [('missing parent', lion_id), 
272
219
                          ('versioning no contents', lion_id)])
273
220
        transform.adjust_path('name2', root, trans_id2)
274
 
        self.assertEqual(transform.find_conflicts(),
 
221
        self.assertEqual(transform.find_conflicts(), 
275
222
                         [('versioning no contents', lion_id)])
276
223
        transform.create_file('Contents, okay?', lion_id)
277
224
        transform.adjust_path('name2', trans_id2, trans_id2)
278
 
        self.assertEqual(transform.find_conflicts(),
279
 
                         [('parent loop', trans_id2),
 
225
        self.assertEqual(transform.find_conflicts(), 
 
226
                         [('parent loop', trans_id2), 
280
227
                          ('non-directory parent', trans_id2)])
281
228
        transform.adjust_path('name2', root, trans_id2)
282
229
        oz_id = transform.new_directory('oz', root)
283
230
        transform.set_executability(True, oz_id)
284
 
        self.assertEqual(transform.find_conflicts(),
 
231
        self.assertEqual(transform.find_conflicts(), 
285
232
                         [('unversioned executability', oz_id)])
286
233
        transform.version_file('oz-id', oz_id)
287
 
        self.assertEqual(transform.find_conflicts(),
 
234
        self.assertEqual(transform.find_conflicts(), 
288
235
                         [('non-file executability', oz_id)])
289
236
        transform.set_executability(None, oz_id)
290
237
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
299
246
        self.assert_('oz/tip' in transform2._tree_path_ids)
300
247
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
301
248
        self.assertEqual(len(result), 2)
302
 
        self.assertEqual((result[0][0], result[0][1]),
 
249
        self.assertEqual((result[0][0], result[0][1]), 
303
250
                         ('duplicate', newtip))
304
 
        self.assertEqual((result[1][0], result[1][2]),
 
251
        self.assertEqual((result[1][0], result[1][2]), 
305
252
                         ('duplicate id', newtip))
306
253
        transform2.finalize()
307
254
        transform3 = TreeTransform(self.wt)
308
255
        self.addCleanup(transform3.finalize)
309
256
        oz_id = transform3.trans_id_tree_file_id('oz-id')
310
257
        transform3.delete_contents(oz_id)
311
 
        self.assertEqual(transform3.find_conflicts(),
 
258
        self.assertEqual(transform3.find_conflicts(), 
312
259
                         [('missing parent', oz_id)])
313
260
        root_id = transform3.root
314
261
        tip_id = transform3.trans_id_tree_file_id('tip-id')
423
370
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
424
371
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
425
372
 
426
 
    def test_adjust_path_updates_child_limbo_names(self):
427
 
        tree = self.make_branch_and_tree('tree')
428
 
        transform = TreeTransform(tree)
429
 
        self.addCleanup(transform.finalize)
430
 
        foo_id = transform.new_directory('foo', transform.root)
431
 
        bar_id = transform.new_directory('bar', foo_id)
432
 
        baz_id = transform.new_directory('baz', bar_id)
433
 
        qux_id = transform.new_directory('qux', baz_id)
434
 
        transform.adjust_path('quxx', foo_id, bar_id)
435
 
        self.assertStartsWith(transform._limbo_name(qux_id),
436
 
                              transform._limbo_name(bar_id))
437
 
 
438
373
    def test_add_del(self):
439
374
        start, root = self.get_transform()
440
375
        start.new_directory('a', root, 'a')
453
388
        self.addCleanup(unversion.finalize)
454
389
        parent = unversion.trans_id_tree_path('parent')
455
390
        unversion.unversion_file(parent)
456
 
        self.assertEqual(unversion.find_conflicts(),
 
391
        self.assertEqual(unversion.find_conflicts(), 
457
392
                         [('unversioned parent', parent_id)])
458
393
        file_id = unversion.trans_id_tree_file_id('child-id')
459
394
        unversion.unversion_file(file_id)
479
414
        mangle_tree.adjust_path('name2', root, name1)
480
415
        mangle_tree.adjust_path('name1', root, name2)
481
416
 
482
 
        #tests for deleting parent directories
 
417
        #tests for deleting parent directories 
483
418
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
484
419
        mangle_tree.delete_contents(ddir)
485
420
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
514
449
        create_tree,root = self.get_transform()
515
450
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
516
451
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
517
 
        create_tree.apply()
 
452
        create_tree.apply()        
518
453
        mangle_tree,root = self.get_transform()
519
454
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
520
455
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
528
463
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
529
464
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
530
465
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
531
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
 
466
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
532
467
                             'test_too_much-id')
533
 
        create_tree.apply()
 
468
        create_tree.apply()        
534
469
        mangle_tree,root = self.get_transform()
535
470
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
536
471
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
537
472
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
538
473
        mangle_tree.adjust_path('selftest', bzrlib, tests)
539
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
 
474
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
540
475
        mangle_tree.set_executability(True, test_too_much)
541
476
        mangle_tree.apply()
542
477
 
543
478
    def test_both_rename3(self):
544
479
        create_tree,root = self.get_transform()
545
480
        tests = create_tree.new_directory('tests', root, 'tests-id')
546
 
        create_tree.new_file('test_too_much.py', tests, 'hello1',
 
481
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
547
482
                             'test_too_much-id')
548
 
        create_tree.apply()
 
483
        create_tree.apply()        
549
484
        mangle_tree,root = self.get_transform()
550
485
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
551
486
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
552
487
        mangle_tree.adjust_path('selftest', root, tests)
553
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
 
488
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
554
489
        mangle_tree.set_executability(True, test_too_much)
555
490
        mangle_tree.apply()
556
491
 
569
504
        newdir = move_id.new_directory('dir', root, 'newdir')
570
505
        move_id.adjust_path('name2', newdir, name1)
571
506
        move_id.apply()
572
 
 
 
507
        
573
508
    def test_replace_dangling_ie(self):
574
509
        create_tree, root = self.get_transform()
575
510
        # prepare tree
591
526
        resolve_conflicts(replace)
592
527
        replace.apply()
593
528
 
594
 
    def _test_symlinks(self, link_name1,link_target1,
595
 
                       link_name2, link_target2):
596
 
 
597
 
        def ozpath(p): return 'oz/' + p
598
 
 
 
529
    def test_symlinks(self):
599
530
        self.requireFeature(SymlinkFeature)
600
 
        transform, root = self.get_transform()
 
531
        transform,root = self.get_transform()
601
532
        oz_id = transform.new_directory('oz', root, 'oz-id')
602
 
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
 
533
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
603
534
                                       'wizard-id')
604
 
        wiz_id = transform.create_path(link_name2, oz_id)
605
 
        transform.create_symlink(link_target2, wiz_id)
606
 
        transform.version_file('wiz-id2', wiz_id)
 
535
        wiz_id = transform.create_path('wizard2', oz_id)
 
536
        transform.create_symlink('behind_curtain', wiz_id)
 
537
        transform.version_file('wiz-id2', wiz_id)            
607
538
        transform.set_executability(True, wiz_id)
608
 
        self.assertEqual(transform.find_conflicts(),
 
539
        self.assertEqual(transform.find_conflicts(), 
609
540
                         [('non-file executability', wiz_id)])
610
541
        transform.set_executability(None, wiz_id)
611
542
        transform.apply()
612
 
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
613
 
        self.assertEqual('symlink',
614
 
                         file_kind(self.wt.abspath(ozpath(link_name1))))
615
 
        self.assertEqual(link_target2,
616
 
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
617
 
        self.assertEqual(link_target1,
618
 
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
619
 
 
620
 
    def test_symlinks(self):
621
 
        self._test_symlinks('wizard', 'wizard-target',
622
 
                            'wizard2', 'behind_curtain')
623
 
 
624
 
    def test_symlinks_unicode(self):
625
 
        self.requireFeature(tests.UnicodeFilenameFeature)
626
 
        self._test_symlinks(u'\N{Euro Sign}wizard',
627
 
                            u'wizard-targ\N{Euro Sign}t',
628
 
                            u'\N{Euro Sign}wizard2',
629
 
                            u'b\N{Euro Sign}hind_curtain')
 
543
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
 
544
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
 
545
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
 
546
                         'behind_curtain')
 
547
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
 
548
                         'wizard-target')
630
549
 
631
550
    def test_unable_create_symlink(self):
632
551
        def tt_helper():
656
575
        create.apply()
657
576
        conflicts,root = self.get_transform()
658
577
        # set up duplicate entry, duplicate id
659
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
 
578
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
660
579
                                         'dorothy-id')
661
580
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
662
581
        oz = conflicts.trans_id_tree_file_id('oz-id')
686
605
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
687
606
        raw_conflicts = resolve_conflicts(tt)
688
607
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
689
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
 
608
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
690
609
                                   'dorothy', None, 'dorothy-id')
691
610
        self.assertEqual(cooked_conflicts[0], duplicate)
692
 
        duplicate_id = DuplicateID('Unversioned existing file',
 
611
        duplicate_id = DuplicateID('Unversioned existing file', 
693
612
                                   'dorothy.moved', 'dorothy', None,
694
613
                                   'dorothy-id')
695
614
        self.assertEqual(cooked_conflicts[1], duplicate_id)
703
622
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
704
623
                                               'oz-id')
705
624
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
706
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
 
625
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
707
626
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
708
627
        self.assertEqual(cooked_conflicts[4], deleted_parent)
709
628
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
735
654
                                         ' versioned, but has versioned'
736
655
                                         ' children.  Versioned directory.')
737
656
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
738
 
                                         ' oz/emeraldcity. Cancelled move.')
 
657
                                         ' oz/emeraldcity.  Cancelled move.')
739
658
 
740
659
    def prepare_wrong_parent_kind(self):
741
660
        tt, root = self.get_transform()
812
731
        create.apply()
813
732
        transform, root = self.get_transform()
814
733
        transform.adjust_root_path('oldroot', fun)
815
 
        new_root = transform.trans_id_tree_path('')
 
734
        new_root=transform.trans_id_tree_path('')
816
735
        transform.version_file('new-root', new_root)
817
736
        transform.apply()
818
737
 
832
751
 
833
752
    def test_set_executability_order(self):
834
753
        """Ensure that executability behaves the same, no matter what order.
835
 
 
 
754
        
836
755
        - create file and set executability simultaneously
837
756
        - create file and set executability afterward
838
757
        - unsetting the executability of a file whose executability has not been
1492
1411
        # textual merge
1493
1412
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1494
1413
        # three-way text conflict
1495
 
        self.assertEqual(this.wt.get_file('b').read(),
 
1414
        self.assertEqual(this.wt.get_file('b').read(), 
1496
1415
                         conflict_text('b', 'b2'))
1497
1416
        # OTHER wins
1498
1417
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1502
1421
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1503
1422
        # No change
1504
1423
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1505
 
        # Correct correct results when THIS == OTHER
 
1424
        # Correct correct results when THIS == OTHER 
1506
1425
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1507
1426
        # Text conflict when THIS & OTHER are text and BASE is dir
1508
 
        self.assertEqual(this.wt.get_file('h').read(),
 
1427
        self.assertEqual(this.wt.get_file('h').read(), 
1509
1428
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1510
1429
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1511
1430
                         '1\n2\n3\n4\n')
1512
1431
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1513
1432
                         'h\ni\nj\nk\n')
1514
1433
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1515
 
        self.assertEqual(this.wt.get_file('i').read(),
 
1434
        self.assertEqual(this.wt.get_file('i').read(), 
1516
1435
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1517
1436
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1518
1437
                         '1\n2\n3\n4\n')
1542
1461
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1543
1462
            tg.tt.new_file('c', tg.root, 'c', 'c')
1544
1463
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1545
 
        targets = ((base, 'base-e', 'base-f', None, None),
1546
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
 
1464
        targets = ((base, 'base-e', 'base-f', None, None), 
 
1465
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
1547
1466
                   (other, 'other-e', None, 'other-g', 'other-h'))
1548
1467
        for tg, e_target, f_target, g_target, h_target in targets:
1549
 
            for link, target in (('e', e_target), ('f', f_target),
 
1468
            for link, target in (('e', e_target), ('f', f_target), 
1550
1469
                                 ('g', g_target), ('h', h_target)):
1551
1470
                if target is not None:
1552
1471
                    tg.tt.new_symlink(link, tg.root, target, link)
1578
1497
        base = TransformGroup("BASE", root_id)
1579
1498
        this = TransformGroup("THIS", root_id)
1580
1499
        other = TransformGroup("OTHER", root_id)
1581
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
 
1500
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1582
1501
                                   for t in [base, this, other]]
1583
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
 
1502
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1584
1503
                                   for t in [base, this, other]]
1585
1504
        base.tt.new_directory('c', base_a, 'c')
1586
1505
        this.tt.new_directory('c1', this_a, 'c')
1611
1530
        base = TransformGroup("BASE", root_id)
1612
1531
        this = TransformGroup("THIS", root_id)
1613
1532
        other = TransformGroup("OTHER", root_id)
1614
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
 
1533
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1615
1534
                                   for t in [base, this, other]]
1616
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
 
1535
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1617
1536
                                   for t in [base, this, other]]
1618
1537
 
1619
1538
        base.tt.new_file('g', base_a, 'g', 'g')
1710
1629
        os.symlink('foo', 'target2/symlink')
1711
1630
        build_tree(source.basis_tree(), target)
1712
1631
        self.assertEqual([], target.conflicts())
1713
 
 
 
1632
        
1714
1633
    def test_directory_conflict_handling(self):
1715
1634
        """Ensure that when building trees, conflict handling is done"""
1716
1635
        source = self.make_branch_and_tree('source')
1772
1691
        target = self.make_branch_and_tree('target')
1773
1692
        self.build_tree(['target/name'])
1774
1693
        target.add('name')
1775
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
 
1694
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
1776
1695
            build_tree, source.basis_tree(), target)
1777
1696
 
1778
1697
    def test_build_tree_rename_count(self):
1922
1841
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1923
1842
        self.assertTrue(source.is_executable('file1-id'))
1924
1843
 
1925
 
    def install_rot13_content_filter(self, pattern):
1926
 
        # We could use
1927
 
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
1928
 
        # below, but that looks a bit... hard to read even if it's exactly
1929
 
        # the same thing.
1930
 
        original_registry = filters._reset_registry()
1931
 
        def restore_registry():
1932
 
            filters._reset_registry(original_registry)
1933
 
        self.addCleanup(restore_registry)
1934
 
        def rot13(chunks, context=None):
1935
 
            return [''.join(chunks).encode('rot13')]
1936
 
        rot13filter = filters.ContentFilter(rot13, rot13)
1937
 
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1938
 
        os.mkdir(self.test_home_dir + '/.bazaar')
1939
 
        rules_filename = self.test_home_dir + '/.bazaar/rules'
1940
 
        f = open(rules_filename, 'wb')
1941
 
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
1942
 
        f.close()
1943
 
        def uninstall_rules():
1944
 
            os.remove(rules_filename)
1945
 
            rules.reset_rules()
1946
 
        self.addCleanup(uninstall_rules)
1947
 
        rules.reset_rules()
1948
 
 
1949
 
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
1950
 
        """build_tree will not hardlink files that have content filtering rules
1951
 
        applied to them (but will still hardlink other files from the same tree
1952
 
        if it can).
1953
 
        """
1954
 
        self.requireFeature(HardlinkFeature)
1955
 
        self.install_rot13_content_filter('file1')
1956
 
        source = self.create_ab_tree()
1957
 
        target = self.make_branch_and_tree('target')
1958
 
        revision_tree = source.basis_tree()
1959
 
        revision_tree.lock_read()
1960
 
        self.addCleanup(revision_tree.unlock)
1961
 
        build_tree(revision_tree, target, source, hardlink=True)
1962
 
        target.lock_read()
1963
 
        self.addCleanup(target.unlock)
1964
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1965
 
        source_stat = os.stat('source/file1')
1966
 
        target_stat = os.stat('target/file1')
1967
 
        self.assertNotEqual(source_stat, target_stat)
1968
 
        source_stat = os.stat('source/file2')
1969
 
        target_stat = os.stat('target/file2')
1970
 
        self.assertEqualStat(source_stat, target_stat)
1971
 
 
1972
1844
    def test_case_insensitive_build_tree_inventory(self):
1973
 
        if (tests.CaseInsensitiveFilesystemFeature.available()
1974
 
            or tests.CaseInsCasePresFilenameFeature.available()):
1975
 
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1976
1845
        source = self.make_branch_and_tree('source')
1977
1846
        self.build_tree(['source/file', 'source/FILE'])
1978
1847
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1986
1855
        self.assertEqual('FILE', target.id2path('upper-id'))
1987
1856
 
1988
1857
 
1989
 
class TestCommitTransform(tests.TestCaseWithTransport):
1990
 
 
1991
 
    def get_branch(self):
1992
 
        tree = self.make_branch_and_tree('tree')
1993
 
        tree.lock_write()
1994
 
        self.addCleanup(tree.unlock)
1995
 
        tree.commit('empty commit')
1996
 
        return tree.branch
1997
 
 
1998
 
    def get_branch_and_transform(self):
1999
 
        branch = self.get_branch()
2000
 
        tt = TransformPreview(branch.basis_tree())
2001
 
        self.addCleanup(tt.finalize)
2002
 
        return branch, tt
2003
 
 
2004
 
    def test_commit_wrong_basis(self):
2005
 
        branch = self.get_branch()
2006
 
        basis = branch.repository.revision_tree(
2007
 
            _mod_revision.NULL_REVISION)
2008
 
        tt = TransformPreview(basis)
2009
 
        self.addCleanup(tt.finalize)
2010
 
        e = self.assertRaises(ValueError, tt.commit, branch, '')
2011
 
        self.assertEqual('TreeTransform not based on branch basis: null:',
2012
 
                         str(e))
2013
 
 
2014
 
    def test_empy_commit(self):
2015
 
        branch, tt = self.get_branch_and_transform()
2016
 
        rev = tt.commit(branch, 'my message')
2017
 
        self.assertEqual(2, branch.revno())
2018
 
        repo = branch.repository
2019
 
        self.assertEqual('my message', repo.get_revision(rev).message)
2020
 
 
2021
 
    def test_merge_parents(self):
2022
 
        branch, tt = self.get_branch_and_transform()
2023
 
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2024
 
        self.assertEqual(['rev1b', 'rev1c'],
2025
 
                         branch.basis_tree().get_parent_ids()[1:])
2026
 
 
2027
 
    def test_first_commit(self):
2028
 
        branch = self.make_branch('branch')
2029
 
        branch.lock_write()
2030
 
        self.addCleanup(branch.unlock)
2031
 
        tt = TransformPreview(branch.basis_tree())
2032
 
        self.addCleanup(tt.finalize)
2033
 
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2034
 
        rev = tt.commit(branch, 'my message')
2035
 
        self.assertEqual([], branch.basis_tree().get_parent_ids())
2036
 
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2037
 
                            branch.last_revision())
2038
 
 
2039
 
    def test_first_commit_with_merge_parents(self):
2040
 
        branch = self.make_branch('branch')
2041
 
        branch.lock_write()
2042
 
        self.addCleanup(branch.unlock)
2043
 
        tt = TransformPreview(branch.basis_tree())
2044
 
        self.addCleanup(tt.finalize)
2045
 
        e = self.assertRaises(ValueError, tt.commit, branch,
2046
 
                          'my message', ['rev1b-id'])
2047
 
        self.assertEqual('Cannot supply merge parents for first commit.',
2048
 
                         str(e))
2049
 
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2050
 
 
2051
 
    def test_add_files(self):
2052
 
        branch, tt = self.get_branch_and_transform()
2053
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2054
 
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2055
 
        if SymlinkFeature.available():
2056
 
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2057
 
        rev = tt.commit(branch, 'message')
2058
 
        tree = branch.basis_tree()
2059
 
        self.assertEqual('file', tree.id2path('file-id'))
2060
 
        self.assertEqual('contents', tree.get_file_text('file-id'))
2061
 
        self.assertEqual('dir', tree.id2path('dir-id'))
2062
 
        if SymlinkFeature.available():
2063
 
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2064
 
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2065
 
 
2066
 
    def test_add_unversioned(self):
2067
 
        branch, tt = self.get_branch_and_transform()
2068
 
        tt.new_file('file', tt.root, 'contents')
2069
 
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2070
 
                          'message', strict=True)
2071
 
 
2072
 
    def test_modify_strict(self):
2073
 
        branch, tt = self.get_branch_and_transform()
2074
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2075
 
        tt.commit(branch, 'message', strict=True)
2076
 
        tt = TransformPreview(branch.basis_tree())
2077
 
        self.addCleanup(tt.finalize)
2078
 
        trans_id = tt.trans_id_file_id('file-id')
2079
 
        tt.delete_contents(trans_id)
2080
 
        tt.create_file('contents', trans_id)
2081
 
        tt.commit(branch, 'message', strict=True)
2082
 
 
2083
 
    def test_commit_malformed(self):
2084
 
        """Committing a malformed transform should raise an exception.
2085
 
 
2086
 
        In this case, we are adding a file without adding its parent.
2087
 
        """
2088
 
        branch, tt = self.get_branch_and_transform()
2089
 
        parent_id = tt.trans_id_file_id('parent-id')
2090
 
        tt.new_file('file', parent_id, 'contents', 'file-id')
2091
 
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2092
 
                          'message')
2093
 
 
2094
 
 
2095
1858
class MockTransform(object):
2096
1859
 
2097
1860
    def has_named_child(self, by_parent, parent_id, name):
2264
2027
    def create_tree(self):
2265
2028
        tree = self.make_branch_and_tree('.')
2266
2029
        self.build_tree_contents([('a', 'content 1')])
2267
 
        tree.set_root_id('TREE_ROOT')
2268
2030
        tree.add('a', 'a-id')
2269
2031
        tree.commit('rev1', rev_id='rev1')
2270
2032
        return tree.branch.repository.revision_tree('rev1')
2371
2133
    def test_ignore_pb(self):
2372
2134
        # pb could be supported, but TT.iter_changes doesn't support it.
2373
2135
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2374
 
        preview_tree.iter_changes(revision_tree)
 
2136
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2375
2137
 
2376
2138
    def test_kind(self):
2377
2139
        revision_tree = self.create_tree()
2392
2154
        self.assertEqual(os.stat(limbo_path).st_mtime,
2393
2155
                         preview_tree.get_file_mtime('file-id'))
2394
2156
 
2395
 
    def test_get_file_mtime_renamed(self):
2396
 
        work_tree = self.make_branch_and_tree('tree')
2397
 
        self.build_tree(['tree/file'])
2398
 
        work_tree.add('file', 'file-id')
2399
 
        preview = TransformPreview(work_tree)
2400
 
        self.addCleanup(preview.finalize)
2401
 
        file_trans_id = preview.trans_id_tree_file_id('file-id')
2402
 
        preview.adjust_path('renamed', preview.root, file_trans_id)
2403
 
        preview_tree = preview.get_preview_tree()
2404
 
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2405
 
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
2406
 
 
2407
2157
    def test_get_file(self):
2408
2158
        preview = self.get_empty_preview()
2409
2159
        preview.new_file('file', preview.root, 'contents', 'file-id')
2557
2307
        self.assertEqual(('missing', None, None, None), summary)
2558
2308
 
2559
2309
    def test_file_content_summary_executable(self):
 
2310
        if not osutils.supports_executable():
 
2311
            raise TestNotApplicable()
2560
2312
        preview = self.get_empty_preview()
2561
2313
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2562
2314
        preview.set_executability(True, path_id)
2571
2323
        self.assertIs(None, summary[3])
2572
2324
 
2573
2325
    def test_change_executability(self):
 
2326
        if not osutils.supports_executable():
 
2327
            raise TestNotApplicable()
2574
2328
        tree = self.make_branch_and_tree('tree')
2575
2329
        self.build_tree(['tree/path'])
2576
2330
        tree.add('path')
2590
2344
        # size must be known
2591
2345
        self.assertEqual(len('contents'), summary[1])
2592
2346
        # not executable
2593
 
        self.assertEqual(False, summary[2])
 
2347
        if osutils.supports_executable():
 
2348
            self.assertEqual(False, summary[2])
 
2349
        else:
 
2350
            self.assertEqual(None, summary[2])
2594
2351
        # will not have hash (not cheap to determine)
2595
2352
        self.assertIs(None, summary[3])
2596
2353
 
2737
2494
 
2738
2495
    def test_walkdirs(self):
2739
2496
        preview = self.get_empty_preview()
2740
 
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2741
 
        # FIXME: new_directory should mark root.
2742
 
        preview.fixup_new_roots()
 
2497
        preview.version_file('tree-root', preview.root)
2743
2498
        preview_tree = preview.get_preview_tree()
2744
2499
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2745
2500
                                         'a-id')
2776
2531
        self.addCleanup(work_tree.unlock)
2777
2532
        preview = TransformPreview(work_tree)
2778
2533
        self.addCleanup(preview.finalize)
 
2534
        preview_tree = preview.get_preview_tree()
2779
2535
        file_trans_id = preview.trans_id_file_id('file-id')
2780
2536
        preview.delete_contents(file_trans_id)
2781
2537
        preview.create_file('a\nb\n', file_trans_id)
2782
 
        preview_tree = preview.get_preview_tree()
2783
 
        merger = Merger.from_revision_ids(None, preview_tree,
 
2538
        pb = progress.DummyProgress()
 
2539
        merger = Merger.from_revision_ids(pb, preview_tree,
2784
2540
                                          child_tree.branch.last_revision(),
2785
2541
                                          other_branch=child_tree.branch,
2786
2542
                                          tree_branch=work_tree.branch)
2792
2548
 
2793
2549
    def test_merge_preview_into_workingtree(self):
2794
2550
        tree = self.make_branch_and_tree('tree')
2795
 
        tree.set_root_id('TREE_ROOT')
2796
2551
        tt = TransformPreview(tree)
2797
2552
        self.addCleanup(tt.finalize)
2798
2553
        tt.new_file('name', tt.root, 'content', 'file-id')
2799
2554
        tree2 = self.make_branch_and_tree('tree2')
2800
 
        tree2.set_root_id('TREE_ROOT')
 
2555
        pb = progress.DummyProgress()
2801
2556
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2802
 
                                         None, tree.basis_tree())
 
2557
                                         pb, tree.basis_tree())
2803
2558
        merger.merge_type = Merge3Merger
2804
2559
        merger.do_merge()
2805
2560
 
2815
2570
        tt.create_file('baz', trans_id)
2816
2571
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2817
2572
        self.build_tree_contents([('tree2/foo', 'qux')])
2818
 
        pb = None
 
2573
        pb = progress.DummyProgress()
2819
2574
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2820
2575
                                         pb, tree.basis_tree())
2821
2576
        merger.merge_type = Merge3Merger
2831
2586
                                                           'tree/foo'))
2832
2587
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2833
2588
 
2834
 
    def test_commit_preview_tree(self):
2835
 
        tree = self.make_branch_and_tree('tree')
2836
 
        rev_id = tree.commit('rev1')
2837
 
        tree.branch.lock_write()
2838
 
        self.addCleanup(tree.branch.unlock)
2839
 
        tt = TransformPreview(tree)
2840
 
        tt.new_file('file', tt.root, 'contents', 'file_id')
2841
 
        self.addCleanup(tt.finalize)
2842
 
        preview = tt.get_preview_tree()
2843
 
        preview.set_parent_ids([rev_id])
2844
 
        builder = tree.branch.get_commit_builder([rev_id])
2845
 
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2846
 
        builder.finish_inventory()
2847
 
        rev2_id = builder.commit('rev2')
2848
 
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2849
 
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2850
 
 
2851
 
    def test_ascii_limbo_paths(self):
2852
 
        self.requireFeature(tests.UnicodeFilenameFeature)
2853
 
        branch = self.make_branch('any')
2854
 
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2855
 
        tt = TransformPreview(tree)
2856
 
        self.addCleanup(tt.finalize)
2857
 
        foo_id = tt.new_directory('', ROOT_PARENT)
2858
 
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2859
 
        limbo_path = tt._limbo_name(bar_id)
2860
 
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2861
 
 
2862
2589
 
2863
2590
class FakeSerializer(object):
2864
2591
    """Serializer implementation that simply returns the input.
2959
2686
        self.assertSerializesTo(self.symlink_creation_records(), tt)
2960
2687
 
2961
2688
    def test_deserialize_symlink_creation(self):
2962
 
        self.requireFeature(tests.SymlinkFeature)
2963
2689
        tt = self.get_preview()
2964
2690
        tt.deserialize(iter(self.symlink_creation_records()))
2965
 
        abspath = tt._limbo_name('new-1')
2966
 
        foo_content = osutils.readlink(abspath)
 
2691
        # XXX readlink should be returning unicode, not utf-8
 
2692
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
2967
2693
        self.assertEqual(u'bar\u1234', foo_content)
2968
2694
 
2969
2695
    def make_destruction_preview(self):