~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testinv.py

- help for global options

- test for this

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005 by 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
20
19
 
21
 
from bzrlib import errors, inventory, osutils
22
20
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, 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
 
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
31
28
 
32
29
 
33
30
class TestInventory(TestCase):
34
31
 
35
32
    def test_is_within(self):
 
33
        from bzrlib.osutils import is_inside_any
36
34
 
37
 
        SRC_FOO_C = pathjoin('src', 'foo.c')
 
35
        SRC_FOO_C = os.path.join('src', 'foo.c')
38
36
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
39
37
                         (['src'], SRC_FOO_C),
40
38
                         (['src'], 'src'),
44
42
        for dirs, fn in [(['src'], 'srccontrol'),
45
43
                         (['src'], 'srccontrol/foo')]:
46
44
            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))
56
45
            
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
 
 
62
46
    def test_ids(self):
63
47
        """Test detection of files within selected directories."""
64
48
        inv = Inventory()
75
59
        
76
60
        self.assert_('src-id' in inv)
77
61
 
78
 
    def test_iter_entries(self):
79
 
        inv = Inventory()
80
 
        
81
 
        for args in [('src', 'directory', 'src-id'), 
82
 
                     ('doc', 'directory', 'doc-id'), 
83
 
                     ('src/hello.c', 'file', 'hello-id'),
84
 
                     ('src/bye.c', 'file', 'bye-id'),
85
 
                     ('Makefile', 'file', 'makefile-id')]:
86
 
            inv.add_path(*args)
87
 
 
88
 
        self.assertEqual([
89
 
            ('', ROOT_ID),
90
 
            ('Makefile', 'makefile-id'),
91
 
            ('doc', 'doc-id'),
92
 
            ('src', 'src-id'),
93
 
            ('src/bye.c', 'bye-id'),
94
 
            ('src/hello.c', 'hello-id'),
95
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
96
 
            
97
 
    def test_iter_entries_by_dir(self):
98
 
        inv = Inventory()
99
 
        
100
 
        for args in [('src', 'directory', 'src-id'), 
101
 
                     ('doc', 'directory', 'doc-id'), 
102
 
                     ('src/hello.c', 'file', 'hello-id'),
103
 
                     ('src/bye.c', 'file', 'bye-id'),
104
 
                     ('zz', 'file', 'zz-id'),
105
 
                     ('src/sub/', 'directory', 'sub-id'),
106
 
                     ('src/zz.c', 'file', 'zzc-id'),
107
 
                     ('src/sub/a', 'file', 'a-id'),
108
 
                     ('Makefile', 'file', 'makefile-id')]:
109
 
            inv.add_path(*args)
110
 
 
111
 
        self.assertEqual([
112
 
            ('', ROOT_ID),
113
 
            ('Makefile', 'makefile-id'),
114
 
            ('doc', 'doc-id'),
115
 
            ('src', 'src-id'),
116
 
            ('zz', 'zz-id'),
117
 
            ('src/bye.c', 'bye-id'),
118
 
            ('src/hello.c', 'hello-id'),
119
 
            ('src/sub', 'sub-id'),
120
 
            ('src/zz.c', 'zzc-id'),
121
 
            ('src/sub/a', 'a-id'),
122
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir()])
123
 
            
 
62
 
124
63
    def test_version(self):
125
64
        """Inventory remembers the text's version."""
126
65
        inv = Inventory()
193
132
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
194
133
        self.failIf(link.has_text())
195
134
 
196
 
    def test_make_entry(self):
197
 
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
198
 
            inventory.InventoryFile)
199
 
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
200
 
            inventory.InventoryLink)
201
 
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
202
 
            inventory.InventoryDirectory)
203
 
 
204
 
    def test_make_entry_non_normalized(self):
205
 
        orig_normalized_filename = osutils.normalized_filename
206
 
 
207
 
        try:
208
 
            osutils.normalized_filename = osutils._accessible_normalized_filename
209
 
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
210
 
            self.assertEqual(u'\xe5', entry.name)
211
 
            self.assertIsInstance(entry, inventory.InventoryFile)
