~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testinv.py

  • Committer: Robert Collins
  • Date: 2005-08-25 01:13:32 UTC
  • mto: (974.1.50) (1185.1.10) (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1139.
  • Revision ID: robertc@robertcollins.net-20050825011331-6d549d5de7edcec1
two bugfixes to smart_add - do not add paths from nested trees to the parent tree, and do not mutate the user supplied file list

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from cStringIO import StringIO
18
 
import os
 
17
from bzrlib.selftest import TestCase
19
18
 
20
 
from bzrlib.branch import Branch
21
 
import bzrlib.errors as errors
22
 
from bzrlib.diff import internal_diff
23
 
from bzrlib.inventory import Inventory, ROOT_ID
24
 
import bzrlib.inventory as inventory
25
 
from bzrlib.osutils import has_symlinks, rename, pathjoin
26
 
from bzrlib.tests import TestCase, TestCaseInTempDir
 
19
from bzrlib.inventory import Inventory, InventoryEntry
27
20
 
28
21
 
29
22
class TestInventory(TestCase):
30
23
 
31
24
    def test_is_within(self):
32
25
        from bzrlib.osutils import is_inside_any
33
 
 
34
 
        SRC_FOO_C = pathjoin('src', 'foo.c')
35
 
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
36
 
                         (['src'], SRC_FOO_C),
 
26
        
 
27
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
 
28
                         (['src'], 'src/foo.c'),
37
29
                         (['src'], 'src'),
38
30
                         ]:
39
31
            self.assert_(is_inside_any(dirs, fn))
57
49
        self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
58
50
        
59
51
        self.assert_('src-id' in inv)
60
 
 
61
 
 
62
 
    def test_version(self):
63
 
        """Inventory remembers the text's version."""
64
 
        inv = Inventory()
65
 
        ie = inv.add_path('foo.txt', 'file')
66
 
        ## XXX
67
 
 
68
 
 
69
 
class TestInventoryEntry(TestCase):
70
 
 
71
 
    def test_file_kind_character(self):
72
 
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
73
 
        self.assertEqual(file.kind_character(), '')
74
 
 
75
 
    def test_dir_kind_character(self):
76
 
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
77
 
        self.assertEqual(dir.kind_character(), '/')
78
 
 
79
 
    def test_link_kind_character(self):
80
 
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
81
 
        self.assertEqual(dir.kind_character(), '')
82
 
 
83
 
    def test_dir_detect_changes(self):
84
 
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
85
 
        left.text_sha1 = 123
86
 
        left.executable = True
87
 
        left.symlink_target='foo'
88
 
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
89
 
        right.text_sha1 = 321
90
 
        right.symlink_target='bar'
91
 
        self.assertEqual((False, False), left.detect_changes(right))
92
 
        self.assertEqual((False, False), right.detect_changes(left))
93
 
 
94
 
    def test_file_detect_changes(self):
95
 
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
96
 
        left.text_sha1 = 123
97
 
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
98
 
        right.text_sha1 = 123
99
 
        self.assertEqual((False, False), left.detect_changes(right))
100
 
        self.assertEqual((False, False), right.detect_changes(left))
101
 
        left.executable = True
102
 
        self.assertEqual((False, True), left.detect_changes(right))
103
 
        self.assertEqual((False, True), right.detect_changes(left))
104
 
        right.text_sha1 = 321
105
 
        self.assertEqual((True, True), left.detect_changes(right))
106
 
        self.assertEqual((True, True), right.detect_changes(left))
107
 
 
108
 
    def test_symlink_detect_changes(self):
109
 
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
110
 
        left.text_sha1 = 123
111
 
        left.executable = True
112
 
        left.symlink_target='foo'
113
 
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
114
 
        right.text_sha1 = 321
115
 
        right.symlink_target='foo'
116
 
        self.assertEqual((False, False), left.detect_changes(right))
117
 
        self.assertEqual((False, False), right.detect_changes(left))
118
 
        left.symlink_target = 'different'
119
 
        self.assertEqual((True, False), left.detect_changes(right))
120
 
        self.assertEqual((True, False), right.detect_changes(left))
121
 
 
122
 
    def test_file_has_text(self):
123
 
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
124
 
        self.failUnless(file.has_text())
