From eb69b268d4af3dc749f8fcd2d72b5a6a9d796061 Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Tue, 5 Apr 2016 22:03:57 +0200 Subject: [PATCH 1/6] Revert "Fix Windows. Again." This reverts commit e522a9f2684e38955aefda3b4413a78997ccdbc9. --- mesonbuild/environment.py | 1 - run_tests.py | 14 ++------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index bfd1c356a..df848f877 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -58,7 +58,6 @@ class Environment(): log_dir = 'meson-logs' coredata_file = os.path.join(private_dir, 'coredata.dat') version_regex = '\d+(\.\d+)+(-[a-zA-Z0-9]+)?' - def __init__(self, source_dir, build_dir, main_script_file, options, original_cmd_line_args): assert(os.path.isabs(main_script_file)) assert(not os.path.islink(main_script_file)) diff --git a/run_tests.py b/run_tests.py index 52111ac41..978625ac4 100755 --- a/run_tests.py +++ b/run_tests.py @@ -309,20 +309,10 @@ def run_tests(extra_args): # and getting it wrong by not doing logical number sorting. (testnum, testbase) = os.path.split(t)[-1].split(' ', 1) testname = '%.3d %s' % (int(testnum), testbase) - # Windows errors out when calling result.result() below with - # a bizarre error about appending None to an array that comes - # from the standard library. This is probably either because I use - # XP or the Python version is old. Anyhow, fall back to immediate - # evaluation. This causes output not to be printed until the end, - # which is unfortunate but least it works. - if mesonlib.is_windows(): - result = run_test(skipped, t, extra_args, name != 'failing') - else: - result = executor.submit(run_test, skipped, t, extra_args, name != 'failing') + result = executor.submit(run_test, skipped, t, extra_args, name != 'failing') futures.append((testname, t, result)) for (testname, t, result) in futures: - if not mesonlib.is_windows(): # See above. - result = result.result() + result = result.result() if result is None: print('Skipping:', t) current_test = ET.SubElement(current_suite, 'testcase', {'name' : testname, From ff9d879eeab87df01d201ac9c09c094721dc256d Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Tue, 5 Apr 2016 22:37:01 +0200 Subject: [PATCH 2/6] properly fix Windows parallel tests by not using global variables The _run_test method uses several global variables (unity_flags, backend_flags, compile_commands, install_commands) which are not set when the method is run by the executor (at least on Windows). To resolve this, pass the variables as method parameters. --- run_tests.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/run_tests.py b/run_tests.py index 978625ac4..714ebe1a1 100755 --- a/run_tests.py +++ b/run_tests.py @@ -197,22 +197,21 @@ def parse_test_args(testdir): pass return args -def run_test(skipped, testdir, extra_args, should_succeed): +def run_test(skipped, testdir, extra_args, flags, compile_commands, install_commands, should_succeed): if skipped: return None with tempfile.TemporaryDirectory(prefix='b ', dir='.') as build_dir: with tempfile.TemporaryDirectory(prefix='i ', dir=os.getcwd()) as install_dir: try: - return _run_test(testdir, build_dir, install_dir, extra_args, should_succeed) + return _run_test(testdir, build_dir, install_dir, extra_args, flags, compile_commands, install_commands, should_succeed) finally: mlog.shutdown() # Close the log file because otherwise Windows wets itself. -def _run_test(testdir, test_build_dir, install_dir, extra_args, should_succeed): - global compile_commands +def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_commands, install_commands, should_succeed): test_args = parse_test_args(testdir) gen_start = time.time() gen_command = [meson_command, '--prefix', '/usr', '--libdir', 'lib', testdir, test_build_dir]\ - + unity_flags + backend_flags + test_args + extra_args + + flags + test_args + extra_args (returncode, stdo, stde) = run_configure_inprocess(gen_command) gen_time = time.time() - gen_start if not should_succeed: @@ -309,7 +308,7 @@ def run_tests(extra_args): # and getting it wrong by not doing logical number sorting. (testnum, testbase) = os.path.split(t)[-1].split(' ', 1) testname = '%.3d %s' % (int(testnum), testbase) - result = executor.submit(run_test, skipped, t, extra_args, name != 'failing') + result = executor.submit(run_test, skipped, t, extra_args, unity_flags + backend_flags, compile_commands, install_commands, name != 'failing') futures.append((testname, t, result)) for (testname, t, result) in futures: result = result.result() From 42e6b78351a05eeb284bb287aa2b5e4e84e1d94d Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Tue, 5 Apr 2016 22:38:27 +0200 Subject: [PATCH 3/6] do not print anything during tests Otherwise, output from parallel tests might interleave. Let the main loop handle printing of additional info. --- run_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_tests.py b/run_tests.py index 714ebe1a1..3a5ccbd42 100755 --- a/run_tests.py +++ b/run_tests.py @@ -245,7 +245,6 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c if returncode != 0: return TestResult('Running unit tests failed.', stdo, stde, gen_time, build_time, test_time) if len(install_commands) == 0: - print("Skipping install test") return TestResult('', '', '', gen_time, build_time, test_time) else: env = os.environ.copy() @@ -320,7 +319,8 @@ def run_tests(extra_args): global skipped_tests skipped_tests += 1 else: - print('Running test: ' + t) + without_install = "" if len(install_commands) > 0 else " (without install)" + print('Running test%s: %s' % (without_install, t)) conf_time += result.conftime build_time += result.buildtime test_time += result.testtime From 32e0973ef1e502e22e3c848dad5279b4d58e044b Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Wed, 6 Apr 2016 12:38:39 +0200 Subject: [PATCH 4/6] fix randomly failing test execution on Windows shutil.rmtree, which is used by tempfile.TemporaryDirectory, randomly fails on Windows, because the directory is not empty although it should be, because all files were deleted by shutil.rmtree internals before trying to remove the directory. A simple retry approach fixes the issue. --- run_tests.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/run_tests.py b/run_tests.py index 3a5ccbd42..f8d6aa2a5 100755 --- a/run_tests.py +++ b/run_tests.py @@ -42,6 +42,25 @@ class TestResult: self.buildtime = buildtime self.testtime = testtime +class AutoDeletedDir(): + def __init__(self, dir): + self.dir = dir + def __enter__(self): + os.makedirs(self.dir, exist_ok=True) + return self.dir + def __exit__(self, type, value, traceback): + # On Windows, shutil.rmtree fails sometimes, because 'the directory is not empty'. + # Retrying fixes this. + # That's why we don't use tempfile.TemporaryDirectory, but wrap the deletion in the AutoDeletedDir class. + retries = 5 + for i in range(0, retries): + try: + shutil.rmtree(self.dir) + return + except OSError: + if i == retries: + raise + passing_tests = 0 failing_tests = 0 skipped_tests = 0 @@ -200,8 +219,8 @@ def parse_test_args(testdir): def run_test(skipped, testdir, extra_args, flags, compile_commands, install_commands, should_succeed): if skipped: return None - with tempfile.TemporaryDirectory(prefix='b ', dir='.') as build_dir: - with tempfile.TemporaryDirectory(prefix='i ', dir=os.getcwd()) as install_dir: + with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir: + with AutoDeletedDir(tempfile.mkdtemp(prefix='i ', dir=os.getcwd())) as install_dir: try: return _run_test(testdir, build_dir, install_dir, extra_args, flags, compile_commands, install_commands, should_succeed) finally: From 5decddf09f47c9fddce91c6c3bb4b45dba084ef4 Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Wed, 6 Apr 2016 21:35:51 +0200 Subject: [PATCH 5/6] fix off by one, since range() does not include the end of the range --- run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index f8d6aa2a5..b23776cb9 100755 --- a/run_tests.py +++ b/run_tests.py @@ -58,7 +58,7 @@ class AutoDeletedDir(): shutil.rmtree(self.dir) return except OSError: - if i == retries: + if i == retries-1: raise passing_tests = 0 From 435700aeb00d9267b027801b688d677ccdc93903 Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Wed, 6 Apr 2016 21:39:51 +0200 Subject: [PATCH 6/6] add exponential backoff for deleting temp directories --- run_tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/run_tests.py b/run_tests.py index b23776cb9..556a0a527 100755 --- a/run_tests.py +++ b/run_tests.py @@ -60,6 +60,7 @@ class AutoDeletedDir(): except OSError: if i == retries-1: raise + time.sleep(0.1 * (2**i)) passing_tests = 0 failing_tests = 0