~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Robert Collins
  • Date: 2007-03-06 10:51:27 UTC
  • mto: (2321.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: robertc@robertcollins.net-20070306105127-tdec4zgv1tkfgi1d
Fix failing detection of changes restricted to subtrees causing spurious pointless commit errors.

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, TreeReference)
 
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
    def test_add_recursive(self):
 
194
        parent = InventoryDirectory('src-id', 'src', ROOT_ID)
 
195
        child = InventoryFile('hello-id', 'hello.c', 'src-id')
 
196
        parent.children[child.file_id] = child
 
197
        inv = Inventory()
 
198
        inv.add(parent)
 
199
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
 
200
 
 
201
 
 
202
class TestInventoryEntry(TestCase):
 
203
 
 
204
    def test_file_kind_character(self):
 
205
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
206
        self.assertEqual(file.kind_character(), '')
 
207
 
 
208
    def test_dir_kind_character(self):
 
209
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
210
        self.assertEqual(dir.kind_character(), '/')
 
211
 
 
212
    def test_link_kind_character(self):
 
213
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
214
        self.assertEqual(dir.kind_character(), '')
 
215
 
 
216
    def test_dir_detect_changes(self):
 
217
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
218
        left.text_sha1 = 123
 
219
        left.executable = True
 
220
        left.symlink_target='foo'
 
221
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
222
        right.text_sha1 = 321
 
223
        right.symlink_target='bar'
 
224
        self.assertEqual((False, False), left.detect_changes(right))
 
225
        self.assertEqual((False, False), right.detect_changes(left))
 
226
 
 
227
    def test_file_detect_changes(self):
 
228
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
229
        left.text_sha1 = 123
 
230
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
231
        right.text_sha1 = 123
 
232
        self.assertEqual((False, False), left.detect_changes(right))
 
233
        self.assertEqual((False, False), right.detect_changes(left))
 
234
        left.executable = True
 
235
        self.assertEqual((False, True), left.detect_changes(right))
 
236
        self.assertEqual((False, True), right.detect_changes(left))
 
237
        right.text_sha1 = 321
 
238
        self.assertEqual((True, True), left.detect_changes(right))
 
239
        self.assertEqual((True, True), right.detect_changes(left))
 
240
 
 
241
    def test_symlink_detect_changes(self):
 
242
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
243
        left.text_sha1 = 123
 
244
        left.executable = True
 
245
        left.symlink_target='foo'
 
246
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
247
        right.text_sha1 = 321
 
248
        right.symlink_target='foo'
 
249
        self.assertEqual((False, False), left.detect_changes(right))
 
250
        self.assertEqual((False, False), right.detect_changes(left))
 
251
        left.symlink_target = 'different'
 
252
        self.assertEqual((True, False), left.detect_changes(right))
 
253
        self.assertEqual((True, False), right.detect_changes(left))
 
254
 
 
255
    def test_file_has_text(self):
 
256
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
257
        self.failUnless(file.has_text())
 
258
 
 
259
    def test_directory_has_text(self):
 
260
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
261
        self.failIf(dir.has_text())
 
262
 
 
263
    def test_link_has_text(self):
 
264
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
265
        self.failIf(link.has_text())
 
266
 
 
267
    def test_make_entry(self):
 
268
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
 
269
            inventory.InventoryFile)
 
270
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
 
271
            inventory.InventoryLink)
 
272
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
 
273
            inventory.InventoryDirectory)
 
274
 
 
275
    def test_make_entry_non_normalized(self):
 
276
        orig_normalized_filename = osutils.normalized_filename
 
277
 
 
278
        try:
 
279
            osutils.normalized_filename = osutils._accessible_normalized_filename
 
280
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
 
281
            self.assertEqual(u'\xe5', entry.name)
 
282
            self.assertIsInstance(entry, inventory.InventoryFile)
 
