~bzr-pqm/bzr/bzr.dev

4988.10.5 by John Arbash Meinel
Merge bzr.dev 5021 to resolve NEWS
1
# Copyright (C) 2007-2010 Canonical Ltd
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
16
17
"""Tests for repository write groups."""
18
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
19
import sys
20
4343.3.2 by John Arbash Meinel
All stacking tests seem to be passing for dev6 repos
21
from bzrlib import (
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
22
    branch,
4343.3.2 by John Arbash Meinel
All stacking tests seem to be passing for dev6 repos
23
    bzrdir,
24
    errors,
25
    graph,
26
    memorytree,
27
    osutils,
28
    remote,
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
29
    tests,
4343.3.6 by John Arbash Meinel
Take out the _delta_across_repos flag.
30
    versionedfile,
4343.3.2 by John Arbash Meinel
All stacking tests seem to be passing for dev6 repos
31
    )
5017.3.43 by Vincent Ladeuil
-s bt.per_repository.test_write_group passing
32
from bzrlib.tests import (
33
    per_repository,
34
    test_server,
35
    )
5017.3.45 by Vincent Ladeuil
Move MemoryServer back into bzrlib.transport.memory as it's needed as soon as a MemoryTransport is used. Add a NEWS entry.
36
from bzrlib.transport import memory
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
37
38
39
class TestWriteGroup(per_repository.TestCaseWithRepository):
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
40
41
    def test_start_write_group_unlocked_needs_write_lock(self):
42
        repo = self.make_repository('.')
43
        self.assertRaises(errors.NotWriteLocked, repo.start_write_group)
44
45
    def test_start_write_group_read_locked_needs_write_lock(self):
46
        repo = self.make_repository('.')
47
        repo.lock_read()
48
        try:
49
            self.assertRaises(errors.NotWriteLocked, repo.start_write_group)
50
        finally:
51
            repo.unlock()
52
53
    def test_start_write_group_write_locked_gets_None(self):
54
        repo = self.make_repository('.')
55
        repo.lock_write()
56
        self.assertEqual(None, repo.start_write_group())
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
57
        repo.commit_write_group()
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
58
        repo.unlock()
59
60
    def test_start_write_group_twice_errors(self):
61
        repo = self.make_repository('.')
62
        repo.lock_write()
63
        repo.start_write_group()
64
        try:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
65
            # don't need a specific exception for now - this is
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
66
            # really to be sure it's used right, not for signalling
67
            # semantic information.
68
            self.assertRaises(errors.BzrError, repo.start_write_group)
69
        finally:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
70
            repo.commit_write_group()
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
71
            repo.unlock()
72
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
73
    def test_commit_write_group_does_not_error(self):
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
74
        repo = self.make_repository('.')
75
        repo.lock_write()
76
        repo.start_write_group()
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
77
        # commit_write_group can either return None (for repositories without
78
        # isolated transactions) or a hint for pack(). So we only check it
79
        # works in this interface test, because all repositories are exercised.
80
        repo.commit_write_group()
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
81
        repo.unlock()
82
2592.3.242 by Martin Pool
New method TestCase.call_catch_warnings
83
    def test_unlock_in_write_group(self):
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
84
        repo = self.make_repository('.')
85
        repo.lock_write()
86
        repo.start_write_group()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
87
        # don't need a specific exception for now - this is
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
88
        # really to be sure it's used right, not for signalling
89
        # semantic information.
4634.85.10 by Andrew Bennetts
Change test_unlock_in_write_group to expect a log_exception_quietly rather than a raise.
90
        self.assertLogsError(errors.BzrError, repo.unlock)
2592.3.244 by Martin Pool
unlock while in a write group now aborts the write group, unlocks, and errors.
91
        # after this error occurs, the repository is unlocked, and the write
92
        # group is gone.  you've had your chance, and you blew it. ;-)
93
        self.assertFalse(repo.is_locked())
94
        self.assertRaises(errors.BzrError, repo.commit_write_group)
95
        self.assertRaises(errors.BzrError, repo.unlock)
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
96
97
    def test_is_in_write_group(self):
98
        repo = self.make_repository('.')
99
        self.assertFalse(repo.is_in_write_group())
100
        repo.lock_write()
101
        repo.start_write_group()
102
        self.assertTrue(repo.is_in_write_group())
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
103
        repo.commit_write_group()
104
        self.assertFalse(repo.is_in_write_group())
105
        # abort also removes the in_write_group status.
106
        repo.start_write_group()
107
        self.assertTrue(repo.is_in_write_group())
108
        repo.abort_write_group()
109
        self.assertFalse(repo.is_in_write_group())
110
        repo.unlock()
111
112
    def test_abort_write_group_gets_None(self):
