~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Aaron Bentley
  • Date: 2006-11-10 01:55:55 UTC
  • mto: This revision was merged to the branch mainline in revision 2127.
  • Revision ID: aaron.bentley@utoronto.ca-20061110015555-f48202744b630209
Ignore html docs (both kinds)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
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
17
from cStringIO import StringIO
18
18
import os
 
19
import time
19
20
 
 
21
from bzrlib import errors, inventory, osutils
20
22
from bzrlib.branch import Branch
21
 
from bzrlib.clone import copy_branch
22
 
import bzrlib.errors as errors
23
23
from bzrlib.diff import internal_diff
24
 
from bzrlib.inventory import Inventory, ROOT_ID
25
 
import bzrlib.inventory as inventory
26
 
from bzrlib.osutils import has_symlinks, rename
27
 
from bzrlib.selftest import TestCase, TestCaseInTempDir
 
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
28
31
 
29
32
 
30
33
class TestInventory(TestCase):
31
34
 
32
35
    def test_is_within(self):
33
 
        from bzrlib.osutils import is_inside_any
34
36
 
35
 
        SRC_FOO_C = os.path.join('src', 'foo.c')
 
37
        SRC_FOO_C = pathjoin('src', 'foo.c')
36
38
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
37
39
                         (['src'], SRC_FOO_C),
38
40
                         (['src'], 'src'),
42
44
        for dirs, fn in [(['src'], 'srccontrol'),
43
45
                         (['src'], 'srccontrol/foo')]:
44
46
            self.assertFalse(is_inside_any(dirs, fn))
 
47
 
 
48
    def test_is_within_or_parent(self):
 
49
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
 
50
                         (['src'], 'src/foo.c'),
 
51
                         (['src/bar.c'], 'src'),
 
52
                         (['src/bar.c', 'bla/foo.c'], 'src'),
 
53
                         (['src'], 'src'),
 
54
                         ]:
 
55
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
45
56
            
 
57
        for dirs, fn in [(['src'], 'srccontrol'),
 
58
                         (['srccontrol/foo.c'], 'src'),
 
59
                         (['src'], 'srccontrol/foo')]:
 
60
            self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
 
61
 
46
62
    def test_ids(self):
47
63
        """Test detection of files within selected directories."""
48
64
        inv = Inventory()
59
75
        
60
76
        self.assert_('src-id' in inv)
61
77
 
62
 
 
 
78
    def test_non_directory_children(self):
 
79
        """Test path2id when a parent directory has no children"""
 
80
        inv = inventory.Inventory('tree_root')
 
81
        inv.add(inventory.InventoryFile('file-id','file', 
 
82
                                        parent_id='tree_root'))
 
83
        inv.add(inventory.InventoryLink('link-id','link', 
 
84
                                        parent_id='tree_root'))
 
85
        self.assertIs(None, inv.path2id('file/subfile'))
 
86
        self.assertIs(None, inv.path2id('link/subfile'))
 
87
 
 
88
    def test_iter_entries(self):
 
89
        inv = Inventory()
 
90
        
 
91
        for args in [('src', 'directory', 'src-id'), 
 
92
                     ('doc', 'directory', 'doc-id'), 
 
93
                     ('src/hello.c', 'file', 'hello-id'),
 
94
                     ('src/bye.c', 'file', 'bye-id'),
 
95
                     ('Makefile', 'file', 'makefile-id')]:
 
96
            inv.add_path(*args)
 
