/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/workingtree_implementations/test_workingtree.py

First attempt to merge .dev and resolve the conflicts (but tests are 
failing)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
# Authors:  Robert Collins <robert.collins@canonical.com>
 
3
#           and others
3
4
#
4
5
# This program is free software; you can redistribute it and/or modify
5
6
# it under the terms of the GNU General Public License as published by
16
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
 
18
19
from cStringIO import StringIO
 
20
import errno
19
21
import os
20
22
import sys
21
23
 
22
 
import bzrlib
23
 
from bzrlib import branch, bzrdir, errors, osutils, urlutils, workingtree
 
24
from bzrlib import (
 
25
    branch,
 
26
    bzrdir,
 
27
    errors,
 
28
    osutils,
 
29
    tests,
 
30
    urlutils,
 
31
    workingtree,
 
32
    )
24
33
from bzrlib.errors import (NotBranchError, NotVersionedError,
25
34
                           UnsupportedOperation, PathsNotVersionedError)
 
35
from bzrlib.inventory import Inventory
26
36
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
27
37
from bzrlib.tests import TestSkipped
28
38
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
29
39
from bzrlib.trace import mutter
30
40
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
31
 
                                WorkingTree)
 
41
                                WorkingTree, WorkingTree2)
32
42
from bzrlib.conflicts import ConflictList, TextConflict, ContentsConflict
33
43
 
34
44
 
35
 
 
36
45
class TestWorkingTree(TestCaseWithWorkingTree):
37
46
 
38
47
    def test_list_files(self):
40
49
        self.build_tree(['dir/', 'file'])
41
50
        if has_symlinks():
42
51
            os.symlink('target', 'symlink')
 
52
        tree.lock_read()
43
53
        files = list(tree.list_files())
 
54
        tree.unlock()
44
55
        self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
45
56
        self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
46
57
        if has_symlinks():
51
62
        self.build_tree(['dir/', 'file', 'dir/file', 'dir/b',
52
63
                         'dir/subdir/', 'a', 'dir/subfile',
53
64
                         'zz_dir/', 'zz_dir/subfile'])
 
65
        tree.lock_read()
54
66
        files = [(path, kind) for (path, v, kind, file_id, entry)
55
67
                               in tree.list_files()]
 
68
        tree.unlock()
56
69
        self.assertEqual([
57
70
            ('a', 'file'),
58
71
            ('dir', 'directory'),
61
74
            ], files)
62
75
 
63
76
        tree.add(['dir', 'zz_dir'])
 
77
        tree.lock_read()
64
78
        files = [(path, kind) for (path, v, kind, file_id, entry)
65
79
                               in tree.list_files()]
 
80
        tree.unlock()
66
81
        self.assertEqual([
67
82
            ('a', 'file'),
68
83
            ('dir', 'directory'),
75
90
            ('zz_dir/subfile', 'file'),
76
91
            ], files)
77
92
 
 
93
    def test_list_files_kind_change(self):
 
94
        tree = self.make_branch_and_tree('tree')
 
95
        self.build_tree(['tree/filename'])
 
96
        tree.add('filename', 'file-id')
 
97
        os.unlink('tree/filename')
 
98
        self.build_tree(['tree/filename/'])
 
99
        tree.lock_read()
 
100
        self.addCleanup(tree.unlock)
 
101
        result = list(tree.list_files())
 
102
        self.assertEqual(1, len(result))
 
103
        self.assertEqual(('filename', 'V', 'directory', 'file-id'),
 
104
                         result[0][:4])
 
105
 
78
106
    def test_open_containing(self):
79
107
        branch = self.make_branch_and_tree('.').branch
80
108
        local_base = urlutils.local_path_from_url(branch.base)
107
135
        self.assertEqual('foo', relpath)
108
136
        self.assertEqual(wt.basedir + '/', local_base)
109
137
 
110
 
 
111
138
    def test_basic_relpath(self):
112
139
        # for comprehensive relpath tests, see whitebox.py.
113
140
        tree = self.make_branch_and_tree('.')
212
239
        self.log('contents of inventory: %r' % inv.entries())
213
240
 
214
241
        self.check_inventory_shape(inv,
215
 
                                   ['dir', 'dir/sub', 'dir/sub/file'])
216
 
 
 
242
                                   ['dir/', 'dir/sub/', 'dir/sub/file'])
