~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Mark Hammond
  • Date: 2009-01-12 01:55:34 UTC
  • mto: (3995.8.2 prepare-1.12)
  • mto: This revision was merged to the branch mainline in revision 4007.
  • Revision ID: mhammond@skippinet.com.au-20090112015534-yfxg50p7mpds9j4v
Include all .html files from the tortoise doc directory.

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)
50
48
    TestCaseInTempDir,
51
49
    TestSkipped,
52
50
    )
53
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
54
 
                              resolve_conflicts, cook_conflicts,
 
51
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
 
52
                              resolve_conflicts, cook_conflicts, 
55
53
                              build_tree, get_backup_name,
56
54
                              _FileMover, resolve_checkout,
57
55
                              TransformPreview, create_from_tree)
 
56
from bzrlib.util import bencode
58
57
 
59
58
 
60
59
class TestTreeTransform(tests.TestCaseWithTransport):
130
129
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
131
130
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
132
131
        self.assertEqual(len(modified_paths), 3)
133
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in
 
132
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
134
133
                          ('ozzie', 'my_pretties', 'my_pretties2')]
135
134
        self.assertSubset(tree_mod_paths, modified_paths)
136
135
        # is it safe to finalize repeatedly?
137
136
        transform.finalize()
138
137
        transform.finalize()
139
138
 
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
139
    def test_hardlink(self):
194
140
        self.requireFeature(HardlinkFeature)
195
141
        transform, root = self.get_transform()
209
155
        transform, root = self.get_transform()
210
156
        self.wt.lock_tree_write()
211
157
        self.addCleanup(self.wt.unlock)
