/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_repository/test_write_group.py

merge bzr.dev r4054

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Tests for repository write groups."""
18
18
 
 
19
import sys
 
20
 
19
21
from bzrlib import errors
20
22
from bzrlib.transport import local, memory
21
23
from bzrlib.tests import TestNotApplicable
48
50
        repo.lock_write()
49
51
        repo.start_write_group()
50
52
        try:
51
 
            # don't need a specific exception for now - this is 
 
53
            # don't need a specific exception for now - this is
52
54
            # really to be sure it's used right, not for signalling
53
55
            # semantic information.
54
56
            self.assertRaises(errors.BzrError, repo.start_write_group)
67
69
        repo = self.make_repository('.')
68
70
        repo.lock_write()
69
71
        repo.start_write_group()
70
 
        # don't need a specific exception for now - this is 
 
72
        # don't need a specific exception for now - this is
71
73
        # really to be sure it's used right, not for signalling
72
74
        # semantic information.
73
75
        self.assertRaises(errors.BzrError, repo.unlock)
115
117
        self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
116
118
        if token is not None:
117
119
            repo.leave_lock_in_place()
 
120
 
 
121
 
 
122
class TestResumeableWriteGroup(TestCaseWithRepository):
 
123
 
 
124
    def make_write_locked_repo(self, relpath='repo'):
 
125
        repo = self.make_repository(relpath)
 
126
        repo.lock_write()
 
127
        self.addCleanup(repo.unlock)
 
128
        return repo
 
129
 
 
130
    def reopen_repo(self, repo):
 
131
        same_repo = repo.bzrdir.open_repository()
 
132
        same_repo.lock_write()
 
133
        self.addCleanup(same_repo.unlock)
 
134
        return same_repo
 
135
 
 
136
    def require_suspendable_write_groups(self, reason):
 
137
        repo = self.make_repository('__suspend_test')
 
138
        repo.lock_write()
 
139
        self.addCleanup(repo.unlock)
 
140
        repo.start_write_group()
 
141
        try:
 
142
            wg_tokens = repo.suspend_write_group()
 
143
        except errors.UnsuspendableWriteGroup:
 
144
            repo.abort_write_group()
 
145
            raise TestNotApplicable(reason)
 
146
 
 
147
    def test_suspend_write_group(self):
 
148
        repo = self.make_write_locked_repo()
 
149
        repo.start_write_group()
 
150
        # Add some content so this isn't an empty write group (which may return
 
151
        # 0 tokens)
 
152
        repo.texts.add_lines(('file-id', 'revid'), (), ['lines'])
 
153
        try:
 
154
            wg_tokens = repo.suspend_write_group()
 
155
        except errors.UnsuspendableWriteGroup:
 
156
            # The contract for repos that don't support suspending write groups
 
157
            # is that suspend_write_group raises UnsuspendableWriteGroup, but
 
158
            # is otherwise a no-op.  So we can still e.g. abort the write group
 
159
            # as usual.
 
160
            self.assertTrue(repo.is_in_write_group())
 
161
            repo.abort_write_group()
 
162
        else:
 
163
            # After suspending a write group we are no longer in a write group
 
164
            self.assertFalse(repo.is_in_write_group())
 
165
            # suspend_write_group returns a list of tokens, which are strs.  If
 
166
            # no other write groups were resumed, there will only be one token.
 
167
            self.assertEqual(1, len(wg_tokens))
 
168
            self.assertIsInstance(wg_tokens[0], str)
 
169
            # See also test_pack_repository's test of the same name.
 
170
 
 
171
    def test_resume_write_group_then_abort(self):
 
172
        repo = self.make_write_locked_repo()
 
173
        repo.start_write_group()
 
174
        # Add some content so this isn't an empty write group (which may return
 
175
        # 0 tokens)
 
176
        text_key = ('file-id', 'revid')
 
177
        repo.texts.add_lines(text_key, (), ['lines'])
 
178
        try:
 
179
            wg_tokens = repo.suspend_write_group()
 
180
        except errors.UnsuspendableWriteGroup:
 
181
            # If the repo does not support suspending write groups, it doesn't
 
182
            # support resuming them either.
 
183
            repo.abort_write_group()
 
184
            self.assertRaises(
 
185
                errors.UnsuspendableWriteGroup, repo.resume_write_group, [])
 
186
        else:
 
187
            #self.assertEqual([], list(repo.texts.keys()))
 
188
            same_repo = self.reopen_repo(repo)
 
189
            same_repo.resume_write_group(wg_tokens)
 
190
            self.assertEqual([text_key], list(same_repo.texts.keys()))
 
191
            self.assertTrue(same_repo.is_in_write_group())
 
192
            same_repo.abort_write_group()
 
193
            self.assertEqual([], list(repo.texts.keys()))
 
194
            # See also test_pack_repository's test of the same name.
 
195
 
 
196
    def test_multiple_resume_write_group(self):
 
197
        self.require_suspendable_write_groups(
 
198
            'Cannot test resume on repo that does not support suspending')
 
199
        repo = self.make_write_locked_repo()
 
200
        repo.start_write_group()
 
201
        # Add some content so this isn't an empty write group (which may return
 
202
        # 0 tokens)
 
203
        first_key = ('file-id', 'revid')
 
204
        repo.texts.add_lines(first_key, (), ['lines'])
 
205
        wg_tokens = repo.suspend_write_group()
 
206
        same_repo = self.reopen_repo(repo)
 
207
        same_repo.resume_write_group(wg_tokens)
 
208
        self.assertTrue(same_repo.is_in_write_group())
 
209
        second_key = ('file-id', 'second-revid')
 
210
        same_repo.texts.add_lines(second_key, (first_key,), ['more lines'])
 
211
        try:
 
212
            new_wg_tokens = same_repo.suspend_write_group()
 
213
        except:
 
214
            e = sys.exc_info()
 
215
            same_repo.abort_write_group(suppress_errors=True)
 
216
            raise e[0], e[1], e[2]
 
217
        self.assertEqual(2, len(new_wg_tokens))
 
218
        self.assertSubset(wg_tokens, new_wg_tokens)
 
219
        same_repo = self.reopen_repo(repo)
 
220
        same_repo.resume_write_group(new_wg_tokens)
 
221
        both_keys = set([first_key, second_key])
 
222
        self.assertEqual(both_keys, same_repo.texts.keys())
 
223
        same_repo.abort_write_group()
 
224
 
 
225
    def test_no_op_suspend_resume(self):
 
226
        self.require_suspendable_write_groups(
 
227
            'Cannot test resume on repo that does not support suspending')
 
228
        repo = self.make_write_locked_repo()
 
229
        repo.start_write_group()
 
230
        # Add some content so this isn't an empty write group (which may return
 
231
        # 0 tokens)
 
232
        text_key = ('file-id', 'revid')
 
233
        repo.texts.add_lines(text_key, (), ['lines'])
 
234
        wg_tokens = repo.suspend_write_group()
 
235
        same_repo = self.reopen_repo(repo)
 
236
        same_repo.resume_write_group(wg_tokens)
 
237
        new_wg_tokens = same_repo.suspend_write_group()
 
238
        self.assertEqual(wg_tokens, new_wg_tokens)
 
239
        same_repo = self.reopen_repo(repo)
 
240
        same_repo.resume_write_group(wg_tokens)
 
241
        self.assertEqual([text_key], list(same_repo.texts.keys()))
 
242
        same_repo.abort_write_group()
 
243
 
 
244
    def test_read_after_suspend_fails(self):
 
245
        self.require_suspendable_write_groups(
 
246
            'Cannot test suspend on repo that does not support suspending')
 
247
        repo = self.make_write_locked_repo()
 
248
        repo.start_write_group()
 
249
        # Add some content so this isn't an empty write group (which may return
 
250
        # 0 tokens)
 
251
        text_key = ('file-id', 'revid')
 
252
        repo.texts.add_lines(text_key, (), ['lines'])
 
253
        wg_tokens = repo.suspend_write_group()
 
254
        self.assertEqual([], list(repo.texts.keys()))
 
255
 
 
256
    def test_read_after_second_suspend_fails(self):
 
257
        self.require_suspendable_write_groups(
 
258
            'Cannot test suspend on repo that does not support suspending')
 
259
        repo = self.make_write_locked_repo()
 
260
        repo.start_write_group()
 
261
        # Add some content so this isn't an empty write group (which may return
 
262
        # 0 tokens)
 
263
        text_key = ('file-id', 'revid')
 
264
        repo.texts.add_lines(text_key, (), ['lines'])
 
265
        wg_tokens = repo.suspend_write_group()
 
266
        same_repo = self.reopen_repo(repo)
 
267
        same_repo.resume_write_group(wg_tokens)
 
268
        same_repo.suspend_write_group()
 
269
        self.assertEqual([], list(same_repo.texts.keys()))
 
270
 
 
271
    def test_read_after_resume_abort_fails(self):
 
272
        self.require_suspendable_write_groups(
 
273
            'Cannot test suspend on repo that does not support suspending')
 
274
        repo = self.make_write_locked_repo()
 
275
        repo.start_write_group()
 
276
        # Add some content so this isn't an empty write group (which may return
 
277
        # 0 tokens)
 
278
        text_key = ('file-id', 'revid')
 
279
        repo.texts.add_lines(text_key, (), ['lines'])
 
280
        wg_tokens = repo.suspend_write_group()
 
281
        same_repo = self.reopen_repo(repo)
 
282
        same_repo.resume_write_group(wg_tokens)
 
283
        same_repo.abort_write_group()
 
284
        self.assertEqual([], list(same_repo.texts.keys()))
 
285
 
 
286
    def test_cannot_resume_aborted_write_group(self):
 
287
        self.require_suspendable_write_groups(
 
288
            'Cannot test resume on repo that does not support suspending')
 
289
        repo = self.make_write_locked_repo()
 
290
        repo.start_write_group()
 
291
        # Add some content so this isn't an empty write group (which may return
 
292
        # 0 tokens)
 
293
        text_key = ('file-id', 'revid')
 
294
        repo.texts.add_lines(text_key, (), ['lines'])
 
295
        wg_tokens = repo.suspend_write_group()
 
296
        same_repo = self.reopen_repo(repo)
 
297
        same_repo.resume_write_group(wg_tokens)
 
298
        same_repo.abort_write_group()
 
299
        same_repo = self.reopen_repo(repo)
 
300
        self.assertRaises(
 
301
            errors.UnresumableWriteGroup, same_repo.resume_write_group,
 
302
            wg_tokens)
 
303
 
 
304
    def test_commit_resumed_write_group_no_new_data(self):
 
305
        self.require_suspendable_write_groups(
 
306
            'Cannot test resume on repo that does not support suspending')
 
307
        repo = self.make_write_locked_repo()
 
308
        repo.start_write_group()
 
309
        # Add some content so this isn't an empty write group (which may return
 
310
        # 0 tokens)
 
311
        text_key = ('file-id', 'revid')
 
312
        repo.texts.add_lines(text_key, (), ['lines'])
 
313
        wg_tokens = repo.suspend_write_group()
 
314
        same_repo = self.reopen_repo(repo)
 
315
        same_repo.resume_write_group(wg_tokens)
 
316
        same_repo.commit_write_group()
 
317
        self.assertEqual([text_key], list(same_repo.texts.keys()))
 
318
        self.assertEqual(
 
319
            'lines', same_repo.texts.get_record_stream([text_key],
 
320
                'unordered', True).next().get_bytes_as('fulltext'))
 
321
        self.assertRaises(
 
322
            errors.UnresumableWriteGroup, same_repo.resume_write_group,
 
323
            wg_tokens)
 
324
 
 
325
    def test_commit_resumed_write_group_plus_new_data(self):
 
326
        self.require_suspendable_write_groups(
 
327
            'Cannot test resume on repo that does not support suspending')
 
328
        repo = self.make_write_locked_repo()
 
329
        repo.start_write_group()
 
330
        # Add some content so this isn't an empty write group (which may return
 
331
        # 0 tokens)
 
332
        first_key = ('file-id', 'revid')
 
333
        repo.texts.add_lines(first_key, (), ['lines'])
 
334
        wg_tokens = repo.suspend_write_group()
 
335
        same_repo = self.reopen_repo(repo)
 
336
        same_repo.resume_write_group(wg_tokens)
 
337
        second_key = ('file-id', 'second-revid')
 
338
        same_repo.texts.add_lines(second_key, (first_key,), ['more lines'])
 
339
        same_repo.commit_write_group()
 
340
        self.assertEqual(
 
341
            set([first_key, second_key]), set(same_repo.texts.keys()))
 
342
        self.assertEqual(
 
343
            'lines', same_repo.texts.get_record_stream([first_key],
 
344
                'unordered', True).next().get_bytes_as('fulltext'))
 
345
        self.assertEqual(
 
346
            'more lines', same_repo.texts.get_record_stream([second_key],
 
347
                'unordered', True).next().get_bytes_as('fulltext'))
 
348
 
 
349
    def make_source_with_delta_record(self):
 
350
        # Make a source repository with a delta record in it.
 
351
        source_repo = self.make_write_locked_repo('source')
 
352
        source_repo.start_write_group()
 
353
        key_base = ('file-id', 'base')
 
354
        key_delta = ('file-id', 'delta')
 
355
        source_repo.texts.add_lines(key_base, (), ['lines\n'])
 
356
        source_repo.texts.add_lines(
 
357
            key_delta, (key_base,), ['more\n', 'lines\n'])
 
358
        source_repo.commit_write_group()
 
359
        return source_repo
 
360
 
 
361
    def test_commit_resumed_write_group_with_missing_parents(self):
 
362
        self.require_suspendable_write_groups(
 
363
            'Cannot test resume on repo that does not support suspending')
 
364
        source_repo = self.make_source_with_delta_record()
 
365
        key_base = ('file-id', 'base')
 
366
        key_delta = ('file-id', 'delta')
 
367
        # Start a write group, insert just a delta.
 
368
        repo = self.make_write_locked_repo()
 
369
        repo.start_write_group()
 
370
        stream = source_repo.texts.get_record_stream(
 
371
            [key_delta], 'unordered', False)
 
372
        repo.texts.insert_record_stream(stream)
 
373
        # It's not commitable due to the missing compression parent.
 
374
        self.assertRaises(
 
375
            errors.BzrCheckError, repo.commit_write_group)
 
376
        # Merely suspending and resuming doesn't make it commitable either.
 
377
        wg_tokens = repo.suspend_write_group()
 
378
        same_repo = self.reopen_repo(repo)
 
379
        same_repo.resume_write_group(wg_tokens)
 
380
        self.assertRaises(
 
381
            errors.BzrCheckError, same_repo.commit_write_group)
 
382
        same_repo.abort_write_group()
 
383
 
 
384
    def test_commit_resumed_write_group_adding_missing_parents(self):
 
385
        self.require_suspendable_write_groups(
 
386
            'Cannot test resume on repo that does not support suspending')
 
387
        source_repo = self.make_source_with_delta_record()
 
388
        key_base = ('file-id', 'base')
 
389
        key_delta = ('file-id', 'delta')
 
390
        # Start a write group.
 
391
        repo = self.make_write_locked_repo()
 
392
        repo.start_write_group()
 
393
        # Add some content so this isn't an empty write group (which may return
 
394
        # 0 tokens)
 
395
        text_key = ('file-id', 'revid')
 
396
        repo.texts.add_lines(text_key, (), ['lines'])
 
397
        # Suspend it, then resume it.
 
398
        wg_tokens = repo.suspend_write_group()
 
399
        same_repo = self.reopen_repo(repo)
 
400
        same_repo.resume_write_group(wg_tokens)
 
401
        # Add a record with a missing compression parent
 
402
        stream = source_repo.texts.get_record_stream(
 
403
            [key_delta], 'unordered', False)
 
404
        same_repo.texts.insert_record_stream(stream)
 
405
        # Just like if we'd added that record without a suspend/resume cycle,
 
406
        # commit_write_group fails.
 
407
        self.assertRaises(
 
408
            errors.BzrCheckError, same_repo.commit_write_group)
 
409
        same_repo.abort_write_group()
 
410
 
 
411
    def test_add_missing_parent_after_resume(self):
 
412
        self.require_suspendable_write_groups(
 
413
            'Cannot test resume on repo that does not support suspending')
 
414
        source_repo = self.make_source_with_delta_record()
 
415
        key_base = ('file-id', 'base')
 
416
        key_delta = ('file-id', 'delta')
 
417
        # Start a write group, insert just a delta.
 
418
        repo = self.make_write_locked_repo()
 
419
        repo.start_write_group()
 
420
        stream = source_repo.texts.get_record_stream(
 
421
            [key_delta], 'unordered', False)
 
422
        repo.texts.insert_record_stream(stream)
 
423
        # Suspend it, then resume it.
 
424
        wg_tokens = repo.suspend_write_group()
 
425
        same_repo = self.reopen_repo(repo)
 
426
        same_repo.resume_write_group(wg_tokens)
 
427
        # Fill in the missing compression parent.
 
428
        stream = source_repo.texts.get_record_stream(
 
429
            [key_base], 'unordered', False)
 
430
        same_repo.texts.insert_record_stream(stream)
 
431
        same_repo.commit_write_group()
 
432
 
 
433
    def test_suspend_empty_initial_write_group(self):
 
434
        """Suspending a write group with no writes returns an empty token
 
435
        list.
 
436
        """
 
437
        self.require_suspendable_write_groups(
 
438
            'Cannot test suspend on repo that does not support suspending')
 
439
        repo = self.make_write_locked_repo()
 
440
        repo.start_write_group()
 
441
        wg_tokens = repo.suspend_write_group()
 
442
        self.assertEqual([], wg_tokens)
 
443
 
 
444
    def test_suspend_empty_initial_write_group(self):
 
445
        """Resuming an empty token list is equivalent to start_write_group."""
 
446
        self.require_suspendable_write_groups(
 
447
            'Cannot test resume on repo that does not support suspending')
 
448
        repo = self.make_write_locked_repo()
 
449
        repo.resume_write_group([])
 
450
        repo.abort_write_group()
 
451
 
 
452