~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
963 by Martin Pool
- add the start of a test for inventory file-id matching
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
963 by Martin Pool
- add the start of a test for inventory file-id matching
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
963 by Martin Pool
- add the start of a test for inventory file-id matching
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1830.3.5 by John Arbash Meinel
make_entry refuses to create non-normalized entries.
17
from bzrlib import errors, inventory, osutils
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
18
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
2100.3.1 by Aaron Bentley
Start roundtripping tree-reference entries
19
    InventoryDirectory, InventoryEntry, TreeReference)
2338.4.2 by Marien Zwart
Move the workingtree-related inventory tests to a separate file.
20
from bzrlib.osutils import (pathjoin, is_inside_any, 
1740.3.4 by Jelmer Vernooij
Move inventory to commit builder.
21
    is_inside_or_parent_of_any)
2338.4.2 by Marien Zwart
Move the workingtree-related inventory tests to a separate file.
22
from bzrlib.tests import TestCase
963 by Martin Pool
- add the start of a test for inventory file-id matching
23
969 by Martin Pool
- Add less-sucky is_within_any
24
1102 by Martin Pool
- merge test refactoring from robertc
25
class TestInventory(TestCase):
26
2178.2.2 by Jelmer Vernooij
Make add_path() return inventory entry for root just like it does for other entries.
27
    def test_add_path(self):
28
29
        inv = Inventory(root_id=None)
30
        self.assertIs(None, inv.root)
31
        ie = inv.add_path("", "directory", "my-root")
32
        self.assertEqual("my-root", ie.file_id)
33
        self.assertIs(ie, inv.root)
34
1102 by Martin Pool
- merge test refactoring from robertc
35
    def test_is_within(self):
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
36
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
37
        SRC_FOO_C = pathjoin('src', 'foo.c')
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
38
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
39
                         (['src'], SRC_FOO_C),
968 by Martin Pool
- add some passing tests for is_inside_any
40
                         (['src'], 'src'),
41
                         ]:
42
            self.assert_(is_inside_any(dirs, fn))
43
            
969 by Martin Pool
- Add less-sucky is_within_any
44
        for dirs, fn in [(['src'], 'srccontrol'),
45
                         (['src'], 'srccontrol/foo')]:
46
            self.assertFalse(is_inside_any(dirs, fn))
1740.3.4 by Jelmer Vernooij
Move inventory to commit builder.
47
48
    def test_is_within_or_parent(self):
49
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
50
                         (['src'], 'src/foo.c'),
51
                         (['src/bar.c'], 'src'),
52
                         (['src/bar.c', 'bla/foo.c'], 'src'),
53
                         (['src'], 'src'),
54
                         ]:
55
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
969 by Martin Pool
- Add less-sucky is_within_any
56
            
1740.3.4 by Jelmer Vernooij
Move inventory to commit builder.
57
        for dirs, fn in [(['src'], 'srccontrol'),
58
                         (['srccontrol/foo.c'], 'src'),
59
                         (['src'], 'srccontrol/foo')]:
60
            self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
61
1102 by Martin Pool
- merge test refactoring from robertc
62
    def test_ids(self):
963 by Martin Pool
- add the start of a test for inventory file-id matching
63
        """Test detection of files within selected directories."""
64
        inv = Inventory()
65
        
66
        for args in [('src', 'directory', 'src-id'), 
67
                     ('doc', 'directory', 'doc-id'), 
68
                     ('src/hello.c', 'file'),
69
                     ('src/bye.c', 'file', 'bye-id'),
70
                     ('Makefile', 'file')]:
71
            inv.add_path(*args)
72
            
73
        self.assertEqual(inv.path2id('src'), 'src-id')
74
        self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
75
        
76
        self.assert_('src-id' in inv)
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
77
2091.3.1 by Aaron Bentley
When 'directory' path element isn't a directory, return None from path2id
78
    def test_non_directory_children(self):
79
        """Test path2id when a parent directory has no children"""
80
        inv = inventory.Inventory('tree_root')
