mirror of
https://github.com/AdaCore/langkit.git
synced 2026-02-12 12:28:12 -08:00
Introduce the testsuite `--with-langkit-python` option so that it is possible to have: * one interpreter with the e3 distribution (to run the testsuite), * one interpreter with Langkit installed (to run lkm and the various scripts using Langkit modules), * one bare interpreter to import Langkit-generated libraries.
367 lines
13 KiB
Python
Executable File
367 lines
13 KiB
Python
Executable File
#! /usr/bin/env python
|
|
|
|
"""
|
|
Usage::
|
|
|
|
testsuite.py [OPTIONS]
|
|
|
|
Run the Langkit testsuite.
|
|
"""
|
|
|
|
import glob
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
from e3.testsuite import Testsuite
|
|
|
|
import drivers.adalog_driver
|
|
import drivers.langkit_support_driver
|
|
import drivers.lkt_build_and_run_driver
|
|
import drivers.lkt_compile_driver
|
|
import drivers.lkt_parse_driver
|
|
import drivers.lkt_toolbox_driver
|
|
import drivers.lkt_unparse_driver
|
|
import drivers.python_driver
|
|
|
|
|
|
def add_to_path(env_var, directory):
|
|
"""
|
|
Add the given directory to the `env_var` path.
|
|
"""
|
|
path = os.environ.get(env_var)
|
|
path = (
|
|
"{}{}{}".format(directory, os.path.pathsep, path) if path else env_var
|
|
)
|
|
os.environ[env_var] = path
|
|
|
|
|
|
class LangkitTestsuite(Testsuite):
|
|
tests_subdir = "tests"
|
|
test_driver_map = {
|
|
"adalog": drivers.adalog_driver.AdalogDriver,
|
|
"langkit_support": drivers.langkit_support_driver.LangkitSupportDriver,
|
|
"lkt": drivers.lkt_toolbox_driver.LktToolboxDriver,
|
|
"lkt_build_and_run": (
|
|
drivers.lkt_build_and_run_driver.LktBuildAndRunDriver
|
|
),
|
|
"lkt_compile": drivers.lkt_compile_driver.LktCompileDriver,
|
|
"lkt_parse": drivers.lkt_parse_driver.LktParseDriver,
|
|
"lkt_unparse": drivers.lkt_unparse_driver.LktUnparseDriver,
|
|
"python": drivers.python_driver.PythonDriver,
|
|
}
|
|
|
|
def add_options(self, parser):
|
|
parser.add_argument(
|
|
"--with-langkit-python",
|
|
help="If provided, use as the Python interpreter in scripts that"
|
|
" use Langkit modules.",
|
|
)
|
|
parser.add_argument(
|
|
"--with-python",
|
|
help="If provided, use as the Python interpreter in testcases.",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--coverage",
|
|
"-C",
|
|
action="store_true",
|
|
help="Enable computation of code coverage for Langkit and"
|
|
" Langkit_Support. This requires coverage.py and"
|
|
" GNATcoverage.",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--valgrind",
|
|
action="store_true",
|
|
help="Run tests with Valgrind to check memory issues.",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--no-auto-path",
|
|
action="store_true",
|
|
help="Do not automatically add Langkit to PYTHONPATH. This is"
|
|
" useful to test that Langkit was properly installed in the"
|
|
" environment that runs the testsuite.",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--disable-ocaml",
|
|
action="store_true",
|
|
help="Disable testcases that require OCaml (they are enabled by"
|
|
" default).",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--disable-java",
|
|
action="store_true",
|
|
help="Disable testcases that require Java (they are enabled by"
|
|
" default).",
|
|
)
|
|
parser.add_argument(
|
|
"--disable-native-image",
|
|
action="store_true",
|
|
help="Disable testcases that require native-image (they are"
|
|
" enabled by default).",
|
|
)
|
|
parser.add_argument(
|
|
"--maven-local-repo",
|
|
help="Specify the Maven repository to use, default one is the"
|
|
" user's repository (~/.m2).",
|
|
)
|
|
parser.add_argument(
|
|
"--maven-executable",
|
|
help="Specify the Maven executable to use. The default one is"
|
|
' "mvn".',
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--disable-gdb",
|
|
action="store_true",
|
|
help="Disable testcases that require GDB (they are enabled by"
|
|
" default).",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--restricted-env",
|
|
action="store_true",
|
|
help="Skip testcases that cannot run in a restricted environment"
|
|
" (need for non-standard Python packages).",
|
|
)
|
|
|
|
#
|
|
# Convenience options for developpers
|
|
#
|
|
|
|
parser.add_argument(
|
|
"--disable-tear-up-builds",
|
|
"-B",
|
|
action="store_true",
|
|
help="Disable the automatic build of Langkit_Support during the"
|
|
" testsuite tear_up step. This is used to speed up successive"
|
|
" testsuite runs during development.",
|
|
)
|
|
parser.add_argument(
|
|
"--pretty-print",
|
|
action="store_true",
|
|
help="Pretty-print generated source code.",
|
|
)
|
|
|
|
# Tests update
|
|
parser.add_argument(
|
|
"--rewrite",
|
|
"-r",
|
|
action="store_true",
|
|
help="Rewrite test baselines according to current output.",
|
|
)
|
|
|
|
def set_up(self):
|
|
super().set_up()
|
|
|
|
args = self.main.args
|
|
|
|
self.env.rewrite_baselines = args.rewrite
|
|
self.env.control_condition_env = {
|
|
"restricted_env": args.restricted_env,
|
|
"has_ocaml": not args.disable_ocaml,
|
|
"has_java": not args.disable_java,
|
|
"has_native_image": not args.disable_native_image,
|
|
"has_gdb": not args.disable_gdb,
|
|
"os": self.env.build.os.name,
|
|
"valgrind": args.valgrind,
|
|
}
|
|
|
|
if args.coverage:
|
|
# Create a directory that we'll use to:
|
|
#
|
|
# 1) collect coverage data for each testcase;
|
|
# 2) generate the HTML report.
|
|
self.env.coverage_dir = os.path.join(self.output_dir, "coverage")
|
|
self.env.langkit_support_coverage_dir = os.path.join(
|
|
self.env.coverage_dir, "langkit_support"
|
|
)
|
|
os.mkdir(self.env.coverage_dir)
|
|
os.mkdir(self.env.langkit_support_coverage_dir)
|
|
|
|
def run(name, args):
|
|
p = subprocess.run(
|
|
args,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
encoding="utf-8",
|
|
)
|
|
if p.returncode != 0:
|
|
raise RuntimeError(
|
|
"{} build failed (GPRbuild returned {})\n{}".format(
|
|
name, p.returncode, p.stdout
|
|
)
|
|
)
|
|
|
|
# Build Langkit_Support so that each testcase does not try to build it
|
|
# in parallel. To achieve this, we build+install it with all library
|
|
# kinds, and then update the environment so that testcases can assume
|
|
# it is installed.
|
|
if not args.disable_tear_up_builds:
|
|
langkit_root_dir = os.path.join(self.root_dir, "..")
|
|
self.env.langkit_support_project_file = os.path.join(
|
|
langkit_root_dir, "langkit", "support", "langkit_support.gpr"
|
|
)
|
|
install_prefix = os.path.join(self.working_dir, "install")
|
|
if os.path.exists(install_prefix):
|
|
shutil.rmtree(install_prefix)
|
|
|
|
gargs = ["-p", "-P", self.env.langkit_support_project_file]
|
|
cargs = ["-cargs", "-O0", "-g", "-gnatwae"]
|
|
if args.coverage:
|
|
gargs.append("--subdirs=gnatcov")
|
|
cargs.extend(["-fdump-scos", "-fpreserve-control-flow"])
|
|
|
|
for build in ("static", "relocatable"):
|
|
run(
|
|
f"Langkit support - build {build}",
|
|
["gprbuild"]
|
|
+ gargs
|
|
+ ["-XLIBRARY_TYPE={}".format(build)]
|
|
+ cargs,
|
|
)
|
|
|
|
run(
|
|
f"Langkit support - install {build}",
|
|
[
|
|
"gprinstall",
|
|
"-P",
|
|
self.env.langkit_support_project_file,
|
|
"-p",
|
|
"--sources-subdir=include/langkit_support",
|
|
"-XLIBRARY_TYPE={}".format(build),
|
|
"--prefix={}".format(install_prefix),
|
|
"--build-var=LIBRARY_TYPE",
|
|
"--build-name={}".format(build),
|
|
],
|
|
)
|
|
|
|
# Make the installed library available to all testcases
|
|
add_to_path("PATH", os.path.join(install_prefix, "bin"))
|
|
add_to_path("LD_LIBRARY_PATH", os.path.join(install_prefix, "lib"))
|
|
add_to_path(
|
|
"GPR_PROJECT_PATH",
|
|
os.path.join(install_prefix, "share", "gpr"),
|
|
)
|
|
|
|
# On GNU/Linux, the OCaml and Java bindings need to have access
|
|
# to the sigsegv_handler shared object. Build it and add it to
|
|
# the environment.
|
|
if (
|
|
not args.disable_ocaml or not args.disable_java
|
|
) and self.env.build.os.name == "linux":
|
|
sigsegv_handler_dir = os.path.join(
|
|
langkit_root_dir, "sigsegv_handler"
|
|
)
|
|
sigsegv_handler_prj = os.path.join(
|
|
sigsegv_handler_dir, "langkit_sigsegv_handler.gpr"
|
|
)
|
|
run(
|
|
"Langkit_SIGSEGV_Handler",
|
|
["gprbuild", "-p", "-P", sigsegv_handler_prj],
|
|
)
|
|
add_to_path(
|
|
"LD_LIBRARY_PATH", os.path.join(sigsegv_handler_dir, "lib")
|
|
)
|
|
|
|
# If Java is enabled and there is a Maven local repo, export it
|
|
if not args.disable_java:
|
|
if args.maven_executable:
|
|
os.environ["MAVEN_EXECUTABLE"] = args.maven_executable
|
|
if args.maven_local_repo:
|
|
os.environ["MAVEN_LOCAL_REPO"] = args.maven_local_repo
|
|
|
|
def adjust_dag_dependencies(self, dag):
|
|
# Crude assumption that should do the work.
|
|
#
|
|
# If there are more tests than testsuite jobs, assume running tests in
|
|
# parallel will keep all cores busy most of the time, so disable
|
|
# parallelism inside each tests. This is necessary to avoid exceeding
|
|
# our job limit in full testsuite runs.
|
|
#
|
|
# Otherwise, assume we are running only a handful of tests, in which
|
|
# case we want to run them as fast as possible (for dev convenience):
|
|
# forward testsuite parallelism inside each test.
|
|
self.env.inner_jobs = (
|
|
1
|
|
if len(dag.vertex_data) >= self.main.args.jobs
|
|
else self.main.args.jobs
|
|
)
|
|
|
|
def tear_down(self):
|
|
if self.main.args.coverage:
|
|
# Consolidate coverage data for each testcase and generate both a
|
|
# sumary textual report on the standard output and a detailed HTML
|
|
# report.
|
|
langkit_cov_files = glob.glob(
|
|
os.path.join(self.env.coverage_dir, "*.coverage")
|
|
)
|
|
|
|
lksp_cov_files = glob.glob(
|
|
os.path.join(self.env.coverage_dir, "*.trace")
|
|
)
|
|
|
|
if langkit_cov_files:
|
|
rcfile = os.path.join(self.root_dir, "..", "coverage.ini")
|
|
for argv in (
|
|
["combine"] + langkit_cov_files,
|
|
["report"],
|
|
["html", "-d.", "--title=Langkit coverage report"],
|
|
):
|
|
if argv[0] != "combine":
|
|
argv.append("--rcfile=" + rcfile)
|
|
subprocess.check_call(
|
|
["coverage"] + argv, cwd=self.env.coverage_dir
|
|
)
|
|
|
|
html_index = os.path.join(self.env.coverage_dir, "index.html")
|
|
assert os.path.exists(html_index)
|
|
print(
|
|
"Detailed HTML coverage report for Langkit available at:"
|
|
" {}".format(html_index)
|
|
)
|
|
else:
|
|
print(
|
|
"No test exercised Langkit: no coverage report to"
|
|
" produce"
|
|
)
|
|
|
|
if lksp_cov_files:
|
|
subprocess.check_call(
|
|
[
|
|
"gnatcov",
|
|
"coverage",
|
|
"-P",
|
|
self.env.langkit_support_project_file,
|
|
"--level=stmt+decision",
|
|
"--annotate=dhtml",
|
|
"--output-dir",
|
|
self.env.langkit_support_coverage_dir,
|
|
"--subdirs=gnatcov",
|
|
]
|
|
+ lksp_cov_files
|
|
)
|
|
print(
|
|
"HTML coverage report for Langkit_Support: {}".format(
|
|
os.path.join(
|
|
self.env.langkit_support_coverage_dir, "index.html"
|
|
)
|
|
)
|
|
)
|
|
else:
|
|
print(
|
|
"No test exercised Langkit_Support: no coverage report"
|
|
" to produce"
|
|
)
|
|
|
|
super().tear_down()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(LangkitTestsuite().testsuite_main())
|