283
 
 
284
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
285
            self.assertRaises(errors.InvalidNormalization,
 
286
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
 
287
        finally:
 
288
            osutils.normalized_filename = orig_normalized_filename
 
289
 
 
290
 
 
291
class TestEntryDiffing(TestCaseWithTransport):
 
292
 
 
293
    def setUp(self):
 
294
        super(TestEntryDiffing, self).setUp()
 
295
        self.wt = self.make_branch_and_tree('.')
 
296
        self.branch = self.wt.branch
 
297
        print >> open('file', 'wb'), 'foo'
 
298
        print >> open('binfile', 'wb'), 'foo'
 
299
        self.wt.add(['file'], ['fileid'])
 
300
        self.wt.add(['binfile'], ['binfileid'])
 
301
        if has_symlinks():
 
302
            os.symlink('target1', 'symlink')
 
303
            self.wt.add(['symlink'], ['linkid'])
 
304
        self.wt.commit('message_1', rev_id = '1')
 
305
        print >> open('file', 'wb'), 'bar'
 
306
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
 
307
        if has_symlinks():
 
308
            os.unlink('symlink')
 
309
            os.symlink('target2', 'symlink')
 
310
        self.tree_1 = self.branch.repository.revision_tree('1')
 
311
        self.inv_1 = self.branch.repository.get_inventory('1')
 
312
        self.file_1 = self.inv_1['fileid']
 
313
        self.file_1b = self.inv_1['binfileid']
 
314
        self.tree_2 = self.wt
 
315
        self.tree_2.lock_read()
 
316
        self.addCleanup(self.tree_2.unlock)
 
317
        self.inv_2 = self.tree_2.read_working_inventory()
 
318
        self.file_2 = self.inv_2['fileid']
 
319
        self.file_2b = self.inv_2['binfileid']
 
320
        if has_symlinks():
 
321
            self.link_1 = self.inv_1['linkid']
 
322
            self.link_2 = self.inv_2['linkid']
 
323
 
 
324
    def test_file_diff_deleted(self):
 
325
        output = StringIO()
 
326
        self.file_1.diff(internal_diff, 
 
327
                          "old_label", self.tree_1,
 
328
                          "/dev/null", None, None,
 
329
                          output)
 
330
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
331
                                            "+++ /dev/null\n"
 
332
                                            "@@ -1,1 +0,0 @@\n"
 
333
                                            "-foo\n"
 
334
                                            "\n")
 
335
 
 
336
    def test_file_diff_added(self):
 
337
        output = StringIO()
 
338
        self.file_1.diff(internal_diff, 
 
339
                          "new_label", self.tree_1,
 
340
                          "/dev/null", None, None,
 
341
                          output, reverse=True)
 
342
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
343
                                            "+++ new_label\n"
 
344
                                            "@@ -0,0 +1,1 @@\n"
 
345
                                            "+foo\n"
 
346
                                            "\n")
 
347
 
 
348
    def test_file_diff_changed(self):
 
349
        output = StringIO()
 
350
        self.file_1.diff(internal_diff, 
 
351
                          "/dev/null", self.tree_1, 
 
352
                          "new_label", self.file_2, self.tree_2,
 
353
                          output)
 
354
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
355
                                            "+++ new_label\n"
 
356
                                            "@@ -1,1 +1,1 @@\n"
 
357
                                            "-foo\n"
 
358
                                            "+bar\n"
 
359
                                            "\n")
 
360
        
 
361
    def test_file_diff_binary(self):
 
362
        output = StringIO()
 
363
        self.file_1.diff(internal_diff, 
 
364
                          "/dev/null", self.tree_1, 
 
365
                          "new_label", self.file_2b, self.tree_2,
 
366
                          output)
 
367
        self.assertEqual(output.getvalue(), 
 
368
                         "Binary files /dev/null and new_label differ\n")
 
369
    def test_link_diff_deleted(self):
 
370
        if not has_symlinks():
 
371
            return
 
372
        output = StringIO()
 
373
        self.link_1.diff(internal_diff, 
 
374
                          "old_label", self.tree_1,
 
375
                          "/dev/null", None, None,
 
376
                          output)
 
377
        self.assertEqual(output.getvalue(),
 
378
                         "=== target was 'target1'\n")
 
379
 
 
380
    def test_link_diff_added(self):
 
381
        if not has_symlinks():
 
382
            return
 
383
        output = StringIO()
 
