~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Keir Mierle
  • Date: 2006-11-23 18:56:25 UTC
  • mto: (2168.1.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2171.
  • Revision ID: keir@cs.utoronto.ca-20061123185625-ndto53ylcb8zo1y6
Fix spacing error and add tests for status --short command flag.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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
 
 
17
from cStringIO import StringIO
 
18
import os
 
19
import time
 
20
 
 
21
from bzrlib import errors, inventory, osutils
 
22
from bzrlib.branch import Branch
 
23
from bzrlib.diff import internal_diff
 
24
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
 
25
    InventoryDirectory, InventoryEntry)
 
26
from bzrlib.osutils import (has_symlinks, rename, pathjoin, is_inside_any, 
 
27
    is_inside_or_parent_of_any)
 
28
from bzrlib.tests import TestCase, TestCaseWithTransport
 
29
from bzrlib.transform import TreeTransform
 
30
from bzrlib.uncommit import uncommit
 
31
 
 
32
 
 
33
class TestInventory(TestCase):
 
34
 
 
35
    def test_is_within(self):
 
36
 
 
37
        SRC_FOO_C = pathjoin('src', 'foo.c')
 
38
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
 
39
                         (['src'], SRC_FOO_C),
 
40
                         (['src'], 'src'),
 
41
                         ]:
 
42
            self.assert_(is_inside_any(dirs, fn))
 
43
            
 
44
        for dirs, fn in [(['src'], 'srccontrol'),
 
45
                         (['src'], 'srccontrol/foo')]:
 
46
            self.assertFalse(is_inside_any(dirs, fn))
 
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))
 
56
            
 
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
 
 
62
    def test_ids(self):
 
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)
 
