~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: John Arbash Meinel
  • Date: 2007-03-01 21:56:19 UTC
  • mto: (2255.7.84 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: john@arbash-meinel.com-20070301215619-wpt6kz8yem3ypu1b
Update to dirstate locking.
Move all of WT4.lock_* functions locally, so that they can
properly interact and cleanup around when we lock/unlock the
dirstate file.
Change all Lock objects to be non-blocking. So that if someone
grabs a lock on the DirState we find out immediately, rather
than blocking.
Change WT4.unlock() so that if the dirstate is dirty, it will
save the contents even if it only has a read lock.
It does this by trying to take a write lock, if it fails
we just ignore it. If it succeeds, then we can flush to disk.
This is more important now that DirState tracks file changes.
It allows 'bzr status' to update the cached stat and sha values.

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.tree_2.lock_read()
 
308
        self.addCleanup(self.tree_2.unlock)
 
309
        self.inv_2 = self.tree_2.read_working_inventory()
 
310
        self.file_2 = self.inv_2['fileid']
 
311
        self.file_2b = self.inv_2['binfileid']
 
312
        if has_symlinks():
 
313
            self.link_1 = self.inv_1['linkid']
 
314
            self.link_2 = self.inv_2['linkid']
 
315
 
 
316
    def test_file_diff_deleted(self):
 
317
        output = StringIO()
 
318
        self.file_1.diff(internal_diff, 
 
319
                          "old_label", self.tree_1,
 
320
                          "/dev/null", None, None,
 
321
                          output)
 
322
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
323
                                            "+++ /dev/null\n"
 
324
                                            "@@ -1,1 +0,0 @@\n"
 
325
                                            "-foo\n"
 
326
                                            "\n")
 
327
 
 
328
    def test_file_diff_added(self):
 
329
        output = StringIO()
 
330
        self.file_1.diff(internal_diff, 
 
331
                          "new_label", self.tree_1,
 
332
                          "/dev/null", None, None,
 
333
                          output, reverse=True)
 
334
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
335
                                            "+++ new_label\n"
 
336
                                            "@@ -0,0 +1,1 @@\n"
 
337
                                            "+foo\n"
 
338
                                            "\n")
 
339
 
 
340
    def test_file_diff_changed(self):
 
341
        output = StringIO()
 
342
        self.file_1.diff(internal_diff, 
 
343
                          "/dev/null", self.tree_1, 
 
344
                          "new_label", self.file_2, self.tree_2,
 
345
                          output)
 
346
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
347
                                            "+++ new_label\n"
 
348
                                            "@@ -1,1 +1,1 @@\n"
 
349
                                            "-foo\n"
 
350
                                            "+bar\n"
 
351
                                            "\n")
 
352
        
 
353
    def test_file_diff_binary(self):
 
354
        output = StringIO()
 
355
        self.file_1.diff(internal_diff, 
 
356
                          "/dev/null", self.tree_1, 
 
357
                          "new_label", self.file_2b, self.tree_2,
 
358
                          output)
 
359
        self.assertEqual(output.getvalue(), 
 
360
                         "Binary files /dev/null and new_label differ\n")
 
361
    def test_link_diff_deleted(self):
 
362
        if not has_symlinks():
 
363
            return
 
364
        output = StringIO()
 
365
        self.link_1.diff(internal_diff, 
 
366
                          "old_label", self.tree_1,
 
367
                          "/dev/null", None, None,
 
368
                          output)
 
369
        self.assertEqual(output.getvalue(),
 
370
                         "=== target was 'target1'\n")
 
371
 
 
372
    def test_link_diff_added(self):
 
373
        if not has_symlinks():
 
374
            return
 
375
        output = StringIO()
 
376
        self.link_1.diff(internal_diff, 
 
377
                          "new_label", self.tree_1,
 
378
                          "/dev/null", None, None,
 
379
                          output, reverse=True)
 
380
        self.assertEqual(output.getvalue(),
 
381
                         "=== target is 'target1'\n")
 
