From b8b05497af4d6f2189fe35eb04c514b5a42893b4 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 12 Mar 2017 19:11:42 +0530 Subject: tests/37 has header: Also test the fallback include check Also forcibly undefine __has_include and test that the fallback include check in cc.has_header() works. This is important because all the latest compilers support it now and we might have no test coverage at all by accident. GCC 5, ICC 17, Clang 3.8, and VS2015 Update 2 already support it. --- test cases/common/37 has header/meson.build | 68 +++++++++++++++-------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/test cases/common/37 has header/meson.build b/test cases/common/37 has header/meson.build index 9709183..c87f244 100644 --- a/test cases/common/37 has header/meson.build +++ b/test cases/common/37 has header/meson.build @@ -9,39 +9,41 @@ configure_file(input : non_existant_header, output : non_existant_header, configuration : configuration_data()) -foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')] - if not comp.has_header('stdio.h') - error('Stdio missing.') - endif - - # stdio.h doesn't actually need stdlib.h, but just test that setting the - # prefix does not result in an error. - if not comp.has_header('stdio.h', prefix : '#include ') - error('Stdio missing.') - endif - - # XInput.h should not require type definitions from windows.h, but it does - # require macro definitions. Specifically, it requires an arch setting for - # VS2015 at least. - # We only do this check on MSVC because MinGW often defines its own wrappers - # that pre-include windows.h - if comp.get_id() == 'msvc' - if not comp.has_header('XInput.h', prefix : '#include ') - error('XInput.h should not be missing on Windows') +# Test that the fallback to __has_include also works on all compilers +args = [[], ['-U__has_include']] + +foreach arg : args + foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')] + assert(comp.has_header('stdio.h', args : arg), 'Stdio missing.') + + # stdio.h doesn't actually need stdlib.h, but just test that setting the + # prefix does not result in an error. + assert(comp.has_header('stdio.h', prefix : '#include ', args : arg), + 'Stdio missing.') + + # XInput.h should not require type definitions from windows.h, but it does + # require macro definitions. Specifically, it requires an arch setting for + # VS2015 at least. + # We only do this check on MSVC because MinGW often defines its own wrappers + # that pre-include windows.h + if comp.get_id() == 'msvc' + assert(comp.has_header('XInput.h', prefix : '#include ', args : arg), + 'XInput.h should not be missing on Windows') + assert(comp.has_header('XInput.h', prefix : '#define _X86_', args : arg), + 'XInput.h should not need windows.h') endif - if not comp.has_header('XInput.h', prefix : '#define _X86_') - error('XInput.h should not need windows.h') + + # Test that the following GCC bug doesn't happen: + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80005 + # https://github.com/mesonbuild/meson/issues/1458 + if host_system == 'linux' + assert(comp.has_header('linux/if.h', args : arg), + 'Could not find ') endif - endif - - # Test that the following GCC bug doesn't happen: - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80005 - # https://github.com/mesonbuild/meson/issues/1458 - if host_system == 'linux' - assert(comp.has_header('linux/if.h'), 'Could not find ') - endif - - # This header exists in the source and the builddir, but we still must not - # find it since we are looking in the system directories. - assert(not comp.has_header(non_existant_header), 'Found non-existant header.') + + # This header exists in the source and the builddir, but we still must not + # find it since we are looking in the system directories. + assert(not comp.has_header(non_existant_header, args : arg), + 'Found non-existant header.') + endforeach endforeach -- cgit v1.1 From 50e0543cd7675fcffce3876d2db3745134304c52 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 12 Mar 2017 22:20:48 +0530 Subject: unittests: Print output for failing commands Because we are using check_output, if the command fails no output will be printed at all. So, we use subprocess.run instead. Also, on configure failures, print the meson-log.txt instead of stdout. --- run_unittests.py | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/run_unittests.py b/run_unittests.py index 82c1b80..9945057 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -356,16 +356,31 @@ class BasePlatformTests(unittest.TestCase): self.unit_test_dir = os.path.join(src_root, 'test cases/unit') self.orig_env = os.environ.copy() + def _print_meson_log(self): + log = os.path.join(self.logdir, 'meson-log.txt') + if not os.path.isfile(log): + print("{!r} doesn't exist".format(log)) + return + with open(log, 'r', encoding='utf-8') as f: + print(f.read()) + def tearDown(self): shutil.rmtree(self.builddir) os.environ = self.orig_env super().tearDown() def _run(self, command): - output = subprocess.check_output(command, stderr=subprocess.STDOUT, - env=os.environ.copy(), - universal_newlines=True) + ''' + Run a command while printing the stdout and stderr to stdout, + and also return a copy of it + ''' + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, env=os.environ.copy(), + universal_newlines=True) + output = p.communicate()[0] print(output) + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, command) return output def init(self, srcdir, extra_args=None, default_args=True): @@ -375,7 +390,11 @@ class BasePlatformTests(unittest.TestCase): if default_args: args += ['--prefix', self.prefix, '--libdir', self.libdir] - self._run(self.meson_command + args + extra_args) + try: + self._run(self.meson_command + args + extra_args) + except: + self._print_meson_log() + raise self.privatedir = os.path.join(self.builddir, 'meson-private') def build(self, extra_args=None): @@ -394,11 +413,11 @@ class BasePlatformTests(unittest.TestCase): self._run(self.ninja_command + ['uninstall']) def run_target(self, target): - output = subprocess.check_output(self.ninja_command + [target], - stderr=subprocess.STDOUT, - universal_newlines=True) - print(output) - return output + ''' + Run a Ninja target while printing the stdout and stderr to stdout, + and also return a copy of it + ''' + return self._run(self.ninja_command + [target]) def setconf(self, arg, will_build=True): # This is needed to increase the difference between build.ninja's -- cgit v1.1 From 665dd78ffed601f09db4cff784dce6e06ce1b797 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 14 Mar 2017 12:15:56 +0530 Subject: project tests: Print meson log instead of stdout for configure failures --- run_project_tests.py | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/run_project_tests.py b/run_project_tests.py index 1457432..3684de5 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -18,6 +18,7 @@ from glob import glob import os, subprocess, shutil, sys, signal from io import StringIO from ast import literal_eval +from enum import Enum import tempfile import mesontest from mesonbuild import environment @@ -33,9 +34,17 @@ import concurrent.futures as conc from mesonbuild.coredata import backendlist +class BuildStep(Enum): + configure = 1 + build = 2 + test = 3 + install = 4 + clean = 5 + class TestResult: - def __init__(self, msg, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0): + def __init__(self, msg, step, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0): self.msg = msg + self.step = step self.stdo = stdo self.stde = stde self.mlog = mlog @@ -74,6 +83,7 @@ class AutoDeletedDir: failing_logs = [] print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ do_debug = not {'MESON_PRINT_TEST_OUTPUT', 'TRAVIS', 'APPVEYOR'}.isdisjoint(os.environ) +no_meson_log_msg = 'No meson-log.txt found.' meson_command = os.path.join(os.getcwd(), 'meson') if not os.path.exists(meson_command): @@ -270,14 +280,14 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c with open(logfile, errors='ignore') as f: mesonlog = f.read() except Exception: - mesonlog = 'No meson-log.txt found.' + mesonlog = no_meson_log_msg gen_time = time.time() - gen_start if should_fail == 'meson': if returncode != 0: - return TestResult('', stdo, stde, mesonlog, gen_time) - return TestResult('Test that should have failed succeeded', stdo, stde, mesonlog, gen_time) + return TestResult('', BuildStep.configure, stdo, stde, mesonlog, gen_time) + return TestResult('Test that should have failed succeeded', BuildStep.configure, stdo, stde, mesonlog, gen_time) if returncode != 0: - return TestResult('Generating the build system failed.', stdo, stde, mesonlog, gen_time) + return TestResult('Generating the build system failed.', BuildStep.configure, stdo, stde, mesonlog, gen_time) # Build with subprocess comp = get_compile_commands_for_dir(compile_commands, test_build_dir) build_start = time.time() @@ -287,10 +297,10 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stde += e if should_fail == 'build': if pc.returncode != 0: - return TestResult('', stdo, stde, mesonlog, gen_time) - return TestResult('Test that should have failed to build succeeded', stdo, stde, mesonlog, gen_time) + return TestResult('', BuildStep.build, stdo, stde, mesonlog, gen_time) + return TestResult('Test that should have failed to build succeeded', BuildStep.build, stdo, stde, mesonlog, gen_time) if pc.returncode != 0: - return TestResult('Compiling source code failed.', stdo, stde, mesonlog, gen_time, build_time) + return TestResult('Compiling source code failed.', BuildStep.build, stdo, stde, mesonlog, gen_time, build_time) # Touch the meson.build file to force a regenerate so we can test that # regeneration works. We need to sleep for 0.2s because Ninja tracks mtimes # at a low resolution: https://github.com/ninja-build/ninja/issues/371 @@ -304,12 +314,12 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stde += tstde if should_fail == 'test': if returncode != 0: - return TestResult('', stdo, stde, mesonlog, gen_time) - return TestResult('Test that should have failed to run unit tests succeeded', stdo, stde, mesonlog, gen_time) + return TestResult('', BuildStep.test, stdo, stde, mesonlog, gen_time) + return TestResult('Test that should have failed to run unit tests succeeded', BuildStep.test, stdo, stde, mesonlog, gen_time) if returncode != 0: - return TestResult('Running unit tests failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult('Running unit tests failed.', BuildStep.test, stdo, stde, mesonlog, gen_time, build_time, test_time) if len(install_commands) == 0: - return TestResult('', '', '', gen_time, build_time, test_time) + return TestResult('', BuildStep.install, '', '', mesonlog, gen_time, build_time, test_time) env = os.environ.copy() env['DESTDIR'] = install_dir # Install with subprocess @@ -317,7 +327,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stdo += o stde += e if pi.returncode != 0: - return TestResult('Running install failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult('Running install failed.', BuildStep.install, stdo, stde, mesonlog, gen_time, build_time, test_time) if len(clean_commands) != 0: env = os.environ.copy() # Clean with subprocess @@ -325,8 +335,8 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stdo += o stde += e if pi.returncode != 0: - return TestResult('Running clean failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) - return TestResult(validate_install(testdir, install_dir), stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult(validate_install(testdir, install_dir), BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time) def gather_tests(testdir): tests = [t.replace('\\', '/').split('/', 2)[2] for t in glob(os.path.join(testdir, '*'))] @@ -441,10 +451,16 @@ def run_tests(all_tests, log_name_base, extra_args): else: without_install = "" if len(install_commands) > 0 else " (without install)" if result.msg != '': - print('Failed test%s: %s' % (without_install, t)) + print('Failed test{} during {}: {!r}'.format(without_install, result.step.name, t)) print('Reason:', result.msg) failing_tests += 1 - failing_logs.append(result.stdo) + if result.step == BuildStep.configure and result.mlog != no_meson_log_msg: + # For configure failures, instead of printing stdout, + # print the meson log if available since it's a superset + # of stdout and often has very useful information. + failing_logs.append(result.mlog) + else: + failing_logs.append(result.stdo) failing_logs.append(result.stde) else: print('Succeeded test%s: %s' % (without_install, t)) -- cgit v1.1 From f427603aa26e6db9dfeaff06eb6a4bb9967f25cc Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 14 Mar 2017 12:33:17 +0530 Subject: tests/37 has header: Disable fallback test on macOS --- test cases/common/37 has header/meson.build | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test cases/common/37 has header/meson.build b/test cases/common/37 has header/meson.build index c87f244..4299ce5 100644 --- a/test cases/common/37 has header/meson.build +++ b/test cases/common/37 has header/meson.build @@ -10,7 +10,12 @@ configure_file(input : non_existant_header, configuration : configuration_data()) # Test that the fallback to __has_include also works on all compilers -args = [[], ['-U__has_include']] +if host_system != 'darwin' + args = [[], ['-U__has_include']] +else + # On Darwin's clang you can't redefine builtin macros so the above doesn't work + args = [[]] +endif foreach arg : args foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')] -- cgit v1.1