125
 
 
126
 
    def test_directory_has_text(self):
127
 
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
128
 
        self.failIf(dir.has_text())
129
 
 
130
 
    def test_link_has_text(self):
131
 
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
132
 
        self.failIf(link.has_text())
133
 
 
134
 
 
135
 
class TestEntryDiffing(TestCaseInTempDir):
136
 
 
137
 
    def setUp(self):
138
 
        super(TestEntryDiffing, self).setUp()
139
 
        self.branch = Branch.initialize(u'.')
140
 
        self.wt = self.branch.working_tree()
141
 
        print >> open('file', 'wb'), 'foo'
142
 
        self.branch.working_tree().add(['file'], ['fileid'])
143
 
        if has_symlinks():
144
 
            os.symlink('target1', 'symlink')
145
 
            self.branch.working_tree().add(['symlink'], ['linkid'])
146
 
        self.wt.commit('message_1', rev_id = '1')
147
 
        print >> open('file', 'wb'), 'bar'
148
 
        if has_symlinks():
149
 
            os.unlink('symlink')
150
 
            os.symlink('target2', 'symlink')
151
 
        self.tree_1 = self.branch.repository.revision_tree('1')
152
 
        self.inv_1 = self.branch.repository.get_inventory('1')
153
 
        self.file_1 = self.inv_1['fileid']
154
 
        self.tree_2 = self.branch.working_tree()
155
 
        self.inv_2 = self.tree_2.read_working_inventory()
156
 
        self.file_2 = self.inv_2['fileid']
157
 
        if has_symlinks():
158
 
            self.link_1 = self.inv_1['linkid']
159
 
            self.link_2 = self.inv_2['linkid']
160
 
 
161
 
    def test_file_diff_deleted(self):
162
 
        output = StringIO()
163
 
        self.file_1.diff(internal_diff, 
164
 
                          "old_label", self.tree_1,
165
 
                          "/dev/null", None, None,
166
 
                          output)
167
 
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
168
 
                                            "+++ /dev/null\t\n"
169
 
                                            "@@ -1,1 +0,0 @@\n"
170
 
                                            "-foo\n"
171
 
                                            "\n")
172
 
 
173
 
    def test_file_diff_added(self):
174
 
        output = StringIO()
175
 
        self.file_1.diff(internal_diff, 
176
 
                          "new_label", self.tree_1,
177
 
                          "/dev/null", None, None,
178
 
                          output, reverse=True)
179
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
180
 
                                            "+++ new_label\t\n"
181
 
                                            "@@ -0,0 +1,1 @@\n"
182
 
                                            "+foo\n"
183
 
                                            "\n")
184
 
 
185
 
    def test_file_diff_changed(self):
186
 
        output = StringIO()
187
 
        self.file_1.diff(internal_diff, 
188
 
                          "/dev/null", self.tree_1, 
189
 
                          "new_label", self.file_2, self.tree_2,
190
 
                          output)
191
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
192
 
                                            "+++ new_label\t\n"
193
 
                                            "@@ -1,1 +1,1 @@\n"
194
 
                                            "-foo\n"
195
 
                                            "+bar\n"
196
 
                                            "\n")
197
 
        
198
 
    def test_link_diff_deleted(self):
199
 
        if not has_symlinks():
200
 
            return
201
 
        output = StringIO()
202
 
        self.link_1.diff(internal_diff, 
203
 
                          "old_label", self.tree_1,
204
 
                          "/dev/null", None, None,
205
 
                          output)
206
 
        self.assertEqual(output.getvalue(),
207
 
                         "=== target was 'target1'\n")
208
 
 
209
 
    def test_link_diff_added(self):
210
 
        if not has_symlinks():
211
 
            return
212
 
        output = StringIO()
213
 
        self.link_1.diff(internal_diff, 
214
 
                          "new_label", self.tree_1,
215
 
                          "/dev/null", None, None,
216
 
                          output, reverse=True)
217
 
        self.assertEqual(output.getvalue(),
218
 
                         "=== target is 'target1'\n")
219
 
 
220
 
    def test_link_diff_changed(self):
221
 
        if not has_symlinks():
222
 
            return
223
 
        output = StringIO()