113
        repo = self.make_repository('.')
114
        repo.lock_write()
115
        repo.start_write_group()
116
        self.assertEqual(None, repo.abort_write_group())
2617.6.1 by Robert Collins
* New method on Repository - ``start_write_group``, ``end_write_group``
117
        repo.unlock()
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
118
119
    def test_abort_write_group_does_not_raise_when_suppressed(self):
5017.3.43 by Vincent Ladeuil
-s bt.per_repository.test_write_group passing
120
        if self.transport_server is test_server.LocalURLServer:
3825.4.3 by Andrew Bennetts
Conditionally replace LocalURLServer in the test rather than changing the default_transport behaviour of per_repository tests.
121
            self.transport_server = None
5017.3.45 by Vincent Ladeuil
Move MemoryServer back into bzrlib.transport.memory as it's needed as soon as a MemoryTransport is used. Add a NEWS entry.
122
        self.vfs_transport_factory = memory.MemoryServer
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
123
        repo = self.make_repository('repo')
124
        token = repo.lock_write()
125
        self.addCleanup(repo.unlock)
126
        repo.start_write_group()
127
        # Damage the repository on the filesystem
4327.1.9 by Vincent Ladeuil
Fix 4 more lock-related test failures.
128
        t = self.get_transport('')
129
        t.rename('repo', 'foo')
130
        self.addCleanup(t.rename, 'foo', 'repo')
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
131
        # abort_write_group will not raise an error, because either an
132
        # exception was not generated, or the exception was caught and
133
        # suppressed.  See also test_pack_repository's test of the same name.
134
        self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
