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