Chromium Code Reviews| Index: tools/testrunner/local/execution.py |
| diff --git a/tools/testrunner/local/execution.py b/tools/testrunner/local/execution.py |
| index d9d72f459e9055ee165bebfd6dc1ee990cc143e9..fca8096a21827fb68818fb7f898946b95a96c84f 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): |
|
Michael Achenbach
2015/11/27 09:39:41
The next two methods are the old GetCommand and Ge
Michael Achenbach
2015/11/27 10:58:29
Done.
|
| + 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 send over the multi-process boundary. |
|
Jakob Kummerow
2015/11/27 10:09:00
nit: s/send/sent/
|
| + |
| + 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): |
| + start_time = time.time() |
|
Jakob Kummerow
2015/11/27 10:09:00
It doesn't matter much, but I'd move this to its o
Michael Achenbach
2015/11/27 10:58:29
Done.
|
| + # 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) |
| + |
| + if instr.dep_command is not None: |
|
Michael Achenbach
2015/11/27 09:39:41
This part is a 1:1 copy from the old RunTest.
|
| + 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): |