~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-11 21:12:06 UTC
  • mfrom: (2321.2.6 mergeable)
  • Revision ID: pqm@pqm.ubuntu.com-20070311211206-0fd0176ac1e77ef7
(bialix) 0.15 NEWS cleanup

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