212
 
        trans_id = transform.new_file('name', root, 'contents',
 
158
        trans_id = transform.new_file('name', root, 'contents', 
213
159
                                      'my_pretties', True)
214
160
        oz = transform.new_directory('oz', root, 'oz-id')
215
161
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
216
 
        toto = transform.new_file('toto', dorothy, 'toto-contents',
 
162
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
217
163
                                  'toto-id', False)
218
164
 
219
165
        self.assertEqual(len(transform.find_conflicts()), 0)
243
189
 
244
190
    def test_conflicts(self):
245
191
        transform, root = self.get_transform()
246
 
        trans_id = transform.new_file('name', root, 'contents',
 
192
        trans_id = transform.new_file('name', root, 'contents', 
247
193
                                      'my_pretties')
248
194
        self.assertEqual(len(transform.find_conflicts()), 0)
249
195
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
250
 
        self.assertEqual(transform.find_conflicts(),
 
196
        self.assertEqual(transform.find_conflicts(), 
251
197
                         [('duplicate', trans_id, trans_id2, 'name')])
252
198
        self.assertRaises(MalformedTransform, transform.apply)
253
199
        transform.adjust_path('name', trans_id, trans_id2)
254
 
        self.assertEqual(transform.find_conflicts(),
 
200
        self.assertEqual(transform.find_conflicts(), 
255
201
                         [('non-directory parent', trans_id)])
256
202
        tinman_id = transform.trans_id_tree_path('tinman')
257
203
        transform.adjust_path('name', tinman_id, trans_id2)
258
 
        self.assertEqual(transform.find_conflicts(),
259
 
                         [('unversioned parent', tinman_id),
 
204
        self.assertEqual(transform.find_conflicts(), 
 
205
                         [('unversioned parent', tinman_id), 
260
206
                          ('missing parent', tinman_id)])
261
207
        lion_id = transform.create_path('lion', root)
262
 
        self.assertEqual(transform.find_conflicts(),
263
 
                         [('unversioned parent', tinman_id),
 
208
        self.assertEqual(transform.find_conflicts(), 
 
209
                         [('unversioned parent', tinman_id), 
264
210
                          ('missing parent', tinman_id)])
265
211
        transform.adjust_path('name', lion_id, trans_id2)
266
 
        self.assertEqual(transform.find_conflicts(),
 
212
        self.assertEqual(transform.find_conflicts(), 
267
213
                         [('unversioned parent', lion_id),
268
214
                          ('missing parent', lion_id)])
269
215
        transform.version_file("Courage", lion_id)
270
 
        self.assertEqual(transform.find_conflicts(),
271
 
                         [('missing parent', lion_id),
 
216
        self.assertEqual(transform.find_conflicts(), 
 
217
                         [('missing parent', lion_id), 
272
218
                          ('versioning no contents', lion_id)])
273
219
        transform.adjust_path('name2', root, trans_id2)
274
 
        self.assertEqual(transform.find_conflicts(),
 
220
        self.assertEqual(transform.find_conflicts(), 
275
221
                         [('versioning no contents', lion_id)])
276
222
        transform.create_file('Contents, okay?', lion_id)
277
223
        transform.adjust_path('name2', trans_id2, trans_id2)
278
 
        self.assertEqual(transform.find_conflicts(),
279
 
                         [('parent loop', trans_id2),
 
224
        self.assertEqual(transform.find_conflicts(), 
 
225
                         [('parent loop', trans_id2), 
280
226
                          ('non-directory parent', trans_id2)])
281
227
        transform.adjust_path('name2', root, trans_id2)
282
228
        oz_id = transform.new_directory('oz', root)
283
229
        transform.set_executability(True, oz_id)
284
 
        self.assertEqual(transform.find_conflicts(),
 
230
        self.assertEqual(transform.find_conflicts(), 
285
231
                         [('unversioned executability', oz_id)])
286
232
        transform.version_file('oz-id', oz_id)
287
 
        self.assertEqual(transform.find_conflicts(),
 
233
        self.assertEqual(transform.find_conflicts(), 
288
234
                         [('non-file executability', oz_id)])
289
235
        transform.set_executability(None, oz_id)
290
236
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
299
245
        self.assert_('oz/tip' in transform2._tree_path_ids)
300
246
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
301
247
        self.assertEqual(len(result), 2)
302
 
        self.assertEqual((result[0][0], result[0][1]),
 
248
        self.assertEqual((result[0][0], result[0][1]), 
303
249
                         ('duplicate', newtip))
304
 
        self.assertEqual((result[1][0], result[1][2]),
 
250
        self.assertEqual((result[1][0], result[1][2]), 
305
251
                         ('duplicate id', newtip))
306
252
        transform2.finalize()
307
253
        transform3 = TreeTransform(self.wt)
308
254
        self.addCleanup(transform3.finalize)
309
255
        oz_id = transform3.trans_id_tree_file_id('oz-id')
310
256
        transform3.delete_contents(oz_id)
311
 
        self.assertEqual(transform3.find_conflicts(),
 
257
        self.assertEqual(transform3.find_conflicts(), 
312
258
                         [('missing parent', oz_id)])
313
259
        root_id = transform3.root
314
260
        tip_id = transform3.trans_id_tree_file_id('tip-id')
423
369
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
424
370
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
425
371
 
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
372
    def test_add_del(self):
439
373
        start, root = self.get_transform()
440
374
        start.new_directory('a', root, 'a')
453
387
        self.addCleanup(unversion.finalize)
454
388
        parent = unversion.trans_id_tree_path('parent')
455
389
        unversion.unversion_file(parent)
456
 
        self.assertEqual(unversion.find_conflicts(),
 
390
        self.assertEqual(unversion.find_conflicts(), 
457
391
                         [('unversioned parent', parent_id)])
458
392
        file_id = unversion.trans_id_tree_file_id('child-id')
459
393
        unversion.unversion_file(file_id)
479
413
        mangle_tree.adjust_path('name2', root, name1)
480
414
        mangle_tree.adjust_path('name1', root, name2)
481
415
 
482
 
        #tests for deleting parent directories
 
416
        #tests for deleting parent directories 
483
417
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
484
418
        mangle_tree.delete_contents(ddir)
485
419
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
514
448
        create_tree,root = self.get_transform()
515
449
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
516
450
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
517
 
        create_tree.apply()
 
451
        create_tree.apply()        
518
452
        mangle_tree,root = self.get_transform()
519
453
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
520
454
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
528
462
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
529
463
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
530
464
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
531
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
 
465
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
532
466
                             'test_too_much-id')
533
 
        create_tree.apply()
 
467
        create_tree.apply()        
534
468
        mangle_tree,root = self.get_transform()
535
469
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
536
470
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
537
471
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
538
472
        mangle_tree.adjust_path('selftest', bzrlib, tests)
539
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
 
473
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
540
474
        mangle_tree.set_executability(True, test_too_much)
541
475
        mangle_tree.apply()
542
476
 
543
477
    def test_both_rename3(self):
544
478
        create_tree,root = self.get_transform()
545
479
        tests = create_tree.new_directory('tests', root, 'tests-id')
546
 
        create_tree.new_file('test_too_much.py', tests, 'hello1',
 
480
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
547
481
                             'test_too_much-id')
548
 
        create_tree.apply()
 
482
        create_tree.apply()        
549
483
        mangle_tree,root = self.get_transform()
550
484
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
551
485
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
552
486
        mangle_tree.adjust_path('selftest', root, tests)
553
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
 
487
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
554
488
        mangle_tree.set_executability(True, test_too_much)
555
489
        mangle_tree.apply()
556
490
 
569
503
        newdir = move_id.new_directory('dir', root, 'newdir')
570
504
        move_id.adjust_path('name2', newdir, name1)
571
505
        move_id.apply()
572
 
 
 
506
        
573
507
    def test_replace_dangling_ie(self):
574
508
        create_tree, root = self.get_transform()
575
509
        # prepare tree
591
525
        resolve_conflicts(replace)
592
526
        replace.apply()
593
527
 
594
 
    def _test_symlinks(self, link_name1,link_target1,
595
 
                       link_name2, link_target2):
596
 
 
597
 
        def ozpath(p): return 'oz/' + p
598
 
 
 
528
    def test_symlinks(self):
599
529
        self.requireFeature(SymlinkFeature)
600
 
        transform, root = self.get_transform()
 
530
        transform,root = self.get_transform()
601
531
        oz_id = transform.new_directory('oz', root, 'oz-id')
602
 
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
 
532
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
603
533
                                       '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)
 
534
        wiz_id = transform.create_path('wizard2', oz_id)
 
535
        transform.create_symlink('behind_curtain', wiz_id)
 
536
        transform.version_file('wiz-id2', wiz_id)            
607
537
        transform.set_executability(True, wiz_id)
608
 
        self.assertEqual(transform.find_conflicts(),
 
538
        self.assertEqual(transform.find_conflicts(), 
609
539
                         [('non-file executability', wiz_id)])
610
540
        transform.set_executability(None, wiz_id)
611
541
        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')
 
542
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
 
543
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
 
544
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
 
545
                         'behind_curtain')
 
546
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
 
547
                         'wizard-target')
630
548
 
631
549
    def test_unable_create_symlink(self):
632
550
        def tt_helper():
656
574
        create.apply()
657
575
        conflicts,root = self.get_transform()
658
576
        # set up duplicate entry, duplicate id
659
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
 
577
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
660
578
                                         'dorothy-id')
661
579
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
662
580
        oz = conflicts.trans_id_tree_file_id('oz-id')
686
604
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
687
605
        raw_conflicts = resolve_conflicts(tt)
688
606
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
689
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
 
607
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
690
608
                                   'dorothy', None, 'dorothy-id')
691
609
        self.assertEqual(cooked_conflicts[0], duplicate)
692
 
        duplicate_id = DuplicateID('Unversioned existing file',
 
610
        duplicate_id = DuplicateID('Unversioned existing file', 
693
611
                                   'dorothy.moved', 'dorothy', None,
694
612
                                   'dorothy-id')
695
613
        self.assertEqual(cooked_conflicts[1], duplicate_id)
703
621
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
704
622
                                               'oz-id')
705
623
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
706
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
 
624
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
707
625
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
708
626
        self.assertEqual(cooked_conflicts[4], deleted_parent)
709
627
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
812
730
        create.apply()
813
731
        transform, root = self.get_transform()
814
732
        transform.adjust_root_path('oldroot', fun)
815
 
        new_root = transform.trans_id_tree_path('')
 
733
        new_root=transform.trans_id_tree_path('')
816
734
        transform.version_file('new-root', new_root)
817
735
        transform.apply()
818
736
 
832
750
 
833
751
    def test_set_executability_order(self):
834
752
        """Ensure that executability behaves the same, no matter what order.
835
 
 
 
753
        
836
754
        - create file and set executability simultaneously
837
755
        - create file and set executability afterward
838
756
        - unsetting the executability of a file whose executability has not been
1492
1410
        # textual merge
1493
1411
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1494
1412
        # three-way text conflict
1495
 
        self.assertEqual(this.wt.get_file('b').read(),
 
1413
        self.assertEqual(this.wt.get_file('b').read(), 
1496
1414
                         conflict_text('b', 'b2'))
1497
1415
        # OTHER wins
1498
1416
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1502
1420
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1503
1421
        # No change
1504
1422
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1505
 
        # Correct correct results when THIS == OTHER
 
1423
        # Correct correct results when THIS == OTHER 
1506
1424
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1507
1425
        # Text conflict when THIS & OTHER are text and BASE is dir
1508
 
        self.assertEqual(this.wt.get_file('h').read(),
 
1426
        self.assertEqual(this.wt.get_file('h').read(), 
1509
1427
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1510
1428
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1511
1429
                         '1\n2\n3\n4\n')
1512
1430
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1513
1431
                         'h\ni\nj\nk\n')
1514
1432
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1515
 
        self.assertEqual(this.wt.get_file('i').read(),
 
1433
        self.assertEqual(this.wt.get_file('i').read(), 
1516
1434
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1517
1435
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1518
1436
                         '1\n2\n3\n4\n')
1542
1460
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1543
1461
            tg.tt.new_file('c', tg.root, 'c', 'c')
1544
1462
            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'),
 
1463
        targets = ((base, 'base-e', 'base-f', None, None), 
 
1464
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
1547
1465
                   (other, 'other-e', None, 'other-g', 'other-h'))
1548
1466
        for tg, e_target, f_target, g_target, h_target in targets:
1549
 
            for link, target in (('e', e_target), ('f', f_target),
 
1467
            for link, target in (('e', e_target), ('f', f_target), 
1550
1468
                                 ('g', g_target), ('h', h_target)):
1551
1469
                if target is not None:
1552
1470
                    tg.tt.new_symlink(link, tg.root, target, link)
1578
1496
        base = TransformGroup("BASE", root_id)
1579
1497
        this = TransformGroup("THIS", root_id)
1580
1498
        other = TransformGroup("OTHER", root_id)
1581
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
 
1499
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1582
1500
                                   for t in [base, this, other]]
1583
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
 
1501
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1584
1502
                                   for t in [base, this, other]]
1585
1503
        base.tt.new_directory('c', base_a, 'c')
1586
1504
        this.tt.new_directory('c1', this_a, 'c')
1611
1529
        base = TransformGroup("BASE", root_id)
1612
1530
        this = TransformGroup("THIS", root_id)
1613
1531
        other = TransformGroup("OTHER", root_id)
1614
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
 
1532
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1615
1533
                                   for t in [base, this, other]]
1616
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
 
1534
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1617
1535
                                   for t in [base, this, other]]
1618
1536
 
1619
1537
        base.tt.new_file('g', base_a, 'g', 'g')
1710
1628
        os.symlink('foo', 'target2/symlink')
1711
1629
        build_tree(source.basis_tree(), target)
1712
1630
        self.assertEqual([], target.conflicts())
1713
 
 
 
1631
        
1714
1632
    def test_directory_conflict_handling(self):
1715
1633
        """Ensure that when building trees, conflict handling is done"""
1716
1634
        source = self.make_branch_and_tree('source')
1772
1690
        target = self.make_branch_and_tree('target')
1773
1691
        self.build_tree(['target/name'])
1774
1692
        target.add('name')
1775
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
 
1693
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
1776
1694
            build_tree, source.basis_tree(), target)
1777
1695
 
1778
1696
    def test_build_tree_rename_count(self):
1922
1840
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1923
1841
        self.assertTrue(source.is_executable('file1-id'))
1924
1842
 
1925
 
    def install_rot13_content_filter(self, pattern):
1926
 
        original_registry = filters._reset_registry()
1927
 
        def restore_registry():
1928
 
            filters._reset_registry(original_registry)
1929
 
        self.addCleanup(restore_registry)
1930
 
        def rot13(chunks, context=None):
1931
 
            return [''.join(chunks).encode('rot13')]
1932
 
        rot13filter = filters.ContentFilter(rot13, rot13)
1933
 
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1934
 
        os.mkdir(self.test_home_dir + '/.bazaar')
1935
 
        rules_filename = self.test_home_dir + '/.bazaar/rules'
1936
 
        f = open(rules_filename, 'wb')
1937
 
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
1938
 
        f.close()
1939
 
        def uninstall_rules():
1940
 
            os.remove(rules_filename)
1941
 
            rules.reset_rules()
1942
 
        self.addCleanup(uninstall_rules)
1943
 
        rules.reset_rules()
1944
 
 
1945
 
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
1946
 
        """build_tree will not hardlink files that have content filtering rules
1947
 
        applied to them (but will still hardlink other files from the same tree
1948
 
        if it can).
1949
 
        """
1950
 
        self.requireFeature(HardlinkFeature)
1951
 
        self.install_rot13_content_filter('file1')
1952
 
        source = self.create_ab_tree()
1953
 
        target = self.make_branch_and_tree('target')
1954
 
        revision_tree = source.basis_tree()
1955
 
        revision_tree.lock_read()
1956
 
        self.addCleanup(revision_tree.unlock)
1957
 
        build_tree(revision_tree, target, source, hardlink=True)
1958
 
        target.lock_read()
1959
 
        self.addCleanup(target.unlock)
1960
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1961
 
        source_stat = os.stat('source/file1')
1962
 
        target_stat = os.stat('target/file1')
1963
 
        self.assertNotEqual(source_stat, target_stat)
1964
 
        source_stat = os.stat('source/file2')
1965
 
        target_stat = os.stat('target/file2')
1966
 
        self.assertEqualStat(source_stat, target_stat)
1967
 
 
1968
1843
    def test_case_insensitive_build_tree_inventory(self):
1969
 
        if (tests.CaseInsensitiveFilesystemFeature.available()
1970
 
            or tests.CaseInsCasePresFilenameFeature.available()):
1971
 
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1972
1844
        source = self.make_branch_and_tree('source')
1973
1845
        self.build_tree(['source/file', 'source/FILE'])
1974
1846
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1982
1854
        self.assertEqual('FILE', target.id2path('upper-id'))
1983
1855
 
1984
1856
 
1985
 
class TestCommitTransform(tests.TestCaseWithTransport):
1986
 
 
1987
 
    def get_branch(self):
1988
 
        tree = self.make_branch_and_tree('tree')
1989
 
        tree.lock_write()
1990
 
        self.addCleanup(tree.unlock)
1991
 
        tree.commit('empty commit')
1992
 
        return tree.branch
1993
 
 
1994
 
    def get_branch_and_transform(self):
1995
 
        branch = self.get_branch()
1996
 
        tt = TransformPreview(branch.basis_tree())
1997
 
        self.addCleanup(tt.finalize)
1998
 
        return branch, tt
1999
 
 
2000
 
    def test_commit_wrong_basis(self):
2001
 
        branch = self.get_branch()
2002
 
        basis = branch.repository.revision_tree(
2003
 
            _mod_revision.NULL_REVISION)
2004
 
        tt = TransformPreview(basis)
2005
 
        self.addCleanup(tt.finalize)
2006
 
        e = self.assertRaises(ValueError, tt.commit, branch, '')
2007
 
        self.assertEqual('TreeTransform not based on branch basis: null:',
2008
 
                         str(e))
2009
 
 
2010
 
    def test_empy_commit(self):
2011
 
        branch, tt = self.get_branch_and_transform()
2012
 
        rev = tt.commit(branch, 'my message')
2013
 
        self.assertEqual(2, branch.revno())
2014
 
        repo = branch.repository
2015
 
        self.assertEqual('my message', repo.get_revision(rev).message)
2016
 
 
2017
 
    def test_merge_parents(self):
2018
 
        branch, tt = self.get_branch_and_transform()
2019
 
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2020
 
        self.assertEqual(['rev1b', 'rev1c'],
2021
 
                         branch.basis_tree().get_parent_ids()[1:])
2022
 
 
2023
 
    def test_first_commit(self):
2024
 
        branch = self.make_branch('branch')
2025
 
        branch.lock_write()
2026
 
        self.addCleanup(branch.unlock)
2027
 
        tt = TransformPreview(branch.basis_tree())
2028
 
        self.addCleanup(tt.finalize)
2029
 
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2030
 
        rev = tt.commit(branch, 'my message')
2031
 
        self.assertEqual([], branch.basis_tree().get_parent_ids())
2032
 
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2033
 
                            branch.last_revision())
2034
 
 
2035
 
    def test_first_commit_with_merge_parents(self):
2036
 
        branch = self.make_branch('branch')
2037
 
        branch.lock_write()
2038
 
        self.addCleanup(branch.unlock)
2039
 
        tt = TransformPreview(branch.basis_tree())
2040
 
        self.addCleanup(tt.finalize)
2041
 
        e = self.assertRaises(ValueError, tt.commit, branch,
2042
 
                          'my message', ['rev1b-id'])
2043
 
        self.assertEqual('Cannot supply merge parents for first commit.',
2044
 
                         str(e))
2045
 
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2046
 
 
2047
 
    def test_add_files(self):
2048
 
        branch, tt = self.get_branch_and_transform()
2049
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2050
 
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2051
 
        if SymlinkFeature.available():
2052
 
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2053
 
        rev = tt.commit(branch, 'message')
2054
 
        tree = branch.basis_tree()
2055
 
        self.assertEqual('file', tree.id2path('file-id'))
2056
 
        self.assertEqual('contents', tree.get_file_text('file-id'))
2057
 
        self.assertEqual('dir', tree.id2path('dir-id'))
2058
 
        if SymlinkFeature.available():
2059
 
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2060
 
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2061
 
 
2062
 
    def test_add_unversioned(self):
2063
 
        branch, tt = self.get_branch_and_transform()
2064
 
        tt.new_file('file', tt.root, 'contents')
2065
 
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2066
 
                          'message', strict=True)
2067
 
 
2068
 
    def test_modify_strict(self):
2069
 
        branch, tt = self.get_branch_and_transform()
2070
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2071
 
        tt.commit(branch, 'message', strict=True)
2072
 
        tt = TransformPreview(branch.basis_tree())
2073
 
        self.addCleanup(tt.finalize)
2074
 
        trans_id = tt.trans_id_file_id('file-id')
2075
 
        tt.delete_contents(trans_id)
2076
 
        tt.create_file('contents', trans_id)
2077
 
        tt.commit(branch, 'message', strict=True)
2078
 
 
2079
 
    def test_commit_malformed(self):
2080
 
        """Committing a malformed transform should raise an exception.
2081
 
 
2082
 
        In this case, we are adding a file without adding its parent.
2083
 
        """
2084
 
        branch, tt = self.get_branch_and_transform()
2085
 
        parent_id = tt.trans_id_file_id('parent-id')
2086
 
        tt.new_file('file', parent_id, 'contents', 'file-id')
2087
 
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2088
 
                          'message')
2089
 
 
2090
 
 
2091
1857
class MockTransform(object):
2092
1858
 
2093
1859
    def has_named_child(self, by_parent, parent_id, name):
2260
2026
    def create_tree(self):
2261
2027
        tree = self.make_branch_and_tree('.')
2262
2028
        self.build_tree_contents([('a', 'content 1')])
2263
 
        tree.set_root_id('TREE_ROOT')
2264
2029
        tree.add('a', 'a-id')
2265
2030
        tree.commit('rev1', rev_id='rev1')
2266
2031
        return tree.branch.repository.revision_tree('rev1')
2388
2153
        self.assertEqual(os.stat(limbo_path).st_mtime,
2389
2154
                         preview_tree.get_file_mtime('file-id'))
2390
2155
 
2391
 
    def test_get_file_mtime_renamed(self):
2392
 
        work_tree = self.make_branch_and_tree('tree')
2393
 
        self.build_tree(['tree/file'])
2394
 
        work_tree.add('file', 'file-id')
2395
 
        preview = TransformPreview(work_tree)
2396
 
        self.addCleanup(preview.finalize)
2397
 
        file_trans_id = preview.trans_id_tree_file_id('file-id')
2398
 
        preview.adjust_path('renamed', preview.root, file_trans_id)
2399
 
        preview_tree = preview.get_preview_tree()
2400
 
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2401
 
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
2402
 
 
2403
2156
    def test_get_file(self):
2404
2157
        preview = self.get_empty_preview()
2405
2158
        preview.new_file('file', preview.root, 'contents', 'file-id')
2553
2306
        self.assertEqual(('missing', None, None, None), summary)
2554
2307
 
2555
2308
    def test_file_content_summary_executable(self):
 
2309
        if not osutils.supports_executable():
 
2310
            raise TestNotApplicable()
2556
2311
        preview = self.get_empty_preview()
2557
2312
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2558
2313
        preview.set_executability(True, path_id)
2567
2322
        self.assertIs(None, summary[3])
2568
2323
 
2569
2324
    def test_change_executability(self):
 
2325
        if not osutils.supports_executable():
 
2326
            raise TestNotApplicable()
2570
2327
        tree = self.make_branch_and_tree('tree')
2571
2328
        self.build_tree(['tree/path'])
2572
2329
        tree.add('path')
2586
2343
        # size must be known
2587
2344
        self.assertEqual(len('contents'), summary[1])
2588
2345
        # not executable
2589
 
        self.assertEqual(False, summary[2])
 
2346
        if osutils.supports_executable():
 
2347
            self.assertEqual(False, summary[2])
 
2348
        else:
 
2349
            self.assertEqual(None, summary[2])
2590
2350
        # will not have hash (not cheap to determine)
2591
2351
        self.assertIs(None, summary[3])
2592
2352
 
2733
2493
 
2734
2494
    def test_walkdirs(self):
2735
2495
        preview = self.get_empty_preview()
2736
 
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2737
 
        # FIXME: new_directory should mark root.
2738
 
        preview.fixup_new_roots()
 
2496
        preview.version_file('tree-root', preview.root)
2739
2497
        preview_tree = preview.get_preview_tree()
2740
2498
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2741
2499
                                         'a-id')
2772
2530
        self.addCleanup(work_tree.unlock)
2773
2531
        preview = TransformPreview(work_tree)
2774
2532
        self.addCleanup(preview.finalize)
 
2533
        preview_tree = preview.get_preview_tree()
2775
2534
        file_trans_id = preview.trans_id_file_id('file-id')
2776
2535
        preview.delete_contents(file_trans_id)
2777
2536
        preview.create_file('a\nb\n', file_trans_id)
2778
2537
        pb = progress.DummyProgress()
2779
 
        preview_tree = preview.get_preview_tree()
2780
2538
        merger = Merger.from_revision_ids(pb, preview_tree,
2781
2539
                                          child_tree.branch.last_revision(),
2782
2540
                                          other_branch=child_tree.branch,
2789
2547
 
2790
2548
    def test_merge_preview_into_workingtree(self):
2791
2549
        tree = self.make_branch_and_tree('tree')
2792
 
        tree.set_root_id('TREE_ROOT')
2793
2550
        tt = TransformPreview(tree)
2794
2551
        self.addCleanup(tt.finalize)
2795
2552
        tt.new_file('name', tt.root, 'content', 'file-id')
2796
2553
        tree2 = self.make_branch_and_tree('tree2')
2797
 
        tree2.set_root_id('TREE_ROOT')
2798
2554
        pb = progress.DummyProgress()
2799
2555
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2800
2556
                                         pb, tree.basis_tree())
2829
2585
                                                           'tree/foo'))
2830
2586
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2831
2587
 
2832
 
    def test_commit_preview_tree(self):
2833
 
        tree = self.make_branch_and_tree('tree')
2834
 
        rev_id = tree.commit('rev1')
2835
 
        tree.branch.lock_write()
2836
 
        self.addCleanup(tree.branch.unlock)
2837
 
        tt = TransformPreview(tree)
2838
 
        tt.new_file('file', tt.root, 'contents', 'file_id')
2839
 
        self.addCleanup(tt.finalize)
2840
 
        preview = tt.get_preview_tree()
2841
 
        preview.set_parent_ids([rev_id])
2842
 
        builder = tree.branch.get_commit_builder([rev_id])
2843
 
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2844
 
        builder.finish_inventory()
2845
 
        rev2_id = builder.commit('rev2')
2846
 
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2847
 
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2848
 
 
2849
 
    def test_ascii_limbo_paths(self):
2850
 
        self.requireFeature(tests.UnicodeFilenameFeature)
2851
 
        branch = self.make_branch('any')
2852
 
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2853
 
        tt = TransformPreview(tree)
2854
 
        self.addCleanup(tt.finalize)
2855
 
        foo_id = tt.new_directory('', ROOT_PARENT)
2856
 
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2857
 
        limbo_path = tt._limbo_name(bar_id)
2858
 
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2859
 
 
2860
2588
 
2861
2589
class FakeSerializer(object):
2862
2590
    """Serializer implementation that simply returns the input.
2957
2685
        self.assertSerializesTo(self.symlink_creation_records(), tt)
2958
2686
 
2959
2687
    def test_deserialize_symlink_creation(self):
2960
 
        self.requireFeature(tests.SymlinkFeature)
2961
2688
        tt = self.get_preview()
2962
2689
        tt.deserialize(iter(self.symlink_creation_records()))
2963
 
        abspath = tt._limbo_name('new-1')
2964
 
        foo_content = osutils.readlink(abspath)
 
2690
        # XXX readlink should be returning unicode, not utf-8
 
2691
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
2965
2692
        self.assertEqual(u'bar\u1234', foo_content)
2966
2693
 
2967
2694
    def make_destruction_preview(self):