97
 
 
98
        self.assertEqual([
 
99
            ('', ROOT_ID),
 
100
            ('Makefile', 'makefile-id'),
 
101
            ('doc', 'doc-id'),
 
102
            ('src', 'src-id'),
 
103
            ('src/bye.c', 'bye-id'),
 
104
            ('src/hello.c', 'hello-id'),
 
105
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
 
106
            
 
107
    def test_iter_entries_by_dir(self):
 
108
        inv = Inventory()
 
109
        
 
110
        for args in [('src', 'directory', 'src-id'), 
 
111
                     ('doc', 'directory', 'doc-id'), 
 
112
                     ('src/hello.c', 'file', 'hello-id'),
 
113
                     ('src/bye.c', 'file', 'bye-id'),
 
114
                     ('zz', 'file', 'zz-id'),
 
115
                     ('src/sub/', 'directory', 'sub-id'),
 
116
                     ('src/zz.c', 'file', 'zzc-id'),
 
117
                     ('src/sub/a', 'file', 'a-id'),
 
118
                     ('Makefile', 'file', 'makefile-id')]:
 
119
            inv.add_path(*args)
 
120
 
 
121
        self.assertEqual([
 
122
            ('', ROOT_ID),
 
123
            ('Makefile', 'makefile-id'),
 
124
            ('doc', 'doc-id'),
 
125
            ('src', 'src-id'),
 
126
            ('zz', 'zz-id'),
 
127
            ('src/bye.c', 'bye-id'),
 
128
            ('src/hello.c', 'hello-id'),
 
129
            ('src/sub', 'sub-id'),
 
130
            ('src/zz.c', 'zzc-id'),
 
131
            ('src/sub/a', 'a-id'),
 
132
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir()])
 
133
            
63
134
    def test_version(self):
64
135
        """Inventory remembers the text's version."""
65
136
        inv = Inventory()
132
203
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
133
204
        self.failIf(link.has_text())
134
205
 
135
 
 
136
 
class TestEntryDiffing(TestCaseInTempDir):
 
206
    def test_make_entry(self):
 
207
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
 
208
            inventory.InventoryFile)
 
209
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
 
210
            inventory.InventoryLink)
 
211
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
 
212
            inventory.InventoryDirectory)
 
213
 
 
214
    def test_make_entry_non_normalized(self):
 
215
        orig_normalized_filename = osutils.normalized_filename
 
216
 
 
217
        try:
 
218
            osutils.normalized_filename = osutils._accessible_normalized_filename
 
219
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
 
220
            self.assertEqual(u'\xe5', entry.name)
 
221
            self.assertIsInstance(entry, inventory.InventoryFile)
 
222
 
 
223
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
224
            self.assertRaises(errors.InvalidNormalization,
 
225
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
 
226
        finally:
 
227
            osutils.normalized_filename = orig_normalized_filename
 
228
 
 
229
 
 
230
class TestEntryDiffing(TestCaseWithTransport):
137
231
 
138
232
    def setUp(self):
139
233
        super(TestEntryDiffing, self).setUp()
140
 
        self.branch = Branch.initialize('.')
 
234
        self.wt = self.make_branch_and_tree('.')
 
235
        self.branch = self.wt.branch
141
236
        print >> open('file', 'wb'), 'foo'
142
 
        self.branch.add(['file'], ['fileid'])
 
237
        print >> open('binfile', 'wb'), 'foo'
 
238
        self.wt.add(['file'], ['fileid'])
 
239
        self.wt.add(['binfile'], ['binfileid'])
143
240
        if has_symlinks():
144
241
            os.symlink('target1', 'symlink')
145
 
            self.branch.add(['symlink'], ['linkid'])
146
 
        self.branch.commit('message_1', rev_id = '1')
 
242
            self.wt.add(['symlink'], ['linkid'])
 
243
        self.wt.commit('message_1', rev_id = '1')
147
244
        print >> open('file', 'wb'), 'bar'
 
245
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
148
246
        if has_symlinks():
149
247
            os.unlink('symlink')
150
248
            os.symlink('target2', 'symlink')
151
 
        self.tree_1 = self.branch.revision_tree('1')
152
 
        self.inv_1 = self.branch.get_inventory('1')
 
249
        self.tree_1 = self.branch.repository.revision_tree('1')
 
250
        self.inv_1 = self.branch.repository.get_inventory('1')
153
251
        self.file_1 = self.inv_1['fileid']
154
 
        self.tree_2 = self.branch.working_tree()
155
 
        self.inv_2 = self.branch.inventory
 
252
        self.file_1b = self.inv_1['binfileid']
 
253
        self.tree_2 = self.wt
 
254
        self.inv_2 = self.tree_2.read_working_inventory()
156
255
        self.file_2 = self.inv_2['fileid']
 
256
        self.file_2b = self.inv_2['binfileid']
157
257
        if has_symlinks():
158
258
            self.link_1 = self.inv_1['linkid']
159
259
            self.link_2 = self.inv_2['linkid']
195
295
                                            "+bar\n"
196
296
                                            "\n")
