test-rust-ancestor.py
170 lines
| 6.4 KiB
| text/x-python
|
PythonLexer
/ tests / test-rust-ancestor.py
Georges Racinet
|
r41004 | from __future__ import absolute_import | ||
Georges Racinet
|
r41083 | import sys | ||
Georges Racinet
|
r41004 | import unittest | ||
Georges Racinet
|
r41386 | from mercurial import ( | ||
error, | ||||
node, | ||||
) | ||||
Georges Racinet
|
r41004 | try: | ||
from mercurial import rustext | ||||
Georges Racinet
|
r41083 | rustext.__name__ # trigger immediate actual import | ||
Georges Racinet
|
r41004 | except ImportError: | ||
rustext = None | ||||
Georges Racinet
|
r41083 | else: | ||
# this would fail already without appropriate ancestor.__package__ | ||||
Georges Racinet
|
r41149 | from mercurial.rustext.ancestor import ( | ||
AncestorsIterator, | ||||
Georges Racinet
|
r41224 | LazyAncestors, | ||
MissingAncestors, | ||||
Georges Racinet
|
r41149 | ) | ||
Georges Racinet
|
r41004 | |||
try: | ||||
from mercurial.cext import parsers as cparsers | ||||
except ImportError: | ||||
cparsers = None | ||||
Georges Racinet
|
r41083 | # picked from test-parse-index2, copied rather than imported | ||
# so that it stays stable even if test-parse-index2 changes or disappears. | ||||
data_non_inlined = ( | ||||
b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' | ||||
b'\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' | ||||
b'\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' | ||||
b'\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' | ||||
b'\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' | ||||
b'\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' | ||||
b'\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' | ||||
b'\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | ||||
b'\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' | ||||
b'\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' | ||||
b'\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' | ||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' | ||||
b'\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' | ||||
b'\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' | ||||
b'\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' | ||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00' | ||||
) | ||||
Georges Racinet
|
r41004 | @unittest.skipIf(rustext is None or cparsers is None, | ||
Georges Racinet
|
r41083 | "rustext or the C Extension parsers module " | ||
"ancestor relies on is not available") | ||||
Georges Racinet
|
r41004 | class rustancestorstest(unittest.TestCase): | ||
"""Test the correctness of binding to Rust code. | ||||
This test is merely for the binding to Rust itself: extraction of | ||||
Python variable, giving back the results etc. | ||||
It is not meant to test the algorithmic correctness of the operations | ||||
on ancestors it provides. Hence the very simple embedded index data is | ||||
good enough. | ||||
Algorithmic correctness is asserted by the Rust unit tests. | ||||
""" | ||||
Georges Racinet
|
r41083 | def parseindex(self): | ||
return cparsers.parse_index2(data_non_inlined, False)[0] | ||||
def testiteratorrevlist(self): | ||||
idx = self.parseindex() | ||||
# checking test assumption about the index binary data: | ||||
self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)}, | ||||
{0: (-1, -1), | ||||
1: (0, -1), | ||||
2: (1, -1), | ||||
3: (2, -1)}) | ||||
ait = AncestorsIterator(idx, [3], 0, True) | ||||
self.assertEqual([r for r in ait], [3, 2, 1, 0]) | ||||
ait = AncestorsIterator(idx, [3], 0, False) | ||||
self.assertEqual([r for r in ait], [2, 1, 0]) | ||||
Georges Racinet
|
r41149 | def testlazyancestors(self): | ||
idx = self.parseindex() | ||||
start_count = sys.getrefcount(idx) # should be 2 (see Python doc) | ||||
self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)}, | ||||
{0: (-1, -1), | ||||
1: (0, -1), | ||||
2: (1, -1), | ||||
3: (2, -1)}) | ||||
lazy = LazyAncestors(idx, [3], 0, True) | ||||
# we have two more references to the index: | ||||
# - in its inner iterator for __contains__ and __bool__ | ||||
# - in the LazyAncestors instance itself (to spawn new iterators) | ||||
self.assertEqual(sys.getrefcount(idx), start_count + 2) | ||||
self.assertTrue(2 in lazy) | ||||
self.assertTrue(bool(lazy)) | ||||
self.assertEqual(list(lazy), [3, 2, 1, 0]) | ||||
# a second time to validate that we spawn new iterators | ||||
self.assertEqual(list(lazy), [3, 2, 1, 0]) | ||||
# now let's watch the refcounts closer | ||||
ait = iter(lazy) | ||||
self.assertEqual(sys.getrefcount(idx), start_count + 3) | ||||
del ait | ||||
self.assertEqual(sys.getrefcount(idx), start_count + 2) | ||||
del lazy | ||||
self.assertEqual(sys.getrefcount(idx), start_count) | ||||
# let's check bool for an empty one | ||||
self.assertFalse(LazyAncestors(idx, [0], 0, False)) | ||||
Georges Racinet
|
r41224 | def testmissingancestors(self): | ||
idx = self.parseindex() | ||||
missanc = MissingAncestors(idx, [1]) | ||||
self.assertTrue(missanc.hasbases()) | ||||
self.assertEqual(missanc.missingancestors([3]), [2, 3]) | ||||
missanc.addbases({2}) | ||||
Georges Racinet
|
r41279 | self.assertEqual(missanc.bases(), {1, 2}) | ||
Georges Racinet
|
r41224 | self.assertEqual(missanc.missingancestors([3]), [3]) | ||
Georges Racinet
|
r41282 | self.assertEqual(missanc.basesheads(), {2}) | ||
Georges Racinet
|
r41224 | |||
def testmissingancestorsremove(self): | ||||
idx = self.parseindex() | ||||
missanc = MissingAncestors(idx, [1]) | ||||
revs = {0, 1, 2, 3} | ||||
missanc.removeancestorsfrom(revs) | ||||
self.assertEqual(revs, {2, 3}) | ||||
Georges Racinet
|
r41083 | def testrefcount(self): | ||
idx = self.parseindex() | ||||
start_count = sys.getrefcount(idx) | ||||
# refcount increases upon iterator init... | ||||
ait = AncestorsIterator(idx, [3], 0, True) | ||||
self.assertEqual(sys.getrefcount(idx), start_count + 1) | ||||
self.assertEqual(next(ait), 3) | ||||
# and decreases once the iterator is removed | ||||
del ait | ||||
self.assertEqual(sys.getrefcount(idx), start_count) | ||||
# and removing ref to the index after iterator init is no issue | ||||
ait = AncestorsIterator(idx, [3], 0, True) | ||||
del idx | ||||
Georges Racinet
|
r41149 | self.assertEqual(list(ait), [3, 2, 1, 0]) | ||
Georges Racinet
|
r41004 | |||
def testgrapherror(self): | ||||
Georges Racinet
|
r41083 | data = (data_non_inlined[:64 + 27] + | ||
b'\xf2' + | ||||
data_non_inlined[64 + 28:]) | ||||
idx = cparsers.parse_index2(data, False)[0] | ||||
with self.assertRaises(rustext.GraphError) as arc: | ||||
AncestorsIterator(idx, [1], -1, False) | ||||
exc = arc.exception | ||||
self.assertIsInstance(exc, ValueError) | ||||
# rust-cpython issues appropriate str instances for Python 2 and 3 | ||||
self.assertEqual(exc.args, ('ParentOutOfRange', 1)) | ||||
Georges Racinet
|
r41004 | |||
Georges Racinet
|
r41386 | def testwdirunsupported(self): | ||
# trying to access ancestors of the working directory raises | ||||
# WdirUnsupported directly | ||||
idx = self.parseindex() | ||||
with self.assertRaises(error.WdirUnsupported): | ||||
list(AncestorsIterator(idx, [node.wdirrev], -1, False)) | ||||
Georges Racinet
|
r41004 | |||
if __name__ == '__main__': | ||||
import silenttestrunner | ||||
silenttestrunner.main(__name__) | ||||