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()
122
class TestResumeableWriteGroup(TestCaseWithRepository):
124
def make_write_locked_repo(self, relpath='repo'):
125
repo = self.make_repository(relpath)
127
self.addCleanup(repo.unlock)
130
def reopen_repo(self, repo):
131
same_repo = repo.bzrdir.open_repository()
132
same_repo.lock_write()
133
self.addCleanup(same_repo.unlock)
136
def require_suspendable_write_groups(self, reason):
137
repo = self.make_repository('__suspend_test')
139
self.addCleanup(repo.unlock)
140
repo.start_write_group()
142
wg_tokens = repo.suspend_write_group()
143
except errors.UnsuspendableWriteGroup:
144
repo.abort_write_group()
145
raise TestNotApplicable(reason)
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
152
repo.texts.add_lines(('file-id', 'revid'), (), ['lines'])
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
160
self.assertTrue(repo.is_in_write_group())
161
repo.abort_write_group()
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.
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
176
text_key = ('file-id', 'revid')
177
repo.texts.add_lines(text_key, (), ['lines'])
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()
185
errors.UnsuspendableWriteGroup, repo.resume_write_group, [])
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.
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
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'])
212
new_wg_tokens = same_repo.suspend_write_group()
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()
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
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()
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
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()))
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
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()))
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
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()))
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
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)
301
errors.UnresumableWriteGroup, same_repo.resume_write_group,
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
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()))
319
'lines', same_repo.texts.get_record_stream([text_key],
320
'unordered', True).next().get_bytes_as('fulltext'))
322
errors.UnresumableWriteGroup, same_repo.resume_write_group,
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
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()
341
set([first_key, second_key]), set(same_repo.texts.keys()))
343
'lines', same_repo.texts.get_record_stream([first_key],
344
'unordered', True).next().get_bytes_as('fulltext'))
346
'more lines', same_repo.texts.get_record_stream([second_key],
347
'unordered', True).next().get_bytes_as('fulltext'))
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()
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.
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)
381
errors.BzrCheckError, same_repo.commit_write_group)
382
same_repo.abort_write_group()
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
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.
408
errors.BzrCheckError, same_repo.commit_write_group)
409
same_repo.abort_write_group()
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()
433
def test_suspend_empty_initial_write_group(self):
434
"""Suspending a write group with no writes returns an empty token
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)
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()