Mercurial > public > mercurial-scm > hg
diff tests/test-rust-ancestor.py @ 52532:3ffcdbf0b432
rust: test AncestorsIterator without duplication
Up to now, it was acceptable to duplicate the test
in `test-rust-ancestor.py` that was relevant to `dagop`.
Now that we have more PyO3 implementations, it is better, and
actually more convincing, to express the tests in a common base
class (has technically to be a mixin to avoid running the tests
from the base class itself).
author | Georges Racinet <georges.racinet@cloudcrane.io> |
---|---|
date | Sat, 07 Dec 2024 18:42:06 +0100 |
parents | 20fe0bf9a9a5 |
children | 6b694bdf752a |
line wrap: on
line diff
--- a/tests/test-rust-ancestor.py Sat Dec 07 18:38:37 2024 +0100 +++ b/tests/test-rust-ancestor.py Sat Dec 07 18:42:06 2024 +0100 @@ -11,14 +11,6 @@ pyo3_rustext.__name__ except ImportError: rustext = pyo3_rustext = None -else: - # this would fail already without appropriate ancestor.__package__ - from mercurial.rustext.ancestor import ( - AncestorsIterator, - LazyAncestors, - MissingAncestors, - ) - from mercurial.rustext import dagop try: from mercurial.cext import parsers as cparsers @@ -26,7 +18,7 @@ cparsers = None -class rustancestorstest(revlogtesting.RustRevlogBasedTestBase): +class RustAncestorsTestMixin: """Test the correctness of binding to Rust code. This test is merely for the binding to Rust itself: extraction of @@ -37,9 +29,33 @@ good enough. Algorithmic correctness is asserted by the Rust unit tests. + + At this point, we have two sets of bindings, in `hg-cpython` and + `hg-pyo3`. This class used to be for the first and now contains + the tests that are identical in both bindings. As of this writing, + there are more implementations in `hg-cpython` than `hg-pyo3`, hence + some more tests in the subclass for `hg-cpython`. When the work on PyO3 + is complete, the subclasses for `hg-cpython` should have no specific + test left. Later on, when we remove the dead code in `hg-cpython`, the tests + should migrate from the mixin to the class for `hg-pyo3`, until we can + simply remove the mixin. """ + @classmethod + def ancestors_mod(cls): + return cls.rustext_pkg.ancestor + + @classmethod + def dagop_mod(cls): + return cls.rustext_pkg.dagop + + @classmethod + def graph_error(cls): + return cls.rustext_pkg.GraphError + def testiteratorrevlist(self): + AncestorsIterator = self.ancestors_mod().AncestorsIterator + idx = self.parserustindex() # checking test assumption about the index binary data: self.assertEqual( @@ -52,7 +68,76 @@ ait = AncestorsIterator(idx, [3], 0, False) self.assertEqual([r for r in ait], [2, 1, 0]) + def testrefcount(self): + AncestorsIterator = self.ancestors_mod().AncestorsIterator + + idx = self.parserustindex() + start_count = sys.getrefcount(idx.inner) + + # refcount increases upon iterator init... + ait = AncestorsIterator(idx, [3], 0, True) + self.assertEqual(sys.getrefcount(idx.inner), start_count + 1) + self.assertEqual(next(ait), 3) + + # and decreases once the iterator is removed + del ait + self.assertEqual(sys.getrefcount(idx.inner), start_count) + + # and removing ref to the index after iterator init is no issue + ait = AncestorsIterator(idx, [3], 0, True) + del idx + self.assertEqual(list(ait), [3, 2, 1, 0]) + + # the index is not tracked by the GC, hence there is nothing more + # we can assert to check that it is properly deleted once its refcount + # drops to 0 + + def testgrapherror(self): + AncestorsIterator = self.ancestors_mod().AncestorsIterator + GraphError = self.graph_error() + + data = ( + revlogtesting.data_non_inlined[: 64 + 27] + + b'\xf2' + + revlogtesting.data_non_inlined[64 + 28 :] + ) + idx = self.parserustindex(data=data) + with self.assertRaises(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)) + + def testwdirunsupported(self): + AncestorsIterator = self.ancestors_mod().AncestorsIterator + GraphError = self.graph_error() + + # trying to access ancestors of the working directory raises + idx = self.parserustindex() + with self.assertRaises(GraphError) as arc: + list(AncestorsIterator(idx, [wdirrev], -1, False)) + + exc = arc.exception + self.assertIsInstance(exc, ValueError) + # rust-cpython issues appropriate str instances for Python 2 and 3 + self.assertEqual(exc.args, ('InvalidRevision', wdirrev)) + + def testheadrevs(self): + dagop = self.dagop_mod() + + idx = self.parserustindex() + self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3}) + + +class RustCPythonAncestorsTest( + revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin +): + rustext_pkg = rustext + def testlazyancestors(self): + LazyAncestors = self.ancestors_mod().LazyAncestors + idx = self.parserustindex() start_count = sys.getrefcount(idx.inner) # should be 2 (see Python doc) self.assertEqual( @@ -82,6 +167,8 @@ self.assertFalse(LazyAncestors(idx, [0], 0, False)) def testmissingancestors(self): + MissingAncestors = self.ancestors_mod().MissingAncestors + idx = self.parserustindex() missanc = MissingAncestors(idx, [1]) self.assertTrue(missanc.hasbases()) @@ -92,71 +179,26 @@ self.assertEqual(missanc.basesheads(), {2}) def testmissingancestorsremove(self): + MissingAncestors = self.ancestors_mod().MissingAncestors + idx = self.parserustindex() missanc = MissingAncestors(idx, [1]) revs = {0, 1, 2, 3} missanc.removeancestorsfrom(revs) self.assertEqual(revs, {2, 3}) - def testrefcount(self): - idx = self.parserustindex() - start_count = sys.getrefcount(idx.inner) - - # refcount increases upon iterator init... - ait = AncestorsIterator(idx, [3], 0, True) - self.assertEqual(sys.getrefcount(idx.inner), start_count + 1) - self.assertEqual(next(ait), 3) - - # and decreases once the iterator is removed - del ait - self.assertEqual(sys.getrefcount(idx.inner), start_count) - - # and removing ref to the index after iterator init is no issue - ait = AncestorsIterator(idx, [3], 0, True) - del idx - self.assertEqual(list(ait), [3, 2, 1, 0]) - - # the index is not tracked by the GC, hence there is nothing more - # we can assert to check that it is properly deleted once its refcount - # drops to 0 - def testgrapherror(self): - data = ( - revlogtesting.data_non_inlined[: 64 + 27] - + b'\xf2' - + revlogtesting.data_non_inlined[64 + 28 :] - ) - idx = self.parserustindex(data=data) - 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)) +class PyO3AncestorsTest( + revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin +): + rustext_pkg = pyo3_rustext - def testwdirunsupported(self): - # trying to access ancestors of the working directory raises - idx = self.parserustindex() - with self.assertRaises(rustext.GraphError) as arc: - list(AncestorsIterator(idx, [wdirrev], -1, False)) + def test_rank(self): + dagop = self.dagop_mod() - exc = arc.exception - self.assertIsInstance(exc, ValueError) - # rust-cpython issues appropriate str instances for Python 2 and 3 - self.assertEqual(exc.args, ('InvalidRevision', wdirrev)) - - def testheadrevs(self): - idx = self.parserustindex() - self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3}) - - def testpyo3_headrevs(self): - idx = self.parserustindex() - self.assertEqual(pyo3_rustext.dagop.headrevs(idx, [1, 2, 3]), {3}) - - def testpyo3_rank(self): idx = self.parserustindex() try: - pyo3_rustext.dagop.rank(idx, 1, 2) + dagop.rank(idx, 1, 2) except pyo3_rustext.GraphError as exc: self.assertEqual(exc.args, ("InconsistentGraphData",))