/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Robert Collins
  • Date: 2007-03-01 05:02:47 UTC
  • mto: (2255.11.3 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: robertc@robertcollins.net-20070301050247-ufh99m2ze45d7mol
Whitespace fix.

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