382
 
 
383
    def test_link_diff_changed(self):
 
384
        if not has_symlinks():
 
385
            return
 
386
        output = StringIO()
 
387
        self.link_1.diff(internal_diff, 
 
388
                          "/dev/null", self.tree_1, 
 
389
                          "new_label", self.link_2, self.tree_2,
 
390
                          output)
 
391
        self.assertEqual(output.getvalue(),
 
392
                         "=== target changed 'target1' => 'target2'\n")
 
393
 
 
394
 
 
395
class TestSnapshot(TestCaseWithTransport):
 
396
 
 
397
    def setUp(self):
 
398
        # for full testing we'll need a branch
 
399
        # with a subdir to test parent changes.
 
400
        # and a file, link and dir under that.
 
401
        # but right now I only need one attribute
 
402
        # to change, and then test merge patterns
 
403
        # with fake parent entries.
 
404
        super(TestSnapshot, self).setUp()
 
405
        self.wt = self.make_branch_and_tree('.')
 
406
        self.branch = self.wt.branch
 
407
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
 
408
        self.wt.add(['subdir', 'subdir/file'],
 
409
                                       ['dirid', 'fileid'])
 
410
        if has_symlinks():
 
411
            pass
 
412
        self.wt.commit('message_1', rev_id = '1')
 
413
        self.tree_1 = self.branch.repository.revision_tree('1')
 
414
        self.inv_1 = self.branch.repository.get_inventory('1')
 
415
        self.file_1 = self.inv_1['fileid']
 
416
        self.wt.lock_write()
 
417
        self.addCleanup(self.wt.unlock)
 
418
        self.file_active = self.wt.inventory['fileid']
 
419
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
 
420
 
 
421
    def test_snapshot_new_revision(self):
 
422
        # This tests that a simple commit with no parents makes a new
 
423
        # revision value in the inventory entry
 
424
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
 
425
        # expected outcome - file_1 has a revision id of '2', and we can get
 
426
        # its text of 'file contents' out of the weave.
 
427
        self.assertEqual(self.file_1.revision, '1')
 
428
        self.assertEqual(self.file_active.revision, '2')
 
429
        # this should be a separate test probably, but lets check it once..
 
430
        lines = self.branch.repository.weave_store.get_weave(
 
431
            'fileid', 
 
432
            self.branch.get_transaction()).get_lines('2')
 
433
        self.assertEqual(lines, ['contents of subdir/file\n'])
 
434
 
 
435
    def test_snapshot_unchanged(self):
 
436
        #This tests that a simple commit does not make a new entry for
 
437
        # an unchanged inventory entry
 
438
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
 
439
                                  self.wt, self.builder)
 
440
        self.assertEqual(self.file_1.revision, '1')
 
441
        self.assertEqual(self.file_active.revision, '1')
 
442
        vf = self.branch.repository.weave_store.get_weave(
 
443
            'fileid', 
 
444
            self.branch.repository.get_transaction())
 
445
        self.assertRaises(errors.RevisionNotPresent,
 
446
                          vf.get_lines,
 
447
                          '2')
 
448
 
 
449
    def test_snapshot_merge_identical_different_revid(self):
 
450
        # This tests that a commit with two identical parents, one of which has
 
451
        # a different revision id, results in a new revision id in the entry.
 
452
        # 1->other, commit a merge of other against 1, results in 2.
 
453
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
 
454
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
 
455
        other_ie.revision = '1'
 
456
        other_ie.text_sha1 = self.file_1.text_sha1
 
457
        other_ie.text_size = self.file_1.text_size
 
458
        self.assertEqual(self.file_1, other_ie)
 
459
        other_ie.revision = 'other'
 
460
        self.assertNotEqual(self.file_1, other_ie)
 
461
        versionfile = self.branch.repository.weave_store.get_weave(
 
462
            'fileid', self.branch.repository.get_transaction())
 
463
        versionfile.clone_text('other', '1', ['1'])
 