384
        self.link_1.diff(internal_diff, 
 
385
                          "new_label", self.tree_1,
 
386
                          "/dev/null", None, None,
 
387
                          output, reverse=True)
 
388
        self.assertEqual(output.getvalue(),
 
389
                         "=== target is 'target1'\n")
 
390
 
 
391
    def test_link_diff_changed(self):
 
392
        if not has_symlinks():
 
393
            return
 
394
        output = StringIO()
 
395
        self.link_1.diff(internal_diff, 
 
396
                          "/dev/null", self.tree_1, 
 
397
                          "new_label", self.link_2, self.tree_2,
 
398
                          output)
 
399
        self.assertEqual(output.getvalue(),
 
400
                         "=== target changed 'target1' => 'target2'\n")
 
401
 
 
402
 
 
403
class TestSnapshot(TestCaseWithTransport):
 
404
 
 
405
    def setUp(self):
 
406
        # for full testing we'll need a branch
 
407
        # with a subdir to test parent changes.
 
408
        # and a file, link and dir under that.
 
409
        # but right now I only need one attribute
 
410
        # to change, and then test merge patterns
 
411
        # with fake parent entries.
 
412
        super(TestSnapshot, self).setUp()
 
413
        self.wt = self.make_branch_and_tree('.')
 
414
        self.branch = self.wt.branch
 
415
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
 
416
        self.wt.add(['subdir', 'subdir/file'],
 
417
                                       ['dirid', 'fileid'])
 
418
        if has_symlinks():
 
419
            pass
 
420
        self.wt.commit('message_1', rev_id = '1')
 
421
        self.tree_1 = self.branch.repository.revision_tree('1')
 
422
        self.inv_1 = self.branch.repository.get_inventory('1')
 
423
        self.file_1 = self.inv_1['fileid']
 
424
        self.wt.lock_write()
 
425
        self.addCleanup(self.wt.unlock)
 
426
        self.file_active = self.wt.inventory['fileid']
 
427
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
 
428
 
 
429
    def test_snapshot_new_revision(self):
 
430
        # This tests that a simple commit with no parents makes a new
 
431
        # revision value in the inventory entry
 
432
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
 
433
        # expected outcome - file_1 has a revision id of '2', and we can get
 
434
        # its text of 'file contents' out of the weave.
 
435
        self.assertEqual(self.file_1.revision, '1')
 
436
        self.assertEqual(self.file_active.revision, '2')
 
437
        # this should be a separate test probably, but lets check it once..
 
438
        lines = self.branch.repository.weave_store.get_weave(
 
439
            'fileid', 
 
440
            self.branch.get_transaction()).get_lines('2')
 
441
        self.assertEqual(lines, ['contents of subdir/file\n'])
 
442
 
 
443
    def test_snapshot_unchanged(self):
 
444
        #This tests that a simple commit does not make a new entry for
 
445
        # an unchanged inventory entry
 
446
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
 
447
                                  self.wt, self.builder)
 
448
        self.assertEqual(self.file_1.revision, '1')
 
449
        self.assertEqual(self.file_active.revision, '1')
 
450
        vf = self.branch.repository.weave_store.get_weave(
 
451
            'fileid', 
 
452
            self.branch.repository.get_transaction())
 
453
        self.assertRaises(errors.RevisionNotPresent,
 
454
                          vf.get_lines,
 
455
                          '2')
 
456
 
 
457
    def test_snapshot_merge_identical_different_revid(self):
 
458
        # This tests that a commit with two identical parents, one of which has
 
459
        # a different revision id, results in a new revision id in the entry.
 
460
        # 1->other, commit a merge of other against 1, results in 2.
 
461
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
 
462
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
 
463
        other_ie.revision = '1'
 
464
        other_ie.text_sha1 = self.file_1.text_sha1
 
465
        other_ie.text_size = self.file_1.text_size
 
466
        self.assertEqual(self.file_1, other_ie)
 
467
        other_ie.revision = 'other'
 
468
        self.assertNotEqual(self.file_1, other_ie)
 
469
        versionfile = self.branch.repository.weave_store.get_weave(
 
470
            'fileid', self.branch.repository.get_transaction())
 