77
 
 
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
 
 
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([
 
99
            ('', ROOT_ID),
 
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
            
 
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([
 
122
            ('', ROOT_ID),
 
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
            
 
134
    def test_version(self):
 
135
        """Inventory remembers the text's version."""
 
136
        inv = Inventory()
 
137
        ie = inv.add_path('foo.txt', 'file')
 
138
        ## XXX
 
139
 
 
140
 
 
141
class TestInventoryEntry(TestCase):
 
142
 
 
143
    def test_file_kind_character(self):
 
144
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
145
        self.assertEqual(file.kind_character(), '')
 
146
 
 
147
    def test_dir_kind_character(self):
 
148
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
149
        self.assertEqual(dir.kind_character(), '/')
 
150
 
 
151
    def test_link_kind_character(self):
 
152
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
153
        self.assertEqual(dir.kind_character(), '')
 
154
 
 
155
    def test_dir_detect_changes(self):
 
156
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
157
        left.text_sha1 = 123
 
158
        left.executable = True
 
159
        left.symlink_target='foo'
 
160
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
161
        right.text_sha1 = 321
 
162
        right.symlink_target='bar'
 
163
        self.assertEqual((False, False), left.detect_changes(right))
 
164
        self.assertEqual((False, False), right.detect_changes(left))
 
165
 
 
166
    def test_file_detect_changes(self):
 
167
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
168
        left.text_sha1 = 123
 
169
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
170
        right.text_sha1 = 123
 
171
        self.assertEqual((False, False), left.detect_changes(right))
 
172
        self.assertEqual((False, False), right.detect_changes(left))
 
173
        left.executable = True
 
174
        self.assertEqual((False, True), left.detect_changes(right))
 
175
        self.assertEqual((False, True), right.detect_changes(left))
 
176
        right.text_sha1 = 321
 
177
        self.assertEqual((True, True), left.detect_changes(right))
 
178
        self.assertEqual((True, True), right.detect_changes(left))
 
179
 
 
180
    def test_symlink_detect_changes(self):
 
181
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
182
        left.text_sha1 = 123
 
183
        left.executable = True
 
184
        left.symlink_target='foo'
 
185
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
186
        right.text_sha1 = 321
 
187
        right.symlink_target='foo'
 
188
        self.assertEqual((False, False), left.detect_changes(right))
 
189
        self.assertEqual((False, False), right.detect_changes(left))
 
190
        left.symlink_target = 'different'
 
191
        self.assertEqual((True, False), left.detect_changes(right))
 
192
        self.assertEqual((True, False), right.detect_changes(left))
 
193
 
 
194
    def test_file_has_text(self):
 
195
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
196
        self.failUnless(file.has_text())
 
197
 
 
198
    def test_directory_has_text(self):
 
199
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
200
        self.failIf(dir.has_text())
 
201
 
 
202
    def test_link_has_text(self):
 
203
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
204
        self.failIf(link.has_text())
 
205
 
 
206
    def test_make_entry(self):
 
207
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
 
208
            inventory.InventoryFile)
 
209
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
 
210
            inventory.InventoryLink)
 
211
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
 
212
            inventory.InventoryDirectory)
 
213
 
 
214
    def test_make_entry_non_normalized(self):
 
215
        orig_normalized_filename = osutils.normalized_filename
 
216
 
 
217
        try:
 
218
            osutils.normalized_filename = osutils._accessible_normalized_filename
 
219
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
 
220
            self.assertEqual(u'\xe5', entry.name)
 
221
            self.assertIsInstance(entry, inventory.InventoryFile)
 
222
 
 
223
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
224
            self.assertRaises(errors.InvalidNormalization,
 
225
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
 
226
        finally:
 
227
            osutils.normalized_filename = orig_normalized_filename
 
228
 
 
229
 
 
230
class TestEntryDiffing(TestCaseWithTransport):
 
231
 
 
232
    def setUp(self):
 
233
        super(TestEntryDiffing, self).setUp()
 
234
        self.wt = self.make_branch_and_tree('.')
 
235
        self.branch = self.wt.branch
 
236
        print >> open('file', 'wb'), 'foo'
 
237
        print >> open('binfile', 'wb'), 'foo'
 
238
        self.wt.add(['file'], ['fileid'])
 
239
        self.wt.add(['binfile'], ['binfileid'])
 
240
        if has_symlinks():
 
241
            os.symlink('target1', 'symlink')
 
242
            self.wt.add(['symlink'], ['linkid'])
 
243
        self.wt.commit('message_1', rev_id = '1')
 
244
        print >> open('file', 'wb'), 'bar'
 
245
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
 
246
        if has_symlinks():
 
247
            os.unlink('symlink')
 
248
            os.symlink('target2', 'symlink')
 
249
        self.tree_1 = self.branch.repository.revision_tree('1')
 
250
        self.inv_1 = self.branch.repository.get_inventory('1')
 
251
        self.file_1 = self.inv_1['fileid']
 
252
        self.file_1b = self.inv_1['binfileid']
 
253
        self.tree_2 = self.wt
 
254
        self.inv_2 = self.tree_2.read_working_inventory()
 
255
        self.file_2 = self.inv_2['fileid']
 
256
        self.file_2b = self.inv_2['binfileid']
 
257
        if has_symlinks():
 
258
            self.link_1 = self.inv_1['linkid']
 
259
            self.link_2 = self.inv_2['linkid']
 
260
 
 
261
    def test_file_diff_deleted(self):
 
262
        output = StringIO()
 
263
        self.file_1.diff(internal_diff, 
 
264
                          "old_label", self.tree_1,
 
265
                          "/dev/null", None, None,
 
266
                          output)
 
267
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
268
                                            "+++ /dev/null\n"
 
269
                                            "@@ -1,1 +0,0 @@\n"
 
270
                                            "-foo\n"
 
271
                                            "\n")
 
272
 
 
273
    def test_file_diff_added(self):
 
274
        output = StringIO()
 
275
        self.file_1.diff(internal_diff, 
 
276
                          "new_label", self.tree_1,
 
277
                          "/dev/null", None, None,
 
278
                          output, reverse=True)
 
279
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
280
                                            "+++ new_label\n"
 
281
                                            "@@ -0,0 +1,1 @@\n"
 
282
                                            "+foo\n"
 
283
                                            "\n")
 
284
 
 
285
    def test_file_diff_changed(self):
 
286
        output = StringIO()
 
287
        self.file_1.diff(internal_diff, 
 
288
                          "/dev/null", self.tree_1, 
 
289
                          "new_label", self.file_2, self.tree_2,
 
290
                          output)
 
291
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
292
                                            "+++ new_label\n"
 
293
                                            "@@ -1,1 +1,1 @@\n"
 
294
                                            "-foo\n"
 