81
        inv.add(inventory.InventoryFile('file-id','file', 
82
                                        parent_id='tree_root'))
83
        inv.add(inventory.InventoryLink('link-id','link', 
84
                                        parent_id='tree_root'))
85
        self.assertIs(None, inv.path2id('file/subfile'))
86
        self.assertIs(None, inv.path2id('link/subfile'))
87
1732.1.23 by John Arbash Meinel
Switch iter_entries from being a recursive function and using pathjoin
88
    def test_iter_entries(self):
89
        inv = Inventory()
90
        
91
        for args in [('src', 'directory', 'src-id'), 
92
                     ('doc', 'directory', 'doc-id'), 
93
                     ('src/hello.c', 'file', 'hello-id'),
94
                     ('src/bye.c', 'file', 'bye-id'),
95
                     ('Makefile', 'file', 'makefile-id')]:
96
            inv.add_path(*args)
97
98
        self.assertEqual([
1852.6.3 by Robert Collins
Make iter(Tree) consistent for all tree types.
99
            ('', ROOT_ID),
1732.1.23 by John Arbash Meinel
Switch iter_entries from being a recursive function and using pathjoin
100
            ('Makefile', 'makefile-id'),
101
            ('doc', 'doc-id'),
102
            ('src', 'src-id'),
103
            ('src/bye.c', 'bye-id'),
104
            ('src/hello.c', 'hello-id'),
105
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
106
            
1711.2.36 by John Arbash Meinel
Add an iter_entries_by_dir which returns directory children before their children.
107
    def test_iter_entries_by_dir(self):
108
        inv = Inventory()
109
        
110
        for args in [('src', 'directory', 'src-id'), 
111
                     ('doc', 'directory', 'doc-id'), 
112
                     ('src/hello.c', 'file', 'hello-id'),
113
                     ('src/bye.c', 'file', 'bye-id'),
114
                     ('zz', 'file', 'zz-id'),
115
                     ('src/sub/', 'directory', 'sub-id'),
116
                     ('src/zz.c', 'file', 'zzc-id'),
117
                     ('src/sub/a', 'file', 'a-id'),
118
                     ('Makefile', 'file', 'makefile-id')]:
119
            inv.add_path(*args)
120
121
        self.assertEqual([
1852.6.6 by Robert Collins
Finish updating iter_entries change to make all tests pass.
122
            ('', ROOT_ID),
1711.2.36 by John Arbash Meinel
Add an iter_entries_by_dir which returns directory children before their children.
123
            ('Makefile', 'makefile-id'),
124
            ('doc', 'doc-id'),
125
            ('src', 'src-id'),
126
            ('zz', 'zz-id'),
127
            ('src/bye.c', 'bye-id'),
128
            ('src/hello.c', 'hello-id'),
129
            ('src/sub', 'sub-id'),
130
            ('src/zz.c', 'zzc-id'),
131
            ('src/sub/a', 'a-id'),
132
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir()])
133
            
1551.9.29 by Aaron Bentley
Optimize Tree._iter_changes with specific file_ids
134
        self.assertEqual([
135
            ('', ROOT_ID),
136
            ('Makefile', 'makefile-id'),
137
            ('doc', 'doc-id'),
138
            ('src', 'src-id'),
139
            ('zz', 'zz-id'),
140
            ('src/bye.c', 'bye-id'),
141
            ('src/hello.c', 'hello-id'),
142
            ('src/sub', 'sub-id'),
143
            ('src/zz.c', 'zzc-id'),
144
            ('src/sub/a', 'a-id'),
145
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
146
                specific_file_ids=('a-id', 'zzc-id', 'doc-id', ROOT_ID,
147
                'hello-id', 'bye-id', 'zz-id', 'src-id', 'makefile-id', 
148
                'sub-id'))])
149
150
        self.assertEqual([
151
            ('Makefile', 'makefile-id'),
152
            ('doc', 'doc-id'),
153
            ('zz', 'zz-id'),
154
            ('src/bye.c', 'bye-id'),
155
            ('src/hello.c', 'hello-id'),
156
            ('src/zz.c', 'zzc-id'),
157
            ('src/sub/a', 'a-id'),
158
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
159
                specific_file_ids=('a-id', 'zzc-id', 'doc-id',
160
                'hello-id', 'bye-id', 'zz-id', 'makefile-id'))])
