Home | History | Annotate | Download | only in local
      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 
     29 import multiprocessing
     30 import os
     31 import threading
     32 import time
     33 
     34 from . import commands
     35 from . import utils
     36 
     37 
     38 BREAK_NOW = -1
     39 EXCEPTION = -2
     40 
     41 
     42 class 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 
     51 def 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 
     71 class 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 
    179 class BreakNowException(Exception):
    180   def __init__(self, value):
    181     self.value = value
    182   def __str__(self):
    183     return repr(self.value)
    184