Skip to content

Commit

Permalink
[lit] Send back whole lit.Test object from worker process
Browse files Browse the repository at this point in the history
In previous commits [1,2] I changed worker.py to only send back the test
result from the worker process instead of the whole test object.  This
was a mistake.  lit.Test contains fields (e.g., xfials, requires,
unsupported) that are only populated when we actually execute the test,
but are queried when we report the results in the parent process.  This
commit essentially reverts the following changes:

[1] a3d2f9b
[2] 17bb660
  • Loading branch information
yln authored and Julian Lettner committed Mar 31, 2020
1 parent 221fa96 commit 357a17e
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 34 deletions.
14 changes: 14 additions & 0 deletions llvm/utils/lit/lit/Test.py
Expand Up @@ -230,6 +230,20 @@ def __init__(self, suite, path_in_suite, config, file_path = None):
def setResult(self, result):
assert self.result is None, "result already set"
assert isinstance(result, Result), "unexpected result type"
try:
expected_to_fail = self.isExpectedToFail()
except ValueError as err:
# Syntax error in an XFAIL line.
result.code = UNRESOLVED
result.output = str(err)
else:
if expected_to_fail:
# pass -> unexpected pass
if result.code is PASS:
result.code = XPASS
# fail -> expected fail
elif result.code is FAIL:
result.code = XFAIL
self.result = result

def isFailure(self):
Expand Down
15 changes: 9 additions & 6 deletions llvm/utils/lit/lit/run.py
Expand Up @@ -67,15 +67,13 @@ def execute(self):

# TODO(yln): as the comment says.. this is racing with the main thread waiting
# for results
def _process_result(self, test, result):
def _process_completed(self, test):
# Don't add any more test results after we've hit the maximum failure
# count. Otherwise we're racing with the main thread, which is going
# to terminate the process pool soon.
if self.hit_max_failures:
return

test.setResult(result)

# Use test.isFailure() for correct XFAIL and XPASS handling
if test.isFailure():
self.failure_count += 1
Expand All @@ -93,7 +91,8 @@ def _execute(self, deadline):
# TODO(yln): ignores deadline
for test in self.tests:
result = lit.worker._execute(test, self.lit_config)
self._process_result(test, result)
test.setResult(result)
self._process_completed(test)
if self.hit_max_failures:
break

Expand Down Expand Up @@ -121,10 +120,14 @@ def _execute(self, deadline):

self._install_win32_signal_handler(pool)

def process_completed(test, idx):
self.tests[idx] = test
self._process_completed(test)

async_results = [
pool.apply_async(lit.worker.execute, args=[test],
callback=lambda r, t=test: self._process_result(t, r))
for test in self.tests]
callback=lambda t, i=idx: process_completed(t, i))
for idx, test in enumerate(self.tests)]
pool.close()

for ar in async_results:
Expand Down
39 changes: 11 additions & 28 deletions llvm/utils/lit/lit/worker.py
Expand Up @@ -11,16 +11,19 @@
import lit.Test
import lit.util


_lit_config = None
_parallelism_semaphores = None


def initialize(lit_config, parallelism_semaphores):
"""Copy data shared by all test executions into worker processes"""
global _lit_config
global _parallelism_semaphores
_lit_config = lit_config
_parallelism_semaphores = parallelism_semaphores


def execute(test):
"""Run one test in a multiprocessing.Pool
Expand All @@ -31,14 +34,17 @@ def execute(test):
to copy.
"""
try:
return _execute_in_parallelism_group(test, _lit_config,
_parallelism_semaphores)
result = _execute_in_parallelism_group(test, _lit_config,
_parallelism_semaphores)
test.setResult(result)
return test
except KeyboardInterrupt:
# If a worker process gets an interrupt, abort it immediately.
lit.util.abort_now()
except:
traceback.print_exc()


def _execute_in_parallelism_group(test, lit_config, parallelism_semaphores):
pg = test.config.parallelism_group
if callable(pg):
Expand All @@ -56,39 +62,16 @@ def _execute_in_parallelism_group(test, lit_config, parallelism_semaphores):


def _execute(test, lit_config):
"""Execute one test"""
start = time.time()
result = _execute_test_handle_errors(test, lit_config)
end = time.time()

result.elapsed = end - start
resolve_result_code(result, test)

result.elapsed = time.time() - start
return result


# TODO(yln): is this the right place to deal with this?
# isExpectedToFail() only works after the test has been executed.
def resolve_result_code(result, test):
try:
expected_to_fail = test.isExpectedToFail()
except ValueError as e:
# Syntax error in an XFAIL line.
result.code = lit.Test.UNRESOLVED
result.output = str(e)
else:
if expected_to_fail:
# pass -> unexpected pass
if result.code is lit.Test.PASS:
result.code = lit.Test.XPASS
# fail -> expected fail
if result.code is lit.Test.FAIL:
result.code = lit.Test.XFAIL


def _execute_test_handle_errors(test, lit_config):
try:
return _adapt_result(test.config.test_format.execute(test, lit_config))
result = test.config.test_format.execute(test, lit_config)
return _adapt_result(result)
except KeyboardInterrupt:
raise
except:
Expand Down

0 comments on commit 357a17e

Please sign in to comment.