295
                                            "+bar\n"
 
296
                                            "\n")
 
297
        
 
298
    def test_file_diff_binary(self):
 
299
        output = StringIO()
 
300
        self.file_1.diff(internal_diff, 
 
301
                          "/dev/null", self.tree_1, 
 
302
                          "new_label", self.file_2b, self.tree_2,
 
303
                          output)
 
304
        self.assertEqual(output.getvalue(), 
 
305
                         "Binary files /dev/null and new_label differ\n")
 
306
    def test_link_diff_deleted(self):
 
307
        if not has_symlinks():
 
308
            return
 
309
        output = StringIO()
 
310
        self.link_1.diff(internal_diff, 
 
311
                          "old_label", self.tree_1,
 
312
                          "/dev/null", None, None,
 
313
                          output)
 
314
        self.assertEqual(output.getvalue(),
 
315
                         "=== target was 'target1'\n")
 
316
 
 
317
    def test_link_diff_added(self):
 
318
        if not has_symlinks():
 
319
            return
 
320
        output = StringIO()
 
321
        self.link_1.diff(internal_diff, 
 
322
                          "new_label", self.tree_1,
 
323
                          "/dev/null", None, None,
 
324
                          output, reverse=True)
 
325
        self.assertEqual(output.getvalue(),
 
326
                         "=== target is 'target1'\n")
 
327
 
 
328
    def test_link_diff_changed(self):
 
329
        if not has_symlinks():
 
330
            return
 
331
        output = StringIO()
 
332
        self.link_1.diff(internal_diff, 
 
333
                          "/dev/null", self.tree_1, 
 
334
                          "new_label", self.link_2, self.tree_2,
 
335
                          output)
 
336
        self.assertEqual(output.getvalue(),
 
337
                         "=== target changed 'target1' => 'target2'\n")
 
338
 
 
339
 
 
340
class TestSnapshot(TestCaseWithTransport):
 
341
 
 
342
    def setUp(self):
 
343
        # for full testing we'll need a branch
 
344
        # with a subdir to test parent changes.
 
345
        # and a file, link and dir under that.
 
346
        # but right now I only need one attribute
 
347
        # to change, and then test merge patterns
 
348
        # with fake parent entries.
 
349
        super(TestSnapshot, self).setUp()
 
350
        self.wt = self.make_branch_and_tree('.')
 
351
        self.branch = self.wt.branch
 
352
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
 
353
        self.wt.add(['subdir', 'subdir/file'],
 
354
                                       ['dirid', 'fileid'])
 
355
        if has_symlinks():
 
356
            pass
 
357
        self.wt.commit('message_1', rev_id = '1')
 
358
        self.tree_1 = self.branch.repository.revision_tree('1')
 
359
        self.inv_1 = self.branch.repository.get_inventory('1')
 
360
        self.file_1 = self.inv_1['fileid']
 
361
        self.file_active = self.wt.inventory['fileid']
 
362
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
 
363
 
 
364
    def test_snapshot_new_revision(self):
 
365
        # This tests that a simple commit with no parents makes a new
 
366
        # revision value in the inventory entry
 
367
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
 
368
        # expected outcome - file_1 has a revision id of '2', and we can get
 
369
        # its text of 'file contents' out of the weave.
 
370
        self.assertEqual(self.file_1.revision, '1')
 
371
        self.assertEqual(self.file_active.revision, '2')
 
372
        # this should be a separate test probably, but lets check it once..
 
373
        lines = self.branch.repository.weave_store.get_weave(
 
374
            'fileid', 
 
375
            self.branch.get_transaction()).get_lines('2')
 
376
        self.assertEqual(lines, ['contents of subdir/file\n'])
 
377
 
 
378
    def test_snapshot_unchanged(self):
 
379
        #This tests that a simple commit does not make a new entry for
 
380
        # an unchanged inventory entry
 
381
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
 
382
                                  self.wt, self.builder)
 
383
        self.assertEqual(self.file_1.revision, '1')
 
384
        self.assertEqual(self.file_active.revision, '1')
 
385
        vf = self.branch.repository.weave_store.get_weave(
 
386
            'fileid', 
 
387
            self.branch.repository.get_transaction())
 
388
        self.assertRaises(errors.RevisionNotPresent,
 
389
                          vf.get_lines,
 
390
                          '2')
 
