/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
1740.3.1 by Jelmer Vernooij
Introduce and use CommitBuilder objects.
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 repository commit builder."""
18
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
19
from copy import copy
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
20
import errno
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
21
import os
2804.4.2 by Alexander Belchenko
win32-specific fix for removing file/link/dir
22
import sys
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
23
24
from bzrlib import (
25
    errors,
26
    inventory,
27
    osutils,
28
    repository,
29
    tests,
30
    )
31
from bzrlib.tests.repository_implementations import test_repository
32
33
34
class TestCommitBuilder(test_repository.TestCaseWithRepository):
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
35
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
36
    def test_get_commit_builder(self):
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
37
        branch = self.make_branch('.')
38
        branch.repository.lock_write()
39
        builder = branch.repository.get_commit_builder(
40
            branch, [], branch.get_config())
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
41
        self.assertIsInstance(builder, repository.CommitBuilder)
2805.6.1 by Robert Collins
Set random_revid on CommitBuilder when a commit generated its own revision id.
42
        self.assertTrue(builder.random_revid)
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
43
        branch.repository.commit_write_group()
44
        branch.repository.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
45
1910.2.6 by Aaron Bentley
Update for merge review, handle deprecations
46
    def record_root(self, builder, tree):
47
        if builder.record_root_entry is True:
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
48
            tree.lock_read()
49
            try:
50
                ie = tree.inventory.root
51
            finally:
52
                tree.unlock()
1910.2.6 by Aaron Bentley
Update for merge review, handle deprecations
53
            parent_tree = tree.branch.repository.revision_tree(None)
1910.2.22 by Aaron Bentley
Make commits preserve root entry data
54
            parent_invs = []
2776.4.4 by Robert Collins
Move content summary generation outside of record_entry_contents.
55
            builder.record_entry_contents(ie, parent_invs, '', tree,
56
                tree.path_content_summary(''))
1731.1.33 by Aaron Bentley
Revert no-special-root changes
57
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
58
    def test_finish_inventory(self):
1740.3.7 by Jelmer Vernooij
Move committer, log, revprops, timestamp and timezone to CommitBuilder.
59
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
60
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
61
        try:
62
            builder = tree.branch.get_commit_builder([])
63
            self.record_root(builder, tree)
64
            builder.finish_inventory()
65
            tree.branch.repository.commit_write_group()
66
        finally:
67
            tree.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
68
2749.3.1 by Jelmer Vernooij
Add CommitBuilder.abort().
69
    def test_abort(self):
70
        tree = self.make_branch_and_tree(".")
71
        tree.lock_write()
72
        try:
73
            builder = tree.branch.get_commit_builder([])
74
            self.record_root(builder, tree)
75
            builder.finish_inventory()
76
            builder.abort()
77
        finally:
78
            tree.unlock()
79
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
80
    def test_commit_message(self):
1740.3.7 by Jelmer Vernooij
Move committer, log, revprops, timestamp and timezone to CommitBuilder.
81
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
82
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
83
        try:
84
            builder = tree.branch.get_commit_builder([])
85
            self.record_root(builder, tree)
86
            builder.finish_inventory()
87
            rev_id = builder.commit('foo bar blah')
88
        finally:
89
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
90
        rev = tree.branch.repository.get_revision(rev_id)
91
        self.assertEqual('foo bar blah', rev.message)
92
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
93
    def test_commit_with_revision_id(self):
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
94
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
95
        tree.lock_write()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
96
        try:
2617.6.8 by Robert Collins
Review feedback and documentation.
97
            # use a unicode revision id to test more corner cases.
98
            # The repository layer is meant to handle this.
99
            revision_id = u'\xc8abc'.encode('utf8')
2150.2.2 by Robert Collins
Change the commit builder selected-revision-id test to use a unicode revision id where possible, leading to stricter testing of the hypothetical unicode revision id support in bzr.
100
            try:
2617.6.8 by Robert Collins
Review feedback and documentation.
101
                try:
102
                    builder = tree.branch.get_commit_builder([],
103
                        revision_id=revision_id)
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
104
                except errors.NonAsciiRevisionId:
2617.6.8 by Robert Collins
Review feedback and documentation.
105
                    revision_id = 'abc'
106
                    builder = tree.branch.get_commit_builder([],
107
                        revision_id=revision_id)
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
108
            except errors.CannotSetRevisionId:
2617.6.8 by Robert Collins
Review feedback and documentation.
109
                # This format doesn't support supplied revision ids
110
                return
2805.6.1 by Robert Collins
Set random_revid on CommitBuilder when a commit generated its own revision id.
111
            self.assertFalse(builder.random_revid)
2617.6.8 by Robert Collins
Review feedback and documentation.
112
            self.record_root(builder, tree)
113
            builder.finish_inventory()
114
            self.assertEqual(revision_id, builder.commit('foo bar'))
115
        finally:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
116
            tree.unlock()
2150.2.2 by Robert Collins
Change the commit builder selected-revision-id test to use a unicode revision id where possible, leading to stricter testing of the hypothetical unicode revision id support in bzr.
117
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
118
        # the revision id must be set on the inventory when saving it. This
119
        # does not precisely test that - a repository that wants to can add it
120
        # on deserialisation, but thats all the current contract guarantees
121
        # anyway.
122
        self.assertEqual(revision_id,
123
            tree.branch.repository.get_inventory(revision_id).revision_id)
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
124
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
125
    def test_commit_without_root_errors(self):
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
126
        tree = self.make_branch_and_tree(".")
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
127
        tree.lock_write()
128
        try:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
129
            self.build_tree(['foo'])
130
            tree.add('foo', 'foo-id')
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
131
            entry = tree.inventory['foo-id']
132
            builder = tree.branch.get_commit_builder([])
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
133
            self.assertRaises(errors.RootMissing,
2776.4.4 by Robert Collins
Move content summary generation outside of record_entry_contents.
134
                builder.record_entry_contents, entry, [], 'foo', tree,
135
                    tree.path_content_summary('foo'))
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
136
            builder.abort()
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
137
        finally:
138
            tree.unlock()
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
139
    
140
    def test_commit_unchanged_root(self):
141
        tree = self.make_branch_and_tree(".")
2903.2.3 by Martin Pool
CommitBuilder tests should expect the root to be in the delta iff it's changed in the commit
142
        old_revision_id = tree.commit('')
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
143
        tree.lock_write()
144
        parent_tree = tree.basis_tree()
145
        parent_tree.lock_read()
146
        self.addCleanup(parent_tree.unlock)
147
        builder = tree.branch.get_commit_builder([parent_tree.inventory])
148
        try:
149
            ie = inventory.make_entry('directory', '', None,
150
                    tree.inventory.root.file_id)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
151
            delta, version_recorded = builder.record_entry_contents(
2776.4.13 by Robert Collins
Merge bzr.dev.
152
                ie, [parent_tree.inventory], '', tree,
2871.1.4 by Robert Collins
Merge bzr.dev.
153
                tree.path_content_summary(''))
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
154
            self.assertFalse(version_recorded)
2903.2.3 by Martin Pool
CommitBuilder tests should expect the root to be in the delta iff it's changed in the commit
155
            # if the repository format recorded a new root revision, that
156
            # should be in the delta
157
            got_new_revision = ie.revision != old_revision_id
158
            if got_new_revision:
159
                self.assertEqual(
160
                    ('', '', ie.file_id, ie),
161
                    delta)
162
            else:
163
                self.assertEqual(None, delta)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
164
            builder.abort()
165
        except:
166
            builder.abort()
167
            tree.unlock()
168
            raise
169
        else:
170
            tree.unlock()
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
171
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
172
    def test_commit(self):
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
173
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
174
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
175
        try:
176
            builder = tree.branch.get_commit_builder([])
177
            self.record_root(builder, tree)
178
            builder.finish_inventory()
179
            rev_id = builder.commit('foo bar')
180
        finally:
181
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
182
        self.assertNotEqual(None, rev_id)
183
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
1757.1.2 by Robert Collins
Bugfix CommitBuilders recording of the inventory revision id.
184
        # the revision id must be set on the inventory when saving it. This does not
185
        # precisely test that - a repository that wants to can add it on deserialisation,
186
        # but thats all the current contract guarantees anyway.
187
        self.assertEqual(rev_id, tree.branch.repository.get_inventory(rev_id).revision_id)
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
188
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
189
    def test_revision_tree(self):
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
190
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
191
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
192
        try:
193
            builder = tree.branch.get_commit_builder([])
194
            self.record_root(builder, tree)
195
            builder.finish_inventory()
196
            rev_id = builder.commit('foo bar')
197
        finally:
198
            tree.unlock()
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
199
        rev_tree = builder.revision_tree()
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
200
        # Just a couple simple tests to ensure that it actually follows
201
        # the RevisionTree api.
202
        self.assertEqual(rev_id, rev_tree.get_revision_id())
203
        self.assertEqual([], rev_tree.get_parent_ids())
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
204
205
    def test_root_entry_has_revision(self):
206
        # test the root revision created and put in the basis
207
        # has the right rev id.
208
        tree = self.make_branch_and_tree('.')
209
        rev_id = tree.commit('message')
210
        basis_tree = tree.basis_tree()
211
        basis_tree.lock_read()
212
        self.addCleanup(basis_tree.unlock)
213
        self.assertEqual(rev_id, basis_tree.inventory.root.revision)
214
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
215
    def _get_revtrees(self, tree, revision_ids):
216
        trees = list(tree.branch.repository.revision_trees(revision_ids))
217
        for tree in trees:
218
            tree.lock_read()
219
            self.addCleanup(tree.unlock)
220
        return trees
221
222
    def test_last_modified_revision_after_commit_root_unchanged(self):
223
        # commiting without changing the root does not change the 
224
        # last modified except on non-rich-root-repositories.
225
        tree = self.make_branch_and_tree('.')
226
        rev1 = tree.commit('')
227
        rev2 = tree.commit('')
228
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
229
        self.assertEqual(rev1, tree1.inventory.root.revision)
230
        if tree.branch.repository.supports_rich_root():
231
            self.assertEqual(rev1, tree2.inventory.root.revision)
232
        else:
233
            self.assertEqual(rev2, tree2.inventory.root.revision)
234
235
    def _add_commit_check_unchanged(self, tree, name):
236
        tree.add([name], [name + 'id'])
237
        rev1 = tree.commit('')
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
238
        rev2 = self.mini_commit(tree, name, name, False, False)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
239
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
240
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
241
        self.assertEqual(rev1, tree2.inventory[name + 'id'].revision)
242
        self.assertFileAncestry([rev1], tree, name)
243
244
    def test_last_modified_revision_after_commit_dir_unchanged(self):
245
        # committing without changing a dir does not change the last modified.
246
        tree = self.make_branch_and_tree('.')
247
        self.build_tree(['dir/'])
248
        self._add_commit_check_unchanged(tree, 'dir')
249
250
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
251
        # committing without changing a dir does not change the last modified
252
        # of the dir even the dirs contents are changed.
253
        tree = self.make_branch_and_tree('.')
254
        self.build_tree(['dir/'])
255
        tree.add(['dir'], ['dirid'])
256
        rev1 = tree.commit('')
257
        self.build_tree(['dir/content'])
258
        tree.add(['dir/content'], ['contentid'])
259
        rev2 = tree.commit('')
260
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
261
        self.assertEqual(rev1, tree1.inventory['dirid'].revision)
262
        self.assertEqual(rev1, tree2.inventory['dirid'].revision)
263
        self.assertFileAncestry([rev1], tree, 'dir')
264
265
    def test_last_modified_revision_after_commit_file_unchanged(self):
266
        # committing without changing a file does not change the last modified.
267
        tree = self.make_branch_and_tree('.')
268
        self.build_tree(['file'])
269
        self._add_commit_check_unchanged(tree, 'file')
270
271
    def test_last_modified_revision_after_commit_link_unchanged(self):
272
        # committing without changing a link does not change the last modified.
273
        self.requireFeature(tests.SymlinkFeature)
274
        tree = self.make_branch_and_tree('.')
275
        os.symlink('target', 'link')
276
        self._add_commit_check_unchanged(tree, 'link')
277
278
    def _add_commit_renamed_check_changed(self, tree, name):
279
        def rename():
280
            tree.rename_one(name, 'new_' + name)
281
        self._add_commit_change_check_changed(tree, name, rename)
282
283
    def test_last_modified_revision_after_rename_dir_changes(self):
284
        # renaming a dir changes the last modified.
285
        tree = self.make_branch_and_tree('.')
286
        self.build_tree(['dir/'])
287
        self._add_commit_renamed_check_changed(tree, 'dir')
288
289
    def test_last_modified_revision_after_rename_file_changes(self):
290
        # renaming a file changes the last modified.
291
        tree = self.make_branch_and_tree('.')
292
        self.build_tree(['file'])
293
        self._add_commit_renamed_check_changed(tree, 'file')
294
295
    def test_last_modified_revision_after_rename_link_changes(self):
296
        # renaming a link changes the last modified.
297
        self.requireFeature(tests.SymlinkFeature)
298
        tree = self.make_branch_and_tree('.')
299
        os.symlink('target', 'link')
300
        self._add_commit_renamed_check_changed(tree, 'link')
301
302
    def _add_commit_reparent_check_changed(self, tree, name):
303
        self.build_tree(['newparent/'])
304
        tree.add(['newparent'])
305
        def reparent():
306
            tree.rename_one(name, 'newparent/new_' + name)
307
        self._add_commit_change_check_changed(tree, name, reparent)
308
309
    def test_last_modified_revision_after_reparent_dir_changes(self):
310
        # reparenting a dir changes the last modified.
311
        tree = self.make_branch_and_tree('.')
312
        self.build_tree(['dir/'])
313
        self._add_commit_reparent_check_changed(tree, 'dir')
314
315
    def test_last_modified_revision_after_reparent_file_changes(self):
316
        # reparenting a file changes the last modified.
317
        tree = self.make_branch_and_tree('.')
318
        self.build_tree(['file'])
319
        self._add_commit_reparent_check_changed(tree, 'file')
320
321
    def test_last_modified_revision_after_reparent_link_changes(self):
322
        # reparenting a link changes the last modified.
323
        self.requireFeature(tests.SymlinkFeature)
324
        tree = self.make_branch_and_tree('.')
325
        os.symlink('target', 'link')
326
        self._add_commit_reparent_check_changed(tree, 'link')
327
328
    def _add_commit_change_check_changed(self, tree, name, changer):
329
        tree.add([name], [name + 'id'])
330
        rev1 = tree.commit('')
331
        changer()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
332
        rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'))
333
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
334
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
335
        self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
336
        self.assertFileAncestry([rev1, rev2], tree, name)
337
338
    def mini_commit(self, tree, name, new_name, records_version=True,
339
        delta_against_basis=True):
340
        """Perform a miniature commit looking for record entry results.
