/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
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
1731.1.1 by Aaron Bentley
Make root entry an InventoryDirectory, make EmptyTree really empty
22
from bzrlib.inventory import Inventory
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
23
from bzrlib.reconcile import reconcile, Reconciler
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
24
from bzrlib.revision import Revision
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
25
from bzrlib.tests import TestSkipped
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
26
from bzrlib.tests.repository_implementations.test_repository import TestCaseWithRepository
27
from bzrlib.transport import get_transport
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
28
from bzrlib.uncommit import uncommit
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
29
from bzrlib.workingtree import WorkingTree
30
31
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
32
class TestReconcile(TestCaseWithRepository):
33
34
    def checkUnreconciled(self, d, reconciler):
35
        """Check that d did not get reconciled."""
36
        # nothing should have been fixed yet:
37
        self.assertEqual(0, reconciler.inconsistent_parents)
38
        # and no garbage inventories
39
        self.assertEqual(0, reconciler.garbage_inventories)
40
        self.checkNoBackupInventory(d)
41
42
    def checkNoBackupInventory(self, aBzrDir):
43
        """Check that there is no backup inventory in aBzrDir."""
44
        repo = aBzrDir.open_repository()
45
        self.assertRaises(errors.NoSuchFile,
46
                          repo.control_weaves.get_weave,
47
                          'inventory.backup',
48
                          repo.get_transaction())
49
50
51
class TestsNeedingReweave(TestReconcile):
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
52
53
    def setUp(self):
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
54
        super(TestsNeedingReweave, self).setUp()
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
55
        
56
        t = get_transport(self.get_url())
57
        # an empty inventory with no revision for testing with.
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
58
        repo = self.make_repository('inventory_without_revision')
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
59
        inv = Inventory(revision_id='missing')
60
        inv.root.revision = 'missing'
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
61
        repo.add_inventory('missing', inv, [])
62
63
        # an empty inventory with no revision for testing with.
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
64
        # this is referenced by 'references_missing' to let us test
65
        # that all the cached data is correctly converted into ghost links
66
        # and the referenced inventory still cleaned.
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
67
        repo = self.make_repository('inventory_without_revision_and_ghost')
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
68
        repo.add_inventory('missing', inv, [])
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
69
        inv = Inventory(revision_id='references_missing')
70
        inv.root.revision = 'references_missing'
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
71
        sha1 = repo.add_inventory('references_missing', inv, ['missing'])
72
        rev = Revision(timestamp=0,
73
                       timezone=None,
74
                       committer="Foo Bar <foo@example.com>",
75
                       message="Message",
76
                       inventory_sha1=sha1,
77
                       revision_id='references_missing')
78
        rev.parent_ids = ['missing']
79
        repo.add_revision('references_missing', rev)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
80
81
        # a inventory with no parents and the revision has parents..
82
        # i.e. a ghost.
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
83
        repo = self.make_repository('inventory_one_ghost')
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
84
        inv = Inventory(revision_id='ghost')
85
        inv.root.revision = 'ghost'
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
86
        sha1 = repo.add_inventory('ghost', inv, [])
87
        rev = Revision(timestamp=0,
88
                       timezone=None,
89
                       committer="Foo Bar <foo@example.com>",
90
                       message="Message",
91
                       inventory_sha1=sha1,
92
                       revision_id='ghost')
93
        rev.parent_ids = ['the_ghost']
94
        repo.add_revision('ghost', rev)
95
         
96
        # a inventory with a ghost that can be corrected now.
97
        t.copy_tree('inventory_one_ghost', 'inventory_ghost_present')
98
        repo = bzrlib.repository.Repository.open('inventory_ghost_present')
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
99
        inv = Inventory(revision_id='the_ghost')
100
        inv.root.revision = 'the_ghost'
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
101
        sha1 = repo.add_inventory('the_ghost', inv, [])
102
        rev = Revision(timestamp=0,
103
                       timezone=None,
104
                       committer="Foo Bar <foo@example.com>",
105
                       message="Message",
106
                       inventory_sha1=sha1,
107
                       revision_id='the_ghost')
108
        rev.parent_ids = []
109
        repo.add_revision('the_ghost', rev)
110
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
111
    def checkEmptyReconcile(self, **kwargs):
112
        """Check a reconcile on an empty repository."""
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
113
        self.make_repository('empty')
114
        d = bzrlib.bzrdir.BzrDir.open('empty')
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
115
        # calling on a empty repository should do nothing
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
116
        reconciler = d.find_repository().reconcile(**kwargs)
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
117
        # no inconsistent parents should have been found
118
        self.assertEqual(0, reconciler.inconsistent_parents)
119
        # and no garbage inventories
120
        self.assertEqual(0, reconciler.garbage_inventories)
121
        # and no backup weave should have been needed/made.
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
122
        self.checkNoBackupInventory(d)
123
124
    def test_reconile_empty(self):
125
        # in an empty repo, theres nothing to do.
126
        self.checkEmptyReconcile()
127
128
    def test_reconcile_empty_thorough(self):
129
        # reconcile should accept thorough=True
