[lit] Move sharding logic into separate function

This commit is contained in:
Julian Lettner 2019-02-20 21:09:19 -08:00 committed by Julian Lettner
parent a88591cff4
commit f3ad8ae7b7
4 changed files with 36 additions and 23 deletions

View File

@ -198,6 +198,9 @@ def parse_args():
parser.error("--num-shards and --run-shard must be used together") parser.error("--num-shards and --run-shard must be used together")
if opts.runShard > opts.numShards: if opts.runShard > opts.numShards:
parser.error("--run-shard must be between 1 and --num-shards (inclusive)") parser.error("--run-shard must be between 1 and --num-shards (inclusive)")
opts.shard = (opts.runShard, opts.numShards)
else:
opts.shard = None
return opts return opts

View File

@ -68,24 +68,12 @@ def main(builtinParameters = {}):
if opts.filter: if opts.filter:
tests = [t for t in tests if opts.filter.search(t.getFullName())] tests = [t for t in tests if opts.filter.search(t.getFullName())]
order_tests(tests, opts) determine_order(tests, opts)
# Then optionally restrict our attention to a shard of the tests. # Then optionally restrict our attention to a shard of the tests.
if (opts.numShards is not None) or (opts.runShard is not None): if opts.shard:
num_tests = len(tests) (run, shards) = opts.shard
# Note: user views tests and shard numbers counting from 1. tests = filter_by_shard(tests, run, shards, litConfig)
test_ixs = range(opts.runShard - 1, num_tests, opts.numShards)
tests = [tests[i] for i in test_ixs]
# Generate a preview of the first few test indices in the shard
# to accompany the arithmetic expression, for clarity.
preview_len = 3
ix_preview = ", ".join([str(i+1) for i in test_ixs[:preview_len]])
if len(test_ixs) > preview_len:
ix_preview += ", ..."
litConfig.note('Selecting shard %d/%d = size %d/%d = tests #(%d*k)+%d = [%s]' %
(opts.runShard, opts.numShards,
len(tests), num_tests,
opts.numShards, opts.runShard, ix_preview))
# Finally limit the number of tests, if desired. # Finally limit the number of tests, if desired.
if opts.maxTests is not None: if opts.maxTests is not None:
@ -96,6 +84,7 @@ def main(builtinParameters = {}):
testing_time = run_tests(tests, litConfig, opts, numTotalTests) testing_time = run_tests(tests, litConfig, opts, numTotalTests)
# move into print_summary
if not opts.quiet: if not opts.quiet:
print('Testing Time: %.2fs' % (testing_time,)) print('Testing Time: %.2fs' % (testing_time,))
@ -160,21 +149,37 @@ def print_suites_or_tests(tests, opts):
for test in ts_tests: for test in ts_tests:
print(' %s' % (test.getFullName(),)) print(' %s' % (test.getFullName(),))
def order_tests(tests, opts): def determine_order(tests, opts):
if opts.shuffle: if opts.shuffle:
import random import random
random.shuffle(tests) random.shuffle(tests)
elif opts.incremental: elif opts.incremental:
def by_mtime(test):
try:
return os.path.getmtime(test.getFilePath())
except:
return 0
tests.sort(key=by_mtime, reverse=True) tests.sort(key=by_mtime, reverse=True)
else: else:
tests.sort(key=lambda t: (not t.isEarlyTest(), t.getFullName())) tests.sort(key=lambda t: (not t.isEarlyTest(), t.getFullName()))
def by_mtime(test): def filter_by_shard(tests, run, shards, litConfig):
fname = test.getFilePath() test_ixs = range(run - 1, len(tests), shards)
try: selected_tests = [tests[i] for i in test_ixs]
return os.path.getmtime(fname)
except: # For clarity, generate a preview of the first few test indices in the shard
return 0 # to accompany the arithmetic expression.
preview_len = 3
preview = ", ".join([str(i + 1) for i in test_ixs[:preview_len]])
if len(test_ixs) > preview_len:
preview += ", ..."
# TODO(python3): string interpolation
msg = 'Selecting shard {run}/{shards} = size {sel_tests}/{total_tests} = ' \
'tests #({shards}*k)+{run} = [{preview}]'.format(
run=run, shards=shards, sel_tests=len(selected_tests),
total_tests=len(tests), preview=preview)
litConfig.note(msg)
return selected_tests
def update_incremental_cache(test): def update_incremental_cache(test):
if not test.result.code.isFailure: if not test.result.code.isFailure:

View File

@ -66,6 +66,8 @@ class Run(object):
return end - start return end - start
# TODO(yln): as the comment says.. this is racing with the main thread waiting
# for results
def _process_result(self, test, result): def _process_result(self, test, result):
# Don't add any more test results after we've hit the maximum failure # 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 # count. Otherwise we're racing with the main thread, which is going
@ -147,6 +149,7 @@ class ParallelRun(Run):
pool.terminate() pool.terminate()
break break
# TODO(yln): interferes with progress bar
# Some tests use threads internally, and at least on Linux each of these # Some tests use threads internally, and at least on Linux each of these
# threads counts toward the current process limit. Try to raise the (soft) # threads counts toward the current process limit. Try to raise the (soft)
# process limit so that tests don't fail due to resource exhaustion. # process limit so that tests don't fail due to resource exhaustion.

View File

@ -116,6 +116,8 @@ def to_unicode(s):
return s return s
# TODO(yln): multiprocessing.cpu_count()
# TODO(python3): len(os.sched_getaffinity(0)) and os.cpu_count()
def detectCPUs(): def detectCPUs():
"""Detects the number of CPUs on a system. """Detects the number of CPUs on a system.