##// END OF EJS Templates
testing: stop skipping all Python tests of Rust revlog...
Georges Racinet -
r53302:cf5b47b8 default
parent child Browse files
Show More
@@ -1,64 +1,93
1 1 from __future__ import annotations
2 2
3 3 import unittest
4 4
5 5 # picked from test-parse-index2, copied rather than imported
6 6 # so that it stays stable even if test-parse-index2 changes or disappears.
7 7 data_non_inlined = (
8 8 b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19'
9 9 b'\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff'
10 10 b'\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d'
11 11 b'\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00'
12 12 b'\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00'
13 13 b'\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff'
14 14 b'\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh'
15 15 b'\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
16 16 b'\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00'
17 17 b'\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n'
18 18 b'\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00'
19 19 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F'
20 20 b'\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01'
21 21 b'\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1'
22 22 b'\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00'
23 23 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00'
24 24 )
25 25
26 from ..revlogutils.constants import REVLOGV1
26 from ..revlogutils.constants import (
27 KIND_CHANGELOG,
28 )
29 from .. import revlog
27 30
28 31
29 32 try:
30 33 from ..cext import parsers as cparsers # pytype: disable=import-error
31 34 except ImportError:
32 35 cparsers = None
33 36
34 37 try:
35 from ..rustext.revlog import ( # pytype: disable=import-error
36 Index as RustIndex,
38 from ..rustext import ( # pytype: disable=import-error
39 revlog as rust_revlog,
37 40 )
41
42 rust_revlog.__name__ # force actual import
38 43 except ImportError:
39 RustIndex = None
44 rust_revlog = None
40 45
41 46
42 47 @unittest.skipIf(
43 48 cparsers is None,
44 49 'The C version of the "parsers" module is not available. It is needed for this test.',
45 50 )
46 51 class RevlogBasedTestBase(unittest.TestCase):
47 52 def parseindex(self, data=None):
48 53 if data is None:
49 54 data = data_non_inlined
50 55 return cparsers.parse_index2(data, False)[0]
51 56
52 57
53 58 @unittest.skipIf(
54 RustIndex is None,
55 'The Rust index is not available. It is needed for this test.',
59 rust_revlog is None,
60 'The Rust revlog module is not available. It is needed for this test.',
56 61 )
57 62 class RustRevlogBasedTestBase(unittest.TestCase):
58 def parserustindex(self, data=None):
63 # defaults
64 revlog_data_config = revlog.DataConfig()
65 revlog_delta_config = revlog.DeltaConfig()
66 revlog_feature_config = revlog.FeatureConfig()
67
68 def make_inner_revlog(
69 self, data=None, vfs_is_readonly=True, kind=KIND_CHANGELOG
70 ):
59 71 if data is None:
60 72 data = data_non_inlined
61 # not inheriting RevlogBasedTestCase to avoid having a
62 # `parseindex` method that would be shadowed by future subclasses
63 # this duplication will soon be removed
64 return RustIndex(data, REVLOGV1)
73
74 return rust_revlog.InnerRevlog(
75 vfs_base=b"Just a path",
76 fncache=None, # might be enough for now
77 vfs_is_readonly=vfs_is_readonly,
78 index_data=data,
79 index_file=b'test.i',
80 data_file=b'test.d',
81 sidedata_file=None,
82 inline=False,
83 data_config=self.revlog_data_config,
84 delta_config=self.revlog_delta_config,
85 feature_config=self.revlog_feature_config,
86 chunk_cache=None,
87 default_compression_header=None,
88 revlog_type=kind,
89 use_persistent_nodemap=False, # until we cook one.
90 )
91
92 def parserustindex(self, data=None):
93 return revlog.RustIndexProxy(self.make_inner_revlog(data=data))
@@ -1,167 +1,155
1 1 import sys
2 import unittest
3 2
4 3 from mercurial.node import wdirrev
5 4
6 5 from mercurial.testing import revlog as revlogtesting
7 6
8 7 try:
9 8 from mercurial import rustext
10 9
11 10 rustext.__name__ # trigger immediate actual import
12 11 except ImportError:
13 12 rustext = None
14 13 else:
15 14 # this would fail already without appropriate ancestor.__package__
16 15 from mercurial.rustext.ancestor import (
17 16 AncestorsIterator,
18 17 LazyAncestors,
19 18 MissingAncestors,
20 19 )
21 20 from mercurial.rustext import dagop
22 21
23 22 try:
24 23 from mercurial.cext import parsers as cparsers
25 24 except ImportError:
26 25 cparsers = None
27 26
28 27
29 @unittest.skipIf(
30 rustext is None,
31 'The Rust version of the "ancestor" module is not available. It is needed'
32 ' for this test.',
33 )
34 @unittest.skipIf(
35 rustext is None,
36 'The Rust or C version of the "parsers" module, which the "ancestor" module'
37 ' relies on, is not available.',
38 )
39 28 class rustancestorstest(revlogtesting.RustRevlogBasedTestBase):
40 29 """Test the correctness of binding to Rust code.
41 30
42 31 This test is merely for the binding to Rust itself: extraction of
43 32 Python variable, giving back the results etc.
44 33
45 34 It is not meant to test the algorithmic correctness of the operations
46 35 on ancestors it provides. Hence the very simple embedded index data is
47 36 good enough.
48 37
49 38 Algorithmic correctness is asserted by the Rust unit tests.
50 39 """
51 40
52 41 def testiteratorrevlist(self):
53 42 idx = self.parserustindex()
54 43 # checking test assumption about the index binary data:
55 44 self.assertEqual(
56 45 {i: (r[5], r[6]) for i, r in enumerate(idx)},
57 46 {0: (-1, -1), 1: (0, -1), 2: (1, -1), 3: (2, -1)},
58 47 )
59 48 ait = AncestorsIterator(idx, [3], 0, True)
60 49 self.assertEqual([r for r in ait], [3, 2, 1, 0])
61 50
62 51 ait = AncestorsIterator(idx, [3], 0, False)
63 52 self.assertEqual([r for r in ait], [2, 1, 0])
64 53
65 54 def testlazyancestors(self):
66 55 idx = self.parserustindex()
67 start_count = sys.getrefcount(idx) # should be 2 (see Python doc)
56 start_count = sys.getrefcount(idx.inner) # should be 2 (see Python doc)
68 57 self.assertEqual(
69 58 {i: (r[5], r[6]) for i, r in enumerate(idx)},
70 59 {0: (-1, -1), 1: (0, -1), 2: (1, -1), 3: (2, -1)},
71 60 )
72 61 lazy = LazyAncestors(idx, [3], 0, True)
73 # we have two more references to the index:
74 # - in its inner iterator for __contains__ and __bool__
75 # - in the LazyAncestors instance itself (to spawn new iterators)
76 self.assertEqual(sys.getrefcount(idx), start_count + 2)
62 # the LazyAncestors instance holds just one reference to the
63 # inner revlog.
64 self.assertEqual(sys.getrefcount(idx.inner), start_count + 1)
77 65
78 66 self.assertTrue(2 in lazy)
79 67 self.assertTrue(bool(lazy))
80 68 self.assertEqual(list(lazy), [3, 2, 1, 0])
81 69 # a second time to validate that we spawn new iterators
82 70 self.assertEqual(list(lazy), [3, 2, 1, 0])
83 71
84 72 # now let's watch the refcounts closer
85 73 ait = iter(lazy)
86 self.assertEqual(sys.getrefcount(idx), start_count + 3)
74 self.assertEqual(sys.getrefcount(idx.inner), start_count + 2)
87 75 del ait
88 self.assertEqual(sys.getrefcount(idx), start_count + 2)
76 self.assertEqual(sys.getrefcount(idx.inner), start_count + 1)
89 77 del lazy
90 self.assertEqual(sys.getrefcount(idx), start_count)
78 self.assertEqual(sys.getrefcount(idx.inner), start_count)
91 79
92 80 # let's check bool for an empty one
93 81 self.assertFalse(LazyAncestors(idx, [0], 0, False))
94 82
95 83 def testmissingancestors(self):
96 84 idx = self.parserustindex()
97 85 missanc = MissingAncestors(idx, [1])
98 86 self.assertTrue(missanc.hasbases())
99 87 self.assertEqual(missanc.missingancestors([3]), [2, 3])
100 88 missanc.addbases({2})
101 89 self.assertEqual(missanc.bases(), {1, 2})
102 90 self.assertEqual(missanc.missingancestors([3]), [3])
103 91 self.assertEqual(missanc.basesheads(), {2})
104 92
105 93 def testmissingancestorsremove(self):
106 94 idx = self.parserustindex()
107 95 missanc = MissingAncestors(idx, [1])
108 96 revs = {0, 1, 2, 3}
109 97 missanc.removeancestorsfrom(revs)
110 98 self.assertEqual(revs, {2, 3})
111 99
112 100 def testrefcount(self):
113 101 idx = self.parserustindex()
114 start_count = sys.getrefcount(idx)
102 start_count = sys.getrefcount(idx.inner)
115 103
116 104 # refcount increases upon iterator init...
117 105 ait = AncestorsIterator(idx, [3], 0, True)
118 self.assertEqual(sys.getrefcount(idx), start_count + 1)
106 self.assertEqual(sys.getrefcount(idx.inner), start_count + 1)
119 107 self.assertEqual(next(ait), 3)
120 108
121 109 # and decreases once the iterator is removed
122 110 del ait
123 self.assertEqual(sys.getrefcount(idx), start_count)
111 self.assertEqual(sys.getrefcount(idx.inner), start_count)
124 112
125 113 # and removing ref to the index after iterator init is no issue
126 114 ait = AncestorsIterator(idx, [3], 0, True)
127 115 del idx
128 116 self.assertEqual(list(ait), [3, 2, 1, 0])
129 117
130 118 # the index is not tracked by the GC, hence there is nothing more
131 119 # we can assert to check that it is properly deleted once its refcount
132 120 # drops to 0
133 121
134 122 def testgrapherror(self):
135 123 data = (
136 124 revlogtesting.data_non_inlined[: 64 + 27]
137 125 + b'\xf2'
138 126 + revlogtesting.data_non_inlined[64 + 28 :]
139 127 )
140 128 idx = self.parserustindex(data=data)
141 129 with self.assertRaises(rustext.GraphError) as arc:
142 130 AncestorsIterator(idx, [1], -1, False)
143 131 exc = arc.exception
144 132 self.assertIsInstance(exc, ValueError)
145 133 # rust-cpython issues appropriate str instances for Python 2 and 3
146 134 self.assertEqual(exc.args, ('ParentOutOfRange', 1))
147 135
148 136 def testwdirunsupported(self):
149 137 # trying to access ancestors of the working directory raises
150 138 idx = self.parserustindex()
151 139 with self.assertRaises(rustext.GraphError) as arc:
152 140 list(AncestorsIterator(idx, [wdirrev], -1, False))
153 141
154 142 exc = arc.exception
155 143 self.assertIsInstance(exc, ValueError)
156 144 # rust-cpython issues appropriate str instances for Python 2 and 3
157 145 self.assertEqual(exc.args, ('InvalidRevision', wdirrev))
158 146
159 147 def testheadrevs(self):
160 148 idx = self.parserustindex()
161 149 self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3})
162 150
163 151
164 152 if __name__ == '__main__':
165 153 import silenttestrunner
166 154
167 155 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now