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 import os
     29 import re
     30 
     31 from variants import ALL_VARIANTS
     32 from utils import Freeze
     33 
     34 # These outcomes can occur in a TestCase's outcomes list:
     35 SKIP = "SKIP"
     36 FAIL = "FAIL"
     37 PASS = "PASS"
     38 OKAY = "OKAY"
     39 TIMEOUT = "TIMEOUT"
     40 CRASH = "CRASH"
     41 SLOW = "SLOW"
     42 FAST_VARIANTS = "FAST_VARIANTS"
     43 NO_VARIANTS = "NO_VARIANTS"
     44 # These are just for the status files and are mapped below in DEFS:
     45 FAIL_OK = "FAIL_OK"
     46 PASS_OR_FAIL = "PASS_OR_FAIL"
     47 FAIL_SLOPPY = "FAIL_SLOPPY"
     48 
     49 ALWAYS = "ALWAYS"
     50 
     51 KEYWORDS = {}
     52 for key in [SKIP, FAIL, PASS, OKAY, TIMEOUT, CRASH, SLOW, FAIL_OK,
     53             FAST_VARIANTS, NO_VARIANTS, PASS_OR_FAIL, FAIL_SLOPPY, ALWAYS]:
     54   KEYWORDS[key] = key
     55 
     56 DEFS = {FAIL_OK: [FAIL, OKAY],
     57         PASS_OR_FAIL: [PASS, FAIL]}
     58 
     59 # Support arches, modes to be written as keywords instead of strings.
     60 VARIABLES = {ALWAYS: True}
     61 for var in ["debug", "release", "big", "little",
     62             "android_arm", "android_arm64", "android_ia32", "android_x87",
     63             "android_x64", "arm", "arm64", "ia32", "mips", "mipsel", "mips64",
     64             "mips64el", "x64", "x87", "ppc", "ppc64", "s390", "s390x", "macos",
     65             "windows", "linux", "aix"]:
     66   VARIABLES[var] = var
     67 
     68 # Allow using variants as keywords.
     69 for var in ALL_VARIANTS:
     70   VARIABLES[var] = var
     71 
     72 
     73 def DoSkip(outcomes):
     74   return SKIP in outcomes
     75 
     76 
     77 def IsSlow(outcomes):
     78   return SLOW in outcomes
     79 
     80 
     81 def OnlyStandardVariant(outcomes):
     82   return NO_VARIANTS in outcomes
     83 
     84 
     85 def OnlyFastVariants(outcomes):
     86   return FAST_VARIANTS in outcomes
     87 
     88 
     89 def IsPassOrFail(outcomes):
     90   return ((PASS in outcomes) and (FAIL in outcomes) and
     91           (not CRASH in outcomes) and (not OKAY in outcomes))
     92 
     93 
     94 def IsFailOk(outcomes):
     95     return (FAIL in outcomes) and (OKAY in outcomes)
     96 
     97 
     98 def _AddOutcome(result, new):
     99   global DEFS
    100   if new in DEFS:
    101     mapped = DEFS[new]
    102     if type(mapped) == list:
    103       for m in mapped:
    104         _AddOutcome(result, m)
    105     elif type(mapped) == str:
    106       _AddOutcome(result, mapped)
    107   else:
    108     result.add(new)
    109 
    110 
    111 def _JoinsPassAndFail(outcomes1, outcomes2):
    112   """Indicates if we join PASS and FAIL from two different outcome sets and
    113   the first doesn't already contain both.
    114   """
    115   return (
    116       PASS in outcomes1 and
    117       not FAIL in outcomes1 and
    118       FAIL in outcomes2
    119   )
    120 
    121 VARIANT_EXPRESSION = object()
    122 
    123 def _EvalExpression(exp, variables):
    124   try:
    125     return eval(exp, variables)
    126   except NameError as e:
    127     identifier = re.match("name '(.*)' is not defined", e.message).group(1)
    128     assert identifier == "variant", "Unknown identifier: %s" % identifier
    129     return VARIANT_EXPRESSION
    130 
    131 
    132 def _EvalVariantExpression(section, rules, wildcards, variant, variables):
    133   variables_with_variant = {}
    134   variables_with_variant.update(variables)
    135   variables_with_variant["variant"] = variant
    136   result = _EvalExpression(section[0], variables_with_variant)
    137   assert result != VARIANT_EXPRESSION
    138   if result is True:
    139     _ReadSection(
    140         section[1],
    141         rules[variant],
    142         wildcards[variant],
    143         variables_with_variant,
    144     )
    145   else:
    146     assert result is False, "Make sure expressions evaluate to boolean values"
    147 
    148 
    149 def _ParseOutcomeList(rule, outcomes, target_dict, variables):
    150   result = set([])
    151   if type(outcomes) == str:
    152     outcomes = [outcomes]
    153   for item in outcomes:
    154     if type(item) == str:
    155       _AddOutcome(result, item)
    156     elif type(item) == list:
    157       exp = _EvalExpression(item[0], variables)
    158       assert exp != VARIANT_EXPRESSION, (
    159         "Nested variant expressions are not supported")
    160       if exp is False:
    161         continue
    162 
    163       # Ensure nobody uses an identifier by mistake, like "default",
    164       # which would evaluate to true here otherwise.
    165       assert exp is True, "Make sure expressions evaluate to boolean values"
    166 
    167       for outcome in item[1:]:
    168         assert type(outcome) == str
    169         _AddOutcome(result, outcome)
    170     else:
    171       assert False
    172   if len(result) == 0: return
    173   if rule in target_dict:
    174     # A FAIL without PASS in one rule has always precedence over a single
    175     # PASS (without FAIL) in another. Otherwise the default PASS expectation
    176     # in a rule with a modifier (e.g. PASS, SLOW) would be joined to a FAIL
    177     # from another rule (which intended to mark a test as FAIL and not as
    178     # PASS and FAIL).
    179     if _JoinsPassAndFail(target_dict[rule], result):
    180       target_dict[rule] -= set([PASS])
    181     if _JoinsPassAndFail(result, target_dict[rule]):
    182       result -= set([PASS])
    183     target_dict[rule] |= result
    184   else:
    185     target_dict[rule] = result
    186 
    187 
    188 def ReadContent(content):
    189   global KEYWORDS
    190   return eval(content, KEYWORDS)
    191 
    192 
    193 def ReadStatusFile(content, variables):
    194   # Empty defaults for rules and wildcards. Variant-independent
    195   # rules are mapped by "", others by the variant name.
    196   rules = {variant: {} for variant in ALL_VARIANTS}
    197   rules[""] = {}
    198   wildcards = {variant: {} for variant in ALL_VARIANTS}
    199   wildcards[""] = {}
    200 
    201   variables.update(VARIABLES)
    202   for section in ReadContent(content):
    203     assert type(section) == list
    204     assert len(section) == 2
    205     exp = _EvalExpression(section[0], variables)
    206     if exp is False:
    207       # The expression is variant-independent and evaluates to False.
    208       continue
    209     elif exp == VARIANT_EXPRESSION:
    210       # If the expression contains one or more "variant" keywords, we evaluate
    211       # it for all possible variants and create rules for those that apply.
    212       for variant in ALL_VARIANTS:
    213         _EvalVariantExpression(section, rules, wildcards, variant, variables)
    214     else:
    215       # The expression is variant-independent and evaluates to True.
    216       assert exp is True, "Make sure expressions evaluate to boolean values"
    217       _ReadSection(
    218           section[1],
    219           rules[""],
    220           wildcards[""],
    221           variables,
    222       )
    223   return Freeze(rules), Freeze(wildcards)
    224 
    225 
    226 def _ReadSection(section, rules, wildcards, variables):
    227   assert type(section) == dict
    228   for rule in section:
    229     assert type(rule) == str
    230     if rule[-1] == '*':
    231       _ParseOutcomeList(rule, section[rule], wildcards, variables)
    232     else:
    233       _ParseOutcomeList(rule, section[rule], rules, variables)
    234 
    235 JS_TEST_PATHS = {
    236   'debugger': [[]],
    237   'inspector': [[]],
    238   'intl': [[]],
    239   'message': [[]],
    240   'mjsunit': [[]],
    241   'mozilla': [['data']],
    242   'test262': [['data', 'test'], ['local-tests', 'test']],
    243   'webkit': [[]],
    244 }
    245 
    246 def PresubmitCheck(path):
    247   with open(path) as f:
    248     contents = ReadContent(f.read())
    249   basename = os.path.basename(os.path.dirname(path))
    250   root_prefix = basename + "/"
    251   status = {"success": True}
    252   def _assert(check, message):  # Like "assert", but doesn't throw.
    253     if not check:
    254       print("%s: Error: %s" % (path, message))
    255       status["success"] = False
    256   try:
    257     for section in contents:
    258       _assert(type(section) == list, "Section must be a list")
    259       _assert(len(section) == 2, "Section list must have exactly 2 entries")
    260       section = section[1]
    261       _assert(type(section) == dict,
    262               "Second entry of section must be a dictionary")
    263       for rule in section:
    264         _assert(type(rule) == str, "Rule key must be a string")
    265         _assert(not rule.startswith(root_prefix),
    266                 "Suite name prefix must not be used in rule keys")
    267         _assert(not rule.endswith('.js'),
    268                 ".js extension must not be used in rule keys.")
    269         if basename in JS_TEST_PATHS  and '*' not in rule:
    270           _assert(any(os.path.exists(os.path.join(os.path.dirname(path),
    271                                                   *(paths + [rule + ".js"])))
    272                       for paths in JS_TEST_PATHS[basename]),
    273                   "missing file for %s test %s" % (basename, rule))
    274     return status["success"]
    275   except Exception as e:
    276     print e
    277     return False
    278