|
16 | 16 |
|
17 | 17 |
|
18 | 18 | # This file is based on work under the following copyright and permission notice: |
19 | | -# https://github.com/test262-utils/test262-harness-py |
20 | | -# test262.py, _monkeyYaml.py, parseTestRecord.py |
| 19 | +# https://github.com/test262-utils/test262-harness-py/blob/master/src/test262.py |
21 | 20 |
|
22 | 21 | # license of test262.py: |
23 | 22 | # Copyright 2009 the Sputnik authors. All rights reserved. |
|
31 | 30 | # Copyright (c) 2012 Ecma International. All rights reserved. |
32 | 31 | # This code is governed by the BSD license found in the LICENSE file. |
33 | 32 |
|
34 | | -# license of _monkeyYaml.py: |
35 | | -# Copyright 2014 by Sam Mikes. All rights reserved. |
36 | | -# This code is governed by the BSD license found in the LICENSE file. |
37 | | - |
38 | | -# license of parseTestRecord.py: |
39 | | -# Copyright 2011 by Google, Inc. All rights reserved. |
40 | | -# This code is governed by the BSD license found in the LICENSE file. |
41 | | - |
42 | | - |
43 | 33 | from __future__ import print_function |
44 | 34 |
|
45 | 35 | import logging |
|
58 | 48 | import threading |
59 | 49 | import multiprocessing |
60 | 50 |
|
61 | | -####################################################################### |
62 | | -# based on _monkeyYaml.py |
63 | | -####################################################################### |
64 | | - |
65 | | -M_YAML_LIST_PATTERN = re.compile(r"^\[(.*)\]$") |
66 | | -M_YAML_MULTILINE_LIST = re.compile(r"^ *- (.*)$") |
67 | | - |
68 | | - |
69 | 51 | # The timeout of each test case |
70 | 52 | TEST262_CASE_TIMEOUT = 180 |
71 | 53 |
|
72 | | - |
73 | | -def yaml_load(string): |
74 | | - return my_read_dict(string.splitlines())[1] |
75 | | - |
76 | | - |
77 | | -def my_read_dict(lines, indent=""): |
78 | | - dictionary = {} |
79 | | - key = None |
80 | | - empty_lines = 0 |
81 | | - |
82 | | - while lines: |
83 | | - if not lines[0].startswith(indent): |
84 | | - break |
85 | | - |
86 | | - line = lines.pop(0) |
87 | | - if my_is_all_spaces(line): |
88 | | - empty_lines += 1 |
89 | | - continue |
90 | | - |
91 | | - result = re.match(r"(.*?):(.*)", line) |
92 | | - |
93 | | - if result: |
94 | | - if not dictionary: |
95 | | - dictionary = {} |
96 | | - key = result.group(1).strip() |
97 | | - value = result.group(2).strip() |
98 | | - (lines, value) = my_read_value(lines, value, indent) |
99 | | - dictionary[key] = value |
100 | | - else: |
101 | | - if dictionary and key and key in dictionary: |
102 | | - char = " " if empty_lines == 0 else "\n" * empty_lines |
103 | | - dictionary[key] += char + line.strip() |
104 | | - else: |
105 | | - raise Exception("monkeyYaml is confused at " + line) |
106 | | - empty_lines = 0 |
107 | | - |
108 | | - if not dictionary: |
109 | | - dictionary = None |
110 | | - |
111 | | - return lines, dictionary |
112 | | - |
113 | | - |
114 | | -def my_read_value(lines, value, indent): |
115 | | - if value == ">" or value == "|": |
116 | | - (lines, value) = my_multiline(lines, value == "|") |
117 | | - value = value + "\n" |
118 | | - return (lines, value) |
119 | | - if lines and not value: |
120 | | - if my_maybe_list(lines[0]): |
121 | | - return my_multiline_list(lines, value) |
122 | | - indent_match = re.match("(" + indent + r"\s+)", lines[0]) |
123 | | - if indent_match: |
124 | | - if ":" in lines[0]: |
125 | | - return my_read_dict(lines, indent_match.group(1)) |
126 | | - return my_multiline(lines, False) |
127 | | - return lines, my_read_one_line(value) |
128 | | - |
129 | | - |
130 | | -def my_maybe_list(value): |
131 | | - return M_YAML_MULTILINE_LIST.match(value) |
132 | | - |
133 | | - |
134 | | -def my_multiline_list(lines, value): |
135 | | - # assume no explcit indentor (otherwise have to parse value) |
136 | | - value = [] |
137 | | - indent = 0 |
138 | | - while lines: |
139 | | - line = lines.pop(0) |
140 | | - leading = my_leading_spaces(line) |
141 | | - if my_is_all_spaces(line): |
142 | | - pass |
143 | | - elif leading < indent: |
144 | | - lines.insert(0, line) |
145 | | - break |
146 | | - else: |
147 | | - indent = indent or leading |
148 | | - value += [my_read_one_line(my_remove_list_header(indent, line))] |
149 | | - return (lines, value) |
150 | | - |
151 | | - |
152 | | -def my_remove_list_header(indent, line): |
153 | | - line = line[indent:] |
154 | | - return M_YAML_MULTILINE_LIST.match(line).group(1) |
155 | | - |
156 | | - |
157 | | -def my_read_one_line(value): |
158 | | - if M_YAML_LIST_PATTERN.match(value): |
159 | | - return my_flow_list(value) |
160 | | - elif re.match(r"^[-0-9]*$", value): |
161 | | - try: |
162 | | - value = int(value) |
163 | | - except ValueError: |
164 | | - pass |
165 | | - elif re.match(r"^[-.0-9eE]*$", value): |
166 | | - try: |
167 | | - value = float(value) |
168 | | - except ValueError: |
169 | | - pass |
170 | | - elif re.match(r"^('|\").*\1$", value): |
171 | | - value = value[1:-1] |
172 | | - return value |
173 | | - |
174 | | - |
175 | | -def my_flow_list(value): |
176 | | - result = M_YAML_LIST_PATTERN.match(value) |
177 | | - values = result.group(1).split(",") |
178 | | - return [my_read_one_line(v.strip()) for v in values] |
179 | | - |
180 | | - |
181 | | -def my_multiline(lines, preserve_newlines=False): |
182 | | - # assume no explcit indentor (otherwise have to parse value) |
183 | | - value = "" |
184 | | - indent = my_leading_spaces(lines[0]) |
185 | | - was_empty = None |
186 | | - |
187 | | - while lines: |
188 | | - line = lines.pop(0) |
189 | | - is_empty = my_is_all_spaces(line) |
190 | | - |
191 | | - if is_empty: |
192 | | - if preserve_newlines: |
193 | | - value += "\n" |
194 | | - elif my_leading_spaces(line) < indent: |
195 | | - lines.insert(0, line) |
196 | | - break |
197 | | - else: |
198 | | - if preserve_newlines: |
199 | | - if was_empty != None: |
200 | | - value += "\n" |
201 | | - else: |
202 | | - if was_empty: |
203 | | - value += "\n" |
204 | | - elif was_empty is False: |
205 | | - value += " " |
206 | | - value += line[(indent):] |
207 | | - |
208 | | - was_empty = is_empty |
209 | | - |
210 | | - return (lines, value) |
211 | | - |
212 | | - |
213 | | -def my_is_all_spaces(line): |
214 | | - return len(line.strip()) == 0 |
215 | | - |
216 | | - |
217 | | -def my_leading_spaces(line): |
218 | | - return len(line) - len(line.lstrip(' ')) |
219 | | - |
220 | | - |
221 | | -####################################################################### |
222 | | -# based on parseTestRecord.py |
223 | | -####################################################################### |
224 | | - |
225 | | -# Matches trailing whitespace and any following blank lines. |
226 | | -_BLANK_LINES = r"([ \t]*[\r\n]{1,2})*" |
227 | | - |
228 | | -# Matches the YAML frontmatter block. |
229 | | -# It must be non-greedy because test262-es2015/built-ins/Object/assign/Override.js contains a comment like yaml pattern |
230 | | -_YAML_PATTERN = re.compile(r"/\*---(.*?)---\*/" + _BLANK_LINES, re.DOTALL) |
231 | | - |
232 | | -# Matches all known variants for the license block. |
233 | | -# https://github.com/tc39/test262/blob/705d78299cf786c84fa4df473eff98374de7135a/tools/lint/lib/checks/license.py |
234 | | -_LICENSE_PATTERN = re.compile( |
235 | | - r'// Copyright( \([C]\))? (\w+) .+\. {1,2}All rights reserved\.[\r\n]{1,2}' + |
236 | | - r'(' + |
237 | | - r'// This code is governed by the( BSD)? license found in the LICENSE file\.' + |
238 | | - r'|' + |
239 | | - r'// See LICENSE for details.' + |
240 | | - r'|' + |
241 | | - r'// Use of this source code is governed by a BSD-style license that can be[\r\n]{1,2}' + |
242 | | - r'// found in the LICENSE file\.' + |
243 | | - r'|' + |
244 | | - r'// See LICENSE or https://github\.com/tc39/test262/blob/master/LICENSE' + |
245 | | - r')' + _BLANK_LINES, re.IGNORECASE) |
246 | | - |
247 | | - |
248 | | -def yaml_attr_parser(test_record, attrs, name, onerror=print): |
249 | | - parsed = yaml_load(attrs) |
250 | | - if parsed is None: |
251 | | - onerror("Failed to parse yaml in name %s" % name) |
252 | | - return |
253 | | - |
254 | | - for key in parsed: |
255 | | - value = parsed[key] |
256 | | - if key == "info": |
257 | | - key = "commentary" |
258 | | - test_record[key] = value |
259 | | - |
260 | | - if 'flags' in test_record: |
261 | | - for flag in test_record['flags']: |
262 | | - test_record[flag] = "" |
263 | | - |
264 | | - |
265 | | -def find_license(src): |
266 | | - match = _LICENSE_PATTERN.search(src) |
267 | | - if not match: |
268 | | - return None |
269 | | - |
270 | | - return match.group(0) |
271 | | - |
272 | | - |
273 | | -def find_attrs(src): |
274 | | - match = _YAML_PATTERN.search(src) |
275 | | - if not match: |
276 | | - return (None, None) |
277 | | - |
278 | | - return (match.group(0), match.group(1).strip()) |
279 | | - |
280 | | - |
281 | | -def parse_test_record(src, name, onerror=print): |
282 | | - # Find the license block. |
283 | | - header = find_license(src) |
284 | | - |
285 | | - # Find the YAML frontmatter. |
286 | | - (frontmatter, attrs) = find_attrs(src) |
287 | | - |
288 | | - # YAML frontmatter is required for all tests. |
289 | | - if frontmatter is None: |
290 | | - onerror("Missing frontmatter: %s" % name) |
291 | | - |
292 | | - # The license shuold be placed before the frontmatter and there shouldn't be |
293 | | - # any extra content between the license and the frontmatter. |
294 | | - if header is not None and frontmatter is not None: |
295 | | - header_idx = src.index(header) |
296 | | - frontmatter_idx = src.index(frontmatter) |
297 | | - if header_idx > frontmatter_idx: |
298 | | - onerror("Unexpected license after frontmatter: %s" % name) |
299 | | - |
300 | | - # Search for any extra test content, but ignore whitespace only or comment lines. |
301 | | - extra = src[header_idx + len(header): frontmatter_idx] |
302 | | - if extra and any(line.strip() and not line.lstrip().startswith("//") for line in extra.split("\n")): |
303 | | - onerror( |
304 | | - "Unexpected test content between license and frontmatter: %s" % name) |
305 | | - |
306 | | - # Remove the license and YAML parts from the actual test content. |
307 | | - test = src |
308 | | - if frontmatter is not None: |
309 | | - test = test.replace(frontmatter, '') |
310 | | - if header is not None: |
311 | | - test = test.replace(header, '') |
312 | | - |
313 | | - test_record = {} |
314 | | - test_record['header'] = header.strip() if header else '' |
315 | | - test_record['test'] = test |
316 | | - |
317 | | - if attrs: |
318 | | - yaml_attr_parser(test_record, attrs, name, onerror) |
319 | | - |
320 | | - # Report if the license block is missing in non-generated tests. |
321 | | - if header is None and "generated" not in test_record and "hashbang" not in name: |
322 | | - onerror("No license found in: %s" % name) |
323 | | - |
324 | | - return test_record |
325 | | - |
326 | | - |
327 | | -####################################################################### |
328 | | -# based on test262.py |
329 | | -####################################################################### |
| 54 | +TEST_RE = re.compile(r"(?P<test1>.*)\/\*---(?P<header>.+)---\*\/(?P<test2>.*)", re.DOTALL) |
| 55 | +YAML_INCLUDES_RE = re.compile(r"includes:\s+\[(?P<includes>.+)\]") |
| 56 | +YAML_INCLUDES_LEGACY_RE = re.compile(r"includes:\s+(( *- (.*)\s+)*)", re.MULTILINE) |
| 57 | +YAML_FLAGS_RE = re.compile(r"flags:\s+\[(?P<flags>.+)\]") |
| 58 | +YAML_NEGATIVE_RE = re.compile(r"negative:.*(((phase:\s+(?P<phase>\w+))|(type:\s+(?P<type>\w+))).*){2}", re.DOTALL) |
| 59 | +YAML_NEGATIVE_LEGACY_RE = re.compile(r"negative:\s+(?P<type>\w+)") |
330 | 60 |
|
331 | 61 | class Test262Error(Exception): |
332 | 62 | def __init__(self, message): |
@@ -495,19 +225,43 @@ def __init__(self, suite, name, full_path, strict_mode, command_template, module |
495 | 225 | self.name = name |
496 | 226 | self.full_path = full_path |
497 | 227 | self.strict_mode = strict_mode |
498 | | - with open(self.full_path, "rb") as file_desc: |
499 | | - self.contents = file_desc.read() |
500 | | - test_record = parse_test_record(self.contents, name) |
501 | | - self.test = test_record["test"] |
502 | | - del test_record["test"] |
503 | | - del test_record["header"] |
504 | | - test_record.pop("commentary", None) # do not throw if missing |
505 | | - self.test_record = test_record |
506 | 228 | self.command_template = command_template |
507 | 229 | self.module_flag = module_flag |
508 | | - |
| 230 | + self.test_record = {} |
| 231 | + self.parse_test_record() |
509 | 232 | self.validate() |
510 | 233 |
|
| 234 | + def parse_test_record(self): |
| 235 | + with open(self.full_path, "rb") as file_desc: |
| 236 | + full_test = file_desc.read() |
| 237 | + |
| 238 | + match = TEST_RE.search(full_test) |
| 239 | + header = match.group('header') |
| 240 | + self.test = match.group('test1') + match.group('test2') |
| 241 | + |
| 242 | + match = YAML_INCLUDES_RE.search(header) |
| 243 | + if match: |
| 244 | + self.test_record["includes"] = [inc.strip() for inc in match.group('includes').split(',') if inc] |
| 245 | + else: |
| 246 | + match = YAML_INCLUDES_LEGACY_RE.search(header) |
| 247 | + self.test_record["includes"] = [inc.strip() for inc in match.groups()[0].split('-') if inc] if match else [] |
| 248 | + |
| 249 | + match = YAML_FLAGS_RE.search(header) |
| 250 | + if match: |
| 251 | + for flag in match.group('flags').split(','): |
| 252 | + if flag.strip(): |
| 253 | + self.test_record[flag.strip()] = "" |
| 254 | + |
| 255 | + match = YAML_NEGATIVE_RE.search(header) |
| 256 | + if match and match.group("phase") and match.group("type"): |
| 257 | + self.test_record["negative"] = { |
| 258 | + "phase" : match.group("phase"), |
| 259 | + "type" : match.group("type")} |
| 260 | + else: |
| 261 | + match = YAML_NEGATIVE_LEGACY_RE.search(header) |
| 262 | + if match: |
| 263 | + self.test_record["negative"] = match.group("type") |
| 264 | + |
511 | 265 | def negative_match(self, stderr): |
512 | 266 | neg = re.compile(self.get_negative_type()) |
513 | 267 | return re.search(neg, stderr) |
|
0 commit comments