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 os
     30 import time
     31 
     32 from pool import Pool
     33 from . import commands
     34 from . import perfdata
     35 from . import utils
     36 
     37 
     38 class Job(object):
     39   def __init__(self, command, dep_command, test_id, timeout, verbose):
     40     self.command = command
     41     self.dep_command = dep_command
     42     self.id = test_id
     43     self.timeout = timeout
     44     self.verbose = verbose
     45 
     46 
     47 def RunTest(job):
     48   start_time = time.time()
     49   if job.dep_command is not None:
     50     dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout)
     51     # TODO(jkummerow): We approximate the test suite specific function
     52     # IsFailureOutput() by just checking the exit code here. Currently
     53     # only cctests define dependencies, for which this simplification is
     54     # correct.
     55     if dep_output.exit_code != 0:
     56       return (job.id, dep_output, time.time() - start_time)
     57   output = commands.Execute(job.command, job.verbose, job.timeout)
     58   return (job.id, output, time.time() - start_time)
     59 
     60 class Runner(object):
     61 
     62   def __init__(self, suites, progress_indicator, context):
     63     datapath = os.path.join("out", "testrunner_data")
     64     self.perf_data_manager = perfdata.PerfDataManager(datapath)
     65     self.perfdata = self.perf_data_manager.GetStore(context.arch, context.mode)
     66     self.tests = [ t for s in suites for t in s.tests ]
     67     if not context.no_sorting:
     68       for t in self.tests:
     69         t.duration = self.perfdata.FetchPerfData(t) or 1.0
     70       self.tests.sort(key=lambda t: t.duration, reverse=True)
     71     self._CommonInit(len(self.tests), progress_indicator, context)
     72 
     73   def _CommonInit(self, num_tests, progress_indicator, context):
     74     self.indicator = progress_indicator
     75     progress_indicator.runner = self
     76     self.context = context
     77     self.succeeded = 0
     78     self.total = num_tests
     79     self.remaining = num_tests
     80     self.failed = []
     81     self.crashed = 0
     82 
     83   def Run(self, jobs):
     84     self.indicator.Starting()
     85     self._RunInternal(jobs)
     86     self.indicator.Done()
     87     if self.failed or self.remaining:
     88       return 1
     89     return 0
     90 
     91   def _RunInternal(self, jobs):
     92     pool = Pool(jobs)
     93     test_map = {}
     94     # TODO(machenbach): Instead of filling the queue completely before
     95     # pool.imap_unordered, make this a generator that already starts testing
     96     # while the queue is filled.
     97     queue = []
     98     queued_exception = None
     99     for test in self.tests:
    100       assert test.id >= 0
    101       test_map[test.id] = test
    102       try:
    103         command = self.GetCommand(test)
    104       except Exception, e:
    105         # If this failed, save the exception and re-raise it later (after
    106         # all other tests have had a chance to run).
    107         queued_exception = e
    108         continue
    109       timeout = self.context.timeout
    110       if ("--stress-opt" in test.flags or
    111           "--stress-opt" in self.context.mode_flags or
    112           "--stress-opt" in self.context.extra_flags):
    113         timeout *= 4
    114       if test.dependency is not None:
    115         dep_command = [ c.replace(test.path, test.dependency) for c in command ]
    116       else:
    117         dep_command = None
    118       job = Job(command, dep_command, test.id, timeout, self.context.verbose)
    119       queue.append([job])
    120     try:
    121       it = pool.imap_unordered(RunTest, queue)
    122       for result in it:
    123         test = test_map[result[0]]
    124         self.indicator.AboutToRun(test)
    125         test.output = result[1]
    126         test.duration = result[2]
    127         has_unexpected_output = test.suite.HasUnexpectedOutput(test)
    128         if has_unexpected_output:
    129           self.failed.append(test)
    130           if test.output.HasCrashed():
    131             self.crashed += 1
    132         else:
    133           self.succeeded += 1
    134         self.remaining -= 1
    135         try:
    136           self.perfdata.UpdatePerfData(test)
    137         except Exception, e:
    138           print("UpdatePerfData exception: %s" % e)
    139           pass  # Just keep working.
    140         self.indicator.HasRun(test, has_unexpected_output)
    141     finally:
    142       pool.terminate()
    143       self.perf_data_manager.close()
    144     if queued_exception:
    145       raise queued_exception
    146 
    147 
    148   def GetCommand(self, test):
    149     d8testflag = []
    150     shell = test.suite.shell()
    151     if shell == "d8":
    152       d8testflag = ["--test"]
    153     if utils.IsWindows():
    154       shell += ".exe"
    155     cmd = (self.context.command_prefix +
    156            [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
    157            d8testflag +
    158            ["--random-seed=%s" % self.context.random_seed] +
    159            test.suite.GetFlagsForTestCase(test, self.context) +
    160            self.context.extra_flags)
    161     return cmd
    162 
    163 
    164 class BreakNowException(Exception):
    165   def __init__(self, value):
    166     self.value = value
    167   def __str__(self):
    168     return repr(self.value)
    169