197
297
        
 
298
    def test_file_diff_binary(self):
 
299
        output = StringIO()
 
300
        self.file_1.diff(internal_diff, 
 
301
                          "/dev/null", self.tree_1, 
 
302
                          "new_label", self.file_2b, self.tree_2,
 
303
                          output)
 
304
        self.assertEqual(output.getvalue(), 
 
305
                         "Binary files /dev/null and new_label differ\n")
198
306
    def test_link_diff_deleted(self):
199
307
        if not has_symlinks():
200
308
            return
229
337
                         "=== target changed 'target1' => 'target2'\n")
230
338
 
231
339
 
232
 
class TestSnapshot(TestCaseInTempDir):
 
340
class TestSnapshot(TestCaseWithTransport):
233
341
 
234
342
    def setUp(self):
235
343
        # for full testing we'll need a branch
239
347
        # to change, and then test merge patterns
240
348
        # with fake parent entries.
241
349
        super(TestSnapshot, self).setUp()
242
 
        self.branch = Branch.initialize('.')
243
 
        self.build_tree(['subdir/', 'subdir/file'])
244
 
        self.branch.add(['subdir', 'subdir/file'], ['dirid', 'fileid'])
 
350
        self.wt = self.make_branch_and_tree('.')
 
351
        self.branch = self.wt.branch
 
352
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
 
353
        self.wt.add(['subdir', 'subdir/file'],
 
354
                                       ['dirid', 'fileid'])
245
355
        if has_symlinks():
246
356
            pass
247
 
        self.branch.commit('message_1', rev_id = '1')
248
 
        self.tree_1 = self.branch.revision_tree('1')
249
 
        self.inv_1 = self.branch.get_inventory('1')
 
357
        self.wt.commit('message_1', rev_id = '1')
 
358
        self.tree_1 = self.branch.repository.revision_tree('1')
 
359
        self.inv_1 = self.branch.repository.get_inventory('1')
250
360
        self.file_1 = self.inv_1['fileid']
251
 
        self.work_tree = self.branch.working_tree()
252
 
        self.file_active = self.work_tree.inventory['fileid']
 
361
        self.file_active = self.wt.inventory['fileid']
 
362
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
253
363
 
254
364
    def test_snapshot_new_revision(self):
255
365
        # This tests that a simple commit with no parents makes a new
256
366
        # revision value in the inventory entry
257
 
        self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree, 
258
 
                                  self.branch.weave_store,
259
 
                                  self.branch.get_transaction())
 
367
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
260
368
        # expected outcome - file_1 has a revision id of '2', and we can get
261
369
        # its text of 'file contents' out of the weave.
262
370
        self.assertEqual(self.file_1.revision, '1')
263
371
        self.assertEqual(self.file_active.revision, '2')
264
372
        # this should be a separate test probably, but lets check it once..
265
 
        lines = self.branch.weave_store.get_lines('fileid','2',
266
 
            self.branch.get_transaction())
 
373
        lines = self.branch.repository.weave_store.get_weave(
 
374
            'fileid', 
 
375
            self.branch.get_transaction()).get_lines('2')
267
376
        self.assertEqual(lines, ['contents of subdir/file\n'])
268
377
 
269
378
    def test_snapshot_unchanged(self):
270
379
        #This tests that a simple commit does not make a new entry for
271
380
        # an unchanged inventory entry
272
381
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
273
 
                                  self.work_tree, self.branch.weave_store,
274
 
                                  self.branch.get_transaction())
 
382
                                  self.wt, self.builder)
275
383
        self.assertEqual(self.file_1.revision, '1')
276
384
        self.assertEqual(self.file_active.revision, '1')
277
 
        self.assertRaises(errors.WeaveError,
278
 
                          self.branch.weave_store.get_lines, 'fileid', '2',
279
 
                          self.branch.get_transaction())
 
