tests/test-rust-revlog.py
author Georges Racinet <georges.racinet@octobus.net>
Mon, 30 Oct 2023 22:36:30 +0100
changeset 51251 0409bd6ba663
parent 51249 2966b88d4531
child 51253 96e05f1a99bd
permissions -rw-r--r--
rust-revlog: add invalidation detection to `NodeTree` class This will be useful for callers, such as `scmutil` who reuse a `NodeTree` instance as a cache. They would otherwise get hard errors if any mutation of the index occurred since instantiation. This is something the C index does not provide.

import struct
import unittest

from mercurial.node import hex

try:
    from mercurial import rustext

    rustext.__name__  # trigger immediate actual import
except ImportError:
    rustext = None
else:
    from mercurial.rustext import revlog

    # this would fail already without appropriate ancestor.__package__
    from mercurial.rustext.ancestor import LazyAncestors

from mercurial.testing import revlog as revlogtesting

header = struct.unpack(">I", revlogtesting.data_non_inlined[:4])[0]


@unittest.skipIf(
    rustext is None,
    "rustext module revlog relies on is not available",
)
class RustRevlogIndexTest(revlogtesting.RevlogBasedTestBase):
    def test_heads(self):
        idx = self.parseindex()
        rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
        self.assertEqual(rustidx.headrevs(), idx.headrevs())

    def test_get_cindex(self):
        # drop me once we no longer need the method for shortest node
        idx = self.parseindex()
        rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
        cidx = rustidx.get_cindex()
        self.assertTrue(idx is cidx)

    def test_len(self):
        idx = self.parseindex()
        rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
        self.assertEqual(len(rustidx), len(idx))

    def test_ancestors(self):
        idx = self.parseindex()
        rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
        lazy = LazyAncestors(rustidx, [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.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])

        # let's check bool for an empty one
        self.assertFalse(LazyAncestors(rustidx, [0], 0, False))


@unittest.skipIf(
    rustext is None,
    "rustext module revlog relies on is not available",
)
class RustRevlogNodeTreeClassTest(revlogtesting.RustRevlogBasedTestBase):
    def test_standalone_nodetree(self):
        idx = self.parserustindex()
        nt = revlog.NodeTree(idx)
        for i in range(4):
            nt.insert(i)

        bin_nodes = [entry[7] for entry in idx]
        hex_nodes = [hex(n) for n in bin_nodes]

        for i, node in enumerate(hex_nodes):
            self.assertEqual(nt.prefix_rev_lookup(node), i)
            self.assertEqual(nt.prefix_rev_lookup(node[:5]), i)

        # all 4 revisions in idx (standard data set) have different
        # first nybbles in their Node IDs,
        # hence `nt.shortest()` should return 1 for them, except when
        # the leading nybble is 0 (ambiguity with NULL_NODE)
        for i, (bin_node, hex_node) in enumerate(zip(bin_nodes, hex_nodes)):
            shortest = nt.shortest(bin_node)
            expected = 2 if hex_node[0] == ord('0') else 1
            self.assertEqual(shortest, expected)
            self.assertEqual(nt.prefix_rev_lookup(hex_node[:shortest]), i)

        # test invalidation (generation poisoning) detection
        del idx[3]
        self.assertTrue(nt.is_invalidated())


if __name__ == '__main__':
    import silenttestrunner

    silenttestrunner.main(__name__)