• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2012 the V8 project authors. All rights reserved.
2# Redistribution and use in source and binary forms, with or without
3# modification, are permitted provided that the following conditions are
4# met:
5#
6#     * Redistributions of source code must retain the above copyright
7#       notice, this list of conditions and the following disclaimer.
8#     * Redistributions in binary form must reproduce the above
9#       copyright notice, this list of conditions and the following
10#       disclaimer in the documentation and/or other materials provided
11#       with the distribution.
12#     * Neither the name of Google Inc. nor the names of its
13#       contributors may be used to endorse or promote products derived
14#       from this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29import multiprocessing
30import os
31import threading
32import time
33
34from . import commands
35from . import utils
36
37
38BREAK_NOW = -1
39EXCEPTION = -2
40
41
42class Job(object):
43  def __init__(self, command, dep_command, test_id, timeout, verbose):
44    self.command = command
45    self.dep_command = dep_command
46    self.id = test_id
47    self.timeout = timeout
48    self.verbose = verbose
49
50
51def RunTest(job):
52  try:
53    start_time = time.time()
54    if job.dep_command is not None:
55      dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout)
56      # TODO(jkummerow): We approximate the test suite specific function
57      # IsFailureOutput() by just checking the exit code here. Currently
58      # only cctests define dependencies, for which this simplification is
59      # correct.
60      if dep_output.exit_code != 0:
61        return (job.id, dep_output, time.time() - start_time)
62    output = commands.Execute(job.command, job.verbose, job.timeout)
63    return (job.id, output, time.time() - start_time)
64  except KeyboardInterrupt:
65    return (-1, BREAK_NOW, 0)
66  except Exception, e:
67    print(">>> EXCEPTION: %s" % e)
68    return (-1, EXCEPTION, 0)
69
70
71class Runner(object):
72
73  def __init__(self, suites, progress_indicator, context):
74    self.tests = [ t for s in suites for t in s.tests ]
75    self._CommonInit(len(self.tests), progress_indicator, context)
76
77  def _CommonInit(self, num_tests, progress_indicator, context):
78    self.indicator = progress_indicator
79    progress_indicator.runner = self
80    self.context = context
81    self.succeeded = 0
82    self.total = num_tests
83    self.remaining = num_tests
84    self.failed = []
85    self.crashed = 0
86    self.terminate = False
87    self.lock = threading.Lock()
88
89  def Run(self, jobs):
90    self.indicator.Starting()
91    self._RunInternal(jobs)
92    self.indicator.Done()
93    if self.failed or self.remaining:
94      return 1
95    return 0
96
97  def _RunInternal(self, jobs):
98    pool = multiprocessing.Pool(processes=jobs)
99    test_map = {}
100    queue = []
101    queued_exception = None
102    for test in self.tests:
103      assert test.id >= 0
104      test_map[test.id] = test
105      try:
106        command = self.GetCommand(test)
107      except Exception, e:
108        # If this failed, save the exception and re-raise it later (after
109        # all other tests have had a chance to run).
110        queued_exception = e
111        continue
112      timeout = self.context.timeout
113      if ("--stress-opt" in test.flags or
114          "--stress-opt" in self.context.mode_flags or
115          "--stress-opt" in self.context.extra_flags):
116        timeout *= 4
117      if test.dependency is not None:
118        dep_command = [ c.replace(test.path, test.dependency) for c in command ]
119      else:
120        dep_command = None
121      job = Job(command, dep_command, test.id, timeout, self.context.verbose)
122      queue.append(job)
123    try:
124      kChunkSize = 1
125      it = pool.imap_unordered(RunTest, queue, kChunkSize)
126      for result in it:
127        test_id = result[0]
128        if test_id < 0:
129          if result[1] == BREAK_NOW:
130            self.terminate = True
131          else:
132            continue
133        if self.terminate:
134          pool.terminate()
135          pool.join()
136          raise BreakNowException("User pressed Ctrl+C or IO went wrong")
137        test = test_map[test_id]
138        self.indicator.AboutToRun(test)
139        test.output = result[1]
140        test.duration = result[2]
141        has_unexpected_output = test.suite.HasUnexpectedOutput(test)
142        if has_unexpected_output:
143          self.failed.append(test)
144          if test.output.HasCrashed():
145            self.crashed += 1
146        else:
147          self.succeeded += 1
148        self.remaining -= 1
149        self.indicator.HasRun(test, has_unexpected_output)
150    except KeyboardInterrupt:
151      pool.terminate()
152      pool.join()
153      raise
154    except Exception, e:
155      print("Exception: %s" % e)
156      pool.terminate()
157      pool.join()
158      raise
159    if queued_exception:
160      raise queued_exception
161    return
162
163
164  def GetCommand(self, test):
165    d8testflag = []
166    shell = test.suite.shell()
167    if shell == "d8":
168      d8testflag = ["--test"]
169    if utils.IsWindows():
170      shell += ".exe"
171    cmd = (self.context.command_prefix +
172           [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
173           d8testflag +
174           test.suite.GetFlagsForTestCase(test, self.context) +
175           self.context.extra_flags)
176    return cmd
177
178
179class BreakNowException(Exception):
180  def __init__(self, value):
181    self.value = value
182  def __str__(self):
183    return repr(self.value)
184