mirror of https://github.com/tc39/test262.git
Enable parallel test execution in console runner
Adds a `-j`/`--workers-count` parameter to `tools/packaging/test262.py`, defaulting to `[number of cores] - 1`. Speeds up running the test suite by about ~3x on my 4-core machine, with the SpiderMonkey shell. This could certainly be optimized more by just appending test results to per-thread lists and merging them at the end, but it's better than nothing.
This commit is contained in:
parent
26aeed1428
commit
7ae29d49ae
|
@ -40,6 +40,7 @@ Name | Action
|
||||||
-----|-------
|
-----|-------
|
||||||
-h, --help | displays a brief help message
|
-h, --help | displays a brief help message
|
||||||
--command=COMMAND | **required** command which invokes javascript engine to be tested
|
--command=COMMAND | **required** command which invokes javascript engine to be tested
|
||||||
|
-j, --workers-count | Number of tests to run in parallel (defaults to number of cores - 1)
|
||||||
--tests=TESTS | path to the test suite; default is current directory
|
--tests=TESTS | path to the test suite; default is current directory
|
||||||
--cat | don't execute tests, just print code that would be run
|
--cat | don't execute tests, just print code that would be run
|
||||||
--summary | generate a summary at end of execution
|
--summary | generate a summary at end of execution
|
||||||
|
@ -49,9 +50,9 @@ Name | Action
|
||||||
--unmarked_default=MODE | mode to use for tests that are not marked **onlyStrict** or **noStrict** ; MODE can be `strict` or `non_strict` or `both`
|
--unmarked_default=MODE | mode to use for tests that are not marked **onlyStrict** or **noStrict** ; MODE can be `strict` or `non_strict` or `both`
|
||||||
--logname=LOGNAME | write output to file (in addition to stdout)
|
--logname=LOGNAME | write output to file (in addition to stdout)
|
||||||
--junitname=JUNITNAME | write test results to file in JUnit XML format
|
--junitname=JUNITNAME | write test results to file in JUnit XML format
|
||||||
--loglevel=LOGLEVEL | set log level, primarily useful for debugging `test262.py`
|
--loglevel=LOGLEVEL | set log level, primarily useful for debugging `test262.py`
|
||||||
--print-handle=FUNC | enable async test logging via javascript function e.g., `console.log`
|
--print-handle=FUNC | enable async test logging via javascript function e.g., `console.log`
|
||||||
|
|
||||||
### Usage Notes
|
### Usage Notes
|
||||||
|
|
||||||
Non-option arguments are used as filters to match test names. If no filters are found, the whole test suite is run.
|
Non-option arguments are used as filters to match test names. If no filters are found, the whole test suite is run.
|
||||||
|
@ -66,7 +67,7 @@ The COMMAND argument can be a quoted string. This is useful when testing ECMASc
|
||||||
|
|
||||||
```
|
```
|
||||||
$ test262.py --command="node --harmony" es6
|
$ test262.py --command="node --harmony" es6
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Async Tests
|
#### Async Tests
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ JavaScriptCore<sup>2</sup> | jsc | print
|
||||||
|
|
||||||
***Notes:***
|
***Notes:***
|
||||||
1. As of 2014-Jul-23, SpiderMonkey does not support Promise in the `js` executable ([see bug 911216](https://bugzilla.mozilla.org/show_bug.cgi?id=911216) )
|
1. As of 2014-Jul-23, SpiderMonkey does not support Promise in the `js` executable ([see bug 911216](https://bugzilla.mozilla.org/show_bug.cgi?id=911216) )
|
||||||
2. As of 2014-Jul-23, JavaScriptCore does not support Promise in the `jsc` executable
|
2. As of 2014-Jul-23, JavaScriptCore does not support Promise in the `jsc` executable
|
||||||
|
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
|
@ -18,6 +18,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
import datetime
|
import datetime
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -54,6 +55,8 @@ EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST]
|
||||||
def BuildOptions():
|
def BuildOptions():
|
||||||
result = optparse.OptionParser()
|
result = optparse.OptionParser()
|
||||||
result.add_option("--command", default=None, help="The command-line to run")
|
result.add_option("--command", default=None, help="The command-line to run")
|
||||||
|
result.add_option("-j", "--workers-count", type=int, default=max(1, GetCPUCount() - 1),
|
||||||
|
help="Number of tests to run in parallel (default %default)")
|
||||||
result.add_option("--tests", default=path.abspath('.'),
|
result.add_option("--tests", default=path.abspath('.'),
|
||||||
help="Path to the tests")
|
help="Path to the tests")
|
||||||
result.add_option("--cat", default=False, action="store_true",
|
result.add_option("--cat", default=False, action="store_true",
|
||||||
|
@ -92,6 +95,35 @@ def IsWindows():
|
||||||
p = platform.system()
|
p = platform.system()
|
||||||
return (p == 'Windows') or (p == 'Microsoft')
|
return (p == 'Windows') or (p == 'Microsoft')
|
||||||
|
|
||||||
|
def GetCPUCount():
|
||||||
|
"""
|
||||||
|
Guess at a reasonable parallelism count to set as the default for the
|
||||||
|
current machine and run.
|
||||||
|
"""
|
||||||
|
# Python 2.6+
|
||||||
|
try:
|
||||||
|
import multiprocessing
|
||||||
|
return multiprocessing.cpu_count()
|
||||||
|
except (ImportError, NotImplementedError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# POSIX
|
||||||
|
try:
|
||||||
|
res = int(os.sysconf('SC_NPROCESSORS_ONLN'))
|
||||||
|
if res > 0:
|
||||||
|
return res
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
try:
|
||||||
|
res = int(os.environ['NUMBER_OF_PROCESSORS'])
|
||||||
|
if res > 0:
|
||||||
|
return res
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
class TempFile(object):
|
class TempFile(object):
|
||||||
|
|
||||||
|
@ -526,7 +558,7 @@ class TestSuite(object):
|
||||||
print
|
print
|
||||||
result.ReportOutcome(False)
|
result.ReportOutcome(False)
|
||||||
|
|
||||||
def Run(self, command_template, tests, print_summary, full_summary, logname, junitfile):
|
def Run(self, command_template, tests, print_summary, full_summary, logname, junitfile, workers_count):
|
||||||
if not "{{path}}" in command_template:
|
if not "{{path}}" in command_template:
|
||||||
command_template += " {{path}}"
|
command_template += " {{path}}"
|
||||||
cases = self.EnumerateTests(tests)
|
cases = self.EnumerateTests(tests)
|
||||||
|
@ -551,16 +583,41 @@ class TestSuite(object):
|
||||||
SkipCaseElement.append(SkipElement)
|
SkipCaseElement.append(SkipElement)
|
||||||
TestSuiteElement.append(SkipCaseElement)
|
TestSuiteElement.append(SkipCaseElement)
|
||||||
|
|
||||||
|
if workers_count > 1:
|
||||||
|
pool_sem = threading.Semaphore(workers_count)
|
||||||
|
log_lock = threading.Lock()
|
||||||
|
else:
|
||||||
|
log_lock = None
|
||||||
|
|
||||||
for case in cases:
|
for case in cases:
|
||||||
result = case.Run(command_template)
|
def exec_case():
|
||||||
if junitfile:
|
result = case.Run(command_template)
|
||||||
TestCaseElement = result.XmlAssemble(result)
|
|
||||||
TestSuiteElement.append(TestCaseElement)
|
try:
|
||||||
if case == cases[len(cases)-1]:
|
if workers_count > 1:
|
||||||
xmlj.ElementTree(TestSuitesElement).write(junitfile, "UTF-8")
|
log_lock.acquire()
|
||||||
if logname:
|
|
||||||
self.WriteLog(result)
|
if junitfile:
|
||||||
progress.HasRun(result)
|
TestCaseElement = result.XmlAssemble(result)
|
||||||
|
TestSuiteElement.append(TestCaseElement)
|
||||||
|
if case == cases[len(cases)-1]:
|
||||||
|
xmlj.ElementTree(TestSuitesElement).write(junitfile, "UTF-8")
|
||||||
|
if logname:
|
||||||
|
self.WriteLog(result)
|
||||||
|
finally:
|
||||||
|
if workers_count > 1:
|
||||||
|
log_lock.release()
|
||||||
|
|
||||||
|
progress.HasRun(result)
|
||||||
|
if workers_count == 1:
|
||||||
|
exec_case()
|
||||||
|
else:
|
||||||
|
pool_sem.acquire()
|
||||||
|
threading.Thread(target=exec_case).start()
|
||||||
|
pool_sem.release()
|
||||||
|
|
||||||
|
if workers_count > 1:
|
||||||
|
log_lock.acquire()
|
||||||
|
|
||||||
if print_summary:
|
if print_summary:
|
||||||
self.PrintSummary(progress, logname)
|
self.PrintSummary(progress, logname)
|
||||||
|
@ -570,6 +627,10 @@ class TestSuite(object):
|
||||||
print
|
print
|
||||||
print "Use --full-summary to see output from failed tests"
|
print "Use --full-summary to see output from failed tests"
|
||||||
print
|
print
|
||||||
|
|
||||||
|
if workers_count > 1:
|
||||||
|
log_lock.release()
|
||||||
|
|
||||||
return progress.failed
|
return progress.failed
|
||||||
|
|
||||||
def WriteLog(self, result):
|
def WriteLog(self, result):
|
||||||
|
@ -634,7 +695,8 @@ def Main():
|
||||||
options.summary or options.full_summary,
|
options.summary or options.full_summary,
|
||||||
options.full_summary,
|
options.full_summary,
|
||||||
options.logname,
|
options.logname,
|
||||||
options.junitname)
|
options.junitname,
|
||||||
|
options.workers_count)
|
||||||
return code
|
return code
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in New Issue