471
        versionfile.clone_text('other', '1', ['1'])
 
472
        self.file_active.snapshot('2', 'subdir/file', 
 
473
                                  {'1':self.file_1, 'other':other_ie},
 
474
                                  self.wt, self.builder)
 
475
        self.assertEqual(self.file_active.revision, '2')
 
476
 
 
477
    def test_snapshot_changed(self):
 
478
        # This tests that a commit with one different parent results in a new
 
479
        # revision id in the entry.
 
480
        self.wt.rename_one('subdir/file', 'subdir/newname')
 
481
        self.file_active = self.wt.inventory['fileid']
 
482
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
 
483
                                  self.wt, self.builder)
 
484
        # expected outcome - file_1 has a revision id of '2'
 
485
        self.assertEqual(self.file_active.revision, '2')
 
486
 
 
487
 
 
488
class TestPreviousHeads(TestCaseWithTransport):
 
489
 
 
490
    def setUp(self):
 
491
        # we want several inventories, that respectively
 
492
        # give use the following scenarios:
 
493
        # A) fileid not in any inventory (A),
 
494
        # B) fileid present in one inventory (B) and (A,B)
 
495
        # C) fileid present in two inventories, and they
 
496
        #   are not mutual descendents (B, C)
 
497
        # D) fileid present in two inventories and one is
 
498
        #   a descendent of the other. (B, D)
 
499
        super(TestPreviousHeads, self).setUp()
 
500
        self.wt = self.make_branch_and_tree('.')
 
501
        self.branch = self.wt.branch
 
502
        self.build_tree(['file'])
 
503
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
 
504
        self.inv_A = self.branch.repository.get_inventory('A')
 
505
        self.wt.add(['file'], ['fileid'])
 
506
        self.wt.commit('add file', rev_id='B')
 
507
        self.inv_B = self.branch.repository.get_inventory('B')
 
508
        uncommit(self.branch, tree=self.wt)
 
509
        self.assertEqual(self.branch.revision_history(), ['A'])
 
510
        self.wt.commit('another add of file', rev_id='C')
 
511
        self.inv_C = self.branch.repository.get_inventory('C')
 
512
        self.wt.add_parent_tree_id('B')
 
513
        self.wt.commit('merge in B', rev_id='D')
 
514
        self.inv_D = self.branch.repository.get_inventory('D')
 
515
        self.wt.lock_read()
 
516
        self.addCleanup(self.wt.unlock)
 
517
        self.file_active = self.wt.inventory['fileid']
 
518
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
519
            self.branch.repository.get_transaction())
 
520
        
 
521
    def get_previous_heads(self, inventories):
 
522
        return self.file_active.find_previous_heads(
 
523
            inventories, 
 
524
            self.branch.repository.weave_store,
 
525
            self.branch.repository.get_transaction())
 
526
        
 
527
    def test_fileid_in_no_inventory(self):
 
528
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
 
529
 
 
530
    def test_fileid_in_one_inventory(self):
 
531
        self.assertEqual({'B':self.inv_B['fileid']},
 
532
                         self.get_previous_heads([self.inv_B]))
 
533
        self.assertEqual({'B':self.inv_B['fileid']},
 
534
                         self.get_previous_heads([self.inv_A, self.inv_B]))
 
535
        self.assertEqual({'B':self.inv_B['fileid']},
 
536
                         self.get_previous_heads([self.inv_B, self.inv_A]))
 
537
 
 
538
    def test_fileid_in_two_inventories_gives_both_entries(self):
 
539
        self.assertEqual({'B':self.inv_B['fileid'],
 
540
                          'C':self.inv_C['fileid']},
 
541
                          self.get_previous_heads([self.inv_B, self.inv_C]))
 
542
        self.assertEqual({'B':self.inv_B['fileid'],
 
543
                          'C':self.inv_C['fileid']},
 
544
                          self.get_previous_heads([self.inv_C, self.inv_B]))
 
545
 
 
546
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
 
547
        self.assertEqual({'D':self.inv_D['fileid']},
 
548
                         self.get_previous_heads([self.inv_B, self.inv_D]))
 
