Skip to content

Commit

Permalink
Use test setups from the active (sub)project by default.
Browse files Browse the repository at this point in the history
Replace the logic where a test setup with no project specifier defaults to
the main project with one that takes the test setup from the same
(sub)project from where the to-be-executed test has been read from.
  • Loading branch information
trhd committed Feb 25, 2018
1 parent 4611097 commit 060560b
Showing 1 changed file with 44 additions and 40 deletions.
84 changes: 44 additions & 40 deletions mesonbuild/mtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import platform
import signal
import random
from copy import deepcopy

# GNU autotools interprets a return code of 77 from tests it executes to
# mean that the test should be skipped.
Expand Down Expand Up @@ -89,7 +90,7 @@ def determine_worker_count():
help='Do not redirect stdout and stderr')
parser.add_argument('-q', '--quiet', default=False, action='store_true',
help='Produce less output to the terminal.')
parser.add_argument('-t', '--timeout-multiplier', type=float, default=None,
parser.add_argument('-t', '--timeout-multiplier', type=float, default=1,
help='Define a multiplier for test timeout, for example '
' when running tests in particular conditions they might take'
' more time to execute.')
Expand Down Expand Up @@ -192,7 +193,17 @@ def __del__(self):
if self.jsonlogfile:
self.jsonlogfile.close()

def run_single_test(self, wrap, test):
def get_test_env(self, options, test):
if options.setup:
env = merge_suite_options(options, test)
else:
env = os.environ.copy()
if isinstance(test.env, build.EnvironmentVariables):
test.env = test.env.get_env(env)
env.update(test.env)
return env

def run_single_test(self, test):
if test.fname[0].endswith('.jar'):
cmd = ['java', '-jar'] + test.fname
elif not test.is_cross_built and run_with_mono(test.fname[0]):
Expand All @@ -215,24 +226,26 @@ def run_single_test(self, wrap, test):
stde = None
returncode = GNU_SKIP_RETURNCODE
else:
test_opts = deepcopy(self.options)
test_env = self.get_test_env(test_opts, test)
wrap = self.get_wrapper(test_opts)

if test_opts.gdb:
test.timeout = None

cmd = wrap + cmd + test.cmd_args + self.options.test_args
starttime = time.time()
child_env = os.environ.copy()
child_env.update(self.options.global_env.get_env(child_env))
if isinstance(test.env, build.EnvironmentVariables):
test.env = test.env.get_env(child_env)

child_env.update(test.env)
if len(test.extra_paths) > 0:
child_env['PATH'] = os.pathsep.join(test.extra_paths + ['']) + child_env['PATH']
test_env['PATH'] = os.pathsep.join(test.extra_paths + ['']) + test_env['PATH']

# If MALLOC_PERTURB_ is not set, or if it is set to an empty value,
# (i.e., the test or the environment don't explicitly set it), set
# it ourselves. We do this unconditionally for regular tests
# because it is extremely useful to have.
# Setting MALLOC_PERTURB_="0" will completely disable this feature.
if ('MALLOC_PERTURB_' not in child_env or not child_env['MALLOC_PERTURB_']) and not self.options.benchmark:
child_env['MALLOC_PERTURB_'] = str(random.randint(1, 255))
if ('MALLOC_PERTURB_' not in test_env or not test_env['MALLOC_PERTURB_']) and not self.options.benchmark:
test_env['MALLOC_PERTURB_'] = str(random.randint(1, 255))

setsid = None
stdout = None
Expand All @@ -247,15 +260,15 @@ def run_single_test(self, wrap, test):
p = subprocess.Popen(cmd,
stdout=stdout,
stderr=stderr,
env=child_env,
env=test_env,
cwd=test.workdir,
preexec_fn=setsid)
timed_out = False
kill_test = False
if test.timeout is None:
timeout = None
else:
timeout = test.timeout * self.options.timeout_multiplier
timeout = test.timeout * test_opts.timeout_multiplier
try:
(stdo, stde) = p.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
Expand Down Expand Up @@ -444,7 +457,7 @@ def open_log_files(self):
logfile_base = os.path.join(self.options.wd, 'meson-logs', self.options.logbase)

if self.options.wrapper:
namebase = os.path.basename(self.get_wrapper()[0])
namebase = os.path.basename(self.get_wrapper(self.options)[0])
elif self.options.setup:
namebase = self.options.setup.replace(":", "_")

Expand All @@ -459,16 +472,16 @@ def open_log_files(self):
self.logfile.write('Log of Meson test suite run on %s\n\n'
% datetime.datetime.now().isoformat())

def get_wrapper(self):
def get_wrapper(self, options):
wrap = []
if self.options.gdb:
if options.gdb:
wrap = ['gdb', '--quiet', '--nh']
if self.options.repeat > 1:
if options.repeat > 1:
wrap += ['-ex', 'run', '-ex', 'quit']
# Signal the end of arguments to gdb
wrap += ['--args']
if self.options.wrapper:
wrap += self.options.wrapper
if options.wrapper:
wrap += options.wrapper
assert(isinstance(wrap, list))
return wrap

Expand All @@ -487,7 +500,6 @@ def run_tests(self, tests):
futures = []
numlen = len('%d' % len(tests))
self.open_log_files()
wrap = self.get_wrapper()
startdir = os.getcwd()
if self.options.wd:
os.chdir(self.options.wd)
Expand All @@ -497,18 +509,15 @@ def run_tests(self, tests):
for i, test in enumerate(tests):
visible_name = self.get_pretty_suite(test)

if self.options.gdb:
test.timeout = None

if not test.is_parallel or self.options.gdb:
self.drain_futures(futures)
futures = []
res = self.run_single_test(wrap, test)
res = self.run_single_test(test)
self.print_stats(numlen, tests, visible_name, res, i)
else:
if not executor:
executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes)
f = executor.submit(self.run_single_test, wrap, test)
f = executor.submit(self.run_single_test, test)
futures.append((f, numlen, tests, visible_name, i))
if self.options.repeat > 1 and self.fail_count:
break
Expand Down Expand Up @@ -549,15 +558,19 @@ def list_tests(th):
for t in tests:
print(th.get_pretty_suite(t))