135
4617.1.1 by Robert Collins
Teach the check for get_missing_parent_inventories that this test applies to direct repositories not RemoteRepository.
136
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
137
class TestGetMissingParentInventories(per_repository.TestCaseWithRepository):
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
138
4257.4.6 by Andrew Bennetts
Make get_missing_parent_inventories work for all repo formats (it's a no-op for unstackable formats).
139
    def test_empty_get_missing_parent_inventories(self):
140
        """A new write group has no missing parent inventories."""
141
        repo = self.make_repository('.')
142
        repo.lock_write()
143
        repo.start_write_group()
144
        try:
145
            self.assertEqual(set(), set(repo.get_missing_parent_inventories()))
146
        finally:
147
            repo.commit_write_group()
148
            repo.unlock()
149
4309.1.5 by Andrew Bennetts
Remove lots of cruft.
150
    def branch_trunk_and_make_tree(self, trunk_repo, relpath):
151
        tree = self.make_branch_and_memory_tree('branch')
152
        trunk_repo.lock_read()
153
        self.addCleanup(trunk_repo.unlock)
154
        tree.branch.repository.fetch(trunk_repo, revision_id='rev-1')
155
        tree.set_parent_ids(['rev-1'])
156
        return tree 
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
157
158
    def make_first_commit(self, repo):
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
159
        trunk = repo.bzrdir.create_branch()
4309.1.5 by Andrew Bennetts
Remove lots of cruft.
160
        tree = memorytree.MemoryTree.create_on_branch(trunk)
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
161
        tree.lock_write()
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
162
        tree.add([''], ['TREE_ROOT'], ['directory'])
163
        tree.add(['dir'], ['dir-id'], ['directory'])
164
        tree.add(['filename'], ['file-id'], ['file'])
165
        tree.put_file_bytes_non_atomic('file-id', 'content\n')
166
        tree.commit('Trunk commit', rev_id='rev-0')
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
167
        tree.commit('Trunk commit', rev_id='rev-1')
168
        tree.unlock()
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
169
170
    def make_new_commit_in_new_repo(self, trunk_repo, parents=None):
171
        tree = self.branch_trunk_and_make_tree(trunk_repo, 'branch')
172
        tree.set_parent_ids(parents)
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
173
        tree.commit('Branch commit', rev_id='rev-2')
174
        branch_repo = tree.branch.repository
175
        branch_repo.lock_read()
4257.4.9 by Andrew Bennetts
Add more test coverage, but make a long test even longer in the process.
176
        self.addCleanup(branch_repo.unlock)
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
177
        return branch_repo
178
4309.1.5 by Andrew Bennetts
Remove lots of cruft.
179
    def make_stackable_repo(self, relpath='trunk'):
180
        if isinstance(self.repository_format, remote.RemoteRepositoryFormat):
181
            # RemoteRepository by default builds a default format real
182
            # repository, but the default format is unstackble.  So explicitly
183
            # make a stackable real repository and use that.
184
            repo = self.make_repository(relpath, format='1.9')
185
            repo = bzrdir.BzrDir.open(self.get_url(relpath)).open_repository()
186
        else:
187
            repo = self.make_repository(relpath)
188
        if not repo._format.supports_external_lookups:
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
189
            raise tests.TestNotApplicable('format not stackable')
190
        repo.bzrdir._format.set_branch_format(branch.BzrBranchFormat7())
4309.1.5 by Andrew Bennetts
Remove lots of cruft.
191
        return repo
192
193
    def reopen_repo_and_resume_write_group(self, repo):
194
        try:
195
            resume_tokens = repo.suspend_write_group()
196
        except errors.UnsuspendableWriteGroup:
197
            # If we got this far, and this repo does not support resuming write
198
            # groups, then get_missing_parent_inventories works in all
199
            # cases this repo supports.
200
            repo.unlock()
201
            return
202
        repo.unlock()
203
        reopened_repo = repo.bzrdir.open_repository()
204
        reopened_repo.lock_write()
205
        self.addCleanup(reopened_repo.unlock)
206
        reopened_repo.resume_write_group(resume_tokens)
207
        return reopened_repo
208
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
209
    def test_ghost_revision(self):
210
        """A parent inventory may be absent if all the needed texts are present.
211
        i.e., a ghost revision isn't (necessarily) considered to be a missing
212
        parent inventory.
213
        """
214
        # Make a trunk with one commit.
215
        trunk_repo = self.make_stackable_repo()
216
        self.make_first_commit(trunk_repo)
217
        trunk_repo.lock_read()
218
        self.addCleanup(trunk_repo.unlock)
219
        # Branch the trunk, add a new commit.
220
        branch_repo = self.make_new_commit_in_new_repo(
221
            trunk_repo, parents=['rev-1', 'ghost-rev'])
222
        inv = branch_repo.get_inventory('rev-2')
223
        # Make a new repo stacked on trunk, and then copy into it:
224
        #  - all texts in rev-2
225
        #  - the new inventory (rev-2)
226
        #  - the new revision (rev-2)
227
        repo = self.make_stackable_repo('stacked')
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
228
        repo.lock_write()
229
        repo.start_write_group()
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
230
        # Add all texts from in rev-2 inventory.  Note that this has to exclude
231
        # the root if the repo format does not support rich roots.
232
        rich_root = branch_repo._format.rich_root_data
233
        all_texts = [
234
            (ie.file_id, ie.revision) for ie in inv.iter_just_entries()
235
             if rich_root or inv.id2path(ie.file_id) != '']
236
        repo.texts.insert_record_stream(
237
            branch_repo.texts.get_record_stream(all_texts, 'unordered', False))
238
        # Add inventory and revision for rev-2.
239
        repo.add_inventory('rev-2', inv, ['rev-1', 'ghost-rev'])
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
240
        repo.revisions.insert_record_stream(
241
            branch_repo.revisions.get_record_stream(
242
                [('rev-2',)], 'unordered', False))
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
243
        # Now, no inventories are reported as missing, even though there is a
244
        # ghost.
245
        self.assertEqual(set(), repo.get_missing_parent_inventories())
246
        # Resuming the write group does not affect
247
        # get_missing_parent_inventories.
248
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
249
        self.assertEqual(set(), reopened_repo.get_missing_parent_inventories())
250
        reopened_repo.abort_write_group()
251
252
    def test_get_missing_parent_inventories(self):
253
        """A stacked repo with a single revision and inventory (no parent
254
        inventory) in it must have all the texts in its inventory (even if not
255
        changed w.r.t. to the absent parent), otherwise it will report missing
256
        texts/parent inventory.
4343.3.29 by John Arbash Meinel
Add 'check_for_missing_texts' flag to get_missing_parent_inv..
257
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
258
        The core of this test is that a file was changed in rev-1, but in a
4343.3.29 by John Arbash Meinel
Add 'check_for_missing_texts' flag to get_missing_parent_inv..
259
        stacked repo that only has rev-2
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
260
        """
261
        # Make a trunk with one commit.
262
        trunk_repo = self.make_stackable_repo()
263
        self.make_first_commit(trunk_repo)
4309.1.4 by Andrew Bennetts
Remove some cruft.
264
        trunk_repo.lock_read()
265
        self.addCleanup(trunk_repo.unlock)
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
266
        # Branch the trunk, add a new commit.
4309.1.4 by Andrew Bennetts
Remove some cruft.
267
        branch_repo = self.make_new_commit_in_new_repo(
268
            trunk_repo, parents=['rev-1'])
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
269
        inv = branch_repo.get_inventory('rev-2')
270
        # Make a new repo stacked on trunk, and copy the new commit's revision
271
        # and inventory records to it.
272
        repo = self.make_stackable_repo('stacked')
273
        repo.lock_write()
274
        repo.start_write_group()
4309.1.4 by Andrew Bennetts
Remove some cruft.
275
        # Insert a single fulltext inv (using add_inventory because it's
276
        # simpler than insert_record_stream)
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
277
        repo.add_inventory('rev-2', inv, ['rev-1'])
278
        repo.revisions.insert_record_stream(
279
            branch_repo.revisions.get_record_stream(
280
                [('rev-2',)], 'unordered', False))
281
        # There should be no missing compression parents
282
        self.assertEqual(set(),
283
                repo.inventories.get_missing_compression_parent_keys())
284
        self.assertEqual(
285
            set([('inventories', 'rev-1')]),
286
            repo.get_missing_parent_inventories())
287
        # Resuming the write group does not affect
288
        # get_missing_parent_inventories.
289
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
4257.4.9 by Andrew Bennetts
Add more test coverage, but make a long test even longer in the process.
290
        self.assertEqual(
291
            set([('inventories', 'rev-1')]),
4257.4.10 by Andrew Bennetts
Observe new revisions in _KnitGraphIndex.add_record rather than iterating all the uncommitted packs' indices.
292
            reopened_repo.get_missing_parent_inventories())
4309.1.3 by Andrew Bennetts
Start testing more cases, and start factoring those tests a little more clearly.
293
        # Adding the parent inventory satisfies get_missing_parent_inventories.
4257.4.10 by Andrew Bennetts
Observe new revisions in _KnitGraphIndex.add_record rather than iterating all the uncommitted packs' indices.
294
        reopened_repo.inventories.insert_record_stream(
4257.4.9 by Andrew Bennetts
Add more test coverage, but make a long test even longer in the process.
295
            branch_repo.inventories.get_record_stream(
296
                [('rev-1',)], 'unordered', False))
297
        self.assertEqual(
4257.4.10 by Andrew Bennetts
Observe new revisions in _KnitGraphIndex.add_record rather than iterating all the uncommitted packs' indices.
298
            set(), reopened_repo.get_missing_parent_inventories())
299
        reopened_repo.abort_write_group()
4257.4.8 by Andrew Bennetts
Add a (messy) test for get_missing_parent_inventories.
300
4343.3.29 by John Arbash Meinel
Add 'check_for_missing_texts' flag to get_missing_parent_inv..
301
    def test_get_missing_parent_inventories_check(self):
302
        builder = self.make_branch_builder('test')
303
        builder.build_snapshot('A-id', ['ghost-parent-id'], [
304
            ('add', ('', 'root-id', 'directory', None)),
305
            ('add', ('file', 'file-id', 'file', 'content\n'))],
306
            allow_leftmost_as_ghost=True)
307
        b = builder.get_branch()
308
        b.lock_read()
309
        self.addCleanup(b.unlock)
310
        repo = self.make_repository('test-repo')
311
        repo.lock_write()
312
        self.addCleanup(repo.unlock)
313
        repo.start_write_group()
314
        self.addCleanup(repo.abort_write_group)
315
        # Now, add the objects manually
316
        text_keys = [('file-id', 'A-id')]
317
        if repo.supports_rich_root():
318
            text_keys.append(('root-id', 'A-id'))
319
        # Directly add the texts, inventory, and revision object for 'A-id'
320
        repo.texts.insert_record_stream(b.repository.texts.get_record_stream(
321
            text_keys, 'unordered', True))
322
        repo.add_revision('A-id', b.repository.get_revision('A-id'),
323
                          b.repository.get_inventory('A-id'))
4343.3.30 by John Arbash Meinel
Add tests that when resuming a write group, we start checking if
324
        get_missing = repo.get_missing_parent_inventories
4343.3.29 by John Arbash Meinel
Add 'check_for_missing_texts' flag to get_missing_parent_inv..
325
        if repo._format.supports_external_lookups:
326
            self.assertEqual(set([('inventories', 'ghost-parent-id')]),
4343.3.30 by John Arbash Meinel
Add tests that when resuming a write group, we start checking if
327
                get_missing(check_for_missing_texts=False))
328
            self.assertEqual(set(), get_missing(check_for_missing_texts=True))
329
            self.assertEqual(set(), get_missing())
4343.3.29 by John Arbash Meinel
Add 'check_for_missing_texts' flag to get_missing_parent_inv..
330
        else:
4343.3.30 by John Arbash Meinel
Add tests that when resuming a write group, we start checking if
331
            # If we don't support external lookups, we always return empty
332
            self.assertEqual(set(), get_missing(check_for_missing_texts=False))
333
            self.assertEqual(set(), get_missing(check_for_missing_texts=True))
334
            self.assertEqual(set(), get_missing())
335
336
    def test_insert_stream_passes_resume_info(self):
337
        repo = self.make_repository('test-repo')
4617.1.1 by Robert Collins
Teach the check for get_missing_parent_inventories that this test applies to direct repositories not RemoteRepository.
338
        if (not repo._format.supports_external_lookups or
339
            isinstance(repo, remote.RemoteRepository)):
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
340
            raise tests.TestNotApplicable(
4617.1.1 by Robert Collins
Teach the check for get_missing_parent_inventories that this test applies to direct repositories not RemoteRepository.
341
                'only valid for direct connections to resumable repos')
4343.3.30 by John Arbash Meinel
Add tests that when resuming a write group, we start checking if
342
        # log calls to get_missing_parent_inventories, so that we can assert it
343
        # is called with the correct parameters
344
        call_log = []
345
        orig = repo.get_missing_parent_inventories
346
        def get_missing(check_for_missing_texts=True):
347
            call_log.append(check_for_missing_texts)
348
            return orig(check_for_missing_texts=check_for_missing_texts)
349
        repo.get_missing_parent_inventories = get_missing
350
        repo.lock_write()
351
        self.addCleanup(repo.unlock)
352
        sink = repo._get_sink()
353
        sink.insert_stream((), repo._format, [])
354
        self.assertEqual([False], call_log)
355
        del call_log[:]
356
        repo.start_write_group()
357
        # We need to insert something, or suspend_write_group won't actually
358
        # create a token
359
        repo.texts.insert_record_stream([versionedfile.FulltextContentFactory(
360
            ('file-id', 'rev-id'), (), None, 'lines\n')])
361
        tokens = repo.suspend_write_group()
362
        self.assertNotEqual([], tokens)
363
        sink.insert_stream((), repo._format, tokens)
364
        self.assertEqual([True], call_log)
4343.3.29 by John Arbash Meinel
Add 'check_for_missing_texts' flag to get_missing_parent_inv..
365
4002.1.3 by Andrew Bennetts
Remove more duplication, put resumable write group tests in separate TestCase.
366
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
367
class TestResumeableWriteGroup(per_repository.TestCaseWithRepository):
4002.1.3 by Andrew Bennetts
Remove more duplication, put resumable write group tests in separate TestCase.
368
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
369
    def make_write_locked_repo(self, relpath='repo'):
370
        repo = self.make_repository(relpath)
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
371
        repo.lock_write()
372
        self.addCleanup(repo.unlock)
373
        return repo
374
375
    def reopen_repo(self, repo):
376
        same_repo = repo.bzrdir.open_repository()
377
        same_repo.lock_write()
378
        self.addCleanup(same_repo.unlock)
379
        return same_repo
380
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
381
    def require_suspendable_write_groups(self, reason):
382
        repo = self.make_repository('__suspend_test')
383
        repo.lock_write()
384
        self.addCleanup(repo.unlock)
385
        repo.start_write_group()
386
        try:
387
            wg_tokens = repo.suspend_write_group()
388
        except errors.UnsuspendableWriteGroup:
389
            repo.abort_write_group()
5010.2.7 by Vincent Ladeuil
Fix per_repository/test_write_group.py imports.
390
            raise tests.TestNotApplicable(reason)
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
391
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
392
    def test_suspend_write_group(self):
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
393
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
394
        repo.start_write_group()
395
        # Add some content so this isn't an empty write group (which may return
396
        # 0 tokens)
397
        repo.texts.add_lines(('file-id', 'revid'), (), ['lines'])
398
        try:
399
            wg_tokens = repo.suspend_write_group()
400
        except errors.UnsuspendableWriteGroup:
401
            # The contract for repos that don't support suspending write groups
402
            # is that suspend_write_group raises UnsuspendableWriteGroup, but
403
            # is otherwise a no-op.  So we can still e.g. abort the write group
404
            # as usual.
405
            self.assertTrue(repo.is_in_write_group())
406
            repo.abort_write_group()
407
        else:
408
            # After suspending a write group we are no longer in a write group
409
            self.assertFalse(repo.is_in_write_group())
410
            # suspend_write_group returns a list of tokens, which are strs.  If
411
            # no other write groups were resumed, there will only be one token.
412
            self.assertEqual(1, len(wg_tokens))
413
            self.assertIsInstance(wg_tokens[0], str)
414
            # See also test_pack_repository's test of the same name.
415
416
    def test_resume_write_group_then_abort(self):
4002.1.3 by Andrew Bennetts
Remove more duplication, put resumable write group tests in separate TestCase.
417
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
418
        repo.start_write_group()
419
        # Add some content so this isn't an empty write group (which may return
420
        # 0 tokens)
421
        text_key = ('file-id', 'revid')
422
        repo.texts.add_lines(text_key, (), ['lines'])
423
        try:
424
            wg_tokens = repo.suspend_write_group()
425
        except errors.UnsuspendableWriteGroup:
426
            # If the repo does not support suspending write groups, it doesn't
427
            # support resuming them either.
428
            repo.abort_write_group()
429
            self.assertRaises(
430
                errors.UnsuspendableWriteGroup, repo.resume_write_group, [])
431
        else:
432
            #self.assertEqual([], list(repo.texts.keys()))
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
433
            same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
434
            same_repo.resume_write_group(wg_tokens)
435
            self.assertEqual([text_key], list(same_repo.texts.keys()))
436
            self.assertTrue(same_repo.is_in_write_group())
437
            same_repo.abort_write_group()
438
            self.assertEqual([], list(repo.texts.keys()))
439
            # See also test_pack_repository's test of the same name.
440
441
    def test_multiple_resume_write_group(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
442
        self.require_suspendable_write_groups(
443
            'Cannot test resume on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
444
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
445
        repo.start_write_group()
446
        # Add some content so this isn't an empty write group (which may return
447
        # 0 tokens)
448
        first_key = ('file-id', 'revid')
449
        repo.texts.add_lines(first_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
450
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
451
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
452
        same_repo.resume_write_group(wg_tokens)
453
        self.assertTrue(same_repo.is_in_write_group())
454
        second_key = ('file-id', 'second-revid')
455
        same_repo.texts.add_lines(second_key, (first_key,), ['more lines'])
456
        try:
457
            new_wg_tokens = same_repo.suspend_write_group()
458
        except:
459
            e = sys.exc_info()
460
            same_repo.abort_write_group(suppress_errors=True)
461
            raise e[0], e[1], e[2]
462
        self.assertEqual(2, len(new_wg_tokens))
463
        self.assertSubset(wg_tokens, new_wg_tokens)
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
464
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
465
        same_repo.resume_write_group(new_wg_tokens)
466
        both_keys = set([first_key, second_key])
467
        self.assertEqual(both_keys, same_repo.texts.keys())
468
        same_repo.abort_write_group()
469
470
    def test_no_op_suspend_resume(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
471
        self.require_suspendable_write_groups(
472
            'Cannot test resume on repo that does not support suspending')
4002.1.3 by Andrew Bennetts
Remove more duplication, put resumable write group tests in separate TestCase.
473
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
474
        repo.start_write_group()
475
        # Add some content so this isn't an empty write group (which may return
476
        # 0 tokens)
477
        text_key = ('file-id', 'revid')
478
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
479
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
480
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
481
        same_repo.resume_write_group(wg_tokens)
482
        new_wg_tokens = same_repo.suspend_write_group()
483
        self.assertEqual(wg_tokens, new_wg_tokens)
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
484
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
485
        same_repo.resume_write_group(wg_tokens)
486
        self.assertEqual([text_key], list(same_repo.texts.keys()))
487
        same_repo.abort_write_group()
488
489
    def test_read_after_suspend_fails(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
490
        self.require_suspendable_write_groups(
491
            'Cannot test suspend on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
492
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
493
        repo.start_write_group()
494
        # Add some content so this isn't an empty write group (which may return
495
        # 0 tokens)
496
        text_key = ('file-id', 'revid')
497
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
498
        wg_tokens = repo.suspend_write_group()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
499
        self.assertEqual([], list(repo.texts.keys()))
500
501
    def test_read_after_second_suspend_fails(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
502
        self.require_suspendable_write_groups(
503
            'Cannot test suspend on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
504
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
505
        repo.start_write_group()
506
        # Add some content so this isn't an empty write group (which may return
507
        # 0 tokens)
508
        text_key = ('file-id', 'revid')
509
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
510
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
511
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
512
        same_repo.resume_write_group(wg_tokens)
513
        same_repo.suspend_write_group()
514
        self.assertEqual([], list(same_repo.texts.keys()))
515
516
    def test_read_after_resume_abort_fails(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
517
        self.require_suspendable_write_groups(
518
            'Cannot test suspend on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
519
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
520
        repo.start_write_group()
521
        # Add some content so this isn't an empty write group (which may return
522
        # 0 tokens)
523
        text_key = ('file-id', 'revid')
524
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
525
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
526
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
527
        same_repo.resume_write_group(wg_tokens)
528
        same_repo.abort_write_group()
529
        self.assertEqual([], list(same_repo.texts.keys()))
530
531
    def test_cannot_resume_aborted_write_group(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
532
        self.require_suspendable_write_groups(
533
            'Cannot test resume on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
534
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
535
        repo.start_write_group()
536
        # Add some content so this isn't an empty write group (which may return
537
        # 0 tokens)
538
        text_key = ('file-id', 'revid')
539
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
540
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
541
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
542
        same_repo.resume_write_group(wg_tokens)
543
        same_repo.abort_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
544
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
545
        self.assertRaises(
4002.1.7 by Andrew Bennetts
Rename UnresumableWriteGroups to UnresumableWriteGroup.
546
            errors.UnresumableWriteGroup, same_repo.resume_write_group,
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
547
            wg_tokens)
548
549
    def test_commit_resumed_write_group_no_new_data(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
550
        self.require_suspendable_write_groups(
551
            'Cannot test resume on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
552
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
553
        repo.start_write_group()
554
        # Add some content so this isn't an empty write group (which may return
555
        # 0 tokens)
556
        text_key = ('file-id', 'revid')
557
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
558
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
559
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
560
        same_repo.resume_write_group(wg_tokens)
561
        same_repo.commit_write_group()
562
        self.assertEqual([text_key], list(same_repo.texts.keys()))
563
        self.assertEqual(
564
            'lines', same_repo.texts.get_record_stream([text_key],
565
                'unordered', True).next().get_bytes_as('fulltext'))
566
        self.assertRaises(
4002.1.7 by Andrew Bennetts
Rename UnresumableWriteGroups to UnresumableWriteGroup.
567
            errors.UnresumableWriteGroup, same_repo.resume_write_group,
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
568
            wg_tokens)
569
570
    def test_commit_resumed_write_group_plus_new_data(self):
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
571
        self.require_suspendable_write_groups(
572
            'Cannot test resume on repo that does not support suspending')
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
573
        repo = self.make_write_locked_repo()
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
574
        repo.start_write_group()
575
        # Add some content so this isn't an empty write group (which may return
576
        # 0 tokens)
577
        first_key = ('file-id', 'revid')
578
        repo.texts.add_lines(first_key, (), ['lines'])
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
579
        wg_tokens = repo.suspend_write_group()
4002.1.2 by Andrew Bennetts
Refactor out some duplication in write_group tests.
580
        same_repo = self.reopen_repo(repo)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
581
        same_repo.resume_write_group(wg_tokens)
582
        second_key = ('file-id', 'second-revid')
583
        same_repo.texts.add_lines(second_key, (first_key,), ['more lines'])
584
        same_repo.commit_write_group()
585
        self.assertEqual(
586
            set([first_key, second_key]), set(same_repo.texts.keys()))
587
        self.assertEqual(
588
            'lines', same_repo.texts.get_record_stream([first_key],
589
                'unordered', True).next().get_bytes_as('fulltext'))
590
        self.assertEqual(
591
            'more lines', same_repo.texts.get_record_stream([second_key],
592
                'unordered', True).next().get_bytes_as('fulltext'))
593
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
594
    def make_source_with_delta_record(self):
595
        # Make a source repository with a delta record in it.
596
        source_repo = self.make_write_locked_repo('source')
597
        source_repo.start_write_group()
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
598
        key_base = ('file-id', 'base')
599
        key_delta = ('file-id', 'delta')
4343.3.6 by John Arbash Meinel
Take out the _delta_across_repos flag.
600
        def text_stream():
601
            yield versionedfile.FulltextContentFactory(
602
                key_base, (), None, 'lines\n')
603
            yield versionedfile.FulltextContentFactory(
604
                key_delta, (key_base,), None, 'more\nlines\n')
605
        source_repo.texts.insert_record_stream(text_stream())
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
606
        source_repo.commit_write_group()
607
        return source_repo
608
4002.1.12 by Andrew Bennetts
Add another test, fix the code so it passes, and remove some cruft.
609
    def test_commit_resumed_write_group_with_missing_parents(self):
610
        self.require_suspendable_write_groups(
611
            'Cannot test resume on repo that does not support suspending')
612
        source_repo = self.make_source_with_delta_record()
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
613
        key_base = ('file-id', 'base')
614
        key_delta = ('file-id', 'delta')
4002.1.12 by Andrew Bennetts
Add another test, fix the code so it passes, and remove some cruft.
615
        # Start a write group, insert just a delta.
616
        repo = self.make_write_locked_repo()
617
        repo.start_write_group()
618
        stream = source_repo.texts.get_record_stream(
619
            [key_delta], 'unordered', False)
620
        repo.texts.insert_record_stream(stream)
4343.3.6 by John Arbash Meinel
Take out the _delta_across_repos flag.
621
        # It's either not commitable due to the missing compression parent, or
622
        # the stacked location has already filled in the fulltext.
623
        try:
624
            repo.commit_write_group()
625
        except errors.BzrCheckError:
626
            # It refused to commit because we have a missing parent
627
            pass
628
        else:
629
            same_repo = self.reopen_repo(repo)
630
            same_repo.lock_read()
631
            record = same_repo.texts.get_record_stream([key_delta],
632
                                                       'unordered', True).next()
633
            self.assertEqual('more\nlines\n', record.get_bytes_as('fulltext'))
634
            return
4002.1.12 by Andrew Bennetts
Add another test, fix the code so it passes, and remove some cruft.
635
        # Merely suspending and resuming doesn't make it commitable either.
636
        wg_tokens = repo.suspend_write_group()
637
        same_repo = self.reopen_repo(repo)
638
        same_repo.resume_write_group(wg_tokens)
639
        self.assertRaises(
640
            errors.BzrCheckError, same_repo.commit_write_group)
641
        same_repo.abort_write_group()
642
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
643
    def test_commit_resumed_write_group_adding_missing_parents(self):
644
        self.require_suspendable_write_groups(
645
            'Cannot test resume on repo that does not support suspending')
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
646
        source_repo = self.make_source_with_delta_record()
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
647
        key_base = ('file-id', 'base')
648
        key_delta = ('file-id', 'delta')
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
649
        # Start a write group.
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
650
        repo = self.make_write_locked_repo()
651
        repo.start_write_group()
652
        # Add some content so this isn't an empty write group (which may return
653
        # 0 tokens)
654
        text_key = ('file-id', 'revid')
655
        repo.texts.add_lines(text_key, (), ['lines'])
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
656
        # Suspend it, then resume it.
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
657
        wg_tokens = repo.suspend_write_group()
658
        same_repo = self.reopen_repo(repo)
659
        same_repo.resume_write_group(wg_tokens)
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
660
        # Add a record with a missing compression parent
661
        stream = source_repo.texts.get_record_stream(
662
            [key_delta], 'unordered', False)
663
        same_repo.texts.insert_record_stream(stream)
664
        # Just like if we'd added that record without a suspend/resume cycle,
665
        # commit_write_group fails.
4343.3.6 by John Arbash Meinel
Take out the _delta_across_repos flag.
666
        try:
667
            same_repo.commit_write_group()
668
        except errors.BzrCheckError:
669
            pass
670
        else:
671
            # If the commit_write_group didn't fail, that is because the
672
            # insert_record_stream already gave it a fulltext.
673
            same_repo = self.reopen_repo(repo)
674
            same_repo.lock_read()
675
            record = same_repo.texts.get_record_stream([key_delta],
676
                                                       'unordered', True).next()
677
            self.assertEqual('more\nlines\n', record.get_bytes_as('fulltext'))
678
            return
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
679
        same_repo.abort_write_group()
680
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
681
    def test_add_missing_parent_after_resume(self):
682
        self.require_suspendable_write_groups(
683
            'Cannot test resume on repo that does not support suspending')
684
        source_repo = self.make_source_with_delta_record()
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
685
        key_base = ('file-id', 'base')
686
        key_delta = ('file-id', 'delta')
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
687
        # Start a write group, insert just a delta.
688
        repo = self.make_write_locked_repo()
689
        repo.start_write_group()
690
        stream = source_repo.texts.get_record_stream(
691
            [key_delta], 'unordered', False)
692
        repo.texts.insert_record_stream(stream)
693
        # Suspend it, then resume it.
694
        wg_tokens = repo.suspend_write_group()
695
        same_repo = self.reopen_repo(repo)
4002.1.11 by Andrew Bennetts
Fix latest test.
696
        same_repo.resume_write_group(wg_tokens)
4002.1.10 by Andrew Bennetts
Fix some existing tests, add a new test that fails.
697
        # Fill in the missing compression parent.
698
        stream = source_repo.texts.get_record_stream(
699
            [key_base], 'unordered', False)
700
        same_repo.texts.insert_record_stream(stream)
701
        same_repo.commit_write_group()
702
4002.1.4 by Andrew Bennetts
Remove a little more duplication, add some more tests (some passing, some not).
703
    def test_suspend_empty_initial_write_group(self):
704
        """Suspending a write group with no writes returns an empty token
705
        list.
706
        """
707
        self.require_suspendable_write_groups(
708
            'Cannot test suspend on repo that does not support suspending')
709
        repo = self.make_write_locked_repo()
710
        repo.start_write_group()
711
        wg_tokens = repo.suspend_write_group()
712
        self.assertEqual([], wg_tokens)
713
714
    def test_suspend_empty_initial_write_group(self):
715
        """Resuming an empty token list is equivalent to start_write_group."""
716
        self.require_suspendable_write_groups(
717
            'Cannot test resume on repo that does not support suspending')
718
        repo = self.make_write_locked_repo()
719
        repo.resume_write_group([])
720
        repo.abort_write_group()
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
721
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
722