217
243
        wt.rename_one('dir', 'newdir')
218
244
 
219
 
        self.check_inventory_shape(wt.read_working_inventory(),
220
 
                                   ['newdir', 'newdir/sub', 'newdir/sub/file'])
221
 
 
 
245
        wt.lock_read()
 
246
        self.check_inventory_shape(wt.inventory,
 
247
                                   ['newdir/', 'newdir/sub/', 'newdir/sub/file'])
 
248
        wt.unlock()
222
249
        wt.rename_one('newdir/sub', 'newdir/newsub')
223
 
        self.check_inventory_shape(wt.read_working_inventory(),
224
 
                                   ['newdir', 'newdir/newsub',
 
250
        wt.lock_read()
 
251
        self.check_inventory_shape(wt.inventory,
 
252
                                   ['newdir/', 'newdir/newsub/',
225
253
                                    'newdir/newsub/file'])
 
254
        wt.unlock()
226
255
 
227
256
    def test_add_in_unversioned(self):
228
257
        """Try to add a file in an unversioned directory.
269
298
        wt = self.make_branch_and_tree('source')
270
299
        self.assertEqual([], wt.get_parent_ids())
271
300
        wt.commit('A', allow_pointless=True, rev_id='A')
272
 
        self.assertEqual(['A'], wt.get_parent_ids())
 
301
        parent_ids = wt.get_parent_ids()
 
302
        self.assertEqual(['A'], parent_ids)
 
303
        for parent_id in parent_ids:
 
304
            self.assertIsInstance(parent_id, str)
273
305
 
274
306
    def test_set_last_revision(self):
275
307
        wt = self.make_branch_and_tree('source')
276
308
        # set last-revision to one not in the history
277
309
        wt.set_last_revision('A')
278
310
        # set it back to None for an empty tree.
279
 
        wt.set_last_revision(None)
 
311
        wt.set_last_revision('null:')
280
312
        wt.commit('A', allow_pointless=True, rev_id='A')
281
313
        self.assertEqual(['A'], wt.get_parent_ids())
282
314
        # None is aways in the branch
283
 
        wt.set_last_revision(None)
 
315
        wt.set_last_revision('null:')
284
316
        self.assertEqual([], wt.get_parent_ids())
285
317
        # and now we can set it to 'A'
286
318
        # because some formats mutate the branch to set it on the tree
287
319
        # we need to alter the branch to let this pass.
288
 
        wt.branch.set_revision_history(['A', 'B'])
 
320
        try:
 
321
            wt.branch.set_revision_history(['A', 'B'])
 
322
        except errors.NoSuchRevision, e:
 
323
            self.assertEqual('B', e.revision)
 
324
            raise TestSkipped("Branch format does not permit arbitrary"
 
325
                              " history")
289
326
        wt.set_last_revision('A')
290
327
        self.assertEqual(['A'], wt.get_parent_ids())
 
328
        self.assertRaises(errors.ReservedId, wt.set_last_revision, 'A:')
291
329
 
292
330
    def test_set_last_revision_different_to_branch(self):
293
331
        # working tree formats from the meta-dir format and newer support
325
363
 
326
364
    def test_clone_preserves_content(self):
327
365
        wt = self.make_branch_and_tree('source')
328
 
        self.build_tree(['added', 'deleted', 'notadded'], transport=wt.bzrdir.transport.clone('..'))
 
366
        self.build_tree(['added', 'deleted', 'notadded'],
 
367
                        transport=wt.bzrdir.transport.clone('..'))
329
368
        wt.add('deleted', 'deleted')
330
369
        wt.commit('add deleted')
331
370
        wt.remove('deleted')
349
388
        wt.commit('B', rev_id='B')
350
389
        wt.set_parent_ids(['B'])
351
390
        tree = wt.basis_tree()
 
391
        tree.lock_read()
352
392
        self.failUnless(tree.has_filename('bar'))
 
393
        tree.unlock()
353
394
        wt.set_parent_ids(['A'])
354
395
        tree = wt.basis_tree()
 
396
        tree.lock_read()
355
397
        self.failUnless(tree.has_filename('foo'))
 
398
        tree.unlock()
356
399
 
357
400
    def test_clone_tree_revision(self):
358
401
        # make a tree with a last-revision,
413
456
        self.failUnlessExists('checkout/file')
414
457
        self.assertEqual(['A'], old_tree.get_parent_ids())
415
458
 
 
459
    def test_update_sets_root_id(self):
 
460
        """Ensure tree root is set properly by update.
 
461
        
 
462
        Since empty trees don't have root_ids, but workingtrees do,
 
463
        an update of a checkout of revision 0 to a new revision,  should set
 
464
        the root id.
 
465
        """
 
466
        wt = self.make_branch_and_tree('tree')
 
467
        main_branch = wt.branch
 
468
        # create an out of date working tree by making a checkout in this
 
469
        # current format
 
470
        self.build_tree(['checkout/', 'tree/file'])
 
471
        checkout = main_branch.create_checkout('checkout')
 
472
        # now commit to 'tree'
 
473
        wt.add('file')
 
474
        wt.commit('A', rev_id='A')
 
475
        # and update checkout 
 
476
        self.assertEqual(0, checkout.update())
 
477
        self.failUnlessExists('checkout/file')
 
478
        self.assertEqual(wt.get_root_id(), checkout.get_root_id())
 
479
        self.assertNotEqual(None, wt.get_root_id())
 
480
 
416
481
    def test_update_returns_conflict_count(self):
417
482
        # working tree formats from the meta-dir format and newer support
418
483
        # setting the last revision on a tree independently of that on the 
465
530
        open('b1/d', 'wb').write('d test\n')
466
531
        merge_inner(this.branch, other, base, this_tree=this)
467
532
        self.assertNotEqual(open('b1/a', 'rb').read(), 'a test\n')
468
 
        this.revert([])
 
533
        this.revert()
469
534
        self.assertEqual(open('b1/a', 'rb').read(), 'a test\n')
470
535
        self.assertIs(os.path.exists('b1/b.~1~'), True)
471
536
        self.assertIs(os.path.exists('b1/c'), False)
517
582
        self.assertEqual(master_tree.branch.revision_history(),
518
583
            tree.branch.revision_history())
519
584
 
 
585
    def test_merge_modified_detects_corruption(self):
 
586
        # FIXME: This doesn't really test that it works; also this is not
 
587
        # implementation-independent. mbp 20070226
 
588
        tree = self.make_branch_and_tree('master')
 
589
        tree._transport.put_bytes('merge-hashes', 'asdfasdf')
 
590
        self.assertRaises(errors.MergeModifiedFormatError, tree.merge_modified)
 
591
 
520
592
    def test_merge_modified(self):
521
 
        tree = self.make_branch_and_tree('master')
522
 
        tree._control_files.put('merge-hashes', StringIO('asdfasdf'))
523
 
        self.assertRaises(errors.MergeModifiedFormatError, tree.merge_modified)
 
593
        # merge_modified stores a map from file id to hash
 
594
        tree = self.make_branch_and_tree('tree')
 
595
        d = {'file-id': osutils.sha_string('hello')}
 
596
        self.build_tree_contents([('tree/somefile', 'hello')])
 
597
        tree.lock_write()
 
598
        try:
 
599
            tree.add(['somefile'], ['file-id'])
 
600
            tree.set_merge_modified(d)
 
601
            mm = tree.merge_modified()
 
602
            self.assertEquals(mm, d)
 
603
        finally:
 
604
            tree.unlock()
 
605
        mm = tree.merge_modified()
 
606
        self.assertEquals(mm, d)
524
607
 
525
608
    def test_conflicts(self):
526
609
        from bzrlib.tests.test_conflicts import example_conflicts
532
615
            
533
616
        tree2 = WorkingTree.open('master')
534
617
        self.assertEqual(tree2.conflicts(), example_conflicts)
535
 
        tree2._control_files.put('conflicts', StringIO(''))
536
 
        self.assertRaises(errors.ConflictFormatError, 
 
618
        tree2._transport.put_bytes('conflicts', '')
 
619
        self.assertRaises(errors.ConflictFormatError,
537
620
                          tree2.conflicts)
538
 
        tree2._control_files.put('conflicts', StringIO('a'))
539
 
        self.assertRaises(errors.ConflictFormatError, 
 
621
        tree2._transport.put_bytes('conflicts', 'a')
 
622
        self.assertRaises(errors.ConflictFormatError,
540
623
                          tree2.conflicts)
541
624
 
542
625
    def make_merge_conflicts(self):
543
 
        from bzrlib.merge import merge_inner 
 
626
        from bzrlib.merge import merge_inner
544
627
        tree = self.make_branch_and_tree('mine')
545
628
        file('mine/bloo', 'wb').write('one')
546
 
        tree.add('bloo')
547
629
        file('mine/blo', 'wb').write('on')
548
 
        tree.add('blo')
 
630
        tree.add(['bloo', 'blo'])
549
631
        tree.commit("blah", allow_pointless=False)
550
 
        base = tree.basis_tree()
 
632
        base = tree.branch.repository.revision_tree(tree.last_revision())
551
633
        bzrdir.BzrDir.open("mine").sprout("other")
552
634
        file('other/bloo', 'wb').write('two')
553
635
        othertree = WorkingTree.open('other')
567
649
        try:
568
650
            tree.set_conflicts(ConflictList())
569
651
        except UnsupportedOperation:
570
 
            raise TestSkipped
 
652
            raise TestSkipped('unsupported operation')
571
653
        self.assertEqual(tree.conflicts(), ConflictList())
572
654
 
573
655
    def test_add_conflicts(self):
575
657
        try:
576
658
            tree.add_conflicts([TextConflict('path_a')])
577
659
        except UnsupportedOperation:
578
 
            raise TestSkipped()
 
660
            raise TestSkipped('unsupported operation')
579
661
        self.assertEqual(ConflictList([TextConflict('path_a')]),
580
662
                         tree.conflicts())
581
663
        tree.add_conflicts([TextConflict('path_a')])
602
684
    def test_revert_clear_conflicts2(self):
603
685
        tree = self.make_merge_conflicts()
604
686
        self.assertEqual(len(tree.conflicts()), 1)
605
 
        tree.revert([])
 
687
        tree.revert()
606
688
        self.assertEqual(len(tree.conflicts()), 0)
607
689
 
608
690
    def test_format_description(self):
625
707
        # ensure that foo.pyc is ignored
626
708
        self.build_tree_contents([('.bzrignore', 'foo.pyc')])
627
709
        tree.add('foo.pyc', 'anid')
 
710
        tree.lock_read()
628
711
        files = sorted(list(tree.list_files()))
 
712
        tree.unlock()
629
713
        self.assertEqual((u'.bzrignore', '?', 'file', None), files[0][:-1])
630
714
        self.assertEqual((u'foo.pyc', 'V', 'file', 'anid'), files[1][:-1])
631
715
        self.assertEqual(2, len(files))
640
724
        osutils.normalized_filename = osutils._accessible_normalized_filename
641
725
        try:
642
726
            tree.add([u'a\u030a'])
 
727
            tree.lock_read()
643
728
            self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
644
729
                    [(path, ie.kind) for path,ie in 
645
730
                                tree.inventory.iter_entries()])
 
731
            tree.unlock()
646
732
        finally:
647
733
            osutils.normalized_filename = orig
648
734
 
659
745
                tree.add, [u'a\u030a'])
660
746
        finally:
661
747
            osutils.normalized_filename = orig
 
748
 
 
749
    def test__write_inventory(self):
 
750
        # The private interface _write_inventory is currently used by transform.
 
751
        tree = self.make_branch_and_tree('.')
 
752
        # if we write write an inventory then do a walkdirs we should get back
 
753
        # missing entries, and actual, and unknowns as appropriate.
 
754
        self.build_tree(['present', 'unknown'])
 
755
        inventory = Inventory(tree.get_root_id())
 
756
        inventory.add_path('missing', 'file', 'missing-id')
 
757
        inventory.add_path('present', 'file', 'present-id')
 
758
        # there is no point in being able to write an inventory to an unlocked
 
759
        # tree object - its a low level api not a convenience api.
 
760
        tree.lock_write()
 
761
        tree._write_inventory(inventory)
 
762
        tree.unlock()
 
763
        tree.lock_read()
 
764
        try:
 
765
            present_stat = os.lstat('present')
 
766
            unknown_stat = os.lstat('unknown')
 
767
            expected_results = [
 
768
                (('', tree.get_root_id()),
 
769
                 [('missing', 'missing', 'unknown', None, 'missing-id', 'file'),
 
770
                  ('present', 'present', 'file', present_stat, 'present-id', 'file'),
 
771
                  ('unknown', 'unknown', 'file', unknown_stat, None, None),
 
772
                 ]
 
773
                )]
 
774
            self.assertEqual(expected_results, list(tree.walkdirs()))
 
775
        finally:
 
776
            tree.unlock()
 
777
 
 
778
    def test_path2id(self):
 
779
        # smoke test for path2id
 
780
        tree = self.make_branch_and_tree('.')
 
781
        self.build_tree(['foo'])
 
782
        tree.add(['foo'], ['foo-id'])
 
783
        self.assertEqual('foo-id', tree.path2id('foo'))
 
784
        # the next assertion is for backwards compatability with WorkingTree3,
 
785
        # though its probably a bad idea, it makes things work. Perhaps
 
786
        # it should raise a deprecation warning?
 
787
        self.assertEqual('foo-id', tree.path2id('foo/'))
 
788
 
 
789
    def test_filter_unversioned_files(self):
 
790
        # smoke test for filter_unversioned_files
 
791
        tree = self.make_branch_and_tree('.')
 
792
        paths = ['here-and-versioned', 'here-and-not-versioned',
 
793
            'not-here-and-versioned', 'not-here-and-not-versioned']
 
794
        tree.add(['here-and-versioned', 'not-here-and-versioned'],
 
795
            kinds=['file', 'file'])
 
796
        self.build_tree(['here-and-versioned', 'here-and-not-versioned'])
 
797
        tree.lock_read()
 
798
        self.addCleanup(tree.unlock)
 
799
        self.assertEqual(
 
800
            set(['not-here-and-not-versioned', 'here-and-not-versioned']),
 
801
            tree.filter_unversioned_files(paths))
 
802
 
 
803
    def test_detect_real_kind(self):
 
804
        # working trees report the real kind of the file on disk, not the kind
 
805
        # they had when they were first added
 
806
        # create one file of every interesting type
 
807
        tree = self.make_branch_and_tree('.')
 
808
        self.build_tree(['file', 'directory/'])
 
809
        names = ['file', 'directory']
 
810
        if has_symlinks():
 
811
            os.symlink('target', 'symlink')
 
812
            names.append('symlink')
 
813
        tree.add(names, [n + '-id' for n in names])
 
814
        if tree.supports_tree_reference():
 
815
            sub_tree = self.make_branch_and_tree('tree-reference')
 
816
            sub_tree.set_root_id('tree-reference-id')
 
817
            sub_tree.commit('message')
 
818
            names.append('tree-reference')
 
819
            tree.add_reference(sub_tree)
 
820
        # now when we first look, we should see everything with the same kind
 
821
        # with which they were initially added
 
822
        for n in names:
 
823
            actual_kind = tree.kind(n + '-id')
 
824
            self.assertEqual(n, actual_kind)
 
825
        # move them around so the names no longer correspond to the types
 
826
        os.rename(names[0], 'tmp')
 
827
        for i in range(1, len(names)):
 
828
            os.rename(names[i], names[i-1])
 
829
        os.rename('tmp', names[-1])
 
830
        # now look and expect to see the correct types again
 
831
        for i in range(len(names)):
 
832
            actual_kind = tree.kind(names[i-1] + '-id')
 
833
            expected_kind = names[i]
 
834
            self.assertEqual(expected_kind, actual_kind)
 
835
 
 
836
    def test_stored_kind_with_missing(self):
 
837
        tree = self.make_branch_and_tree('tree')
 
838
        tree.lock_write()
 
839
        self.addCleanup(tree.unlock)
 
840
        self.build_tree(['tree/a', 'tree/b/'])
 
841
        tree.add(['a', 'b'], ['a-id', 'b-id'])
 
842
        os.unlink('tree/a')
 
843
        os.rmdir('tree/b')
 
844
        self.assertEqual('file', tree.stored_kind('a-id'))
 
845
        self.assertEqual('directory', tree.stored_kind('b-id'))
 
846
 
 
847
    def test_missing_file_sha1(self):
 
848
        """If a file is missing, its sha1 should be reported as None."""
 
849
        tree = self.make_branch_and_tree('.')
 
850
        tree.lock_write()
 
851
        self.addCleanup(tree.unlock)
 
852
        self.build_tree(['file'])
 
853
        tree.add('file', 'file-id')
 
854
        tree.commit('file added')
 
855
        os.unlink('file')
 
856
        self.assertIs(None, tree.get_file_sha1('file-id'))
 
857
 
 
858
    def test_no_file_sha1(self):
 
859
        """If a file is not present, get_file_sha1 should raise NoSuchId"""
 
860
        tree = self.make_branch_and_tree('.')
 
861
        tree.lock_write()
 
862
        self.addCleanup(tree.unlock)
 
863
        self.assertRaises(errors.NoSuchId, tree.get_file_sha1, 'file-id')
 
864
        self.build_tree(['file'])
 
865
        tree.add('file', 'file-id')
 
866
        tree.commit('foo')
 
867
        tree.remove('file')
 
868
        self.assertRaises(errors.NoSuchId, tree.get_file_sha1, 'file-id')
 
869
 
 
870
    def test_case_sensitive(self):
 
871
        """If filesystem is case-sensitive, tree should report this.
 
872
 
 
873
        We check case-sensitivity by creating a file with a lowercase name,
 
874
        then testing whether it exists with an uppercase name.
 
875
        """
 
876
        self.build_tree(['filename'])
 
877
        if os.path.exists('FILENAME'):
 
878
            case_sensitive = False
 
879
        else:
 
880
            case_sensitive = True
 
881
        tree = self.make_branch_and_tree('test')
 
882
        if tree.__class__ == WorkingTree2:
 
883
            raise TestSkipped('WorkingTree2 is not supported')
 
884
        self.assertEqual(case_sensitive, tree.case_sensitive)
 
885
 
 
886
    def test_all_file_ids_with_missing(self):
 
887
        tree = self.make_branch_and_tree('tree')
 
888
        tree.lock_write()
 
889
        self.addCleanup(tree.unlock)
 
890
        self.build_tree(['tree/a', 'tree/b'])
 
891
        tree.add(['a', 'b'], ['a-id', 'b-id'])
 
892
        os.unlink('tree/a')
 
893
        self.assertEqual(set(['a-id', 'b-id', tree.get_root_id()]),
 
894
                         tree.all_file_ids())
 
895
 
 
896
    def test_sprout_hardlink(self):
 
897
        source = self.make_branch_and_tree('source')
 
898
        self.build_tree(['source/file'])
 
899
        source.add('file')
 
900
        source.commit('added file')
 
901
        def fake_link(source, target):
 
902
            raise OSError(errno.EPERM, 'Operation not permitted')
 
903
        real_os_link = os.link
 
904
        os.link = fake_link
 
905
        try:
 
906
            # Hard-link support is optional, so supplying hardlink=True may
 
907
            # or may not raise an exception.  But if it does, it must be
 
908
            # HardLinkNotSupported
 
909
            try:
 
910
                source.bzrdir.sprout('target', accelerator_tree=source,
 
911
                                     hardlink=True)
 
912
            except errors.HardLinkNotSupported:
 
913
                pass
 
914
        finally:
 
915
            os.link = real_os_link
 
916
 
 
917
 
 
918
class TestIllegalPaths(TestCaseWithWorkingTree):
 
919
 
 
920
    def test_bad_fs_path(self):
 
921
        self.requireFeature(tests.UTF8Filesystem)
 
922
        # We require a UTF8 filesystem, because otherwise we would need to get
 
923
        # tricky to figure out how to create an illegal filename.
 
924
        # \xb5 is an illegal path because it should be \xc2\xb5 for UTF-8
 
925
        tree = self.make_branch_and_tree('tree')
 
926
        self.build_tree(['tree/subdir/'])
 
927
        tree.add('subdir')
 
928
 
 
929
        f = open('tree/subdir/m\xb5', 'wb')
 
930
        try:
 
931
            f.write('trivial\n')
 
932
        finally:
 
933
            f.close()
 
934
 
 
935
        tree.lock_read()
 
936
        self.addCleanup(tree.unlock)
 
937
        basis = tree.basis_tree()
 
938
        basis.lock_read()
 
939
        self.addCleanup(basis.unlock)
 
940
 
 
941
        e = self.assertListRaises(errors.BadFilenameEncoding,
 
942
                                  tree.iter_changes, tree.basis_tree(),
 
943
                                                     want_unversioned=True)
 
944
        # We should display the relative path
 
945
        self.assertEqual('subdir/m\xb5', e.filename)
 
946
        self.assertEqual(osutils._fs_enc, e.fs_encoding)