464
        self.file_active.snapshot('2', 'subdir/file', 
 
465
                                  {'1':self.file_1, 'other':other_ie},
 
466
                                  self.wt, self.builder)
 
467
        self.assertEqual(self.file_active.revision, '2')
 
468
 
 
469
    def test_snapshot_changed(self):
 
470
        # This tests that a commit with one different parent results in a new
 
471
        # revision id in the entry.
 
472
        self.wt.rename_one('subdir/file', 'subdir/newname')
 
473
        self.file_active = self.wt.inventory['fileid']
 
474
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
 
475
                                  self.wt, self.builder)
 
476
        # expected outcome - file_1 has a revision id of '2'
 
477
        self.assertEqual(self.file_active.revision, '2')
 
478
 
 
479
 
 
480
class TestPreviousHeads(TestCaseWithTransport):
 
481
 
 
482
    def setUp(self):
 
483
        # we want several inventories, that respectively
 
484
        # give use the following scenarios:
 
485
        # A) fileid not in any inventory (A),
 
486
        # B) fileid present in one inventory (B) and (A,B)
 
487
        # C) fileid present in two inventories, and they
 
488
        #   are not mutual descendents (B, C)
 
489
        # D) fileid present in two inventories and one is
 
490
        #   a descendent of the other. (B, D)
 
491
        super(TestPreviousHeads, self).setUp()
 
492
        self.wt = self.make_branch_and_tree('.')
 
493
        self.branch = self.wt.branch
 
494
        self.build_tree(['file'])
 
495
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
 
496
        self.inv_A = self.branch.repository.get_inventory('A')
 
497
        self.wt.add(['file'], ['fileid'])
 
498
        self.wt.commit('add file', rev_id='B')
 
499
        self.inv_B = self.branch.repository.get_inventory('B')
 
500
        uncommit(self.branch, tree=self.wt)
 
501
        self.assertEqual(self.branch.revision_history(), ['A'])
 
502
        self.wt.commit('another add of file', rev_id='C')
 
503
        self.inv_C = self.branch.repository.get_inventory('C')
 
504
        self.wt.add_parent_tree_id('B')
 
505
        self.wt.commit('merge in B', rev_id='D')
 
506
        self.inv_D = self.branch.repository.get_inventory('D')
 
507
        self.wt.lock_read()
 
508
        self.addCleanup(self.wt.unlock)
 
509
        self.file_active = self.wt.inventory['fileid']
 
510
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
511
            self.branch.repository.get_transaction())
 
512
        
 
513
    def get_previous_heads(self, inventories):
 
514
        return self.file_active.find_previous_heads(
 
515
            inventories, 
 
516
            self.branch.repository.weave_store,
 
517
            self.branch.repository.get_transaction())
 
518
        
 
519
    def test_fileid_in_no_inventory(self):
 
520
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
 
521
 
 
522
    def test_fileid_in_one_inventory(self):
 
523
        self.assertEqual({'B':self.inv_B['fileid']},
 
524
                         self.get_previous_heads([self.inv_B]))
 
525
        self.assertEqual({'B':self.inv_B['fileid']},
 
526
                         self.get_previous_heads([self.inv_A, self.inv_B]))
 
527
        self.assertEqual({'B':self.inv_B['fileid']},
 
528
                         self.get_previous_heads([self.inv_B, self.inv_A]))
 
529
 
 
530
    def test_fileid_in_two_inventories_gives_both_entries(self):
 
531
        self.assertEqual({'B':self.inv_B['fileid'],
 
532
                          'C':self.inv_C['fileid']},
 
533
                          self.get_previous_heads([self.inv_B, self.inv_C]))
 
534
        self.assertEqual({'B':self.inv_B['fileid'],
 
535
                          'C':self.inv_C['fileid']},
 
536
                          self.get_previous_heads([self.inv_C, self.inv_B]))
 
537
 
 
538
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
 