130
        self.checkEmptyReconcile(thorough=True)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
131
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
132
    def test_convenience_reconcile_inventory_without_revision_reconcile(self):
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
133
        # smoke test for the all in one ui tool
134
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision')
135
        reconcile(d)
136
        # now the backup should have it but not the current inventory
137
        repo = d.open_repository()
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
138
        self.check_missing_was_removed(repo)
139
140
    def test_reweave_inventory_without_revision(self):
141
        # an excess inventory on its own is only reconciled by using thorough
142
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision')
143
        repo = d.open_repository()
144
        self.checkUnreconciled(d, repo.reconcile())
145
        reconciler = repo.reconcile(thorough=True)
146
        # no bad parents
147
        self.assertEqual(0, reconciler.inconsistent_parents)
148
        # and one garbage inventoriy
149
        self.assertEqual(1, reconciler.garbage_inventories)
150
        self.check_missing_was_removed(repo)
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
151
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
152
    def check_thorough_reweave_missing_revision(self, aBzrDir, reconcile,
153
            **kwargs):
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
154
        # actual low level test.
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
155
        repo = aBzrDir.open_repository()
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
156
        if ([None, 'missing', 'references_missing'] 
157
            != repo.get_ancestry('references_missing')):
158
            # the repo handles ghosts without corruption, so reconcile has
159
            # nothing to do here
160
            expected_inconsistent_parents = 0
161
        else:
162
            expected_inconsistent_parents = 1
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
163
        reconciler = reconcile(**kwargs)
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
164
        # some number of inconsistent parents should have been found
165
        self.assertEqual(expected_inconsistent_parents,
166
                         reconciler.inconsistent_parents)
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
167
        # and one garbage inventories
168
        self.assertEqual(1, reconciler.garbage_inventories)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
169
        # now the backup should have it but not the current inventory
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
170
        repo = aBzrDir.open_repository()
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
171
        self.check_missing_was_removed(repo)
1570.1.14 by Robert Collins
Enforce repository consistency during 'fetch' operations.
172
        # and the parent list for 'references_missing' should have that
173
        # revision a ghost now.
174
        self.assertEqual([None, 'references_missing'],
175
                         repo.get_ancestry('references_missing'))
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
176
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
177
    def check_missing_was_removed(self, repo):
178
        backup = repo.control_weaves.get_weave('inventory.backup',
179
                                               repo.get_transaction())
180
        self.assertTrue('missing' in backup.versions())
181
        self.assertRaises(errors.RevisionNotPresent,
182
                          repo.get_inventory, 'missing')
183
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
184
    def test_reweave_inventory_without_revision_reconciler(self):
185
        # smoke test for the all in one Reconciler class,
186
        # other tests use the lower level repo.reconcile()
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
187
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision_and_ghost')
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
188
        def reconcile():
189
            reconciler = Reconciler(d)
190
            reconciler.reconcile()
191
            return reconciler
192
        self.check_thorough_reweave_missing_revision(d, reconcile)
193
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
194
    def test_reweave_inventory_without_revision_and_ghost(self):
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
195
        # actual low level test.
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
196
        d = bzrlib.bzrdir.BzrDir.open('inventory_without_revision_and_ghost')
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
197
        repo = d.open_repository()
198
        # nothing should have been altered yet : inventories without
199
        # revisions are not data loss incurring for current format
200
        self.check_thorough_reweave_missing_revision(d, repo.reconcile,
201
            thorough=True)
202
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
203
    def test_reweave_inventory_preserves_a_revision_with_ghosts(self):
204
        d = bzrlib.bzrdir.BzrDir.open('inventory_one_ghost')
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
205
        reconciler = d.open_repository().reconcile(thorough=True)
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
206
        # no inconsistent parents should have been found: 
207
        # the lack of a parent for ghost is normal
208
        self.assertEqual(0, reconciler.inconsistent_parents)
209
        # and one garbage inventories
210
        self.assertEqual(0, reconciler.garbage_inventories)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
211
        # now the current inventory should still have 'ghost'
212
        repo = d.open_repository()
213
        repo.get_inventory('ghost')
214
        self.assertEqual([None, 'ghost'], repo.get_ancestry('ghost'))
215
        
216
    def test_reweave_inventory_fixes_ancestryfor_a_present_ghost(self):
217
        d = bzrlib.bzrdir.BzrDir.open('inventory_ghost_present')
218
        repo = d.open_repository()
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
219
        ghost_ancestry = repo.get_ancestry('ghost')
220
        if ghost_ancestry == [None, 'the_ghost', 'ghost']:
221
            # the repo handles ghosts without corruption, so reconcile has
222
            # nothing to do
223
            return
224
        self.assertEqual([None, 'ghost'], ghost_ancestry)
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
225
        reconciler = repo.reconcile()
1692.1.3 by Robert Collins
Finish the reconcile tweak: filled in ghosts are a data loss issue and need to be checked during fast reconciles.
226
        # this is a data corrupting error, so a normal reconcile should fix it.
1570.1.8 by Robert Collins
Only reconcile if doing so will perform gc or correct ancestry.
227
        # one inconsistent parents should have been found : the