385
        vf = self.branch.repository.weave_store.get_weave(
 
386
            'fileid', 
 
387
            self.branch.repository.get_transaction())
 
388
        self.assertRaises(errors.RevisionNotPresent,
 
389
                          vf.get_lines,
 
390
                          '2')
280
391
 
281
392
    def test_snapshot_merge_identical_different_revid(self):
282
393
        # This tests that a commit with two identical parents, one of which has
290
401
        self.assertEqual(self.file_1, other_ie)
291
402
        other_ie.revision = 'other'
292
403
        self.assertNotEqual(self.file_1, other_ie)
293
 
        self.branch.weave_store.add_identical_text('fileid', '1', 'other', ['1'],
294
 
            self.branch.get_transaction())
 
404
        versionfile = self.branch.repository.weave_store.get_weave(
 
405
            'fileid', self.branch.repository.get_transaction())
 
406
        versionfile.clone_text('other', '1', ['1'])
295
407
        self.file_active.snapshot('2', 'subdir/file', 
296
408
                                  {'1':self.file_1, 'other':other_ie},
297
 
                                  self.work_tree, self.branch.weave_store,
298
 
                                  self.branch.get_transaction())
 
409
                                  self.wt, self.builder)
299
410
        self.assertEqual(self.file_active.revision, '2')
300
411
 
301
412
    def test_snapshot_changed(self):
304
415
        self.file_active.name='newname'
305
416
        rename('subdir/file', 'subdir/newname')
306
417
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
307
 
                                  self.work_tree, 
308
 
                                  self.branch.weave_store,
309
 
                                  self.branch.get_transaction())
 
418
                                  self.wt, self.builder)
310
419
        # expected outcome - file_1 has a revision id of '2'
311
420
        self.assertEqual(self.file_active.revision, '2')
312
421
 
313
422
 
314
 
class TestPreviousHeads(TestCaseInTempDir):
 
423
class TestPreviousHeads(TestCaseWithTransport):
315
424
 
316
425
    def setUp(self):
317
426
        # we want several inventories, that respectively
323
432
        # D) fileid present in two inventories and one is
324
433
        #   a descendent of the other. (B, D)
325
434
        super(TestPreviousHeads, self).setUp()
 
435
        self.wt = self.make_branch_and_tree('.')
 
436
        self.branch = self.wt.branch
326
437
        self.build_tree(['file'])
327
 
        self.branch = Branch.initialize('.')
328
 
        self.branch.commit('new branch', allow_pointless=True, rev_id='A')
329
 
        self.inv_A = self.branch.get_inventory('A')
330
 
        self.branch.add(['file'], ['fileid'])
331
 
        self.branch.commit('add file', rev_id='B')
332
 
        self.inv_B = self.branch.get_inventory('B')
333
 
        self.branch.put_controlfile('revision-history', 'A\n')
 
438
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
 
439
        self.inv_A = self.branch.repository.get_inventory('A')
 
440
        self.wt.add(['file'], ['fileid'])
 
441
        self.wt.commit('add file', rev_id='B')
 
442
        self.inv_B = self.branch.repository.get_inventory('B')
 
443
        uncommit(self.branch, tree=self.wt)
334
444
        self.assertEqual(self.branch.revision_history(), ['A'])
335
 
        self.branch.commit('another add of file', rev_id='C')
336
 
        self.inv_C = self.branch.get_inventory('C')
337
 
        self.branch.add_pending_merge('B')
338
 
        self.branch.commit('merge in B', rev_id='D')
339
 
        self.inv_D = self.branch.get_inventory('D')
340
 
        self.file_active = self.branch.working_tree().inventory['fileid']
341
 
        self.weave = self.branch.weave_store.get_weave('fileid',
342
 
            self.branch.get_transaction())
 
445
        self.wt.commit('another add of file', rev_id='C')
 
446
        self.inv_C = self.branch.repository.get_inventory('C')
 
447
        self.wt.add_parent_tree_id('B')
 
448
        self.wt.commit('merge in B', rev_id='D')
 
449
        self.inv_D = self.branch.repository.get_inventory('D')
 