224
 
        self.link_1.diff(internal_diff, 
225
 
                          "/dev/null", self.tree_1, 
226
 
                          "new_label", self.link_2, self.tree_2,
227
 
                          output)
228
 
        self.assertEqual(output.getvalue(),
229
 
                         "=== target changed 'target1' => 'target2'\n")
230
 
 
231
 
 
232
 
class TestSnapshot(TestCaseInTempDir):
233
 
 
234
 
    def setUp(self):
235
 
        # for full testing we'll need a branch
236
 
        # with a subdir to test parent changes.
237
 
        # and a file, link and dir under that.
238
 
        # but right now I only need one attribute
239
 
        # to change, and then test merge patterns
240
 
        # with fake parent entries.
241
 
        super(TestSnapshot, self).setUp()
242
 
        self.branch = Branch.initialize(u'.')
243
 
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
244
 
        self.branch.working_tree().add(['subdir', 'subdir/file'],
245
 
                                       ['dirid', 'fileid'])
246
 
        if has_symlinks():
247
 
            pass
248
 
        self.wt = self.branch.working_tree()
249
 
        self.wt.commit('message_1', rev_id = '1')
250
 
        self.tree_1 = self.branch.repository.revision_tree('1')
251
 
        self.inv_1 = self.branch.repository.get_inventory('1')
252
 
        self.file_1 = self.inv_1['fileid']
253
 
        self.work_tree = self.branch.working_tree()
254
 
        self.file_active = self.work_tree.inventory['fileid']
255
 
 
256
 
    def test_snapshot_new_revision(self):
257
 
        # This tests that a simple commit with no parents makes a new
258
 
        # revision value in the inventory entry
259
 
        self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree, 
260
 
                                  self.branch.repository.weave_store,
261
 
                                  self.branch.get_transaction())
262
 
        # expected outcome - file_1 has a revision id of '2', and we can get
263
 
        # its text of 'file contents' out of the weave.
264
 
        self.assertEqual(self.file_1.revision, '1')
265
 
        self.assertEqual(self.file_active.revision, '2')
266
 
        # this should be a separate test probably, but lets check it once..
267
 
        lines = self.branch.repository.weave_store.get_lines('fileid','2',
268
 
            self.branch.get_transaction())
269
 
        self.assertEqual(lines, ['contents of subdir/file\n'])
270
 
 
271
 
    def test_snapshot_unchanged(self):
272
 
        #This tests that a simple commit does not make a new entry for
273
 
        # an unchanged inventory entry
274
 
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
275
 
                                  self.work_tree, 
276
 
                                  self.branch.repository.weave_store,
277
 
                                  self.branch.get_transaction())
278
 
        self.assertEqual(self.file_1.revision, '1')
279
 
        self.assertEqual(self.file_active.revision, '1')
280
 
        self.assertRaises(errors.WeaveError,
281
 
                          self.branch.repository.weave_store.get_lines, 
282
 
                          'fileid', '2', self.branch.get_transaction())
283
 
 
284
 
    def test_snapshot_merge_identical_different_revid(self):
285
 
        # This tests that a commit with two identical parents, one of which has
286
 
        # a different revision id, results in a new revision id in the entry.
287
 
        # 1->other, commit a merge of other against 1, results in 2.
288
 
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
289
 
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
290
 
        other_ie.revision = '1'
291
 
        other_ie.text_sha1 = self.file_1.text_sha1
292
 
        other_ie.text_size = self.file_1.text_size
293
 
        self.assertEqual(self.file_1, other_ie)
294
 
        other_ie.revision = 'other'
295
 
        self.assertNotEqual(self.file_1, other_ie)
296
 
        self.branch.repository.weave_store.add_identical_text('fileid', '1', 
297
 
            'other', ['1'], self.branch.get_transaction())
298
 
        self.file_active.snapshot('2', 'subdir/file', 
299
 
                                  {'1':self.file_1, 'other':other_ie},
300
 
                                  self.work_tree, 
301
 
                                  self.branch.repository.weave_store,
302
 
                                  self.branch.get_transaction())
303
 
        self.assertEqual(self.file_active.revision, '2')
304
 
 
305
 
    def test_snapshot_changed(self):
306
 
        # This tests that a commit with one different parent results in a new
