110
113
return StringIO("\n".join(self.file_lines))
115
def readv(self, relpath, offsets):
116
fp = self.get(relpath)
117
for offset, size in offsets:
119
yield offset, fp.read(size)
112
121
def __getattr__(self, name):
113
122
def queue_call(*args, **kwargs):
114
123
self.calls.append((name, args, kwargs))
115
124
return queue_call
127
class LowLevelKnitDataTests(TestCase):
129
def create_gz_content(self, text):
131
gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
134
return sio.getvalue()
136
def test_valid_knit_data(self):
137
sha1sum = sha.new('foo\nbar\n').hexdigest()
138
gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
143
transport = MockTransport([gz_txt])
144
data = _KnitData(transport, 'filename', mode='r')
145
records = [('rev-id-1', 0, len(gz_txt))]
147
contents = data.read_records(records)
148
self.assertEqual({'rev-id-1':(['foo\n', 'bar\n'], sha1sum)}, contents)
150
raw_contents = list(data.read_records_iter_raw(records))
151
self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
153
def test_not_enough_lines(self):
154
sha1sum = sha.new('foo\n').hexdigest()
155
# record says 2 lines data says 1
156
gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
160
transport = MockTransport([gz_txt])
161
data = _KnitData(transport, 'filename', mode='r')
162
records = [('rev-id-1', 0, len(gz_txt))]
163
self.assertRaises(errors.KnitCorrupt, data.read_records, records)
165
# read_records_iter_raw won't detect that sort of mismatch/corruption
166
raw_contents = list(data.read_records_iter_raw(records))
167
self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
169
def test_too_many_lines(self):
170
sha1sum = sha.new('foo\nbar\n').hexdigest()
171
# record says 1 lines data says 2
172
gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
177
transport = MockTransport([gz_txt])
178
data = _KnitData(transport, 'filename', mode='r')
179
records = [('rev-id-1', 0, len(gz_txt))]
180
self.assertRaises(errors.KnitCorrupt, data.read_records, records)
182
# read_records_iter_raw won't detect that sort of mismatch/corruption
183
raw_contents = list(data.read_records_iter_raw(records))
184
self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
186
def test_mismatched_version_id(self):
187
sha1sum = sha.new('foo\nbar\n').hexdigest()
188
gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
193
transport = MockTransport([gz_txt])
194
data = _KnitData(transport, 'filename', mode='r')
195
# We are asking for rev-id-2, but the data is rev-id-1
196
records = [('rev-id-2', 0, len(gz_txt))]
197
self.assertRaises(errors.KnitCorrupt, data.read_records, records)
199
# read_records_iter_raw will notice if we request the wrong version.
200
self.assertRaises(errors.KnitCorrupt, list,
201
data.read_records_iter_raw(records))
203
def test_uncompressed_data(self):
204
sha1sum = sha.new('foo\nbar\n').hexdigest()
205
txt = ('version rev-id-1 2 %s\n'
210
transport = MockTransport([txt])
211
data = _KnitData(transport, 'filename', mode='r')
212
records = [('rev-id-1', 0, len(txt))]
214
# We don't have valid gzip data ==> corrupt
215
self.assertRaises(errors.KnitCorrupt, data.read_records, records)
217
# read_records_iter_raw will notice the bad data
218
self.assertRaises(errors.KnitCorrupt, list,
219
data.read_records_iter_raw(records))
221
def test_corrupted_data(self):
222
sha1sum = sha.new('foo\nbar\n').hexdigest()
223
gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
228
# Change 2 bytes in the middle to \xff
229
gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
230
transport = MockTransport([gz_txt])
231
data = _KnitData(transport, 'filename', mode='r')
232
records = [('rev-id-1', 0, len(gz_txt))]
234
self.assertRaises(errors.KnitCorrupt, data.read_records, records)
236
# read_records_iter_raw will notice if we request the wrong version.
237
self.assertRaises(errors.KnitCorrupt, list,
238
data.read_records_iter_raw(records))
118
241
class LowLevelKnitIndexTests(TestCase):
120
243
def test_no_such_file(self):
156
279
transport.calls.pop(0))
158
281
def test_read_utf8_version_id(self):
282
unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
283
utf8_revision_id = unicode_revision_id.encode('utf-8')
159
284
transport = MockTransport([
160
285
_KnitIndex.HEADER,
161
u"version-\N{CYRILLIC CAPITAL LETTER A}"
162
u" option 0 1 :".encode("utf-8")
286
'%s option 0 1 :' % (utf8_revision_id,)
164
288
index = _KnitIndex(transport, "filename", "r")
166
index.has_version(u"version-\N{CYRILLIC CAPITAL LETTER A}"))
289
# _KnitIndex is a private class, and deals in utf8 revision_ids, not
290
# Unicode revision_ids.
291
self.assertTrue(index.has_version(utf8_revision_id))
292
self.assertFalse(index.has_version(unicode_revision_id))
168
294
def test_read_utf8_parents(self):
295
unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
296
utf8_revision_id = unicode_revision_id.encode('utf-8')
169
297
transport = MockTransport([
170
298
_KnitIndex.HEADER,
171
u"version option 0 1"
172
u" .version-\N{CYRILLIC CAPITAL LETTER A} :".encode("utf-8")
299
"version option 0 1 .%s :" % (utf8_revision_id,)
174
301
index = _KnitIndex(transport, "filename", "r")
175
self.assertEqual([u"version-\N{CYRILLIC CAPITAL LETTER A}"],
302
self.assertEqual([utf8_revision_id],
176
303
index.get_parents_with_ghosts("version"))
178
305
def test_read_ignore_corrupted_lines(self):
202
329
index = _KnitIndex(transport, "filename", "r")
203
330
self.assertEqual(2, index.num_versions())
204
self.assertEqual(1, index.lookup(u"version"))
205
self.assertEqual((3, 4), index.get_position(u"version"))
206
self.assertEqual(["options3"], index.get_options(u"version"))
207
self.assertEqual([u"parent", u"other"],
208
index.get_parents_with_ghosts(u"version"))
331
self.assertEqual(1, index.lookup("version"))
332
self.assertEqual((3, 4), index.get_position("version"))
333
self.assertEqual(["options3"], index.get_options("version"))
334
self.assertEqual(["parent", "other"],
335
index.get_parents_with_ghosts("version"))
210
337
def test_read_compressed_parents(self):
211
338
transport = MockTransport([
215
342
"c option 0 1 1 0 :",
217
344
index = _KnitIndex(transport, "filename", "r")
218
self.assertEqual([u"a"], index.get_parents(u"b"))
219
self.assertEqual([u"b", u"a"], index.get_parents(u"c"))
345
self.assertEqual(["a"], index.get_parents("b"))
346
self.assertEqual(["b", "a"], index.get_parents("c"))
221
348
def test_write_utf8_version_id(self):
349
unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
350
utf8_revision_id = unicode_revision_id.encode('utf-8')
222
351
transport = MockTransport([
223
352
_KnitIndex.HEADER
225
354
index = _KnitIndex(transport, "filename", "r")
226
index.add_version(u"version-\N{CYRILLIC CAPITAL LETTER A}",
227
["option"], 0, 1, [])
355
index.add_version(utf8_revision_id, ["option"], 0, 1, [])
228
356
self.assertEqual(("append_bytes", ("filename",
229
u"\nversion-\N{CYRILLIC CAPITAL LETTER A}"
230
u" option 0 1 :".encode("utf-8")),
357
"\n%s option 0 1 :" % (utf8_revision_id,)),
232
359
transport.calls.pop(0))
234
361
def test_write_utf8_parents(self):
362
unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
363
utf8_revision_id = unicode_revision_id.encode('utf-8')
235
364
transport = MockTransport([
236
365
_KnitIndex.HEADER
238
367
index = _KnitIndex(transport, "filename", "r")
239
index.add_version(u"version", ["option"], 0, 1,
240
[u"version-\N{CYRILLIC CAPITAL LETTER A}"])
368
index.add_version("version", ["option"], 0, 1, [utf8_revision_id])
241
369
self.assertEqual(("append_bytes", ("filename",
242
u"\nversion option 0 1"
243
u" .version-\N{CYRILLIC CAPITAL LETTER A} :".encode("utf-8")),
370
"\nversion option 0 1 .%s :" % (utf8_revision_id,)),
245
372
transport.calls.pop(0))
249
376
index = _KnitIndex(transport, "filename", "w", create=True)
250
377
self.assertEqual([], index.get_graph())
252
index.add_version(u"a", ["option"], 0, 1, [u"b"])
253
self.assertEqual([(u"a", [u"b"])], index.get_graph())
379
index.add_version("a", ["option"], 0, 1, ["b"])
380
self.assertEqual([("a", ["b"])], index.get_graph())
255
index.add_version(u"c", ["option"], 0, 1, [u"d"])
256
self.assertEqual([(u"a", [u"b"]), (u"c", [u"d"])],
382
index.add_version("c", ["option"], 0, 1, ["d"])
383
self.assertEqual([("a", ["b"]), ("c", ["d"])],
257
384
sorted(index.get_graph()))
259
386
def test_get_ancestry(self):
267
394
index = _KnitIndex(transport, "filename", "r")
269
396
self.assertEqual([], index.get_ancestry([]))
270
self.assertEqual([u"a"], index.get_ancestry([u"a"]))
271
self.assertEqual([u"a", u"b"], index.get_ancestry([u"b"]))
272
self.assertEqual([u"a", u"b", u"c"], index.get_ancestry([u"c"]))
273
self.assertEqual([u"a", u"b", u"c", u"d"], index.get_ancestry([u"d"]))
274
self.assertEqual([u"a", u"b"], index.get_ancestry([u"a", u"b"]))
275
self.assertEqual([u"a", u"b", u"c"], index.get_ancestry([u"a", u"c"]))
397
self.assertEqual(["a"], index.get_ancestry(["a"]))
398
self.assertEqual(["a", "b"], index.get_ancestry(["b"]))
399
self.assertEqual(["a", "b", "c"], index.get_ancestry(["c"]))
400
self.assertEqual(["a", "b", "c", "d"], index.get_ancestry(["d"]))
401
self.assertEqual(["a", "b"], index.get_ancestry(["a", "b"]))
402
self.assertEqual(["a", "b", "c"], index.get_ancestry(["a", "c"]))
277
self.assertRaises(RevisionNotPresent, index.get_ancestry, [u"e"])
404
self.assertRaises(RevisionNotPresent, index.get_ancestry, ["e"])
279
406
def test_get_ancestry_with_ghosts(self):
280
407
transport = MockTransport([
287
414
index = _KnitIndex(transport, "filename", "r")
289
416
self.assertEqual([], index.get_ancestry_with_ghosts([]))
290
self.assertEqual([u"a"], index.get_ancestry_with_ghosts([u"a"]))
291
self.assertEqual([u"a", u"e", u"b"],
292
index.get_ancestry_with_ghosts([u"b"]))
293
self.assertEqual([u"a", u"g", u"f", u"c"],
294
index.get_ancestry_with_ghosts([u"c"]))
295
self.assertEqual([u"a", u"g", u"f", u"c", u"k", u"j", u"h", u"d"],
296
index.get_ancestry_with_ghosts([u"d"]))
297
self.assertEqual([u"a", u"e", u"b"],
298
index.get_ancestry_with_ghosts([u"a", u"b"]))
299
self.assertEqual([u"a", u"g", u"f", u"c"],
300
index.get_ancestry_with_ghosts([u"a", u"c"]))
417
self.assertEqual(["a"], index.get_ancestry_with_ghosts(["a"]))
418
self.assertEqual(["a", "e", "b"],
419
index.get_ancestry_with_ghosts(["b"]))
420
self.assertEqual(["a", "g", "f", "c"],
421
index.get_ancestry_with_ghosts(["c"]))
422
self.assertEqual(["a", "g", "f", "c", "k", "j", "h", "d"],
423
index.get_ancestry_with_ghosts(["d"]))
424
self.assertEqual(["a", "e", "b"],
425
index.get_ancestry_with_ghosts(["a", "b"]))
426
self.assertEqual(["a", "g", "f", "c"],
427
index.get_ancestry_with_ghosts(["a", "c"]))
301
428
self.assertEqual(
302
[u"a", u"g", u"f", u"c", u"e", u"b", u"k", u"j", u"h", u"d"],
303
index.get_ancestry_with_ghosts([u"b", u"d"]))
429
["a", "g", "f", "c", "e", "b", "k", "j", "h", "d"],
430
index.get_ancestry_with_ghosts(["b", "d"]))
305
432
self.assertRaises(RevisionNotPresent,
306
index.get_ancestry_with_ghosts, [u"e"])
433
index.get_ancestry_with_ghosts, ["e"])
308
435
def test_num_versions(self):
309
436
transport = MockTransport([
314
441
self.assertEqual(0, index.num_versions())
315
442
self.assertEqual(0, len(index))
317
index.add_version(u"a", ["option"], 0, 1, [])
318
self.assertEqual(1, index.num_versions())
319
self.assertEqual(1, len(index))
321
index.add_version(u"a", ["option2"], 1, 2, [])
322
self.assertEqual(1, index.num_versions())
323
self.assertEqual(1, len(index))
325
index.add_version(u"b", ["option"], 0, 1, [])
444
index.add_version("a", ["option"], 0, 1, [])
445
self.assertEqual(1, index.num_versions())
446
self.assertEqual(1, len(index))
448
index.add_version("a", ["option2"], 1, 2, [])
449
self.assertEqual(1, index.num_versions())
450
self.assertEqual(1, len(index))
452
index.add_version("b", ["option"], 0, 1, [])
326
453
self.assertEqual(2, index.num_versions())
327
454
self.assertEqual(2, len(index))
335
462
self.assertEqual([], index.get_versions())
337
index.add_version(u"a", ["option"], 0, 1, [])
338
self.assertEqual([u"a"], index.get_versions())
340
index.add_version(u"a", ["option"], 0, 1, [])
341
self.assertEqual([u"a"], index.get_versions())
343
index.add_version(u"b", ["option"], 0, 1, [])
344
self.assertEqual([u"a", u"b"], index.get_versions())
464
index.add_version("a", ["option"], 0, 1, [])
465
self.assertEqual(["a"], index.get_versions())
467
index.add_version("a", ["option"], 0, 1, [])
468
self.assertEqual(["a"], index.get_versions())
470
index.add_version("b", ["option"], 0, 1, [])
471
self.assertEqual(["a", "b"], index.get_versions())
346
473
def test_idx_to_name(self):
347
474
transport = MockTransport([
374
501
index = _KnitIndex(transport, "filename", "r")
376
index.add_version(u"a", ["option"], 0, 1, [u"b"])
503
index.add_version("a", ["option"], 0, 1, ["b"])
377
504
self.assertEqual(("append_bytes",
378
505
("filename", "\na option 0 1 .b :"),
379
506
{}), transport.calls.pop(0))
380
self.assertTrue(index.has_version(u"a"))
507
self.assertTrue(index.has_version("a"))
381
508
self.assertEqual(1, index.num_versions())
382
self.assertEqual((0, 1), index.get_position(u"a"))
383
self.assertEqual(["option"], index.get_options(u"a"))
384
self.assertEqual([u"b"], index.get_parents_with_ghosts(u"a"))
509
self.assertEqual((0, 1), index.get_position("a"))
510
self.assertEqual(["option"], index.get_options("a"))
511
self.assertEqual(["b"], index.get_parents_with_ghosts("a"))
386
index.add_version(u"a", ["opt"], 1, 2, [u"c"])
513
index.add_version("a", ["opt"], 1, 2, ["c"])
387
514
self.assertEqual(("append_bytes",
388
515
("filename", "\na opt 1 2 .c :"),
389
516
{}), transport.calls.pop(0))
390
self.assertTrue(index.has_version(u"a"))
517
self.assertTrue(index.has_version("a"))
391
518
self.assertEqual(1, index.num_versions())
392
self.assertEqual((1, 2), index.get_position(u"a"))
393
self.assertEqual(["opt"], index.get_options(u"a"))
394
self.assertEqual([u"c"], index.get_parents_with_ghosts(u"a"))
519
self.assertEqual((1, 2), index.get_position("a"))
520
self.assertEqual(["opt"], index.get_options("a"))
521
self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
396
index.add_version(u"b", ["option"], 2, 3, [u"a"])
523
index.add_version("b", ["option"], 2, 3, ["a"])
397
524
self.assertEqual(("append_bytes",
398
525
("filename", "\nb option 2 3 0 :"),
399
526
{}), transport.calls.pop(0))
400
self.assertTrue(index.has_version(u"b"))
527
self.assertTrue(index.has_version("b"))
401
528
self.assertEqual(2, index.num_versions())
402
self.assertEqual((2, 3), index.get_position(u"b"))
403
self.assertEqual(["option"], index.get_options(u"b"))
404
self.assertEqual([u"a"], index.get_parents_with_ghosts(u"b"))
529
self.assertEqual((2, 3), index.get_position("b"))
530
self.assertEqual(["option"], index.get_options("b"))
531
self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
406
533
def test_add_versions(self):
407
534
transport = MockTransport([
410
537
index = _KnitIndex(transport, "filename", "r")
412
539
index.add_versions([
413
(u"a", ["option"], 0, 1, [u"b"]),
414
(u"a", ["opt"], 1, 2, [u"c"]),
415
(u"b", ["option"], 2, 3, [u"a"])
540
("a", ["option"], 0, 1, ["b"]),
541
("a", ["opt"], 1, 2, ["c"]),
542
("b", ["option"], 2, 3, ["a"])
417
544
self.assertEqual(("append_bytes", ("filename",
418
545
"\na option 0 1 .b :"
419
546
"\na opt 1 2 .c :"
420
547
"\nb option 2 3 0 :"
421
548
), {}), transport.calls.pop(0))
422
self.assertTrue(index.has_version(u"a"))
423
self.assertTrue(index.has_version(u"b"))
549
self.assertTrue(index.has_version("a"))
550
self.assertTrue(index.has_version("b"))
424
551
self.assertEqual(2, index.num_versions())
425
self.assertEqual((1, 2), index.get_position(u"a"))
426
self.assertEqual((2, 3), index.get_position(u"b"))
427
self.assertEqual(["opt"], index.get_options(u"a"))
428
self.assertEqual(["option"], index.get_options(u"b"))
429
self.assertEqual([u"c"], index.get_parents_with_ghosts(u"a"))
430
self.assertEqual([u"a"], index.get_parents_with_ghosts(u"b"))
552
self.assertEqual((1, 2), index.get_position("a"))
553
self.assertEqual((2, 3), index.get_position("b"))
554
self.assertEqual(["opt"], index.get_options("a"))
555
self.assertEqual(["option"], index.get_options("b"))
556
self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
557
self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
432
559
def test_delay_create_and_add_versions(self):
433
560
transport = MockTransport()
438
565
self.assertEqual([], transport.calls)
440
567
index.add_versions([
441
(u"a", ["option"], 0, 1, [u"b"]),
442
(u"a", ["opt"], 1, 2, [u"c"]),
443
(u"b", ["option"], 2, 3, [u"a"])
568
("a", ["option"], 0, 1, ["b"]),
569
("a", ["opt"], 1, 2, ["c"]),
570
("b", ["option"], 2, 3, ["a"])
445
572
name, (filename, f), kwargs = transport.calls.pop(0)
446
573
self.assertEqual("put_file_non_atomic", name)