212
 
 
213
 
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
214
 
            self.assertRaises(errors.InvalidNormalization,
215
 
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
216
 
        finally:
217
 
            osutils.normalized_filename = orig_normalized_filename
218
 
 
219
 
 
220
 
class TestEntryDiffing(TestCaseWithTransport):
 
135
 
 
136
class TestEntryDiffing(TestCaseInTempDir):
221
137
 
222
138
    def setUp(self):
223
139
        super(TestEntryDiffing, self).setUp()
224
 
        self.wt = self.make_branch_and_tree('.')
225
 
        self.branch = self.wt.branch
 
140
        self.branch = Branch.initialize('.')
226
141
        print >> open('file', 'wb'), 'foo'
227
 
        print >> open('binfile', 'wb'), 'foo'
228
 
        self.wt.add(['file'], ['fileid'])
229
 
        self.wt.add(['binfile'], ['binfileid'])
 
142
        self.branch.add(['file'], ['fileid'])
230
143
        if has_symlinks():
231
144
            os.symlink('target1', 'symlink')
232
 
            self.wt.add(['symlink'], ['linkid'])
233
 
        self.wt.commit('message_1', rev_id = '1')
 
145
            self.branch.add(['symlink'], ['linkid'])
 
146
        self.branch.commit('message_1', rev_id = '1')
234
147
        print >> open('file', 'wb'), 'bar'
235
 
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
236
148
        if has_symlinks():
237
149
            os.unlink('symlink')
238
150
            os.symlink('target2', 'symlink')
239
 
        self.tree_1 = self.branch.repository.revision_tree('1')
240
 
        self.inv_1 = self.branch.repository.get_inventory('1')
 
151
        self.tree_1 = self.branch.revision_tree('1')
 
152
        self.inv_1 = self.branch.get_inventory('1')
241
153
        self.file_1 = self.inv_1['fileid']
242
 
        self.file_1b = self.inv_1['binfileid']
243
 
        self.tree_2 = self.wt
244
 
        self.inv_2 = self.tree_2.read_working_inventory()
 
154
        self.tree_2 = self.branch.working_tree()
 
155
        self.inv_2 = self.branch.inventory
245
156
        self.file_2 = self.inv_2['fileid']
246
 
        self.file_2b = self.inv_2['binfileid']
247
157
        if has_symlinks():
248
158
            self.link_1 = self.inv_1['linkid']
249
159
            self.link_2 = self.inv_2['linkid']
285
195
                                            "+bar\n"
286
196
                                            "\n")
287
197
        
288
 
    def test_file_diff_binary(self):
289
 
        output = StringIO()
290
 
        self.file_1.diff(internal_diff, 
291
 
                          "/dev/null", self.tree_1, 
292
 
                          "new_label", self.file_2b, self.tree_2,
293
 
                          output)
294
 
        self.assertEqual(output.getvalue(), 
295
 
                         "Binary files /dev/null and new_label differ\n")
296
198
    def test_link_diff_deleted(self):
297
199
        if not has_symlinks():
298
200
            return
327
229
                         "=== target changed 'target1' => 'target2'\n")
328
230
 
329
231
 
330
 
class TestSnapshot(TestCaseWithTransport):
 
232
class TestSnapshot(TestCaseInTempDir):
331
233
 
332
234
    def setUp(self):
333
235
        # for full testing we'll need a branch
337
239
        # to change, and then test merge patterns
338
240
        # with fake parent entries.
339
241
        super(TestSnapshot, self).setUp()
340
 
        self.wt = self.make_branch_and_tree('.')
341
 
        self.branch = self.wt.branch
342
 
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
343
 
        self.wt.add(['subdir', 'subdir/file'],
344
 
                                       ['dirid', 'fileid'])
 
242
        self.branch = Branch.initialize('.')
 
243
        self.build_tree(['subdir/', 'subdir/file'])
 
244
        self.branch.add(['subdir', 'subdir/file'], ['dirid', 'fileid'])
345
245
        if has_symlinks():
346
246
            pass
347
 
        self.wt.commit('message_1', rev_id = '1')
348
 
        self.tree_1 = self.branch.repository.revision_tree('1')
349
 
        self.inv_1 = self.branch.repository.get_inventory('1')
 
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')
350
250
        self.file_1 = self.inv_1['fileid']
351
 
        self.file_active = self.wt.inventory['fileid']
352
 
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
 
251
        self.work_tree = self.branch.working_tree()
 
