• 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
28import os
29import re
30
31from variants import ALL_VARIANTS
32from utils import Freeze
33
34# These outcomes can occur in a TestCase's outcomes list:
35SKIP = "SKIP"
36FAIL = "FAIL"
37PASS = "PASS"
38OKAY = "OKAY"
39TIMEOUT = "TIMEOUT"
40CRASH = "CRASH"
41SLOW = "SLOW"
42FAST_VARIANTS = "FAST_VARIANTS"
43NO_VARIANTS = "NO_VARIANTS"
44# These are just for the status files and are mapped below in DEFS:
45FAIL_OK = "FAIL_OK"
46PASS_OR_FAIL = "PASS_OR_FAIL"
47FAIL_SLOPPY = "FAIL_SLOPPY"
48
49ALWAYS = "ALWAYS"
50
51KEYWORDS = {}
52for 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
56DEFS = {FAIL_OK: [FAIL, OKAY],
57        PASS_OR_FAIL: [PASS, FAIL]}
58
59# Support arches, modes to be written as keywords instead of strings.
60VARIABLES = {ALWAYS: True}
61for 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.
69for var in ALL_VARIANTS:
70  VARIABLES[var] = var
71
72
73def DoSkip(outcomes):
74  return SKIP in outcomes
75
76
77def IsSlow(outcomes):
78  return SLOW in outcomes
79
80
81def OnlyStandardVariant(outcomes):
82  return NO_VARIANTS in outcomes
83
84
85def OnlyFastVariants(outcomes):
86  return FAST_VARIANTS in outcomes
87
88
89def IsPassOrFail(outcomes):
90  return ((PASS in outcomes) and (FAIL in outcomes) and
91          (not CRASH in outcomes) and (not OKAY in outcomes))
92
93
94def IsFailOk(outcomes):
95    return (FAIL in outcomes) and (OKAY in outcomes)
96
97
98def _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
111def _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
121VARIANT_EXPRESSION = object()
122
123def _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
132def _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
149def _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
188def ReadContent(content):
189  global KEYWORDS
190  return eval(content, KEYWORDS)
191
192
193def 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
226def _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
235JS_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
246def 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