228
        # available but not reference parent for ghost.
229
        self.assertEqual(1, reconciler.inconsistent_parents)
230
        # and no garbage inventories
231
        self.assertEqual(0, reconciler.garbage_inventories)
1570.1.2 by Robert Collins
Import bzrtools' 'fix' command as 'bzr reconcile.'
232
        # now the current inventory should still have 'ghost'
233
        repo = d.open_repository()
234
        repo.get_inventory('ghost')
235
        repo.get_inventory('the_ghost')
236
        self.assertEqual([None, 'the_ghost', 'ghost'], repo.get_ancestry('ghost'))
237
        self.assertEqual([None, 'the_ghost'], repo.get_ancestry('the_ghost'))
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
238
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
239
240
class TestReconcileWithIncorrectRevisionCache(TestReconcile):
241
    """Ancestry data gets cached in knits and weaves should be reconcilable.
242
243
    This class tests that reconcile can correct invalid caches (such as after
244
    a reconcile).
245
    """
246
247
    def setUp(self):
248
        super(TestReconcileWithIncorrectRevisionCache, self).setUp()
249
        
250
        t = get_transport(self.get_url())
251
        # we need a revision with two parents in the wrong order
252
        # which should trigger reinsertion.
253
        # and another with the first one correct but the other two not
254
        # which should not trigger reinsertion.
255
        # these need to be in different repositories so that we don't
256
        # trigger a reconcile based on the other case.
257
        # there is no api to construct a broken knit repository at
258
        # this point. if we ever encounter a bad graph in a knit repo
259
        # we should add a lower level api to allow constructing such cases.
260
        
261
        # first off the common logic:
262
        tree = self.make_branch_and_tree('wrong-first-parent')
263
        tree.commit('1', rev_id='1')
264
        uncommit(tree.branch, tree=tree)
265
        tree.commit('2', rev_id='2')
266
        uncommit(tree.branch, tree=tree)
267
        tree.commit('3', rev_id='3')
268
        uncommit(tree.branch, tree=tree)
269
        repo_secondary = tree.bzrdir.clone(
270
            'reversed-secondary-parents').open_repository()
271
272
        # now setup the wrong-first parent case
273
        repo = tree.branch.repository
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
274
        inv = Inventory(revision_id='wrong-first-parent')
275
        inv.root.revision = 'wrong-first-parent'
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
276
        sha1 = repo.add_inventory('wrong-first-parent', inv, ['2', '1'])
277
        rev = Revision(timestamp=0,
278
                       timezone=None,
279
                       committer="Foo Bar <foo@example.com>",
280
                       message="Message",
281
                       inventory_sha1=sha1,
282
                       revision_id='wrong-first-parent')
283
        rev.parent_ids = ['1', '2']
284
        repo.add_revision('wrong-first-parent', rev)
285
286
        # now setup the wrong-secondary parent case
287
        repo = repo_secondary
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
288
        inv = Inventory(revision_id='wrong-secondary-parent')
289
        inv.root.revision = 'wrong-secondary-parent'
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
290
        sha1 = repo.add_inventory('wrong-secondary-parent', inv, ['1', '3', '2'])
291
        rev = Revision(timestamp=0,
292
                       timezone=None,
293
                       committer="Foo Bar <foo@example.com>",
294
                       message="Message",
295
                       inventory_sha1=sha1,
296
                       revision_id='wrong-secondary-parent')
297
        rev.parent_ids = ['1', '2', '3']
298
        repo.add_revision('wrong-secondary-parent', rev)
299
300
    def test_reconcile_wrong_order(self):
301
        # a wrong order in primary parents is optionally correctable
302
        d = bzrlib.bzrdir.BzrDir.open('wrong-first-parent')
303
        repo = d.open_repository()
304
        g = repo.get_revision_graph()
305
        if g['wrong-first-parent'] == ['1', '2']:
306
            raise TestSkipped('wrong-first-parent is not setup for testing')
307
        self.checkUnreconciled(d, repo.reconcile())
308
        # nothing should have been altered yet : inventories without
309
        # revisions are not data loss incurring for current format
310
        reconciler = repo.reconcile(thorough=True)
311
        # these show up as inconsistent parents
312
        self.assertEqual(1, reconciler.inconsistent_parents)
1692.1.1 by Robert Collins
* Repository.reconcile now takes a thorough keyword parameter to allow
313
        # and no garbage inventories
314
        self.assertEqual(0, reconciler.garbage_inventories)
1692.1.2 by Robert Collins
Teach reconcile to check the left-most parent is correct in the revision graph.
315
        # and should have been fixed:
316
        g = repo.get_revision_graph()
317
        self.assertEqual(['1', '2'], g['wrong-first-parent'])
318
319
    def test_reconcile_wrong_order_secondary(self):
320
        # a wrong order in secondary parents is ignored.
321
        d = bzrlib.bzrdir.BzrDir.open('reversed-secondary-parents')
322
        repo = d.open_repository()
323
        self.checkUnreconciled(d, repo.reconcile())
324
        self.checkUnreconciled(d, repo.reconcile(thorough=True))