def merge_suite_options(options):
def merge_suite_options(options, test):
buildfile = os.path.join(options.wd, 'meson-private/build.dat')
with open(buildfile, 'rb') as f:
build = pickle.load(f)
if ":" not in options.setup:
options.setup = (build.subproject if build.subproject else build.project_name) + ":" + options.setup
if options.setup not in build.test_setups:
sys.exit('Unknown test setup: %s' % options.setup)
current = build.test_setups[options.setup]
if ":" in options.setup:
if options.setup not in build.test_setups:
sys.exit("Unknown test setup '%s'." % options.setup)
current = build.test_setups[options.setup]
else:
full_name = test.project_name + ":" + options.setup
if full_name not in build.test_setups:
sys.exit("Test setup '%s' not found from project '%s'." % (options.setup, test.project_name))
current = build.test_setups[full_name]
if not options.gdb:
options.gdb = current.gdb
if options.timeout_multiplier is None:
Expand All @@ -568,7 +581,7 @@ def merge_suite_options(options):
sys.exit('Conflict: both test setup and command line specify an exe wrapper.')
if options.wrapper is None:
options.wrapper = current.exe_wrapper
return current.env
return current.env.get_env(os.environ.copy())

def rebuild_all(wd):
if not os.path.isfile(os.path.join(wd, 'build.ninja')):
Expand All @@ -595,15 +608,6 @@ def run(args):
if options.benchmark:
options.num_processes = 1

if options.setup is not None:
global_env = merge_suite_options(options)
else:
global_env = build.EnvironmentVariables()
if options.timeout_multiplier is None:
options.timeout_multiplier = 1

setattr(options, 'global_env', global_env)

if options.verbose and options.quiet:
print('Can not be both quiet and verbose at the same time.')
return 1
Expand Down

0 comments on commit 060560b

Please sign in to comment.