~bzr-pqm/bzr/bzr.dev

1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
1
# Copyright (C) 2006 by 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
"""Tests for reconiliation of repositories."""
18
19
20
import bzrlib
21
import bzrlib.errors as errors
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
22
from bzrlib.reconcile import reconcile, Reconciler
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
23
from bzrlib.revision import Revision
24
from bzrlib.tests.repository_implementations.test_repository import TestCaseWithRepository
25
from bzrlib.transport import get_transport
26
from bzrlib.tree import EmptyTree
27
from bzrlib.workingtree import WorkingTree
28
29
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
30
class TestsNeedingReweave(TestCaseWithRepository):
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
31
32
    def setUp(self):
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
33
        super(TestsNeedingReweave, self).setUp()
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
34
        
35
        t = get_transport(self.get_url())
36
        # an empty inventory with no revision for testing with.
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
37
        # this is referenced by 'references_missing' to let us test
38
        # that all the cached data is correctly converted into ghost links
39
        # and the referenced inventory still cleaned.
40
        repo = self.make_repository('inventory_without_revision')
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
41
        inv = EmptyTree().inventory
42
        repo.add_inventory('missing', inv, [])
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
43
        sha1 = repo.add_inventory('references_missing', inv, ['missing'])
44
        rev = Revision(timestamp=0,
45
                       timezone=None,
46
                       committer="Foo Bar <foo@example.com>",
47
                       message="Message",
48
                       inventory_sha1=sha1,
49
                       revision_id='references_missing')
50
        rev.parent_ids = ['missing']
51
        repo.add_revision('references_missing', rev)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
52
53
        # a inventory with no parents and the revision has parents..
54
        # i.e. a ghost.
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
55
        repo = self.make_repository('inventory_one_ghost')
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
56
        sha1 = repo.add_inventory('ghost', inv, [])
57
        rev = Revision(timestamp=0,
58
                       timezone=None,
59
                       committer="Foo Bar <foo@example.com>",
60
                       message="Message",
61
                       inventory_sha1=sha1,
62
                       revision_id='ghost')
63
        rev.parent_ids = ['the_ghost']
64
        repo.add_revision('ghost', rev)
65
         
66
        # a inventory with a ghost that can be corrected now.
67
        t.copy_tree('inventory_one_ghost', 'inventory_ghost_present')
68
        repo = bzrlib.repository.Repository.open('inventory_ghost_present')
69
        sha1 = repo.add_inventory('the_ghost', inv, [])
70
        rev = Revision(timestamp=0,
71
                       timezone=None,
72
                       committer="Foo Bar <foo@example.com>",
73
                       message="Message",
74
                       inventory_sha1=sha1,
75
                       revision_id='the_ghost')
76
        rev.parent_ids = []
77
        repo.add_revision('the_ghost', rev)
78
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
79
    def test_reweave_empty(self):
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
80
        self.make_repository('empty')
81
        d = bzrlib.bzrdir.BzrDir.open('empty')
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
82
        # calling on a empty repository should do nothing
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
83
        reconciler = d.find_repository().reconcile()
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
84
        # no inconsistent parents should have been found
85
        self.assertEqual(0, reconciler.inconsistent_parents)
86
        # and no garbage inventories
87
        self.assertEqual(0, reconciler.garbage_inventories)
88
        # and no backup weave should have been needed/made.
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
89
        repo = d.open_repository()
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
90
        self.assertRaises(errors.NoSuchFile,
91
                          repo.control_weaves.get_weave,
92
                          'inventory.backup',
93
                          repo.get_transaction())
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
94
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
95
    def test_reweave_inventory_without_revision_reconcile(self):
96
        # smoke test for the all in one ui tool
97
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision')
98
        reconcile(d)
99
        # now the backup should have it but not the current inventory
100
        repo = d.open_repository()
101
        backup = repo.control_weaves.get_weave('inventory.backup',
102
                                               repo.get_transaction())
1563.2.25 by Robert Collins
Merge in upstream.
103
        self.assertTrue('missing' in backup.versions())
104
        self.assertRaises(errors.RevisionNotPresent,
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
105
                          repo.get_inventory, 'missing')
106
107
    def test_reweave_inventory_without_revision_reconciler(self):
