| Index: tools/testrunner/local/execution.py
|
| diff --git a/tools/testrunner/local/execution.py b/tools/testrunner/local/execution.py
|
| index d9d72f459e9055ee165bebfd6dc1ee990cc143e9..c9fe54175a3ddd3096c03fb1e398243256daa202 100644
|
| --- a/tools/testrunner/local/execution.py
|
| +++ b/tools/testrunner/local/execution.py
|
| @@ -26,6 +26,7 @@
|
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
| +import collections
|
| import os
|
| import shutil
|
| import sys
|
| @@ -35,10 +36,17 @@ from pool import Pool
|
| from . import commands
|
| from . import perfdata
|
| from . import statusfile
|
| +from . import testsuite
|
| from . import utils
|
|
|
|
|
| -class Job(object):
|
| +# Base dir of the v8 checkout.
|
| +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
|
| + os.path.abspath(__file__)))))
|
| +TEST_DIR = os.path.join(BASE_DIR, "test")
|
| +
|
| +
|
| +class Instructions(object):
|
| def __init__(self, command, dep_command, test_id, timeout, verbose):
|
| self.command = command
|
| self.dep_command = dep_command
|
| @@ -47,18 +55,112 @@ class Job(object):
|
| self.verbose = verbose
|
|
|
|
|
| -def RunTest(job):
|
| - start_time = time.time()
|
| - if job.dep_command is not None:
|
| - dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout)
|
| - # TODO(jkummerow): We approximate the test suite specific function
|
| - # IsFailureOutput() by just checking the exit code here. Currently
|
| - # only cctests define dependencies, for which this simplification is
|
| - # correct.
|
| - if dep_output.exit_code != 0:
|
| - return (job.id, dep_output, time.time() - start_time)
|
| - output = commands.Execute(job.command, job.verbose, job.timeout)
|
| - return (job.id, output, time.time() - start_time)
|
| +# Structure that keeps global information per worker process.
|
| +ProcessContext = collections.namedtuple(
|
| + "process_context", ["suites", "context"])
|
| +
|
| +
|
| +def MakeProcessContext(context):
|
| + """Generate a process-local context.
|
| +
|
| + This reloads all suites per process and stores the global context.
|
| +
|
| + Args:
|
| + context: The global context from the test runner.
|
| + """
|
| + suite_paths = utils.GetSuitePaths(TEST_DIR)
|
| + suites = {}
|
| + for root in suite_paths:
|
| + # Don't reinitialize global state as this is concurrently called from
|
| + # different processes.
|
| + suite = testsuite.TestSuite.LoadTestSuite(
|
| + os.path.join(TEST_DIR, root), global_init=False)
|
| + if suite:
|
| + suites[suite.name] = suite
|
| + return ProcessContext(suites, context)
|
| +
|
| +
|
| +def GetCommand(test, context):
|
| + d8testflag = []
|
| + shell = test.suite.shell()
|
| + if shell == "d8":
|
| + d8testflag = ["--test"]
|
| + if utils.IsWindows():
|
| + shell += ".exe"
|
| + if context.random_seed:
|
| + d8testflag += ["--random-seed=%s" % context.random_seed]
|
| + cmd = (context.command_prefix +
|
| + [os.path.abspath(os.path.join(context.shell_dir, shell))] +
|
| + d8testflag +
|
| + test.suite.GetFlagsForTestCase(test, context) +
|
| + context.extra_flags)
|
| + return cmd
|
| +
|
| +
|
| +def _GetInstructions(test, context):
|
| + command = GetCommand(test, context)
|
| + timeout = context.timeout
|
| + if ("--stress-opt" in test.flags or
|
| + "--stress-opt" in context.mode_flags or
|
| + "--stress-opt" in context.extra_flags):
|
| + timeout *= 4
|
| + if "--noenable-vfp3" in context.extra_flags:
|
| + timeout *= 2
|
| + # FIXME(machenbach): Make this more OO. Don't expose default outcomes or
|
| + # the like.
|
| + if statusfile.IsSlow(test.outcomes or [statusfile.PASS]):
|
| + timeout *= 2
|
| + if test.dependency is not None:
|
| + dep_command = [ c.replace(test.path, test.dependency) for c in command ]
|
| + else:
|
| + dep_command = None
|
| + return Instructions(
|
| + command, dep_command, test.id, timeout, context.verbose)
|
| +
|
| +
|
| +class Job(object):
|
| + """Stores data to be sent over the multi-process boundary.
|
| +
|
| + All contained fields will be pickled/unpickled.
|
| + """
|
| +
|
| + def Run(self, process_context):
|
| + """Executes the job.
|
| +
|
| + Args:
|
| + process_context: Process-local information that is initialized by the
|
| + executing worker.
|
| + """
|
| + raise NotImplementedError()
|
| +
|
| +
|
| +class TestJob(Job):
|
| + def __init__(self, test):
|
| + self.test = test
|
| +
|
| + def Run(self, process_context):
|
| + # Retrieve a new suite object on the worker-process side. The original
|
| + # suite object isn't pickled.
|
| + self.test.SetSuiteObject(process_context.suites)
|
| + instr = _GetInstructions(self.test, process_context.context)
|
| +
|
| + start_time = time.time()
|
| + if instr.dep_command is not None:
|
| + dep_output = commands.Execute(
|
| + instr.dep_command, instr.verbose, instr.timeout)
|
| + # TODO(jkummerow): We approximate the test suite specific function
|
| + # IsFailureOutput() by just checking the exit code here. Currently
|
| + # only cctests define dependencies, for which this simplification is
|
| + # correct.
|
| + if dep_output.exit_code != 0:
|
| + return (instr.id, dep_output, time.time() - start_time)
|
| + output = commands.Execute(instr.command, instr.verbose, instr.timeout)
|
| + return (instr.id, output, time.time() - start_time)
|
| +
|
| +
|
| +def RunTest(job, process_context):
|
| + return job.Run(process_context)
|
| +
|
|
|
| class Runner(object):
|
|
|
| @@ -100,25 +202,6 @@ class Runner(object):
|
| print("PerfData exception: %s" % e)
|
| self.perf_failures = True
|
|
|
| - def _GetJob(self, test):
|
| - command = self.GetCommand(test)
|
| - timeout = self.context.timeout
|
| - if ("--stress-opt" in test.flags or
|
| - "--stress-opt" in self.context.mode_flags or
|
| - "--stress-opt" in self.context.extra_flags):
|
| - timeout *= 4
|
| - if "--noenable-vfp3" in self.context.extra_flags:
|
| - timeout *= 2
|
| - # FIXME(machenbach): Make this more OO. Don't expose default outcomes or
|
| - # the like.
|
| - if statusfile.IsSlow(test.outcomes or [statusfile.PASS]):
|
| - timeout *= 2
|
| - if test.dependency is not None:
|
| - dep_command = [ c.replace(test.path, test.dependency) for c in command ]
|
| - else:
|
| - dep_command = None
|
| - return Job(command, dep_command, test.id, timeout, self.context.verbose)
|
| -
|
| def _MaybeRerun(self, pool, test):
|
| if test.run <= self.context.rerun_failures_count:
|
| # Possibly rerun this test if its run count is below the maximum per
|
| @@ -139,7 +222,7 @@ class Runner(object):
|
| test.duration = None
|
| test.output = None
|
| test.run += 1
|
| - pool.add([self._GetJob(test)])
|
| + pool.add([TestJob(test)])
|
| self.remaining += 1
|
| self.total += 1
|
|
|
| @@ -209,7 +292,7 @@ class Runner(object):
|
| # remember the output for comparison.
|
| test.run += 1
|
| test.output = result[1]
|
| - pool.add([self._GetJob(test)])
|
| + pool.add([TestJob(test)])
|
| # Always update the perf database.
|
| return True
|
|
|
| @@ -232,14 +315,19 @@ class Runner(object):
|
| assert test.id >= 0
|
| test_map[test.id] = test
|
| try:
|
| - yield [self._GetJob(test)]
|
| + yield [TestJob(test)]
|
| except Exception, e:
|
| # If this failed, save the exception and re-raise it later (after
|
| # all other tests have had a chance to run).
|
| queued_exception[0] = e
|
| continue
|
| try:
|
| - it = pool.imap_unordered(RunTest, gen_tests())
|
| + it = pool.imap_unordered(
|
| + fn=RunTest,
|
| + gen=gen_tests(),
|
| + process_context_fn=MakeProcessContext,
|
| + process_context_args=[self.context],
|
| + )
|
| for result in it:
|
| if result.heartbeat:
|
| self.indicator.Heartbeat()
|
| @@ -277,22 +365,6 @@ class Runner(object):
|
| print text
|
| sys.stdout.flush()
|
|
|
| - def GetCommand(self, test):
|
| - d8testflag = []
|
| - shell = test.suite.shell()
|
| - if shell == "d8":
|
| - d8testflag = ["--test"]
|
| - if utils.IsWindows():
|
| - shell += ".exe"
|
| - if self.context.random_seed:
|
| - d8testflag += ["--random-seed=%s" % self.context.random_seed]
|
| - cmd = (self.context.command_prefix +
|
| - [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
|
| - d8testflag +
|
| - test.suite.GetFlagsForTestCase(test, self.context) +
|
| - self.context.extra_flags)
|
| - return cmd
|
| -
|
|
|
| class BreakNowException(Exception):
|
| def __init__(self, value):
|
|
|