view tests/test-doctest.py @ 6906:67b8a57200f4 mercurial-6.2

ci: skip doctests on older branches We either need to install and use newer system hg that supports zstd revlog compression, or we can skip doctests because usually there are other tests that cover the same code (e.g. by testing the relevant commands).
author Anton Shestakov <av6@dwimlabs.net>
date Thu, 24 Oct 2024 19:48:01 +0400
parents 16fd24f6cf22
children
line wrap: on
line source

# this is hack to make sure no escape characters are inserted into the output

from __future__ import absolute_import
from __future__ import print_function

import doctest
import os
import re
import subprocess
import sys

ispy3 = sys.version_info[0] >= 3

if 'TERM' in os.environ:
    del os.environ['TERM']


class py3docchecker(doctest.OutputChecker):
    def check_output(self, want, got, optionflags):
        want2 = re.sub(r'''\bu(['"])(.*?)\1''', r'\1\2\1', want)  # py2: u''
        got2 = re.sub(r'''\bb(['"])(.*?)\1''', r'\1\2\1', got)  # py3: b''
        # py3: <exc.name>: b'<msg>' -> <name>: <msg>
        #      <exc.name>: <others> -> <name>: <others>
        # TODO: more output massaging
        return any(
            doctest.OutputChecker.check_output(self, w, g, optionflags)
            for w, g in [(want, got), (want2, got2)]
        )


def testmod(name, optionflags=0, testtarget=None):
    __import__(name)
    mod = sys.modules[name]
    if testtarget is not None:
        mod = getattr(mod, testtarget)

    # minimal copy of doctest.testmod()
    finder = doctest.DocTestFinder()
    checker = None
    if ispy3:
        checker = py3docchecker()
    runner = doctest.DocTestRunner(checker=checker, optionflags=optionflags)
    for test in finder.find(mod, name):
        runner.run(test)
    runner.summarize()


DONT_RUN = []

# Exceptions to the defaults for a given detected module. The value for each
# module name is a list of dicts that specify the kwargs to pass to testmod.
# testmod is called once per item in the list, so an empty list will cause the
# module to not be tested.
testmod_arg_overrides = {
    # 'mercurial.statprof': DONT_RUN,  # >>> is an example, not a doctest
}

fileset = 'set:(**.py)'

cwd = os.path.dirname(os.environ["TESTDIR"])

# run-tests.py makes an installation of core Mercurial in /tmp/, but evolve is
# not installed together with it, and evolve modules need to be imported to run
# doctests. We insert it at the start to make sure wider install of evolve does not take priority.
sys.path.insert(0, cwd)

if not os.path.isdir(os.path.join(cwd, ".hg")):
    sys.exit(0)

files_cmd = 'hg files --print0 "%s"' % fileset

# we prefer system hg for reading the repository, unless we're on python2
# because then we assume that system hg is too old (this is not always true,
# but it's an easy check and works well enough for us)
if ispy3 and 'HGTEST_RESTOREENV':
    files_cmd = '. $HGTEST_RESTOREENV; ' + files_cmd

files = subprocess.check_output(files_cmd, shell=True, cwd=cwd).split(b'\0')

if sys.version_info[0] >= 3:
    cwd = os.fsencode(cwd)

mods_tested = set()
for f in files:
    if not f:
        continue

    with open(os.path.join(cwd, f), "rb") as fh:
        if not re.search(br'\n\s*>>>', fh.read()):
            continue

    if ispy3:
        f = f.decode()

    modname = f.replace('.py', '').replace('\\', '.').replace('/', '.')

    # Third-party modules aren't our responsibility to test.
    if modname.startswith('hgext3rd.evolve.thirdparty.'):
        continue

    for kwargs in testmod_arg_overrides.get(modname, [{}]):
        mods_tested.add((modname, '%r' % (kwargs,)))
        if modname.startswith('tests.'):
            # On py2, we can't import from tests.foo, but it works on both py2
            # and py3 with the way that PYTHONPATH is setup to import without
            # the 'tests.' prefix, so we do that.
            modname = modname[len('tests.'):]

        testmod(modname, **kwargs)

# Meta-test: let's make sure that we actually ran what we expected to, above.
# Each item in the set is a 2-tuple of module name and stringified kwargs passed
# to testmod.
expected_mods_tested = set(
    [
        ('hgext3rd.evolve.obshistory', '{}'),
        ('hgext3rd.topic.common', '{}'),
    ]
)

unexpectedly_run = mods_tested.difference(expected_mods_tested)
not_run = expected_mods_tested.difference(mods_tested)

if unexpectedly_run:
    print('Unexpectedly ran (probably need to add to list):')
    for r in sorted(unexpectedly_run):
        print('  %r' % (r,))
if not_run:
    print('Expected to run, but was not run (doctest removed?):')
    for r in sorted(not_run):
        print('  %r' % (r,))