391
 
 
392
    def test_snapshot_merge_identical_different_revid(self):
 
393
        # This tests that a commit with two identical parents, one of which has
 
394
        # a different revision id, results in a new revision id in the entry.
 
395
        # 1->other, commit a merge of other against 1, results in 2.
 
396
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
 
397
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
 
398
        other_ie.revision = '1'
 
399
        other_ie.text_sha1 = self.file_1.text_sha1
 
400
        other_ie.text_size = self.file_1.text_size
 
401
        self.assertEqual(self.file_1, other_ie)
 
402
        other_ie.revision = 'other'
 
403
        self.assertNotEqual(self.file_1, other_ie)
 
404
        versionfile = self.branch.repository.weave_store.get_weave(
 
405
            'fileid', self.branch.repository.get_transaction())
 
406
        versionfile.clone_text('other', '1', ['1'])
 
407
        self.file_active.snapshot('2', 'subdir/file', 
 
408
                                  {'1':self.file_1, 'other':other_ie},
 
409
                                  self.wt, self.builder)
 
410
        self.assertEqual(self.file_active.revision, '2')
 
411
 
 
412
    def test_snapshot_changed(self):
 
413
        # This tests that a commit with one different parent results in a new
 
414
        # revision id in the entry.
 
415
        self.file_active.name='newname'
 
416
        rename('subdir/file', 'subdir/newname')
 
417
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
 
418
                                  self.wt, self.builder)
 
419
        # expected outcome - file_1 has a revision id of '2'
 
420
        self.assertEqual(self.file_active.revision, '2')
 
421
 
 
422
 
 
423
class TestPreviousHeads(TestCaseWithTransport):
 
424
 
 
425
    def setUp(self):
 
426
        # we want several inventories, that respectively
 
427
        # give use the following scenarios:
 
428
        # A) fileid not in any inventory (A),
 
429
        # B) fileid present in one inventory (B) and (A,B)
 
430
        # C) fileid present in two inventories, and they
 
431
        #   are not mutual descendents (B, C)
 
432
        # D) fileid present in two inventories and one is
 
433
        #   a descendent of the other. (B, D)
 
434
        super(TestPreviousHeads, self).setUp()
 
435
        self.wt = self.make_branch_and_tree('.')
 
436
        self.branch = self.wt.branch
 
437
        self.build_tree(['file'])
 
438
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
 
439
        self.inv_A = self.branch.repository.get_inventory('A')
 
440
        self.wt.add(['file'], ['fileid'])
 
441
        self.wt.commit('add file', rev_id='B')
 
442
        self.inv_B = self.branch.repository.get_inventory('B')
 
443
        uncommit(self.branch, tree=self.wt)
 
444
        self.assertEqual(self.branch.revision_history(), ['A'])
 
445
        self.wt.commit('another add of file', rev_id='C')
 
446
        self.inv_C = self.branch.repository.get_inventory('C')
 
447
        self.wt.add_parent_tree_id('B')
 
448
        self.wt.commit('merge in B', rev_id='D')
 
449
        self.inv_D = self.branch.repository.get_inventory('D')
 
450
        self.file_active = self.wt.inventory['fileid']
 
451
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
452
            self.branch.repository.get_transaction())
 
453
        
 
454
    def get_previous_heads(self, inventories):
 
455
        return self.file_active.find_previous_heads(
 
456
            inventories, 
 
457
            self.branch.repository.weave_store,
 
458
            self.branch.repository.get_transaction())
 
459
        
 
460
    def test_fileid_in_no_inventory(self):
 
461
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
 
462
 
 
463
    def test_fileid_in_one_inventory(self):
 
464
        self.assertEqual({'B':self.inv_B['fileid']},
 
465
                         self.get_previous_heads([self.inv_B]))
 
466
        self.assertEqual({'B':self.inv_B['fileid']},
 
467
                         self.get_previous_heads([self.inv_A, self.inv_B]))
 
468
        self.assertEqual({'B':self.inv_B['fileid']},
 
469
                         self.get_previous_heads([self.inv_B, self.inv_A]))
 
470
 
 
471
    def test_fileid_in_two_inventories_gives_both_entries(self):
 