252
        self.file_active = self.work_tree.inventory['fileid']
353
253
 
354
254
    def test_snapshot_new_revision(self):
355
255
        # This tests that a simple commit with no parents makes a new
356
256
        # revision value in the inventory entry
357
 
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
 
257
        self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree, 
 
258
                                  self.branch.weave_store,
 
259
                                  self.branch.get_transaction())
358
260
        # expected outcome - file_1 has a revision id of '2', and we can get
359
261
        # its text of 'file contents' out of the weave.
360
262
        self.assertEqual(self.file_1.revision, '1')
361
263
        self.assertEqual(self.file_active.revision, '2')
362
264
        # this should be a separate test probably, but lets check it once..
363
 
        lines = self.branch.repository.weave_store.get_weave(
364
 
            'fileid', 
365
 
            self.branch.get_transaction()).get_lines('2')
 
265
        lines = self.branch.weave_store.get_lines('fileid','2',
 
266
            self.branch.get_transaction())
366
267
        self.assertEqual(lines, ['contents of subdir/file\n'])
367
268
 
368
269
    def test_snapshot_unchanged(self):
369
270
        #This tests that a simple commit does not make a new entry for
370
271
        # an unchanged inventory entry
371
272
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
372
 
                                  self.wt, self.builder)
 
273
                                  self.work_tree, self.branch.weave_store,
 
274
                                  self.branch.get_transaction())
373
275
        self.assertEqual(self.file_1.revision, '1')
374
276
        self.assertEqual(self.file_active.revision, '1')
375
 
        vf = self.branch.repository.weave_store.get_weave(
376
 
            'fileid', 
377
 
            self.branch.repository.get_transaction())
378
 
        self.assertRaises(errors.RevisionNotPresent,
379
 
                          vf.get_lines,
380
 
                          '2')
 
277
        self.assertRaises(errors.WeaveError,
 
278
                          self.branch.weave_store.get_lines, 'fileid', '2',
 
279
                          self.branch.get_transaction())
381
280
 
382
281
    def test_snapshot_merge_identical_different_revid(self):
383
282
        # This tests that a commit with two identical parents, one of which has
391
290
        self.assertEqual(self.file_1, other_ie)
392
291
        other_ie.revision = 'other'
393
292
        self.assertNotEqual(self.file_1, other_ie)
394
 
        versionfile = self.branch.repository.weave_store.get_weave(
395
 
            'fileid', self.branch.repository.get_transaction())
396
 
        versionfile.clone_text('other', '1', ['1'])
 
293
        self.branch.weave_store.add_identical_text('fileid', '1', 'other', ['1'],
 
294
            self.branch.get_transaction())
397
295
        self.file_active.snapshot('2', 'subdir/file', 
398
296
                                  {'1':self.file_1, 'other':other_ie},
399
 
                                  self.wt, self.builder)
 
297
                                  self.work_tree, self.branch.weave_store,
 
298
                                  self.branch.get_transaction())
400
299
        self.assertEqual(self.file_active.revision, '2')
401
300
 
402
301
    def test_snapshot_changed(self):
405
304
        self.file_active.name='newname'
406
305
        rename('subdir/file', 'subdir/newname')
407
306
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
408
 
                                  self.wt, self.builder)
 
307
                                  self.work_tree, 
 
308
                                  self.branch.weave_store,
 
309
                                  self.branch.get_transaction())
409
310
        # expected outcome - file_1 has a revision id of '2'
410
311
        self.assertEqual(self.file_active.revision, '2')
411
312
 
412
313
 
413
 
class TestPreviousHeads(TestCaseWithTransport):
 
314
class TestPreviousHeads(TestCaseInTempDir):
414
315
 
415
316
    def setUp(self):
416
317
        # we want several inventories, that respectively
422
323
        # D) fileid present in two inventories and one is
423
324
        #   a descendent of the other. (B, D)
424
325
        super(TestPreviousHeads, self).setUp()
425
 
        self.wt = self.make_branch_and_tree('.')
426
 
        self.branch = self.wt.branch
427
326
        self.build_tree(['file'])
428
 
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
429
 
        self.inv_A = self.branch.repository.get_inventory('A')
430
 
        self.wt.add(['file'], ['fileid'])
431
 
        self.wt.commit('add file', rev_id='B')
