diff --git a/CONSOLE-RUNNER.md b/CONSOLE-RUNNER.md index 754149eb46..d3efe8cc20 100644 --- a/CONSOLE-RUNNER.md +++ b/CONSOLE-RUNNER.md @@ -40,6 +40,7 @@ Name | Action -----|------- -h, --help | displays a brief help message --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 --cat | don't execute tests, just print code that would be run --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` --logname=LOGNAME | write output to file (in addition to stdout) --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` - + ### Usage Notes 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 -``` +``` #### Async Tests @@ -81,7 +82,7 @@ JavaScriptCore2 | jsc | print ***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) ) -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 diff --git a/tools/packaging/test262.py b/tools/packaging/test262.py index 921360a05e..1192005fc9 100755 --- a/tools/packaging/test262.py +++ b/tools/packaging/test262.py @@ -18,6 +18,7 @@ import subprocess import sys import tempfile import time +import threading import xml.dom.minidom import datetime import shutil @@ -54,6 +55,8 @@ EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST] def BuildOptions(): result = optparse.OptionParser() 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('.'), help="Path to the tests") result.add_option("--cat", default=False, action="store_true", @@ -92,6 +95,35 @@ def IsWindows(): p = platform.system() 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): @@ -526,7 +558,7 @@ class TestSuite(object): print 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: command_template += " {{path}}" cases = self.EnumerateTests(tests) @@ -551,16 +583,41 @@ class TestSuite(object): SkipCaseElement.append(SkipElement) 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: - result = case.Run(command_template) - if junitfile: - 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) - progress.HasRun(result) + def exec_case(): + result = case.Run(command_template) + + try: + if workers_count > 1: + log_lock.acquire() + + if junitfile: + 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: self.PrintSummary(progress, logname) @@ -570,6 +627,10 @@ class TestSuite(object): print print "Use --full-summary to see output from failed tests" print + + if workers_count > 1: + log_lock.release() + return progress.failed def WriteLog(self, result): @@ -634,7 +695,8 @@ def Main(): options.summary or options.full_summary, options.full_summary, options.logname, - options.junitname) + options.junitname, + options.workers_count) return code if __name__ == '__main__':