472
        self.assertEqual({'B':self.inv_B['fileid'],
 
473
                          'C':self.inv_C['fileid']},
 
474
                          self.get_previous_heads([self.inv_B, self.inv_C]))
 
475
        self.assertEqual({'B':self.inv_B['fileid'],
 
476
                          'C':self.inv_C['fileid']},
 
477
                          self.get_previous_heads([self.inv_C, self.inv_B]))
 
478
 
 
479
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
 
480
        self.assertEqual({'D':self.inv_D['fileid']},
 
481
                         self.get_previous_heads([self.inv_B, self.inv_D]))
 
482
        self.assertEqual({'D':self.inv_D['fileid']},
 
483
                         self.get_previous_heads([self.inv_D, self.inv_B]))
 
484
 
 
485
    # TODO: test two inventories with the same file revision 
 
486
 
 
487
 
 
488
class TestDescribeChanges(TestCase):
 
489
 
 
490
    def test_describe_change(self):
 
491
        # we need to test the following change combinations:
 
492
        # rename
 
493
        # reparent
 
494
        # modify
 
495
        # gone
 
496
        # added
 
497
        # renamed/reparented and modified
 
498
        # change kind (perhaps can't be done yet?)
 
499
        # also, merged in combination with all of these?
 
500
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
501
        old_a.text_sha1 = '123132'
 
502
        old_a.text_size = 0
 
503
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
504
        new_a.text_sha1 = '123132'
 
505
        new_a.text_size = 0
 
506
 
 
507
        self.assertChangeDescription('unchanged', old_a, new_a)
 
508
 
 
509
        new_a.text_size = 10
 
510
        new_a.text_sha1 = 'abcabc'
 
511
        self.assertChangeDescription('modified', old_a, new_a)
 
512
 
 
513
        self.assertChangeDescription('added', None, new_a)
 
514
        self.assertChangeDescription('removed', old_a, None)
 
515
        # perhaps a bit questionable but seems like the most reasonable thing...
 
516
        self.assertChangeDescription('unchanged', None, None)
 
517
 
 
518
        # in this case it's both renamed and modified; show a rename and 
 
519
        # modification:
 
520
        new_a.name = 'newfilename'
 
521
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
522
 
 
523
        # reparenting is 'renaming'
 
524
        new_a.name = old_a.name
 
525
        new_a.parent_id = 'somedir-id'
 
526
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
527
 
 
528
        # reset the content values so its not modified
 
529
        new_a.text_size = old_a.text_size
 
530
        new_a.text_sha1 = old_a.text_sha1
 
531
        new_a.name = old_a.name
 
532
 
 
533
        new_a.name = 'newfilename'
 
534
        self.assertChangeDescription('renamed', old_a, new_a)
 
535
 
 
536
        # reparenting is 'renaming'
 
537
        new_a.name = old_a.name
 
538
        new_a.parent_id = 'somedir-id'
 
539
        self.assertChangeDescription('renamed', old_a, new_a)
 
540
 
 
541
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
542
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
543
        self.assertEqual(expected_change, change)
 
544
 
 
545
 
 
546
class TestRevert(TestCaseWithTransport):
 
547
 
 
548
    def test_dangling_id(self):
 
549
        wt = self.make_branch_and_tree('b1')
 
550
        self.assertEqual(len(wt.inventory), 1)
 
551
        open('b1/a', 'wb').write('a test\n')
 
552
        wt.add('a')
 
553
        self.assertEqual(len(wt.inventory), 2)
 
554
        os.unlink('b1/a')
 
555
        wt.revert([])
 
556
        self.assertEqual(len(wt.inventory), 1)
 
557
 
 
558
 
 
559
class TestIsRoot(TestCase):
 
560
    """Ensure our root-checking code is accurate."""
 
561
 
 
562
    def test_is_root(self):
 
563
        inv = Inventory('TREE_ROOT')
 
564
        self.assertTrue(inv.is_root('TREE_ROOT'))
 
565
        self.assertFalse(inv.is_root('booga'))
 
566
        inv.root.file_id = 'booga'
 
567
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
568
        self.assertTrue(inv.is_root('booga'))
 
569
        # works properly even if no root is set
 
570
        inv.root = None
 
571
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
572
        self.assertFalse(inv.is_root('booga'))