539
        self.assertEqual({'D':self.inv_D['fileid']},
 
540
                         self.get_previous_heads([self.inv_B, self.inv_D]))
 
541
        self.assertEqual({'D':self.inv_D['fileid']},
 
542
                         self.get_previous_heads([self.inv_D, self.inv_B]))
 
543
 
 
544
    # TODO: test two inventories with the same file revision 
 
545
 
 
546
 
 
547
class TestDescribeChanges(TestCase):
 
548
 
 
549
    def test_describe_change(self):
 
550
        # we need to test the following change combinations:
 
551
        # rename
 
552
        # reparent
 
553
        # modify
 
554
        # gone
 
555
        # added
 
556
        # renamed/reparented and modified
 
557
        # change kind (perhaps can't be done yet?)
 
558
        # also, merged in combination with all of these?
 
559
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
560
        old_a.text_sha1 = '123132'
 
561
        old_a.text_size = 0
 
562
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
563
        new_a.text_sha1 = '123132'
 
564
        new_a.text_size = 0
 
565
 
 
566
        self.assertChangeDescription('unchanged', old_a, new_a)
 
567
 
 
568
        new_a.text_size = 10
 
569
        new_a.text_sha1 = 'abcabc'
 
570
        self.assertChangeDescription('modified', old_a, new_a)
 
571
 
 
572
        self.assertChangeDescription('added', None, new_a)
 
573
        self.assertChangeDescription('removed', old_a, None)
 
574
        # perhaps a bit questionable but seems like the most reasonable thing...
 
575
        self.assertChangeDescription('unchanged', None, None)
 
576
 
 
577
        # in this case it's both renamed and modified; show a rename and 
 
578
        # modification:
 
579
        new_a.name = 'newfilename'
 
580
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
581
 
 
582
        # reparenting is 'renaming'
 
583
        new_a.name = old_a.name
 
584
        new_a.parent_id = 'somedir-id'
 
585
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
586
 
 
587
        # reset the content values so its not modified
 
588
        new_a.text_size = old_a.text_size
 
589
        new_a.text_sha1 = old_a.text_sha1
 
590
        new_a.name = old_a.name
 
591
 
 
592
        new_a.name = 'newfilename'
 
593
        self.assertChangeDescription('renamed', old_a, new_a)
 
594
 
 
595
        # reparenting is 'renaming'
 
596
        new_a.name = old_a.name
 
597
        new_a.parent_id = 'somedir-id'
 
598
        self.assertChangeDescription('renamed', old_a, new_a)
 
599
 
 
600
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
601
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
602
        self.assertEqual(expected_change, change)
 
603
 
 
604
 
 
605
class TestRevert(TestCaseWithTransport):
 
606
 
 
607
    def test_dangling_id(self):
 
608
        wt = self.make_branch_and_tree('b1')
 
609
        wt.lock_tree_write()
 
610
        self.addCleanup(wt.unlock)
 
611
        self.assertEqual(len(wt.inventory), 1)
 
612
        open('b1/a', 'wb').write('a test\n')
 
613
        wt.add('a')
 
614
        self.assertEqual(len(wt.inventory), 2)
 
615
        wt.flush() # workaround revert doing wt._write_inventory for now.
 
616
        os.unlink('b1/a')
 
617
        wt.revert([])
 
618
        self.assertEqual(len(wt.inventory), 1)
 
619
 
 
620
 
 
621
class TestIsRoot(TestCase):
 
622
    """Ensure our root-checking code is accurate."""
 
623
 
 
624
    def test_is_root(self):
 
625
        inv = Inventory('TREE_ROOT')
 
626
        self.assertTrue(inv.is_root('TREE_ROOT'))
 
627
        self.assertFalse(inv.is_root('booga'))
 
628
        inv.root.file_id = 'booga'
 
629
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
630
        self.assertTrue(inv.is_root('booga'))
 
631
        # works properly even if no root is set
 
632
        inv.root = None
 
633
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
634
        self.assertFalse(inv.is_root('booga'))