432
 
        self.inv_B = self.branch.repository.get_inventory('B')
433
 
        uncommit(self.branch, tree=self.wt)
 
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')
434
334
        self.assertEqual(self.branch.revision_history(), ['A'])
435
 
        self.wt.commit('another add of file', rev_id='C')
436
 
        self.inv_C = self.branch.repository.get_inventory('C')
437
 
        self.wt.add_parent_tree_id('B')
438
 
        self.wt.commit('merge in B', rev_id='D')
439
 
        self.inv_D = self.branch.repository.get_inventory('D')
440
 
        self.file_active = self.wt.inventory['fileid']
441
 
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
442
 
            self.branch.repository.get_transaction())
 
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())
443
343
        
444
344
    def get_previous_heads(self, inventories):
445
 
        return self.file_active.find_previous_heads(
446
 
            inventories, 
447
 
            self.branch.repository.weave_store,
448
 
            self.branch.repository.get_transaction())
 
345
        return self.file_active.find_previous_heads(inventories, self.weave)
449
346
        
450
347
    def test_fileid_in_no_inventory(self):
451
348
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
473
370
                         self.get_previous_heads([self.inv_D, self.inv_B]))
474
371
 
475
372
    # TODO: test two inventories with the same file revision 
476
 
 
477
 
 
478
 
class TestDescribeChanges(TestCase):
479
 
 
480
 
    def test_describe_change(self):
481
 
        # we need to test the following change combinations:
482
 
        # rename
483
 
        # reparent
484
 
        # modify
485
 
        # gone
486
 
        # added
487
 
        # renamed/reparented and modified
488
 
        # change kind (perhaps can't be done yet?)
489
 
        # also, merged in combination with all of these?
490
 
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
491
 
        old_a.text_sha1 = '123132'
492
 
        old_a.text_size = 0
493
 
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
494
 
        new_a.text_sha1 = '123132'
495
 
        new_a.text_size = 0
496
 
 
497
 
        self.assertChangeDescription('unchanged', old_a, new_a)
498
 
 
499
 
        new_a.text_size = 10
500
 
        new_a.text_sha1 = 'abcabc'
501
 
        self.assertChangeDescription('modified', old_a, new_a)
502
 
 
503
 
        self.assertChangeDescription('added', None, new_a)
504
 
        self.assertChangeDescription('removed', old_a, None)
505
 
        # perhaps a bit questionable but seems like the most reasonable thing...
506
 
        self.assertChangeDescription('unchanged', None, None)
507
 
 
508
 
        # in this case it's both renamed and modified; show a rename and 
509
 
        # modification:
510
 
        new_a.name = 'newfilename'
511
 
        self.assertChangeDescription('modified and renamed', old_a, new_a)
512
 
 
513
 
        # reparenting is 'renaming'
514
 
        new_a.name = old_a.name
515
 
        new_a.parent_id = 'somedir-id'
516
 
        self.assertChangeDescription('modified and renamed', old_a, new_a)
517
 
 
518
 
        # reset the content values so its not modified
519
 
        new_a.text_size = old_a.text_size
520
 
        new_a.text_sha1 = old_a.text_sha1
521
 
        new_a.name = old_a.name
522
 
 
523
 
        new_a.name = 'newfilename'
524
 
        self.assertChangeDescription('renamed', old_a, new_a)
525
 
 
526
 
        # reparenting is 'renaming'
527
 
        new_a.name = old_a.name
528
 
        new_a.parent_id = 'somedir-id'
529
 
        self.assertChangeDescription('renamed', old_a, new_a)
530
 
 
531
 
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
532
 
        change = InventoryEntry.describe_change(old_ie, new_ie)
533
 
        self.assertEqual(expected_change, change)
534
 
 
535
 
 
536
 
class TestRevert(TestCaseWithTransport):
537
 
 
538
 
    def test_dangling_id(self):
539
 
        wt = self.make_branch_and_tree('b1')
540
 
        self.assertEqual(len(wt.inventory), 1)
541
 
        open('b1/a', 'wb').write('a test\n')
542
 
        wt.add('a')
543
 
        self.assertEqual(len(wt.inventory), 2)
544
 
        os.unlink('b1/a')
545
 
        wt.revert([])
546
 
        self.assertEqual(len(wt.inventory), 1)