161
162
        self.assertEqual([
163
            ('Makefile', 'makefile-id'),
164
            ('src/bye.c', 'bye-id'),
165
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
166
                specific_file_ids=('bye-id', 'makefile-id'))])
167
168
        self.assertEqual([
169
            ('Makefile', 'makefile-id'),
170
            ('src/bye.c', 'bye-id'),
171
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
172
                specific_file_ids=('bye-id', 'makefile-id'))])
173
174
        self.assertEqual([
175
            ('src/bye.c', 'bye-id'),
176
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
177
                specific_file_ids=('bye-id',))])
178
2100.3.6 by Aaron Bentley
Make add recursive for children of added entries
179
    def test_add_recursive(self):
180
        parent = InventoryDirectory('src-id', 'src', ROOT_ID)
181
        child = InventoryFile('hello-id', 'hello.c', 'src-id')
182
        parent.children[child.file_id] = child
183
        inv = Inventory()
184
        inv.add(parent)
185
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
186
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
187
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
188
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
189
190
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
191
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
192
        self.assertEqual(file.kind_character(), '')
193
194
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
195
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
196
        self.assertEqual(dir.kind_character(), '/')
197
198
    def test_link_kind_character(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
199
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
200
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
201
202
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
203
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
204
        left.text_sha1 = 123
205
        left.executable = True
206
        left.symlink_target='foo'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
207
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
208
        right.text_sha1 = 321
209
        right.symlink_target='bar'
210
        self.assertEqual((False, False), left.detect_changes(right))
211
        self.assertEqual((False, False), right.detect_changes(left))
212
213
    def test_file_detect_changes(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
214
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
215
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
216
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
217
        right.text_sha1 = 123
218
        self.assertEqual((False, False), left.detect_changes(right))
219
        self.assertEqual((False, False), right.detect_changes(left))
220
        left.executable = True
221
        self.assertEqual((False, True), left.detect_changes(right))
222
        self.assertEqual((False, True), right.detect_changes(left))
223
        right.text_sha1 = 321
224
        self.assertEqual((True, True), left.detect_changes(right))
225
        self.assertEqual((True, True), right.detect_changes(left))
226
227
    def test_symlink_detect_changes(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
228
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
229
        left.text_sha1 = 123
230
        left.executable = True
231
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
232
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
233
        right.text_sha1 = 321
234
        right.symlink_target='foo'
235
        self.assertEqual((False, False), left.detect_changes(right))
236
        self.assertEqual((False, False), right.detect_changes(left))
237
        left.symlink_target = 'different'
238
        self.assertEqual((True, False), left.detect_changes(right))
239
        self.assertEqual((True, False), right.detect_changes(left))
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
240
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
241
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
242
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
243
        self.failUnless(file.has_text())
244
245
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
246
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
247
        self.failIf(dir.has_text())
248
249
    def test_link_has_text(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
250
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
251
        self.failIf(link.has_text())
252
1713.1.11 by Robert Collins
refactor smart_add to pass around the parent inventory entry and use that, resulting in another 100bzrlib/inventory.py performance improvement, and making inventory writing the dominating factory in add. (Robert Collins)
253
    def test_make_entry(self):
254
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
255
            inventory.InventoryFile)
256
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
257
            inventory.InventoryLink)
258
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
259
            inventory.InventoryDirectory)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
260
1830.3.5 by John Arbash Meinel
make_entry refuses to create non-normalized entries.
261
    def test_make_entry_non_normalized(self):
262
        orig_normalized_filename = osutils.normalized_filename
263
264
        try:
265
            osutils.normalized_filename = osutils._accessible_normalized_filename
266
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
267
            self.assertEqual(u'\xe5', entry.name)
268
            self.assertIsInstance(entry, inventory.InventoryFile)
269
270
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
271
            self.assertRaises(errors.InvalidNormalization,
272
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
273
        finally:
274
            osutils.normalized_filename = orig_normalized_filename
275
276
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
277
class TestDescribeChanges(TestCase):
278
279
    def test_describe_change(self):
280
        # we need to test the following change combinations:
281
        # rename
282
        # reparent
283
        # modify
284
        # gone
285
        # added
286
        # renamed/reparented and modified
287
        # change kind (perhaps can't be done yet?)
288
        # also, merged in combination with all of these?
289
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
290
        old_a.text_sha1 = '123132'
291
        old_a.text_size = 0
292
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
293
        new_a.text_sha1 = '123132'
294
        new_a.text_size = 0
295
296
        self.assertChangeDescription('unchanged', old_a, new_a)
297
298
        new_a.text_size = 10
299
        new_a.text_sha1 = 'abcabc'
300
        self.assertChangeDescription('modified', old_a, new_a)
301
302
        self.assertChangeDescription('added', None, new_a)
303
        self.assertChangeDescription('removed', old_a, None)
304
        # perhaps a bit questionable but seems like the most reasonable thing...
305
        self.assertChangeDescription('unchanged', None, None)
306
307
        # in this case it's both renamed and modified; show a rename and 
308
        # modification:
309
        new_a.name = 'newfilename'
310
        self.assertChangeDescription('modified and renamed', old_a, new_a)
311
312
        # reparenting is 'renaming'
313
        new_a.name = old_a.name
314
        new_a.parent_id = 'somedir-id'
315
        self.assertChangeDescription('modified and renamed', old_a, new_a)
316
317
        # reset the content values so its not modified
318
        new_a.text_size = old_a.text_size
319
        new_a.text_sha1 = old_a.text_sha1
320
        new_a.name = old_a.name
321
322
        new_a.name = 'newfilename'
323
        self.assertChangeDescription('renamed', old_a, new_a)
324
325
        # reparenting is 'renaming'
326
        new_a.name = old_a.name
327
        new_a.parent_id = 'somedir-id'
328
        self.assertChangeDescription('renamed', old_a, new_a)
329
330
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
331
        change = InventoryEntry.describe_change(old_ie, new_ie)
332
        self.assertEqual(expected_change, change)
333
334
1731.1.39 by Aaron Bentley
Reject removing is_root
335
class TestIsRoot(TestCase):
336
    """Ensure our root-checking code is accurate."""
337
338
    def test_is_root(self):
339
        inv = Inventory('TREE_ROOT')
340
        self.assertTrue(inv.is_root('TREE_ROOT'))
341
        self.assertFalse(inv.is_root('booga'))
342
        inv.root.file_id = 'booga'
343
        self.assertFalse(inv.is_root('TREE_ROOT'))
344
        self.assertTrue(inv.is_root('booga'))
345
        # works properly even if no root is set
346
        inv.root = None
347
        self.assertFalse(inv.is_root('TREE_ROOT'))
348
        self.assertFalse(inv.is_root('booga'))
2100.3.1 by Aaron Bentley
Start roundtripping tree-reference entries
349
350
351
class TestTreeReference(TestCase):
352
    
353
    def test_create(self):
354
        inv = Inventory('tree-root-123')
355
        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
356
                              revision='rev', reference_revision='rev2'))
1551.13.12 by Aaron Bentley
Fix encoding of 'already versioned' error
357
358
359
class TestEncoding(TestCase):
360
361
    def test_error_encoding(self):
362
        inv = Inventory('tree-root')
363
        inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
364
        try:
365
            inv.add(InventoryFile('b-id', u'\u1234', 'tree-root'))
366
        except errors.BzrError, e:
367
            self.assertContainsRe(str(e), u'\u1234'.encode('utf-8'))
1551.13.13 by Aaron Bentley
Fail if BzrError not raised
368
        else:
369
            self.fail('BzrError not raised')