341
        
342
        :param tree: The tree to commit.
343
        :param name: The path in the basis tree of the tree being committed.
344
        :param new_name: The path in the tree being committed.
345
        :param records_version: True if the commit of new_name is expected to
346
            record a new version.
347
        :param delta_against_basis: True of the commit of new_name is expected
348
            to have a delta against the basis.
349
        """
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
350
        tree.lock_write()
351
        try:
352
            # mini manual commit here so we can check the return of
353
            # record_entry_contents.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
354
            parent_ids = tree.get_parent_ids()
355
            builder = tree.branch.get_commit_builder(parent_ids)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
356
            parent_tree = tree.basis_tree()
357
            parent_tree.lock_read()
358
            self.addCleanup(parent_tree.unlock)
359
            parent_invs = [parent_tree.inventory]
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
360
            for parent_id in parent_ids[1:]:
361
                parent_invs.append(tree.branch.repository.revision_tree(
362
                    parent_id).inventory)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
363
            # root
364
            builder.record_entry_contents(
365
                inventory.make_entry('directory', '', None,
2776.4.13 by Robert Collins
Merge bzr.dev.
366
                    tree.inventory.root.file_id), parent_invs, '', tree,
367
                    tree.path_content_summary(''))
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
368
            def commit_id(file_id):
369
                old_ie = tree.inventory[file_id]
370
                path = tree.id2path(file_id)
371
                ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
372
                    old_ie.parent_id, file_id)
2776.4.13 by Robert Collins
Merge bzr.dev.
373
                return builder.record_entry_contents(ie, parent_invs, path,
374
                    tree, tree.path_content_summary(path))
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
375
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
376
            file_id = tree.path2id(new_name)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
377
            parent_id = tree.inventory[file_id].parent_id
378
            if parent_id != tree.inventory.root.file_id:
379
                commit_id(parent_id)
380
            # because a change of some sort is meant to have occurred,
381
            # recording the entry must return True.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
382
            delta, version_recorded = commit_id(file_id)
383
            if records_version:
384
                self.assertTrue(version_recorded)
385
            else:
386
                self.assertFalse(version_recorded)
387
            new_entry = builder.new_inventory[file_id]
388
            if delta_against_basis:
389
                expected_delta = (name, new_name, file_id, new_entry)
390
            else:
391
                expected_delta = None
392
            if expected_delta != delta:
393
                import pdb;pdb.set_trace()
394
            self.assertEqual(expected_delta, delta)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
395
            builder.finish_inventory()
396
            rev2 = builder.commit('')
397
            tree.set_parent_ids([rev2])
398
        except:
399
            builder.abort()
400
            tree.unlock()
401
            raise
402
        else:
403
            tree.unlock()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
404
        return rev2
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
405
406
    def assertFileAncestry(self, ancestry, tree, name, alt_ancestry=None):
407
        # all the changes that have occured should be in the ancestry
408
        # (closest to a public per-file graph API we have today)
409
        tree.lock_read()
410
        self.addCleanup(tree.unlock)
411
        vw = tree.branch.repository.weave_store.get_weave(name + 'id',
412
            tree.branch.repository.get_transaction())
413
        result = vw.get_ancestry([ancestry[-1]])
414
        if alt_ancestry is None:
415
            self.assertEqual(ancestry, result)
416
        else:
417
            self.assertSubset([tuple(result)],
418
                [tuple(ancestry), tuple(alt_ancestry)])
419
420
    def test_last_modified_revision_after_content_file_changes(self):
421
        # altering a file changes the last modified.
422
        tree = self.make_branch_and_tree('.')
423
        self.build_tree(['file'])
424
        def change_file():
425
            tree.put_file_bytes_non_atomic('fileid', 'new content')
426
        self._add_commit_change_check_changed(tree, 'file', change_file)
427
428
    def test_last_modified_revision_after_content_link_changes(self):
429
        # changing a link changes the last modified.
430
        self.requireFeature(tests.SymlinkFeature)
431
        tree = self.make_branch_and_tree('.')
432
        os.symlink('target', 'link')
433
        def change_link():
434
            os.unlink('link')
435
            os.symlink('newtarget', 'link')
436
        self._add_commit_change_check_changed(tree, 'link', change_link)
437
438
    def _commit_sprout(self, tree, name):
439
        tree.add([name], [name + 'id'])
440
        rev_id = tree.commit('')
441
        return rev_id, tree.bzrdir.sprout('t2').open_workingtree()
442
443
    def _rename_in_tree(self, tree, name):
444
        tree.rename_one(name, 'new_' + name)
445
        return tree.commit('')
446
447
    def _commit_sprout_rename_merge(self, tree1, name):
448
        rev1, tree2 = self._commit_sprout(tree1, name)
449
        # change both sides equally
450
        rev2 = self._rename_in_tree(tree1, name)
451
        rev3 = self._rename_in_tree(tree2, name)
452
        tree1.merge_from_branch(tree2.branch)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
453
        rev4 = self.mini_commit(tree1, 'new_' + name, 'new_' + name)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
454
        tree3, = self._get_revtrees(tree1, [rev4])
455
        self.assertEqual(rev4, tree3.inventory[name + 'id'].revision)
2776.4.2 by Robert Collins
nuke _read_tree_state and snapshot from inventory, moving responsibility into the commit builder.
456
        # TODO: change this to an assertFileGraph call to check the
457
        # parent order of rev4: it should be rev2, rev3
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
458
        self.assertFileAncestry([rev1, rev2, rev3, rev4], tree1, name,
459
            [rev1, rev3, rev2, rev4])
460
461
    def test_last_modified_revision_after_merge_dir_changes(self):
462
        # merge a dir changes the last modified.
463
        tree1 = self.make_branch_and_tree('t1')
464
        self.build_tree(['t1/dir/'])
465
        self._commit_sprout_rename_merge(tree1, 'dir')
466
467
    def test_last_modified_revision_after_merge_file_changes(self):
468
        # merge a file changes the last modified.
469
        tree1 = self.make_branch_and_tree('t1')
470
        self.build_tree(['t1/file'])
471
        self._commit_sprout_rename_merge(tree1, 'file')
472
473
    def test_last_modified_revision_after_merge_link_changes(self):
474
        # merge a link changes the last modified.
475
        self.requireFeature(tests.SymlinkFeature)
476
        tree1 = self.make_branch_and_tree('t1')
477
        os.symlink('target', 't1/link')
478
        self._commit_sprout_rename_merge(tree1, 'link')
479
480
    def _commit_sprout_rename_merge_converged(self, tree1, name):
481
        rev1, tree2 = self._commit_sprout(tree1, name)
482
        # change on the other side to merge back
483
        rev2 = self._rename_in_tree(tree2, name)
484
        tree1.merge_from_branch(tree2.branch)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
485
        rev3 = self.mini_commit(tree1, name, 'new_' + name, False)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
486
        tree3, = self._get_revtrees(tree1, [rev2])
487
        self.assertEqual(rev2, tree3.inventory[name + 'id'].revision)
488
        self.assertFileAncestry([rev1, rev2], tree1, name)
489
490
    def test_last_modified_revision_after_converged_merge_dir_changes(self):
491
        # merge a dir changes the last modified.
492
        tree1 = self.make_branch_and_tree('t1')
493
        self.build_tree(['t1/dir/'])
494
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
495
496
    def test_last_modified_revision_after_converged_merge_file_changes(self):
497
        # merge a file changes the last modified.
498
        tree1 = self.make_branch_and_tree('t1')
499
        self.build_tree(['t1/file'])
500
        self._commit_sprout_rename_merge_converged(tree1, 'file')
501
502
    def test_last_modified_revision_after_converged_merge_link_changes(self):
503
        # merge a link changes the last modified.
504
        self.requireFeature(tests.SymlinkFeature)
505
        tree1 = self.make_branch_and_tree('t1')
506
        os.symlink('target', 't1/link')
507
        self._commit_sprout_rename_merge_converged(tree1, 'link')
508
509
    def make_dir(self, name):
510
        self.build_tree([name + '/'])
511
512
    def make_file(self, name):
513
        self.build_tree([name])
514
515
    def make_link(self, name):
516
        self.requireFeature(tests.SymlinkFeature)
517
        os.symlink('target', name)
518
519
    def _check_kind_change(self, make_before, make_after):
520
        tree = self.make_branch_and_tree('.')
521
        path = 'name'
522
        make_before(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
523
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
524
        def change_kind():
2831.5.2 by Vincent Ladeuil
Review feedback.
525
            osutils.delete_any(path)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
526
            make_after(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
527
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
528
        self._add_commit_change_check_changed(tree, path, change_kind)
529
530
    def test_last_modified_dir_file(self):
531
        self._check_kind_change(self.make_dir, self.make_file)
532
533
    def test_last_modified_dir_link(self):
534
        self._check_kind_change(self.make_dir, self.make_link)
535
536
    def test_last_modified_link_file(self):
537
        self._check_kind_change(self.make_link, self.make_file)
538
539
    def test_last_modified_link_dir(self):
540
        self._check_kind_change(self.make_link, self.make_dir)
541
542
    def test_last_modified_file_dir(self):
543
        self._check_kind_change(self.make_file, self.make_dir)
544
545
    def test_last_modified_file_link(self):
546
        self._check_kind_change(self.make_file, self.make_link)