1 from __future__ import absolute_import |
1 from __future__ import absolute_import |
|
2 import sys |
2 import unittest |
3 import unittest |
3 |
4 |
4 try: |
5 try: |
5 from mercurial import rustext |
6 from mercurial import rustext |
|
7 rustext.__name__ # trigger immediate actual import |
6 except ImportError: |
8 except ImportError: |
7 rustext = None |
9 rustext = None |
|
10 else: |
|
11 # this would fail already without appropriate ancestor.__package__ |
|
12 from mercurial.rustext.ancestor import AncestorsIterator |
8 |
13 |
9 try: |
14 try: |
10 from mercurial.cext import parsers as cparsers |
15 from mercurial.cext import parsers as cparsers |
11 except ImportError: |
16 except ImportError: |
12 cparsers = None |
17 cparsers = None |
13 |
18 |
|
19 # picked from test-parse-index2, copied rather than imported |
|
20 # so that it stays stable even if test-parse-index2 changes or disappears. |
|
21 data_non_inlined = ( |
|
22 b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' |
|
23 b'\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' |
|
24 b'\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' |
|
25 b'\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
|
26 b'\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' |
|
27 b'\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' |
|
28 b'\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' |
|
29 b'\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
|
30 b'\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' |
|
31 b'\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' |
|
32 b'\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' |
|
33 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' |
|
34 b'\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' |
|
35 b'\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' |
|
36 b'\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' |
|
37 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
|
38 ) |
|
39 |
|
40 |
14 @unittest.skipIf(rustext is None or cparsers is None, |
41 @unittest.skipIf(rustext is None or cparsers is None, |
15 "rustext.ancestor or the C Extension parsers module " |
42 "rustext or the C Extension parsers module " |
16 "it relies on is not available") |
43 "ancestor relies on is not available") |
17 class rustancestorstest(unittest.TestCase): |
44 class rustancestorstest(unittest.TestCase): |
18 """Test the correctness of binding to Rust code. |
45 """Test the correctness of binding to Rust code. |
19 |
46 |
20 This test is merely for the binding to Rust itself: extraction of |
47 This test is merely for the binding to Rust itself: extraction of |
21 Python variable, giving back the results etc. |
48 Python variable, giving back the results etc. |
25 good enough. |
52 good enough. |
26 |
53 |
27 Algorithmic correctness is asserted by the Rust unit tests. |
54 Algorithmic correctness is asserted by the Rust unit tests. |
28 """ |
55 """ |
29 |
56 |
30 def testmodule(self): |
57 def parseindex(self): |
31 self.assertTrue('DAG' in rustext.ancestor.__doc__) |
58 return cparsers.parse_index2(data_non_inlined, False)[0] |
|
59 |
|
60 def testiteratorrevlist(self): |
|
61 idx = self.parseindex() |
|
62 # checking test assumption about the index binary data: |
|
63 self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)}, |
|
64 {0: (-1, -1), |
|
65 1: (0, -1), |
|
66 2: (1, -1), |
|
67 3: (2, -1)}) |
|
68 ait = AncestorsIterator(idx, [3], 0, True) |
|
69 self.assertEqual([r for r in ait], [3, 2, 1, 0]) |
|
70 |
|
71 ait = AncestorsIterator(idx, [3], 0, False) |
|
72 self.assertEqual([r for r in ait], [2, 1, 0]) |
|
73 |
|
74 def testrefcount(self): |
|
75 idx = self.parseindex() |
|
76 start_count = sys.getrefcount(idx) |
|
77 |
|
78 # refcount increases upon iterator init... |
|
79 ait = AncestorsIterator(idx, [3], 0, True) |
|
80 self.assertEqual(sys.getrefcount(idx), start_count + 1) |
|
81 self.assertEqual(next(ait), 3) |
|
82 |
|
83 # and decreases once the iterator is removed |
|
84 del ait |
|
85 self.assertEqual(sys.getrefcount(idx), start_count) |
|
86 |
|
87 # and removing ref to the index after iterator init is no issue |
|
88 ait = AncestorsIterator(idx, [3], 0, True) |
|
89 del idx |
|
90 self.assertEqual([r for r in ait], [3, 2, 1, 0]) |
32 |
91 |
33 def testgrapherror(self): |
92 def testgrapherror(self): |
34 self.assertTrue('GraphError' in dir(rustext)) |
93 data = (data_non_inlined[:64 + 27] + |
|
94 b'\xf2' + |
|
95 data_non_inlined[64 + 28:]) |
|
96 idx = cparsers.parse_index2(data, False)[0] |
|
97 with self.assertRaises(rustext.GraphError) as arc: |
|
98 AncestorsIterator(idx, [1], -1, False) |
|
99 exc = arc.exception |
|
100 self.assertIsInstance(exc, ValueError) |
|
101 # rust-cpython issues appropriate str instances for Python 2 and 3 |
|
102 self.assertEqual(exc.args, ('ParentOutOfRange', 1)) |
35 |
103 |
36 |
104 |
37 if __name__ == '__main__': |
105 if __name__ == '__main__': |
38 import silenttestrunner |
106 import silenttestrunner |
39 silenttestrunner.main(__name__) |
107 silenttestrunner.main(__name__) |