1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
|
import logging
import os
import subprocess
from e3.fs import mkdir
from e3.os.process import Run, get_rlimit
from e3.testsuite import TestAbort
from e3.testsuite.driver import TestDriver
from e3.testsuite.driver.classic import (
ClassicTestDriver, ProcessResult, TestAbortWithFailure
)
from e3.testsuite.process import check_call
from e3.testsuite.result import Log, TestStatus
# Root directory of respectively the testsuite and the gnatcoll
# repository.
TESTSUITE_ROOT_DIR = os.path.dirname(
os.path.dirname(os.path.abspath(__file__)))
GNATCOLL_ROOT_DIR = os.path.dirname(TESTSUITE_ROOT_DIR)
DEFAULT_TIMEOUT = 5 * 60 # 5 minutes
def make_gnatcoll(work_dir, debug=False, gcov=False, gnatcov=False):
"""Build gnatcoll core with or without gcov instrumentation.
:param str work_dir: Working directory. GNATcoll is built in `build` subdir
and installed in `install` subdir.
:param bool debug: Whether to build GNATCOLL in debug mode. Otherwise, use
the prod mode. Note that gcov and gnatcov modes automatically enable
debug mode.
:param bool gcov: If true, build GNATCOLL with gcov instrumentation in
debgu mode.
:param bool gnatcov: If True, build GNATCOLL with the compile options that
GNATcoverage require in debug mode.
:return: A triplet (project path, source path, object path).
:rtype: (str, str, str)
:raise AssertError: In case compilation of installation fails.
"""
assert not (gcov and gnatcov)
if gcov:
tag = ' (gcov)'
elif gnatcov:
tag = ' (gnatcov)'
else:
tag = ''
logging.info('Compiling gnatcoll{}'.format(tag))
# Create build tree structure
build_dir = os.path.join(work_dir, 'build')
install_dir = os.path.join(work_dir, 'install')
mkdir(build_dir)
mkdir(install_dir)
# Compute make invocation
make_gnatcoll_cmd = [
'make', '-f', os.path.join(GNATCOLL_ROOT_DIR, 'Makefile'),
'ENABLE_SHARED=no',
'BUILD={}'.format('DEBUG' if debug or gcov or gnatcov else 'PROD')]
if gcov:
make_gnatcoll_cmd += [
'GPRBUILD_OPTIONS=-cargs -fprofile-arcs -ftest-coverage'
' -cargs:Ada -gnatwn'
' -gargs']
elif gnatcov:
make_gnatcoll_cmd += [
'GPRBUILD_OPTIONS=-cargs -fdump-scos -fpreserve-control-flow'
' -gargs']
# Build & Install
p = Run(make_gnatcoll_cmd, cwd=build_dir, timeout=DEFAULT_TIMEOUT)
assert p.status == 0, "gnatcoll build failed:\n%s" % p.out
p = Run(make_gnatcoll_cmd + ['prefix=%s' % install_dir, 'install'],
cwd=build_dir, timeout=DEFAULT_TIMEOUT)
assert p.status == 0, "gnatcoll installation failed:\n%s" % p.out
return (os.path.join(install_dir, 'share', 'gpr'),
os.path.join(install_dir, 'include', 'gnatcoll'),
os.path.join(build_dir, 'obj', 'gnatcoll', 'static'))
def gprbuild(driver,
project_file=None,
cwd=None,
gcov=False,
scenario=None,
gpr_project_path=None,
timeout=DEFAULT_TIMEOUT,
**kwargs):
"""Launch gprbuild.
:param project_file: project file to compile. If None, we looks first for
a test.gpr in the test dir and otherwise fallback on the common
test.gpr project of the support subdir of the testsuite.
:type project_file: str
:param cwd: directory in which to run gprbuild. If None the gprbuild build
is run in the default working dir for the test.
:type cwd: str | None
:param gcov: if True link with gcov libraries
:type gcov: bool
:param scenario: scenario variable values
:type scenario: dict
:param gpr_project_path: if not None prepent this value to GPR_PROJECT_PATH
:type gpr_project_path: None | str
:param kwargs: additional keyword arguements are passed to
e3.testsuite.process.check_call function
:return: True on successful completion
:rtype: bool
"""
if scenario is None:
scenario = {}
if project_file is None:
project_file = os.path.join(driver.test_env['test_dir'],
'test.gpr')
if not os.path.isfile(project_file):
project_file = os.path.join(TESTSUITE_ROOT_DIR,
'support', 'test.gpr')
scenario['TEST_SOURCES'] = driver.test_env['test_dir']
if cwd is None:
cwd = driver.test_env['working_dir']
mkdir(cwd)
gprbuild_cmd = [
'gprbuild', '--relocate-build-tree', '-p', '-P', project_file]
for k, v in scenario.items():
gprbuild_cmd.append('-X%s=%s' % (k, v))
if gcov:
gprbuild_cmd += ['-largs', '-lgcov', '-cargs',
'-fprofile-arcs', '-ftest-coverage', '-g']
elif driver.env.gnatcov:
# TODO: GNATcoverage relies on debug info to do its magic. It needs
# consistent paths to source files in the debug info, so do not build
# tests with debug info, as they will reference installed sources
# (while GNATCOLL objects reference original sources).
gprbuild_cmd += ['-g0']
# Adjust process environment
env = kwargs.pop('env', None)
ignore_environ = kwargs.pop('ignore_environ', True)
if env is None:
env = {}
ignore_environ = False
if gpr_project_path:
new_gpr_path = gpr_project_path
if 'GPR_PROJECT_PATH' in os.environ:
new_gpr_path += os.path.pathsep + os.environ['GPR_PROJECT_PATH']
env['GPR_PROJECT_PATH'] = new_gpr_path
check_call(
driver,
gprbuild_cmd,
cwd=cwd,
env=env,
ignore_environ=ignore_environ,
timeout=timeout,
**kwargs)
# If we get there it means the build succeeded.
return True
def bin_check_call(driver, cmd, test_name=None, result=None, timeout=None,
env=None, cwd=None):
if cwd is None and "working_dir" in driver.test_env:
cwd = driver.test_env["working_dir"]
if result is None:
result = driver.result
if test_name is None:
test_name = driver.test_name
if timeout is not None:
cmd = [get_rlimit(), str(timeout)] + cmd
# Use directly subprocess instead of e3.os.process.Run, since the latter
# does not handle binary outputs.
subp = subprocess.Popen(
cmd, cwd=cwd, env=env, stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT
)
stdout, _ = subp.communicate()
process = ProcessResult(subp.returncode, stdout)
result.processes.append(
{
"output": Log(stdout),
"status": process.status,
"cmd": cmd,
"timeout": timeout,
"env": env,
"cwd": cwd,
}
)
# Append the status code and process output to the log to ease post-mortem
# investigation.
result.log += "Status code: {}\n".format(process.status)
result.log += "Output:\n"
try:
stdout = stdout.decode('utf-8')
except UnicodeDecodeError:
stdout = str(stdout)
result.log += stdout
if process.status != 0:
if isinstance(driver, ClassicTestDriver):
raise TestAbortWithFailure('command call fails')
else:
result.set_status(TestStatus.FAIL, "command call fails")
driver.push_result(result)
raise TestAbort
return process
def run_test_program(driver, cmd, test_name=None, result=None, **kwargs):
"""
Run a test program. This dispatches to running it under Valgrind or
"gnatcov run", depending on the testsuite options.
"""
from drivers.gnatcov import gnatcov_run
from drivers.valgrind import check_call_valgrind
if driver.env.valgrind:
wrapper = check_call_valgrind
elif driver.env.gnatcov:
wrapper = gnatcov_run
else:
wrapper = bin_check_call
return wrapper(driver, cmd, test_name, result, **kwargs)
class GNATcollTestDriver(TestDriver):
"""Abstract class to share some common facilities."""
DEFAULT_TIMEOUT = 5 * 60 # 5 minutes
@property
def process_timeout(self):
"""Timeout (in seconds) for subprocess to launch."""
return self.test_env.get('timeout', self.DEFAULT_TIMEOUT)
def run_test_program(self, cmd, test_name=None, result=None, **kwargs):
return run_test_program(self, cmd, test_name, result, **kwargs)
|