450
        self.file_active = self.wt.inventory['fileid']
 
451
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
452
            self.branch.repository.get_transaction())
343
453
        
344
454
    def get_previous_heads(self, inventories):
345
 
        return self.file_active.find_previous_heads(inventories, self.weave)
 
455
        return self.file_active.find_previous_heads(
 
456
            inventories, 
 
457
            self.branch.repository.weave_store,
 
458
            self.branch.repository.get_transaction())
346
459
        
347
460
    def test_fileid_in_no_inventory(self):
348
461
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
370
483
                         self.get_previous_heads([self.inv_D, self.inv_B]))
371
484
 
372
485
    # TODO: test two inventories with the same file revision 
 
486
 
 
487
 
 
488
class TestDescribeChanges(TestCase):
 
489
 
 
490
    def test_describe_change(self):
 
491
        # we need to test the following change combinations:
 
492
        # rename
 
493
        # reparent
 
494
        # modify
 
495
        # gone
 
496
        # added
 
497
        # renamed/reparented and modified
 
498
        # change kind (perhaps can't be done yet?)
 
499
        # also, merged in combination with all of these?
 
500
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
501
        old_a.text_sha1 = '123132'
 
502
        old_a.text_size = 0
 
503
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
504
        new_a.text_sha1 = '123132'
 
505
        new_a.text_size = 0
 
506
 
 
507
        self.assertChangeDescription('unchanged', old_a, new_a)
 
508
 
 
509
        new_a.text_size = 10
 
510
        new_a.text_sha1 = 'abcabc'
 
511
        self.assertChangeDescription('modified', old_a, new_a)
 
512
 
 
513
        self.assertChangeDescription('added', None, new_a)
 
514
        self.assertChangeDescription('removed', old_a, None)
 
515
        # perhaps a bit questionable but seems like the most reasonable thing...
 
516
        self.assertChangeDescription('unchanged', None, None)
 
517
 
 
518
        # in this case it's both renamed and modified; show a rename and 
 
519
        # modification:
 
520
        new_a.name = 'newfilename'
 
521
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
522
 
 
523
        # reparenting is 'renaming'
 
524
        new_a.name = old_a.name
 
525
        new_a.parent_id = 'somedir-id'
 
526
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
527
 
 
528
        # reset the content values so its not modified
 
529
        new_a.text_size = old_a.text_size
 
530
        new_a.text_sha1 = old_a.text_sha1
 
531
        new_a.name = old_a.name
 
532
 
 
533
        new_a.name = 'newfilename'
 
534
        self.assertChangeDescription('renamed', old_a, new_a)
 
535
 
 
536
        # reparenting is 'renaming'
 
537
        new_a.name = old_a.name
 
538
        new_a.parent_id = 'somedir-id'
 
539
        self.assertChangeDescription('renamed', old_a, new_a)
 
540
 
 
541
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
542
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
543
        self.assertEqual(expected_change, change)
 
544
 
 
545
 
 
546
class TestRevert(TestCaseWithTransport):
 
547
 
 
548
    def test_dangling_id(self):
 
549
        wt = self.make_branch_and_tree('b1')
 
550
        self.assertEqual(len(wt.inventory), 1)
 
551
        open('b1/a', 'wb').write('a test\n')
 
552
        wt.add('a')
 
553
        self.assertEqual(len(wt.inventory), 2)
 
554
        os.unlink('b1/a')
 
555
        wt.revert([])
 
556
        self.assertEqual(len(wt.inventory), 1)
 
557
 
 
558
 
 
559
class TestIsRoot(TestCase):
 
560
    """Ensure our root-checking code is accurate."""
 
561
 
 
562
    def test_is_root(self):
 
563
        inv = Inventory('TREE_ROOT')
 
564
        self.assertTrue(inv.is_root('TREE_ROOT'))
 
565
        self.assertFalse(inv.is_root('booga'))
 
566
        inv.root.file_id = 'booga'
 
567
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
568
        self.assertTrue(inv.is_root('booga'))
 
569
        # works properly even if no root is set
 
570
        inv.root = None
 
571
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
572
        self.assertFalse(inv.is_root('booga'))