549
        self.assertEqual({'D':self.inv_D['fileid']},
 
550
                         self.get_previous_heads([self.inv_D, self.inv_B]))
 
551
 
 
552
    # TODO: test two inventories with the same file revision 
 
553
 
 
554
 
 
555
class TestDescribeChanges(TestCase):
 
556
 
 
557
    def test_describe_change(self):
 
558
        # we need to test the following change combinations:
 
559
        # rename
 
560
        # reparent
 
561
        # modify
 
562
        # gone
 
563
        # added
 
564
        # renamed/reparented and modified
 
565
        # change kind (perhaps can't be done yet?)
 
566
        # also, merged in combination with all of these?
 
567
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
568
        old_a.text_sha1 = '123132'
 
569
        old_a.text_size = 0
 
570
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
571
        new_a.text_sha1 = '123132'
 
572
        new_a.text_size = 0
 
573
 
 
574
        self.assertChangeDescription('unchanged', old_a, new_a)
 
575
 
 
576
        new_a.text_size = 10
 
577
        new_a.text_sha1 = 'abcabc'
 
578
        self.assertChangeDescription('modified', old_a, new_a)
 
579
 
 
580
        self.assertChangeDescription('added', None, new_a)
 
581
        self.assertChangeDescription('removed', old_a, None)
 
582
        # perhaps a bit questionable but seems like the most reasonable thing...
 
583
        self.assertChangeDescription('unchanged', None, None)
 
584
 
 
585
        # in this case it's both renamed and modified; show a rename and 
 
586
        # modification:
 
587
        new_a.name = 'newfilename'
 
588
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
589
 
 
590
        # reparenting is 'renaming'
 
591
        new_a.name = old_a.name
 
592
        new_a.parent_id = 'somedir-id'
 
593
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
594
 
 
595
        # reset the content values so its not modified
 
596
        new_a.text_size = old_a.text_size
 
597
        new_a.text_sha1 = old_a.text_sha1
 
598
        new_a.name = old_a.name
 
599
 
 
600
        new_a.name = 'newfilename'
 
601
        self.assertChangeDescription('renamed', old_a, new_a)
 
602
 
 
603
        # reparenting is 'renaming'
 
604
        new_a.name = old_a.name
 
605
        new_a.parent_id = 'somedir-id'
 
606
        self.assertChangeDescription('renamed', old_a, new_a)
 
607
 
 
608
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
609
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
610
        self.assertEqual(expected_change, change)
 
611
 
 
612
 
 
613
class TestRevert(TestCaseWithTransport):
 
614
 
 
615
    def test_dangling_id(self):
 
616
        wt = self.make_branch_and_tree('b1')
 
617
        wt.lock_tree_write()
 
618
        self.addCleanup(wt.unlock)
 
619
        self.assertEqual(len(wt.inventory), 1)
 
620
        open('b1/a', 'wb').write('a test\n')
 
621
        wt.add('a')
 
622
        self.assertEqual(len(wt.inventory), 2)
 
623
        wt.flush() # workaround revert doing wt._write_inventory for now.
 
624
        os.unlink('b1/a')
 
625
        wt.revert([])
 
626
        self.assertEqual(len(wt.inventory), 1)
 
627
 
 
628
 
 
629
class TestIsRoot(TestCase):
 
630
    """Ensure our root-checking code is accurate."""
 
631
 
 
632
    def test_is_root(self):
 
633
        inv = Inventory('TREE_ROOT')
 
634
        self.assertTrue(inv.is_root('TREE_ROOT'))
 
635
        self.assertFalse(inv.is_root('booga'))
 
636
        inv.root.file_id = 'booga'
 
637
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
638
        self.assertTrue(inv.is_root('booga'))
 
639
        # works properly even if no root is set
 
640
        inv.root = None
 
641
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
642
        self.assertFalse(inv.is_root('booga'))
 
643
 
 
644
 
 
645
class TestTreeReference(TestCase):
 
646
    
 
647
    def test_create(self):
 
648
        inv = Inventory('tree-root-123')
 
649
        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
 
650
                              revision='rev', reference_revision='rev2'))