108
        # smoke test for the all in one Reconciler class,
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
109
        # other tests use the lower level repo.reconcile()
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
110
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision')
111
        reconciler = Reconciler(d)
112
        reconciler.reconcile()
113
        # no inconsistent parents should have been found
114
        self.assertEqual(1, reconciler.inconsistent_parents)
115
        # and one garbage inventories
116
        self.assertEqual(1, reconciler.garbage_inventories)
117
        # now the backup should have it but not the current inventory
118
        repo = d.open_repository()
119
        backup = repo.control_weaves.get_weave('inventory.backup',
120
                                               repo.get_transaction())
1563.2.25 by Robert Collins
Merge in upstream.
121
        self.assertTrue('missing' in backup.versions())
122
        self.assertRaises(errors.RevisionNotPresent,
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
123
                          repo.get_inventory, 'missing')
124
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
125
    def test_reweave_inventory_without_revision(self):
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
126
        # actual low level test.
127
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision')
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
128
        repo = d.open_repository()
129
        if ([None, 'missing', 'references_missing'] 
130
            != repo.get_ancestry('references_missing')):
131
            # the repo handles ghosts without corruption, so reconcile has
132
            # nothing to do here
133
            expected_inconsistent_parents = 0
134
        else:
135
            expected_inconsistent_parents = 1
136
        reconciler = repo.reconcile()
137
        # some number of inconsistent parents should have been found
138
        self.assertEqual(expected_inconsistent_parents,
139
                         reconciler.inconsistent_parents)
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
140
        # and one garbage inventories
141
        self.assertEqual(1, reconciler.garbage_inventories)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
142
        # now the backup should have it but not the current inventory
143
        repo = d.open_repository()
144
        backup = repo.control_weaves.get_weave('inventory.backup',
145
                                               repo.get_transaction())
1563.2.25 by Robert Collins
Merge in upstream.
146
        self.assertTrue('missing' in backup.versions())
147
        self.assertRaises(errors.RevisionNotPresent,
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
148
                          repo.get_inventory, 'missing')
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
149
        # and the parent list for 'references_missing' should have that
150
        # revision a ghost now.
151
        self.assertEqual([None, 'references_missing'],
152
                         repo.get_ancestry('references_missing'))
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
153
154
    def test_reweave_inventory_preserves_a_revision_with_ghosts(self):
155
        d = bzrlib.bzrdir.BzrDir.open('inventory_one_ghost')
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
156
        reconciler = d.open_repository().reconcile()
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
157
        # no inconsistent parents should have been found: 
158
        # the lack of a parent for ghost is normal
159
        self.assertEqual(0, reconciler.inconsistent_parents)
160
        # and one garbage inventories
161
        self.assertEqual(0, reconciler.garbage_inventories)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
162
        # now the current inventory should still have 'ghost'
163
        repo = d.open_repository()
164
        repo.get_inventory('ghost')
165
        self.assertEqual([None, 'ghost'], repo.get_ancestry('ghost'))
166
        
167
    def test_reweave_inventory_fixes_ancestryfor_a_present_ghost(self):
168
        d = bzrlib.bzrdir.BzrDir.open('inventory_ghost_present')
169
        repo = d.open_repository()
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
170
        ghost_ancestry = repo.get_ancestry('ghost')
171
        if ghost_ancestry == [None, 'the_ghost', 'ghost']:
172
            # the repo handles ghosts without corruption, so reconcile has
173
            # nothing to do
174
            return
175
        self.assertEqual([None, 'ghost'], ghost_ancestry)
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
176
        reconciler = repo.reconcile()
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
177
        # one inconsistent parents should have been found : the
178
        # available but not reference parent for ghost.
179
        self.assertEqual(1, reconciler.inconsistent_parents)
180
        # and no garbage inventories
181
        self.assertEqual(0, reconciler.garbage_inventories)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
182
        # now the current inventory should still have 'ghost'
183
        repo = d.open_repository()
184
        repo.get_inventory('ghost')
185
        repo.get_inventory('the_ghost')
186
        self.assertEqual([None, 'the_ghost', 'ghost'], repo.get_ancestry('ghost'))
187
        self.assertEqual([None, 'the_ghost'], repo.get_ancestry('the_ghost'))