307
 
        # revision id in the entry.
308
 
        self.file_active.name='newname'
309
 
        rename('subdir/file', 'subdir/newname')
310
 
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
311
 
                                  self.work_tree, 
312
 
                                  self.branch.repository.weave_store,
313
 
                                  self.branch.get_transaction())
314
 
        # expected outcome - file_1 has a revision id of '2'
315
 
        self.assertEqual(self.file_active.revision, '2')
316
 
 
317
 
 
318
 
class TestPreviousHeads(TestCaseInTempDir):
319
 
 
320
 
    def setUp(self):
321
 
        # we want several inventories, that respectively
322
 
        # give use the following scenarios:
323
 
        # A) fileid not in any inventory (A),
324
 
        # B) fileid present in one inventory (B) and (A,B)
325
 
        # C) fileid present in two inventories, and they
326
 
        #   are not mutual descendents (B, C)
327
 
        # D) fileid present in two inventories and one is
328
 
        #   a descendent of the other. (B, D)
329
 
        super(TestPreviousHeads, self).setUp()
330
 
        self.build_tree(['file'])
331
 
        self.branch = Branch.initialize(u'.')
332
 
        self.wt = self.branch.working_tree()
333
 
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
334
 
        self.inv_A = self.branch.repository.get_inventory('A')
335
 
        self.wt.add(['file'], ['fileid'])
336
 
        self.wt.commit('add file', rev_id='B')
337
 
        self.inv_B = self.branch.repository.get_inventory('B')
338
 
        self.branch.lock_write()
339
 
        try:
340
 
            self.branch.control_files.put_utf8('revision-history', 'A\n')
341
 
        finally:
342
 
            self.branch.unlock()
343
 
        self.assertEqual(self.branch.revision_history(), ['A'])
344
 
        self.wt.commit('another add of file', rev_id='C')
345
 
        self.inv_C = self.branch.repository.get_inventory('C')
346
 
        self.wt.add_pending_merge('B')
347
 
        self.wt.commit('merge in B', rev_id='D')
348
 
        self.inv_D = self.branch.repository.get_inventory('D')
349
 
        self.file_active = self.branch.working_tree().inventory['fileid']
350
 
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
351
 
            self.branch.get_transaction())
352
 
        
353
 
    def get_previous_heads(self, inventories):
354
 
        return self.file_active.find_previous_heads(inventories, self.weave)
355
 
        
356
 
    def test_fileid_in_no_inventory(self):
357
 
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
358
 
 
359
 
    def test_fileid_in_one_inventory(self):
360
 
        self.assertEqual({'B':self.inv_B['fileid']},
361
 
                         self.get_previous_heads([self.inv_B]))
362
 
        self.assertEqual({'B':self.inv_B['fileid']},
363
 
                         self.get_previous_heads([self.inv_A, self.inv_B]))
364
 
        self.assertEqual({'B':self.inv_B['fileid']},
365
 
                         self.get_previous_heads([self.inv_B, self.inv_A]))
366
 
 
367
 
    def test_fileid_in_two_inventories_gives_both_entries(self):
368
 
        self.assertEqual({'B':self.inv_B['fileid'],
369
 
                          'C':self.inv_C['fileid']},
370
 
                          self.get_previous_heads([self.inv_B, self.inv_C]))
371
 
        self.assertEqual({'B':self.inv_B['fileid'],
372
 
                          'C':self.inv_C['fileid']},
373
 
                          self.get_previous_heads([self.inv_C, self.inv_B]))
374
 
 
375
 
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
376
 
        self.assertEqual({'D':self.inv_D['fileid']},
377
 
                         self.get_previous_heads([self.inv_B, self.inv_D]))
378
 
        self.assertEqual({'D':self.inv_D['fileid']},
379
 
                         self.get_previous_heads([self.inv_D, self.inv_B]))
380
 
 
381
 
    # TODO: test two inventories with the same file revision 
382
 
 
383
 
 
384
 
class TestExecutable(TestCaseInTempDir):
385
 
 
386
 
    def test_stays_executable(self):
