Skip to content

Commit

Permalink
[libc++] Execute tests from the Lit execution root instead of the tes…
Browse files Browse the repository at this point in the history
…t tree

Instead of executing tests from within the libc++ test suite, we execute
them from the Lit execution directory. However, since some tests have
file dependencies, we must copy those dependencies to the execution
directory where they are executed.

This has the major benefit that if a test modifies a file (whether it
is wanted or not), other tests will not see those modifications. This
is good because current tests assume that input data is never modified,
however this could be an incorrect assumption if some test does not
behave properly.
  • Loading branch information
ldionne committed Apr 2, 2020
1 parent df88d80 commit ff09135
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 13 deletions.
1 change: 0 additions & 1 deletion libcxx/utils/libcxx/test/config.py
Expand Up @@ -1050,7 +1050,6 @@ def configure_substitutions(self):
exec_args.append('--host {}'.format(self.executor.user_prefix + self.executor.host))
executor = os.path.join(self.libcxx_src_root, 'utils', 'ssh.py')
else:
exec_args.append('--working_directory "%S"')
executor = os.path.join(self.libcxx_src_root, 'utils', 'run.py')
sub.append(('%{exec}', '{} {} {} -- '.format(pipes.quote(sys.executable),
pipes.quote(executor),
Expand Down
7 changes: 7 additions & 0 deletions libcxx/utils/libcxx/test/executor.py
Expand Up @@ -10,6 +10,7 @@
import os
import posixpath
import ntpath
import shutil

from libcxx.test import tracing
from libcxx.util import executeCommand
Expand Down Expand Up @@ -61,6 +62,12 @@ def run(self, exe_path, cmd=None, work_dir='.', file_deps=None, env=None):
if env:
env = self.merge_environments(os.environ, env)

for dep in file_deps:
if os.path.isdir(dep):
shutil.copytree(dep, os.path.join(work_dir, os.path.basename(dep)), symlinks=True)
else:
shutil.copy2(dep, work_dir)

out, err, rc = executeCommand(cmd, cwd=work_dir, env=env)
return (cmd, out, err, rc)

Expand Down
15 changes: 11 additions & 4 deletions libcxx/utils/libcxx/test/format.py
Expand Up @@ -9,6 +9,8 @@
import copy
import errno
import os
import shutil
import tempfile
import time
import random

Expand Down Expand Up @@ -209,16 +211,21 @@ def _evaluate_pass_test(self, test, tmpBase, lit_config,
report += "Compilation failed unexpectedly!"
return lit.Test.Result(lit.Test.FAIL, report)
# Run the test
local_cwd = os.path.dirname(source_path)
env = None
if self.exec_env:
env = self.exec_env

max_retry = test.allowed_retries + 1
for retry_count in range(max_retry):
cmd, out, err, rc = self.executor.run(exec_path, [exec_path],
local_cwd, data_files,
env)
# Create a temporary directory just for that test and run the
# test in that directory
try:
execDirTmp = tempfile.mkdtemp(dir=execDir)
cmd, out, err, rc = self.executor.run(exec_path, [exec_path],
execDirTmp, data_files,
env)
finally:
shutil.rmtree(execDirTmp)
report = "Compiled With: '%s'\n" % ' '.join(compile_cmd)
report += libcxx.util.makeReport(cmd, out, err, rc)
if rc == 0:
Expand Down
26 changes: 18 additions & 8 deletions libcxx/utils/run.py
Expand Up @@ -14,14 +14,15 @@

import argparse
import os
import shutil
import subprocess
import sys
import tempfile


def main():
parser = argparse.ArgumentParser()
parser.add_argument('--codesign_identity', type=str, required=False)
parser.add_argument('--working_directory', type=str, required=True)
parser.add_argument('--dependencies', type=str, nargs='*', required=True)
parser.add_argument('--env', type=str, nargs='*', required=True)
(args, remaining) = parser.parse_known_args(sys.argv[1:])
Expand All @@ -42,14 +43,23 @@ def main():
# Extract environment variables into a dictionary
env = {k : v for (k, v) in map(lambda s: s.split('=', 1), args.env)}

# Ensure the file dependencies exist
for file in args.dependencies:
if not os.path.exists(file):
sys.stderr.write('Missing file {} marked as a dependency of a test'.format(file))
exit(1)
try:
tmpDir = tempfile.mkdtemp()

# Run the executable with the given environment in the given working directory
return subprocess.call(' '.join(remaining), cwd=args.working_directory, env=env, shell=True)
# Ensure the file dependencies exist and copy them to a temporary directory.
for dep in args.dependencies:
if not os.path.exists(dep):
sys.stderr.write('Missing file or directory "{}" marked as a dependency of a test'.format(dep))
exit(1)
if os.path.isdir(dep):
shutil.copytree(dep, os.path.join(tmpDir, os.path.basename(dep)), symlinks=True)
else:
shutil.copy2(dep, tmpDir)

# Run the executable with the given environment in the temporary directory.
return subprocess.call(' '.join(remaining), cwd=tmpDir, env=env, shell=True)
finally:
shutil.rmtree(tmpDir)

if __name__ == '__main__':
exit(main())

0 comments on commit ff09135

Please sign in to comment.