##// END OF EJS Templates
testing: add interface unit tests for file storage...
Gregory Szorc -
r39808:ae531f5e default
parent child Browse files
Show More
1 NO CONTENT: new file 100644
This diff has been collapsed as it changes many lines, (984 lines changed) Show them Hide them
@@ -0,0 +1,984 b''
1 # storage.py - Testing of storage primitives.
2 #
3 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 import unittest
11
12 from ..node import (
13 hex,
14 nullid,
15 nullrev,
16 )
17 from .. import (
18 error,
19 mdiff,
20 revlog,
21 )
22
23 class basetestcase(unittest.TestCase):
24 if not getattr(unittest.TestCase, r'assertRaisesRegex', False):
25 assertRaisesRegex = (# camelcase-required
26 unittest.TestCase.assertRaisesRegexp)
27
28 class revisiondeltarequest(object):
29 def __init__(self, node, p1, p2, linknode, basenode, ellipsis):
30 self.node = node
31 self.p1node = p1
32 self.p2node = p2
33 self.linknode = linknode
34 self.basenode = basenode
35 self.ellipsis = ellipsis
36
37 class ifileindextests(basetestcase):
38 """Generic tests for the ifileindex interface.
39
40 All file storage backends for index data should conform to the tests in this
41 class.
42
43 Use ``makeifileindextests()`` to create an instance of this type.
44 """
45 def testempty(self):
46 f = self._makefilefn()
47 self.assertEqual(len(f), 0, 'new file store has 0 length by default')
48 self.assertEqual(list(f), [], 'iter yields nothing by default')
49
50 gen = iter(f)
51 with self.assertRaises(StopIteration):
52 next(gen)
53
54 # revs() should evaluate to an empty list.
55 self.assertEqual(list(f.revs()), [])
56
57 revs = iter(f.revs())
58 with self.assertRaises(StopIteration):
59 next(revs)
60
61 self.assertEqual(list(f.revs(start=20)), [])
62
63 # parents() and parentrevs() work with nullid/nullrev.
64 self.assertEqual(f.parents(nullid), (nullid, nullid))
65 self.assertEqual(f.parentrevs(nullrev), (nullrev, nullrev))
66
67 with self.assertRaises(error.LookupError):
68 f.parents(b'\x01' * 20)
69
70 for i in range(-5, 5):
71 if i == nullrev:
72 continue
73
74 with self.assertRaises(IndexError):
75 f.parentrevs(i)
76
77 # nullid/nullrev lookup always works.
78 self.assertEqual(f.rev(nullid), nullrev)
79 self.assertEqual(f.node(nullrev), nullid)
80
81 with self.assertRaises(error.LookupError):
82 f.rev(b'\x01' * 20)
83
84 for i in range(-5, 5):
85 if i == nullrev:
86 continue
87
88 with self.assertRaises(IndexError):
89 f.node(i)
90
91 self.assertEqual(f.lookup(nullid), nullid)
92 self.assertEqual(f.lookup(nullrev), nullid)
93 self.assertEqual(f.lookup(hex(nullid)), nullid)
94
95 # String converted to integer doesn't work for nullrev.
96 with self.assertRaises(error.LookupError):
97 f.lookup(b'%d' % nullrev)
98
99 self.assertEqual(f.linkrev(nullrev), nullrev)
100
101 for i in range(-5, 5):
102 if i == nullrev:
103 continue
104
105 with self.assertRaises(IndexError):
106 f.linkrev(i)
107
108 self.assertEqual(f.flags(nullrev), 0)
109
110 for i in range(-5, 5):
111 if i == nullrev:
112 continue
113
114 with self.assertRaises(IndexError):
115 f.flags(i)
116
117 self.assertFalse(f.iscensored(nullrev))
118
119 for i in range(-5, 5):
120 if i == nullrev:
121 continue
122
123 with self.assertRaises(IndexError):
124 f.iscensored(i)
125
126 self.assertEqual(list(f.commonancestorsheads(nullid, nullid)), [])
127
128 with self.assertRaises(ValueError):
129 self.assertEqual(list(f.descendants([])), [])
130
131 self.assertEqual(list(f.descendants([nullrev])), [])
132
133 self.assertEqual(f.headrevs(), [nullrev])
134 self.assertEqual(f.heads(), [nullid])
135 self.assertEqual(f.heads(nullid), [nullid])
136 self.assertEqual(f.heads(None, [nullid]), [nullid])
137 self.assertEqual(f.heads(nullid, [nullid]), [nullid])
138
139 self.assertEqual(f.children(nullid), [])
140
141 with self.assertRaises(error.LookupError):
142 f.children(b'\x01' * 20)
143
144 self.assertEqual(f.deltaparent(nullrev), nullrev)
145
146 for i in range(-5, 5):
147 if i == nullrev:
148 continue
149
150 with self.assertRaises(IndexError):
151 f.deltaparent(i)
152
153 def testsinglerevision(self):
154 f = self._makefilefn()
155 with self._maketransactionfn() as tr:
156 node = f.add(b'initial', None, tr, 0, nullid, nullid)
157
158 self.assertEqual(len(f), 1)
159 self.assertEqual(list(f), [0])
160
161 gen = iter(f)
162 self.assertEqual(next(gen), 0)
163
164 with self.assertRaises(StopIteration):
165 next(gen)
166
167 self.assertEqual(list(f.revs()), [0])
168 self.assertEqual(list(f.revs(start=1)), [])
169 self.assertEqual(list(f.revs(start=0)), [0])
170 self.assertEqual(list(f.revs(stop=0)), [0])
171 self.assertEqual(list(f.revs(stop=1)), [0])
172 self.assertEqual(list(f.revs(1, 1)), [])
173 # TODO buggy
174 self.assertEqual(list(f.revs(1, 0)), [1, 0])
175 self.assertEqual(list(f.revs(2, 0)), [2, 1, 0])
176
177 self.assertEqual(f.parents(node), (nullid, nullid))
178 self.assertEqual(f.parentrevs(0), (nullrev, nullrev))
179
180 with self.assertRaises(error.LookupError):
181 f.parents(b'\x01' * 20)
182
183 with self.assertRaises(IndexError):
184 f.parentrevs(1)
185
186 self.assertEqual(f.rev(node), 0)
187
188 with self.assertRaises(error.LookupError):
189 f.rev(b'\x01' * 20)
190
191 self.assertEqual(f.node(0), node)
192
193 with self.assertRaises(IndexError):
194 f.node(1)
195
196 self.assertEqual(f.lookup(node), node)
197 self.assertEqual(f.lookup(0), node)
198 self.assertEqual(f.lookup(b'0'), node)
199 self.assertEqual(f.lookup(hex(node)), node)
200
201 self.assertEqual(f.linkrev(0), 0)
202
203 with self.assertRaises(IndexError):
204 f.linkrev(1)
205
206 self.assertEqual(f.flags(0), 0)
207
208 with self.assertRaises(IndexError):
209 f.flags(1)
210
211 self.assertFalse(f.iscensored(0))
212
213 with self.assertRaises(IndexError):
214 f.iscensored(1)
215
216 self.assertEqual(list(f.descendants([0])), [])
217
218 self.assertEqual(f.headrevs(), [0])
219
220 self.assertEqual(f.heads(), [node])
221 self.assertEqual(f.heads(node), [node])
222 self.assertEqual(f.heads(stop=[node]), [node])
223
224 with self.assertRaises(error.LookupError):
225 f.heads(stop=[b'\x01' * 20])
226
227 self.assertEqual(f.children(node), [])
228
229 self.assertEqual(f.deltaparent(0), nullrev)
230
231 def testmultiplerevisions(self):
232 fulltext0 = b'x' * 1024
233 fulltext1 = fulltext0 + b'y'
234 fulltext2 = b'y' + fulltext0 + b'z'
235
236 f = self._makefilefn()
237 with self._maketransactionfn() as tr:
238 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
239 node1 = f.add(fulltext1, None, tr, 1, node0, nullid)
240 node2 = f.add(fulltext2, None, tr, 3, node1, nullid)
241
242 self.assertEqual(len(f), 3)
243 self.assertEqual(list(f), [0, 1, 2])
244
245 gen = iter(f)
246 self.assertEqual(next(gen), 0)
247 self.assertEqual(next(gen), 1)
248 self.assertEqual(next(gen), 2)
249
250 with self.assertRaises(StopIteration):
251 next(gen)
252
253 self.assertEqual(list(f.revs()), [0, 1, 2])
254 self.assertEqual(list(f.revs(0)), [0, 1, 2])
255 self.assertEqual(list(f.revs(1)), [1, 2])
256 self.assertEqual(list(f.revs(2)), [2])
257 self.assertEqual(list(f.revs(3)), [])
258 self.assertEqual(list(f.revs(stop=1)), [0, 1])
259 self.assertEqual(list(f.revs(stop=2)), [0, 1, 2])
260 self.assertEqual(list(f.revs(stop=3)), [0, 1, 2])
261 self.assertEqual(list(f.revs(2, 0)), [2, 1, 0])
262 self.assertEqual(list(f.revs(2, 1)), [2, 1])
263 # TODO this is wrong
264 self.assertEqual(list(f.revs(3, 2)), [3, 2])
265
266 self.assertEqual(f.parents(node0), (nullid, nullid))
267 self.assertEqual(f.parents(node1), (node0, nullid))
268 self.assertEqual(f.parents(node2), (node1, nullid))
269
270 self.assertEqual(f.parentrevs(0), (nullrev, nullrev))
271 self.assertEqual(f.parentrevs(1), (0, nullrev))
272 self.assertEqual(f.parentrevs(2), (1, nullrev))
273
274 self.assertEqual(f.rev(node0), 0)
275 self.assertEqual(f.rev(node1), 1)
276 self.assertEqual(f.rev(node2), 2)
277
278 with self.assertRaises(error.LookupError):
279 f.rev(b'\x01' * 20)
280
281 self.assertEqual(f.node(0), node0)
282 self.assertEqual(f.node(1), node1)
283 self.assertEqual(f.node(2), node2)
284
285 with self.assertRaises(IndexError):
286 f.node(3)
287
288 self.assertEqual(f.lookup(node0), node0)
289 self.assertEqual(f.lookup(0), node0)
290 self.assertEqual(f.lookup(b'0'), node0)
291 self.assertEqual(f.lookup(hex(node0)), node0)
292
293 self.assertEqual(f.lookup(node1), node1)
294 self.assertEqual(f.lookup(1), node1)
295 self.assertEqual(f.lookup(b'1'), node1)
296 self.assertEqual(f.lookup(hex(node1)), node1)
297
298 self.assertEqual(f.linkrev(0), 0)
299 self.assertEqual(f.linkrev(1), 1)
300 self.assertEqual(f.linkrev(2), 3)
301
302 with self.assertRaises(IndexError):
303 f.linkrev(3)
304
305 self.assertEqual(f.flags(0), 0)
306 self.assertEqual(f.flags(1), 0)
307 self.assertEqual(f.flags(2), 0)
308
309 with self.assertRaises(IndexError):
310 f.flags(3)
311
312 self.assertFalse(f.iscensored(0))
313 self.assertFalse(f.iscensored(1))
314 self.assertFalse(f.iscensored(2))
315
316 with self.assertRaises(IndexError):
317 f.iscensored(3)
318
319 self.assertEqual(f.commonancestorsheads(node1, nullid), [])
320 self.assertEqual(f.commonancestorsheads(node1, node0), [node0])
321 self.assertEqual(f.commonancestorsheads(node1, node1), [node1])
322 self.assertEqual(f.commonancestorsheads(node0, node1), [node0])
323 self.assertEqual(f.commonancestorsheads(node1, node2), [node1])
324 self.assertEqual(f.commonancestorsheads(node2, node1), [node1])
325
326 self.assertEqual(list(f.descendants([0])), [1, 2])
327 self.assertEqual(list(f.descendants([1])), [2])
328 self.assertEqual(list(f.descendants([0, 1])), [1, 2])
329
330 self.assertEqual(f.headrevs(), [2])
331
332 self.assertEqual(f.heads(), [node2])
333 self.assertEqual(f.heads(node0), [node2])
334 self.assertEqual(f.heads(node1), [node2])
335 self.assertEqual(f.heads(node2), [node2])
336
337 # TODO this behavior seems wonky. Is it correct? If so, the
338 # docstring for heads() should be updated to reflect desired
339 # behavior.
340 self.assertEqual(f.heads(stop=[node1]), [node1, node2])
341 self.assertEqual(f.heads(stop=[node0]), [node0, node2])
342 self.assertEqual(f.heads(stop=[node1, node2]), [node1, node2])
343
344 with self.assertRaises(error.LookupError):
345 f.heads(stop=[b'\x01' * 20])
346
347 self.assertEqual(f.children(node0), [node1])
348 self.assertEqual(f.children(node1), [node2])
349 self.assertEqual(f.children(node2), [])
350
351 self.assertEqual(f.deltaparent(0), nullrev)
352 self.assertEqual(f.deltaparent(1), 0)
353 self.assertEqual(f.deltaparent(2), 1)
354
355 def testmultipleheads(self):
356 f = self._makefilefn()
357
358 with self._maketransactionfn() as tr:
359 node0 = f.add(b'0', None, tr, 0, nullid, nullid)
360 node1 = f.add(b'1', None, tr, 1, node0, nullid)
361 node2 = f.add(b'2', None, tr, 2, node1, nullid)
362 node3 = f.add(b'3', None, tr, 3, node0, nullid)
363 node4 = f.add(b'4', None, tr, 4, node3, nullid)
364 node5 = f.add(b'5', None, tr, 5, node0, nullid)
365
366 self.assertEqual(len(f), 6)
367
368 self.assertEqual(list(f.descendants([0])), [1, 2, 3, 4, 5])
369 self.assertEqual(list(f.descendants([1])), [2])
370 self.assertEqual(list(f.descendants([2])), [])
371 self.assertEqual(list(f.descendants([3])), [4])
372 self.assertEqual(list(f.descendants([0, 1])), [1, 2, 3, 4, 5])
373 self.assertEqual(list(f.descendants([1, 3])), [2, 4])
374
375 self.assertEqual(f.headrevs(), [2, 4, 5])
376
377 self.assertEqual(f.heads(), [node2, node4, node5])
378 self.assertEqual(f.heads(node0), [node2, node4, node5])
379 self.assertEqual(f.heads(node1), [node2])
380 self.assertEqual(f.heads(node2), [node2])
381 self.assertEqual(f.heads(node3), [node4])
382 self.assertEqual(f.heads(node4), [node4])
383 self.assertEqual(f.heads(node5), [node5])
384
385 # TODO this seems wrong.
386 self.assertEqual(f.heads(stop=[node0]), [node0, node2, node4, node5])
387 self.assertEqual(f.heads(stop=[node1]), [node1, node2, node4, node5])
388
389 self.assertEqual(f.children(node0), [node1, node3, node5])
390 self.assertEqual(f.children(node1), [node2])
391 self.assertEqual(f.children(node2), [])
392 self.assertEqual(f.children(node3), [node4])
393 self.assertEqual(f.children(node4), [])
394 self.assertEqual(f.children(node5), [])
395
396 class ifiledatatests(basetestcase):
397 """Generic tests for the ifiledata interface.
398
399 All file storage backends for data should conform to the tests in this
400 class.
401
402 Use ``makeifiledatatests()`` to create an instance of this type.
403 """
404 def testempty(self):
405 f = self._makefilefn()
406
407 self.assertEqual(f.rawsize(nullrev), 0)
408
409 for i in range(-5, 5):
410 if i == nullrev:
411 continue
412
413 with self.assertRaises(IndexError):
414 f.rawsize(i)
415
416 self.assertEqual(f.size(nullrev), 0)
417
418 for i in range(-5, 5):
419 if i == nullrev:
420 continue
421
422 with self.assertRaises(IndexError):
423 f.size(i)
424
425 with self.assertRaises(error.RevlogError):
426 f.checkhash(b'', nullid)
427
428 with self.assertRaises(error.LookupError):
429 f.checkhash(b'', b'\x01' * 20)
430
431 self.assertEqual(f.revision(nullid), b'')
432 self.assertEqual(f.revision(nullid, raw=True), b'')
433
434 with self.assertRaises(error.LookupError):
435 f.revision(b'\x01' * 20)
436
437 self.assertEqual(f.read(nullid), b'')
438
439 with self.assertRaises(error.LookupError):
440 f.read(b'\x01' * 20)
441
442 self.assertFalse(f.renamed(nullid))
443
444 with self.assertRaises(error.LookupError):
445 f.read(b'\x01' * 20)
446
447 self.assertTrue(f.cmp(nullid, b''))
448 self.assertTrue(f.cmp(nullid, b'foo'))
449
450 with self.assertRaises(error.LookupError):
451 f.cmp(b'\x01' * 20, b'irrelevant')
452
453 self.assertEqual(f.revdiff(nullrev, nullrev), b'')
454
455 with self.assertRaises(IndexError):
456 f.revdiff(0, nullrev)
457
458 with self.assertRaises(IndexError):
459 f.revdiff(nullrev, 0)
460
461 with self.assertRaises(IndexError):
462 f.revdiff(0, 0)
463
464 gen = f.emitrevisiondeltas([])
465 with self.assertRaises(StopIteration):
466 next(gen)
467
468 requests = [
469 revisiondeltarequest(nullid, nullid, nullid, nullid, nullid, False),
470 ]
471 gen = f.emitrevisiondeltas(requests)
472
473 delta = next(gen)
474
475 self.assertEqual(delta.node, nullid)
476 self.assertEqual(delta.p1node, nullid)
477 self.assertEqual(delta.p2node, nullid)
478 self.assertEqual(delta.linknode, nullid)
479 self.assertEqual(delta.basenode, nullid)
480 self.assertIsNone(delta.baserevisionsize)
481 self.assertEqual(delta.revision, b'')
482 self.assertIsNone(delta.delta)
483
484 with self.assertRaises(StopIteration):
485 next(gen)
486
487 requests = [
488 revisiondeltarequest(nullid, nullid, nullid, nullid, nullid, False),
489 revisiondeltarequest(nullid, b'\x01' * 20, b'\x02' * 20,
490 b'\x03' * 20, nullid, False)
491 ]
492
493 gen = f.emitrevisiondeltas(requests)
494
495 next(gen)
496 delta = next(gen)
497
498 self.assertEqual(delta.node, nullid)
499 self.assertEqual(delta.p1node, b'\x01' * 20)
500 self.assertEqual(delta.p2node, b'\x02' * 20)
501 self.assertEqual(delta.linknode, b'\x03' * 20)
502 self.assertEqual(delta.basenode, nullid)
503 self.assertIsNone(delta.baserevisionsize)
504 self.assertEqual(delta.revision, b'')
505 self.assertIsNone(delta.delta)
506
507 with self.assertRaises(StopIteration):
508 next(gen)
509
510 def testsinglerevision(self):
511 fulltext = b'initial'
512
513 f = self._makefilefn()
514 with self._maketransactionfn() as tr:
515 node = f.add(fulltext, None, tr, 0, nullid, nullid)
516
517 self.assertEqual(f.rawsize(0), len(fulltext))
518
519 with self.assertRaises(IndexError):
520 f.rawsize(1)
521
522 self.assertEqual(f.size(0), len(fulltext))
523
524 with self.assertRaises(IndexError):
525 f.size(1)
526
527 f.checkhash(fulltext, node)
528 f.checkhash(fulltext, node, nullid, nullid)
529
530 with self.assertRaises(error.RevlogError):
531 f.checkhash(fulltext + b'extra', node)
532
533 with self.assertRaises(error.RevlogError):
534 f.checkhash(fulltext, node, b'\x01' * 20, nullid)
535
536 with self.assertRaises(error.RevlogError):
537 f.checkhash(fulltext, node, nullid, b'\x01' * 20)
538
539 self.assertEqual(f.revision(node), fulltext)
540 self.assertEqual(f.revision(node, raw=True), fulltext)
541
542 self.assertEqual(f.read(node), fulltext)
543
544 self.assertFalse(f.renamed(node))
545
546 self.assertFalse(f.cmp(node, fulltext))
547 self.assertTrue(f.cmp(node, fulltext + b'extra'))
548
549 self.assertEqual(f.revdiff(0, 0), b'')
550 self.assertEqual(f.revdiff(nullrev, 0),
551 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07%s' %
552 fulltext)
553
554 self.assertEqual(f.revdiff(0, nullrev),
555 b'\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00')
556
557 requests = [
558 revisiondeltarequest(node, nullid, nullid, nullid, nullid, False),
559 ]
560 gen = f.emitrevisiondeltas(requests)
561
562 delta = next(gen)
563
564 self.assertEqual(delta.node, node)
565 self.assertEqual(delta.p1node, nullid)
566 self.assertEqual(delta.p2node, nullid)
567 self.assertEqual(delta.linknode, nullid)
568 self.assertEqual(delta.basenode, nullid)
569 self.assertIsNone(delta.baserevisionsize)
570 self.assertEqual(delta.revision, fulltext)
571 self.assertIsNone(delta.delta)
572
573 with self.assertRaises(StopIteration):
574 next(gen)
575
576 def testmultiplerevisions(self):
577 fulltext0 = b'x' * 1024
578 fulltext1 = fulltext0 + b'y'
579 fulltext2 = b'y' + fulltext0 + b'z'
580
581 f = self._makefilefn()
582 with self._maketransactionfn() as tr:
583 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
584 node1 = f.add(fulltext1, None, tr, 1, node0, nullid)
585 node2 = f.add(fulltext2, None, tr, 3, node1, nullid)
586
587 self.assertEqual(f.rawsize(0), len(fulltext0))
588 self.assertEqual(f.rawsize(1), len(fulltext1))
589 self.assertEqual(f.rawsize(2), len(fulltext2))
590
591 with self.assertRaises(IndexError):
592 f.rawsize(3)
593
594 self.assertEqual(f.size(0), len(fulltext0))
595 self.assertEqual(f.size(1), len(fulltext1))
596 self.assertEqual(f.size(2), len(fulltext2))
597
598 with self.assertRaises(IndexError):
599 f.size(3)
600
601 f.checkhash(fulltext0, node0)
602 f.checkhash(fulltext1, node1)
603 f.checkhash(fulltext1, node1, node0, nullid)
604 f.checkhash(fulltext2, node2, node1, nullid)
605
606 with self.assertRaises(error.RevlogError):
607 f.checkhash(fulltext1, b'\x01' * 20)
608
609 with self.assertRaises(error.RevlogError):
610 f.checkhash(fulltext1 + b'extra', node1, node0, nullid)
611
612 with self.assertRaises(error.RevlogError):
613 f.checkhash(fulltext1, node1, node0, node0)
614
615 self.assertEqual(f.revision(node0), fulltext0)
616 self.assertEqual(f.revision(node0, raw=True), fulltext0)
617 self.assertEqual(f.revision(node1), fulltext1)
618 self.assertEqual(f.revision(node1, raw=True), fulltext1)
619 self.assertEqual(f.revision(node2), fulltext2)
620 self.assertEqual(f.revision(node2, raw=True), fulltext2)
621
622 with self.assertRaises(error.LookupError):
623 f.revision(b'\x01' * 20)
624
625 self.assertEqual(f.read(node0), fulltext0)
626 self.assertEqual(f.read(node1), fulltext1)
627 self.assertEqual(f.read(node2), fulltext2)
628
629 with self.assertRaises(error.LookupError):
630 f.read(b'\x01' * 20)
631
632 self.assertFalse(f.renamed(node0))
633 self.assertFalse(f.renamed(node1))
634 self.assertFalse(f.renamed(node2))
635
636 with self.assertRaises(error.LookupError):
637 f.renamed(b'\x01' * 20)
638
639 self.assertFalse(f.cmp(node0, fulltext0))
640 self.assertFalse(f.cmp(node1, fulltext1))
641 self.assertFalse(f.cmp(node2, fulltext2))
642
643 self.assertTrue(f.cmp(node1, fulltext0))
644 self.assertTrue(f.cmp(node2, fulltext1))
645
646 with self.assertRaises(error.LookupError):
647 f.cmp(b'\x01' * 20, b'irrelevant')
648
649 self.assertEqual(f.revdiff(0, 1),
650 b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x01' +
651 fulltext1)
652
653 self.assertEqual(f.revdiff(0, 2),
654 b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x02' +
655 fulltext2)
656
657 requests = [
658 revisiondeltarequest(node0, nullid, nullid, b'\x01' * 20, nullid,
659 False),
660 revisiondeltarequest(node1, node0, nullid, b'\x02' * 20, node0,
661 False),
662 revisiondeltarequest(node2, node1, nullid, b'\x03' * 20, node1,
663 False),
664 ]
665 gen = f.emitrevisiondeltas(requests)
666
667 delta = next(gen)
668
669 self.assertEqual(delta.node, node0)
670 self.assertEqual(delta.p1node, nullid)
671 self.assertEqual(delta.p2node, nullid)
672 self.assertEqual(delta.linknode, b'\x01' * 20)
673 self.assertEqual(delta.basenode, nullid)
674 self.assertIsNone(delta.baserevisionsize)
675 self.assertEqual(delta.revision, fulltext0)
676 self.assertIsNone(delta.delta)
677
678 delta = next(gen)
679
680 self.assertEqual(delta.node, node1)
681 self.assertEqual(delta.p1node, node0)
682 self.assertEqual(delta.p2node, nullid)
683 self.assertEqual(delta.linknode, b'\x02' * 20)
684 self.assertEqual(delta.basenode, node0)
685 self.assertIsNone(delta.baserevisionsize)
686 self.assertIsNone(delta.revision)
687 self.assertEqual(delta.delta,
688 b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x01' +
689 fulltext1)
690
691 delta = next(gen)
692
693 self.assertEqual(delta.node, node2)
694 self.assertEqual(delta.p1node, node1)
695 self.assertEqual(delta.p2node, nullid)
696 self.assertEqual(delta.linknode, b'\x03' * 20)
697 self.assertEqual(delta.basenode, node1)
698 self.assertIsNone(delta.baserevisionsize)
699 self.assertIsNone(delta.revision)
700 self.assertEqual(delta.delta,
701 b'\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x04\x02' +
702 fulltext2)
703
704 with self.assertRaises(StopIteration):
705 next(gen)
706
707 def testrenamed(self):
708 fulltext0 = b'foo'
709 fulltext1 = b'bar'
710 fulltext2 = b'baz'
711
712 meta1 = {
713 b'copy': b'source0',
714 b'copyrev': b'a' * 40,
715 }
716
717 meta2 = {
718 b'copy': b'source1',
719 b'copyrev': b'b' * 40,
720 }
721
722 stored1 = b''.join([
723 b'\x01\ncopy: source0\n',
724 b'copyrev: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n\x01\n',
725 fulltext1,
726 ])
727
728 stored2 = b''.join([
729 b'\x01\ncopy: source1\n',
730 b'copyrev: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n\x01\n',
731 fulltext2,
732 ])
733
734 f = self._makefilefn()
735 with self._maketransactionfn() as tr:
736 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
737 node1 = f.add(fulltext1, meta1, tr, 1, node0, nullid)
738 node2 = f.add(fulltext2, meta2, tr, 2, nullid, nullid)
739
740 self.assertEqual(f.rawsize(1), len(stored1))
741 self.assertEqual(f.rawsize(2), len(stored2))
742
743 # Metadata header isn't recognized when parent isn't nullid.
744 self.assertEqual(f.size(1), len(stored1))
745 self.assertEqual(f.size(2), len(fulltext2))
746
747 self.assertEqual(f.revision(node1), stored1)
748 self.assertEqual(f.revision(node1, raw=True), stored1)
749 self.assertEqual(f.revision(node2), stored2)
750 self.assertEqual(f.revision(node2, raw=True), stored2)
751
752 self.assertEqual(f.read(node1), fulltext1)
753 self.assertEqual(f.read(node2), fulltext2)
754
755 # Returns False when first parent is set.
756 self.assertFalse(f.renamed(node1))
757 self.assertEqual(f.renamed(node2), (b'source1', b'\xbb' * 20))
758
759 self.assertTrue(f.cmp(node1, fulltext1))
760 self.assertTrue(f.cmp(node1, stored1))
761 self.assertFalse(f.cmp(node2, fulltext2))
762 self.assertTrue(f.cmp(node2, stored2))
763
764 def testmetadataprefix(self):
765 # Content with metadata prefix has extra prefix inserted in storage.
766 fulltext0 = b'\x01\nfoo'
767 stored0 = b'\x01\n\x01\n\x01\nfoo'
768
769 fulltext1 = b'\x01\nbar'
770 meta1 = {
771 b'copy': b'source0',
772 b'copyrev': b'b' * 40,
773 }
774 stored1 = b''.join([
775 b'\x01\ncopy: source0\n',
776 b'copyrev: %s\n' % (b'b' * 40),
777 b'\x01\n\x01\nbar',
778 ])
779
780 f = self._makefilefn()
781 with self._maketransactionfn() as tr:
782 node0 = f.add(fulltext0, {}, tr, 0, nullid, nullid)
783 node1 = f.add(fulltext1, meta1, tr, 1, nullid, nullid)
784
785 self.assertEqual(f.rawsize(0), len(stored0))
786 self.assertEqual(f.rawsize(1), len(stored1))
787
788 # TODO this is buggy.
789 self.assertEqual(f.size(0), len(fulltext0) + 4)
790
791 self.assertEqual(f.size(1), len(fulltext1))
792
793 self.assertEqual(f.revision(node0), stored0)
794 self.assertEqual(f.revision(node0, raw=True), stored0)
795
796 self.assertEqual(f.revision(node1), stored1)
797 self.assertEqual(f.revision(node1, raw=True), stored1)
798
799 self.assertEqual(f.read(node0), fulltext0)
800 self.assertEqual(f.read(node1), fulltext1)
801
802 self.assertFalse(f.cmp(node0, fulltext0))
803 self.assertTrue(f.cmp(node0, stored0))
804
805 self.assertFalse(f.cmp(node1, fulltext1))
806 self.assertTrue(f.cmp(node1, stored0))
807
808 def testcensored(self):
809 f = self._makefilefn()
810
811 stored1 = revlog.packmeta({
812 b'censored': b'tombstone',
813 }, b'')
814
815 # TODO tests are incomplete because we need the node to be
816 # different due to presence of censor metadata. But we can't
817 # do this with addrevision().
818 with self._maketransactionfn() as tr:
819 node0 = f.add(b'foo', None, tr, 0, nullid, nullid)
820 f.addrevision(stored1, tr, 1, node0, nullid,
821 flags=revlog.REVIDX_ISCENSORED)
822
823 self.assertEqual(f.flags(1), revlog.REVIDX_ISCENSORED)
824 self.assertTrue(f.iscensored(1))
825
826 self.assertEqual(f.revision(1), stored1)
827 self.assertEqual(f.revision(1, raw=True), stored1)
828
829 self.assertEqual(f.read(1), b'')
830
831 class ifilemutationtests(basetestcase):
832 """Generic tests for the ifilemutation interface.
833
834 All file storage backends that support writing should conform to this
835 interface.
836
837 Use ``makeifilemutationtests()`` to create an instance of this type.
838 """
839 def testaddnoop(self):
840 f = self._makefilefn()
841 with self._maketransactionfn() as tr:
842 node0 = f.add(b'foo', None, tr, 0, nullid, nullid)
843 node1 = f.add(b'foo', None, tr, 0, nullid, nullid)
844 # Varying by linkrev shouldn't impact hash.
845 node2 = f.add(b'foo', None, tr, 1, nullid, nullid)
846
847 self.assertEqual(node1, node0)
848 self.assertEqual(node2, node0)
849 self.assertEqual(len(f), 1)
850
851 def testaddrevisionbadnode(self):
852 f = self._makefilefn()
853 with self._maketransactionfn() as tr:
854 # Adding a revision with bad node value fails.
855 with self.assertRaises(error.RevlogError):
856 f.addrevision(b'foo', tr, 0, nullid, nullid, node=b'\x01' * 20)
857
858 def testaddrevisionunknownflag(self):
859 f = self._makefilefn()
860 with self._maketransactionfn() as tr:
861 for i in range(15, 0, -1):
862 if (1 << i) & ~revlog.REVIDX_KNOWN_FLAGS:
863 flags = 1 << i
864 break
865
866 with self.assertRaises(error.RevlogError):
867 f.addrevision(b'foo', tr, 0, nullid, nullid, flags=flags)
868
869 def testaddgroupsimple(self):
870 f = self._makefilefn()
871
872 callbackargs = []
873 def cb(*args, **kwargs):
874 callbackargs.append((args, kwargs))
875
876 def linkmapper(node):
877 return 0
878
879 with self._maketransactionfn() as tr:
880 nodes = f.addgroup([], None, tr, addrevisioncb=cb)
881
882 self.assertEqual(nodes, [])
883 self.assertEqual(callbackargs, [])
884 self.assertEqual(len(f), 0)
885
886 fulltext0 = b'foo'
887 delta0 = mdiff.trivialdiffheader(len(fulltext0)) + fulltext0
888
889 deltas = [
890 (b'\x01' * 20, nullid, nullid, nullid, nullid, delta0, 0),
891 ]
892
893 with self._maketransactionfn() as tr:
894 with self.assertRaises(error.RevlogError):
895 f.addgroup(deltas, linkmapper, tr, addrevisioncb=cb)
896
897 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
898
899 f = self._makefilefn()
900
901 deltas = [
902 (node0, nullid, nullid, nullid, nullid, delta0, 0),
903 ]
904
905 with self._maketransactionfn() as tr:
906 nodes = f.addgroup(deltas, linkmapper, tr, addrevisioncb=cb)
907
908 self.assertEqual(nodes, [
909 b'\x49\xd8\xcb\xb1\x5c\xe2\x57\x92\x04\x47'
910 b'\x00\x6b\x46\x97\x8b\x7a\xf9\x80\xa9\x79'])
911
912 self.assertEqual(len(callbackargs), 1)
913 self.assertEqual(callbackargs[0][0][1], nodes[0])
914
915 self.assertEqual(list(f.revs()), [0])
916 self.assertEqual(f.rev(nodes[0]), 0)
917 self.assertEqual(f.node(0), nodes[0])
918
919 def testaddgroupmultiple(self):
920 f = self._makefilefn()
921
922 fulltexts = [
923 b'foo',
924 b'bar',
925 b'x' * 1024,
926 ]
927
928 nodes = []
929 with self._maketransactionfn() as tr:
930 for fulltext in fulltexts:
931 nodes.append(f.add(fulltext, None, tr, 0, nullid, nullid))
932
933 f = self._makefilefn()
934 deltas = []
935 for i, fulltext in enumerate(fulltexts):
936 delta = mdiff.trivialdiffheader(len(fulltext)) + fulltext
937
938 deltas.append((nodes[i], nullid, nullid, nullid, nullid, delta, 0))
939
940 with self._maketransactionfn() as tr:
941 self.assertEqual(f.addgroup(deltas, lambda x: 0, tr), nodes)
942
943 self.assertEqual(len(f), len(deltas))
944 self.assertEqual(list(f.revs()), [0, 1, 2])
945 self.assertEqual(f.rev(nodes[0]), 0)
946 self.assertEqual(f.rev(nodes[1]), 1)
947 self.assertEqual(f.rev(nodes[2]), 2)
948 self.assertEqual(f.node(0), nodes[0])
949 self.assertEqual(f.node(1), nodes[1])
950 self.assertEqual(f.node(2), nodes[2])
951
952 def makeifileindextests(makefilefn, maketransactionfn):
953 """Create a unittest.TestCase class suitable for testing file storage.
954
955 ``makefilefn`` is a callable which receives the test case as an
956 argument and returns an object implementing the ``ifilestorage`` interface.
957
958 ``maketransactionfn`` is a callable which receives the test case as an
959 argument and returns a transaction object.
960
961 Returns a type that is a ``unittest.TestCase`` that can be used for
962 testing the object implementing the file storage interface. Simply
963 assign the returned value to a module-level attribute and a test loader
964 should find and run it automatically.
965 """
966 d = {
967 r'_makefilefn': makefilefn,
968 r'_maketransactionfn': maketransactionfn,
969 }
970 return type(r'ifileindextests', (ifileindextests,), d)
971
972 def makeifiledatatests(makefilefn, maketransactionfn):
973 d = {
974 r'_makefilefn': makefilefn,
975 r'_maketransactionfn': maketransactionfn,
976 }
977 return type(r'ifiledatatests', (ifiledatatests,), d)
978
979 def makeifilemutationtests(makefilefn, maketransactionfn):
980 d = {
981 r'_makefilefn': makefilefn,
982 r'_maketransactionfn': maketransactionfn,
983 }
984 return type(r'ifilemutationtests', (ifilemutationtests,), d)
@@ -0,0 +1,46 b''
1 # This test verifies the conformance of various classes to various
2 # storage interfaces.
3 from __future__ import absolute_import
4
5 import silenttestrunner
6
7 from mercurial import (
8 filelog,
9 transaction,
10 ui as uimod,
11 vfs as vfsmod,
12 )
13
14 from mercurial.testing import (
15 storage as storagetesting,
16 )
17
18 STATE = {
19 'lastindex': 0,
20 'ui': uimod.ui(),
21 'vfs': vfsmod.vfs(b'.', realpath=True),
22 }
23
24 def makefilefn(self):
25 """Factory for filelog instances."""
26 fl = filelog.filelog(STATE['vfs'], 'filelog-%d' % STATE['lastindex'])
27 STATE['lastindex'] += 1
28 return fl
29
30 def maketransaction(self):
31 vfsmap = {'plain': STATE['vfs']}
32
33 return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap,
34 'journal', 'undo')
35
36 # Assigning module-level attributes that inherit from unittest.TestCase
37 # is all that is needed to register tests.
38 filelogindextests = storagetesting.makeifileindextests(makefilefn,
39 maketransaction)
40 filelogdatatests = storagetesting.makeifiledatatests(makefilefn,
41 maketransaction)
42 filelogmutationtests = storagetesting.makeifilemutationtests(makefilefn,
43 maketransaction)
44
45 if __name__ == '__main__':
46 silenttestrunner.main(__name__)
@@ -822,6 +822,7 b" packages = ['mercurial',"
822 822 'mercurial.thirdparty.zope.interface',
823 823 'mercurial.utils',
824 824 'mercurial.revlogutils',
825 'mercurial.testing',
825 826 'hgext', 'hgext.convert', 'hgext.fsmonitor',
826 827 'hgext.fastannotate',
827 828 'hgext.fsmonitor.pywatchman',
General Comments 0
You need to be logged in to leave comments. Login now