387
 
        basic_inv = """<inventory format="5">
388
 
<file file_id="a-20051208024829-849e76f7968d7a86" name="a" executable="yes" />
389
 
<file file_id="b-20051208024829-849e76f7968d7a86" name="b" />
390
 
</inventory>
391
 
"""
392
 
        os.mkdir('b1')
393
 
        b = Branch.initialize('b1')
394
 
        open('b1/a', 'wb').write('a test\n')
395
 
        open('b1/b', 'wb').write('b test\n')
396
 
        os.chmod('b1/a', 0755)
397
 
        os.chmod('b1/b', 0644)
398
 
        # Manually writing the inventory, to ensure that
399
 
        # the executable="yes" entry is set for 'a' and not for 'b'
400
 
        open('b1/.bzr/inventory', 'wb').write(basic_inv)
401
 
 
402
 
        a_id = "a-20051208024829-849e76f7968d7a86"
403
 
        b_id = "b-20051208024829-849e76f7968d7a86"
404
 
        t = b.working_tree()
405
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
406
 
 
407
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
408
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
409
 
 
410
 
        t.commit('adding a,b', rev_id='r1')
411
 
 
412
 
        rev_tree = b.repository.revision_tree('r1')
413
 
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
414
 
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
415
 
 
416
 
        self.failUnless(rev_tree.inventory[a_id].executable)
417
 
        self.failIf(rev_tree.inventory[b_id].executable)
418
 
 
419
 
        # Make sure the entries are gone
420
 
        os.remove('b1/a')
421
 
        os.remove('b1/b')
422
 
        self.failIf(t.has_id(a_id))
423
 
        self.failIf(t.has_filename('a'))
424
 
        self.failIf(t.has_id(b_id))
425
 
        self.failIf(t.has_filename('b'))
426
 
 
427
 
        # Make sure that revert is able to bring them back,
428
 
        # and sets 'a' back to being executable
429
 
 
430
 
        t.revert(['b1/a', 'b1/b'], rev_tree, backups=False)
431
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
432
 
 
433
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
434
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
435
 
 
436
 
        # Now remove them again, and make sure that after a
437
 
        # commit, they are still marked correctly
438
 
        os.remove('b1/a')
439
 
        os.remove('b1/b')
440
 
        t.commit('removed', rev_id='r2')
441
 
 
442
 
        self.assertEqual([], [cn for cn,ie in t.inventory.iter_entries()])
443
 
        self.failIf(t.has_id(a_id))
444
 
        self.failIf(t.has_filename('a'))
445
 
        self.failIf(t.has_id(b_id))
446
 
        self.failIf(t.has_filename('b'))
447
 
 
448
 
        # Now revert back to the previous commit
449
 
        t.revert([], rev_tree, backups=False)
450
 
        # TODO: FIXME: For some reason, after revert, the tree does not 
451
 
        # regenerate its working inventory, so we have to manually delete
452
 
        # the working tree, and create a new one
453
 
        # This seems to happen any time you do a merge operation on the
454
 
        # working tree
455
 
        del t
456
 
        t = b.working_tree()
457
 
 
458
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
459
 
 
460
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
461
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
462
 
 
463
 
        # Now make sure that 'bzr branch' also preserves the
464
 
        # executable bit
465
 
        # TODO: Maybe this should be a blackbox test
466
 
        b.clone('b2', revision='r1')
467
 
        b2 = Branch.open('b2')
468
 
        self.assertEquals('r1', b2.last_revision())
469
 
        t2 = b2.working_tree()
470
 
 
471
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
472
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
473
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
474
 
 
475
 
        # Make sure pull will delete the files
476
 
        t2.pull(b)
477
 
        self.assertEquals('r2', b2.last_revision())
478
 
        # FIXME: Same thing here, t2 needs to be recreated
479
 
        del t2
480
 
        t2 = b2.working_tree()
481
 
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
482
 
 
483
 
        # Now commit the changes on the first branch
484
 
        # so that the second branch can pull the changes
485
 
        # and make sure that the executable bit has been copied
486
 
        t.commit('resurrected', rev_id='r3')
487
 
 
488
 
        t2.pull(b)
489
 
        # FIXME: And here
490
 
        del t2
491
 
        t2 = b2.working_tree()
492
 
        self.assertEquals('r3', b2.last_revision())
493
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
494
 
 
495
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
496
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")