OLD | NEW |
| (Empty) |
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 cStringIO | |
30 import re | |
31 | |
32 # These outcomes can occur in a TestCase's outcomes list: | |
33 SKIP = 'SKIP' | |
34 FAIL = 'FAIL' | |
35 PASS = 'PASS' | |
36 OKAY = 'OKAY' | |
37 TIMEOUT = 'TIMEOUT' | |
38 CRASH = 'CRASH' | |
39 SLOW = 'SLOW' | |
40 FLAKY = 'FLAKY' | |
41 # These are just for the status files and are mapped below in DEFS: | |
42 FAIL_OK = 'FAIL_OK' | |
43 PASS_OR_FAIL = 'PASS_OR_FAIL' | |
44 | |
45 KEYWORDS = {SKIP: SKIP, | |
46 FAIL: FAIL, | |
47 PASS: PASS, | |
48 OKAY: OKAY, | |
49 TIMEOUT: TIMEOUT, | |
50 CRASH: CRASH, | |
51 SLOW: SLOW, | |
52 FLAKY: FLAKY, | |
53 FAIL_OK: FAIL_OK, | |
54 PASS_OR_FAIL: PASS_OR_FAIL} | |
55 | |
56 class Expression(object): | |
57 pass | |
58 | |
59 | |
60 class Constant(Expression): | |
61 | |
62 def __init__(self, value): | |
63 self.value = value | |
64 | |
65 def Evaluate(self, env, defs): | |
66 return self.value | |
67 | |
68 | |
69 class Variable(Expression): | |
70 | |
71 def __init__(self, name): | |
72 self.name = name | |
73 | |
74 def GetOutcomes(self, env, defs): | |
75 if self.name in env: return set([env[self.name]]) | |
76 else: return set([]) | |
77 | |
78 def Evaluate(self, env, defs): | |
79 return env[self.name] | |
80 | |
81 def __str__(self): | |
82 return self.name | |
83 | |
84 def string(self, logical): | |
85 return self.__str__() | |
86 | |
87 | |
88 class Outcome(Expression): | |
89 | |
90 def __init__(self, name): | |
91 self.name = name | |
92 | |
93 def GetOutcomes(self, env, defs): | |
94 if self.name in defs: | |
95 return defs[self.name].GetOutcomes(env, defs) | |
96 else: | |
97 return set([self.name]) | |
98 | |
99 def __str__(self): | |
100 if self.name in KEYWORDS: | |
101 return "%s" % KEYWORDS[self.name] | |
102 return "'%s'" % self.name | |
103 | |
104 def string(self, logical): | |
105 if logical: | |
106 return "%s" % self.name | |
107 return self.__str__() | |
108 | |
109 | |
110 class Operation(Expression): | |
111 | |
112 def __init__(self, left, op, right): | |
113 self.left = left | |
114 self.op = op | |
115 self.right = right | |
116 | |
117 def Evaluate(self, env, defs): | |
118 if self.op == '||' or self.op == ',': | |
119 return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs) | |
120 elif self.op == 'if': | |
121 return False | |
122 elif self.op == '==': | |
123 return not self.left.GetOutcomes(env, defs).isdisjoint(self.right.GetOutco
mes(env, defs)) | |
124 elif self.op == '!=': | |
125 return self.left.GetOutcomes(env, defs).isdisjoint(self.right.GetOutcomes(
env, defs)) | |
126 else: | |
127 assert self.op == '&&' | |
128 return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs) | |
129 | |
130 def GetOutcomes(self, env, defs): | |
131 if self.op == '||' or self.op == ',': | |
132 return self.left.GetOutcomes(env, defs) | self.right.GetOutcomes(env, defs
) | |
133 elif self.op == 'if': | |
134 if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs) | |
135 else: return set([]) | |
136 else: | |
137 assert self.op == '&&' | |
138 return self.left.GetOutcomes(env, defs) & self.right.GetOutcomes(env, defs
) | |
139 | |
140 def __str__(self): | |
141 return self.string(False) | |
142 | |
143 def string(self, logical=False): | |
144 if self.op == 'if': | |
145 return "['%s', %s]" % (self.right.string(True), self.left.string(logical)) | |
146 elif self.op == "||" or self.op == ",": | |
147 if logical: | |
148 return "%s or %s" % (self.left.string(True), self.right.string(True)) | |
149 else: | |
150 return "%s, %s" % (self.left, self.right) | |
151 elif self.op == "&&": | |
152 return "%s and %s" % (self.left.string(True), self.right.string(True)) | |
153 return "%s %s %s" % (self.left.string(logical), self.op, | |
154 self.right.string(logical)) | |
155 | |
156 | |
157 def IsAlpha(string): | |
158 for char in string: | |
159 if not (char.isalpha() or char.isdigit() or char == '_'): | |
160 return False | |
161 return True | |
162 | |
163 | |
164 class Tokenizer(object): | |
165 """A simple string tokenizer that chops expressions into variables, | |
166 parens and operators""" | |
167 | |
168 def __init__(self, expr): | |
169 self.index = 0 | |
170 self.expr = expr | |
171 self.length = len(expr) | |
172 self.tokens = None | |
173 | |
174 def Current(self, length=1): | |
175 if not self.HasMore(length): return "" | |
176 return self.expr[self.index:self.index + length] | |
177 | |
178 def HasMore(self, length=1): | |
179 return self.index < self.length + (length - 1) | |
180 | |
181 def Advance(self, count=1): | |
182 self.index = self.index + count | |
183 | |
184 def AddToken(self, token): | |
185 self.tokens.append(token) | |
186 | |
187 def SkipSpaces(self): | |
188 while self.HasMore() and self.Current().isspace(): | |
189 self.Advance() | |
190 | |
191 def Tokenize(self): | |
192 self.tokens = [ ] | |
193 while self.HasMore(): | |
194 self.SkipSpaces() | |
195 if not self.HasMore(): | |
196 return None | |
197 if self.Current() == '(': | |
198 self.AddToken('(') | |
199 self.Advance() | |
200 elif self.Current() == ')': | |
201 self.AddToken(')') | |
202 self.Advance() | |
203 elif self.Current() == '$': | |
204 self.AddToken('$') | |
205 self.Advance() | |
206 elif self.Current() == ',': | |
207 self.AddToken(',') | |
208 self.Advance() | |
209 elif IsAlpha(self.Current()): | |
210 buf = "" | |
211 while self.HasMore() and IsAlpha(self.Current()): | |
212 buf += self.Current() | |
213 self.Advance() | |
214 self.AddToken(buf) | |
215 elif self.Current(2) == '&&': | |
216 self.AddToken('&&') | |
217 self.Advance(2) | |
218 elif self.Current(2) == '||': | |
219 self.AddToken('||') | |
220 self.Advance(2) | |
221 elif self.Current(2) == '==': | |
222 self.AddToken('==') | |
223 self.Advance(2) | |
224 elif self.Current(2) == '!=': | |
225 self.AddToken('!=') | |
226 self.Advance(2) | |
227 else: | |
228 return None | |
229 return self.tokens | |
230 | |
231 | |
232 class Scanner(object): | |
233 """A simple scanner that can serve out tokens from a given list""" | |
234 | |
235 def __init__(self, tokens): | |
236 self.tokens = tokens | |
237 self.length = len(tokens) | |
238 self.index = 0 | |
239 | |
240 def HasMore(self): | |
241 return self.index < self.length | |
242 | |
243 def Current(self): | |
244 return self.tokens[self.index] | |
245 | |
246 def Advance(self): | |
247 self.index = self.index + 1 | |
248 | |
249 | |
250 def ParseAtomicExpression(scan): | |
251 if scan.Current() == "true": | |
252 scan.Advance() | |
253 return Constant(True) | |
254 elif scan.Current() == "false": | |
255 scan.Advance() | |
256 return Constant(False) | |
257 elif IsAlpha(scan.Current()): | |
258 name = scan.Current() | |
259 scan.Advance() | |
260 return Outcome(name) | |
261 elif scan.Current() == '$': | |
262 scan.Advance() | |
263 if not IsAlpha(scan.Current()): | |
264 return None | |
265 name = scan.Current() | |
266 scan.Advance() | |
267 return Variable(name.lower()) | |
268 elif scan.Current() == '(': | |
269 scan.Advance() | |
270 result = ParseLogicalExpression(scan) | |
271 if (not result) or (scan.Current() != ')'): | |
272 return None | |
273 scan.Advance() | |
274 return result | |
275 else: | |
276 return None | |
277 | |
278 | |
279 BINARIES = ['==', '!='] | |
280 def ParseOperatorExpression(scan): | |
281 left = ParseAtomicExpression(scan) | |
282 if not left: return None | |
283 while scan.HasMore() and (scan.Current() in BINARIES): | |
284 op = scan.Current() | |
285 scan.Advance() | |
286 right = ParseOperatorExpression(scan) | |
287 if not right: | |
288 return None | |
289 left = Operation(left, op, right) | |
290 return left | |
291 | |
292 | |
293 def ParseConditionalExpression(scan): | |
294 left = ParseOperatorExpression(scan) | |
295 if not left: return None | |
296 while scan.HasMore() and (scan.Current() == 'if'): | |
297 scan.Advance() | |
298 right = ParseOperatorExpression(scan) | |
299 if not right: | |
300 return None | |
301 left = Operation(left, 'if', right) | |
302 return left | |
303 | |
304 | |
305 LOGICALS = ["&&", "||", ","] | |
306 def ParseLogicalExpression(scan): | |
307 left = ParseConditionalExpression(scan) | |
308 if not left: return None | |
309 while scan.HasMore() and (scan.Current() in LOGICALS): | |
310 op = scan.Current() | |
311 scan.Advance() | |
312 right = ParseConditionalExpression(scan) | |
313 if not right: | |
314 return None | |
315 left = Operation(left, op, right) | |
316 return left | |
317 | |
318 | |
319 def ParseCondition(expr): | |
320 """Parses a logical expression into an Expression object""" | |
321 tokens = Tokenizer(expr).Tokenize() | |
322 if not tokens: | |
323 print "Malformed expression: '%s'" % expr | |
324 return None | |
325 scan = Scanner(tokens) | |
326 ast = ParseLogicalExpression(scan) | |
327 if not ast: | |
328 print "Malformed expression: '%s'" % expr | |
329 return None | |
330 if scan.HasMore(): | |
331 print "Malformed expression: '%s'" % expr | |
332 return None | |
333 return ast | |
334 | |
335 | |
336 class Section(object): | |
337 """A section of the configuration file. Sections are enabled or | |
338 disabled prior to running the tests, based on their conditions""" | |
339 | |
340 def __init__(self, condition): | |
341 self.condition = condition | |
342 self.rules = [ ] | |
343 | |
344 def AddRule(self, rule): | |
345 self.rules.append(rule) | |
346 | |
347 | |
348 class Rule(object): | |
349 """A single rule that specifies the expected outcome for a single | |
350 test.""" | |
351 | |
352 def __init__(self, raw_path, path, value): | |
353 self.raw_path = raw_path | |
354 self.path = path | |
355 self.value = value | |
356 | |
357 def GetOutcomes(self, env, defs): | |
358 return self.value.GetOutcomes(env, defs) | |
359 | |
360 def Contains(self, path): | |
361 if len(self.path) > len(path): | |
362 return False | |
363 for i in xrange(len(self.path)): | |
364 if not self.path[i].match(path[i]): | |
365 return False | |
366 return True | |
367 | |
368 | |
369 HEADER_PATTERN = re.compile(r'\[([^]]+)\]') | |
370 RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)') | |
371 DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$') | |
372 PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$') | |
373 | |
374 | |
375 class ConvertNotation(object): | |
376 def __init__(self, path): | |
377 self.path = path | |
378 self.indent = "" | |
379 self.comment = [] | |
380 self.init = False | |
381 self.section = False | |
382 self.out = cStringIO.StringIO() | |
383 | |
384 def OpenGlobal(self): | |
385 if self.init: return | |
386 self.WriteComment() | |
387 print >> self.out, "[" | |
388 self.init = True | |
389 | |
390 def CloseGlobal(self): | |
391 if not self.init: self.OpenGlobal() | |
392 print >> self.out, "]" | |
393 self.init = False | |
394 | |
395 def OpenSection(self, condition="ALWAYS"): | |
396 if self.section: return | |
397 self.OpenGlobal() | |
398 if type(condition) != str: | |
399 condition = "'%s'" % condition.string(True) | |
400 print >> self.out, "%s[%s, {" % (self.indent, condition) | |
401 self.indent += " " * 2 | |
402 self.section = condition | |
403 | |
404 def CloseSection(self): | |
405 if not self.section: return | |
406 self.indent = self.indent[:-2] | |
407 print >> self.out, "%s}], # %s" % (self.indent, self.section) | |
408 self.section = False | |
409 | |
410 def WriteComment(self): | |
411 if not self.comment: return | |
412 for c in self.comment: | |
413 if len(c.strip()) == 0: | |
414 print >> self.out, "" | |
415 else: | |
416 print >> self.out, "%s%s" % (self.indent, c), | |
417 self.comment = [] | |
418 | |
419 def GetOutput(self): | |
420 with open(self.path) as f: | |
421 for line in f: | |
422 if line[0] == '#': | |
423 self.comment += [line] | |
424 continue | |
425 if len(line.strip()) == 0: | |
426 self.comment += [line] | |
427 continue | |
428 header_match = HEADER_PATTERN.match(line) | |
429 if header_match: | |
430 condition = ParseCondition(header_match.group(1).strip()) | |
431 self.CloseSection() | |
432 self.WriteComment() | |
433 self.OpenSection(condition) | |
434 continue | |
435 rule_match = RULE_PATTERN.match(line) | |
436 if rule_match: | |
437 self.OpenSection() | |
438 self.WriteComment() | |
439 path = rule_match.group(1).strip() | |
440 value_str = rule_match.group(2).strip() | |
441 comment = "" | |
442 if '#' in value_str: | |
443 pos = value_str.find('#') | |
444 comment = " %s" % value_str[pos:].strip() | |
445 value_str = value_str[:pos].strip() | |
446 value = ParseCondition(value_str) | |
447 print >> self.out, ("%s'%s': [%s],%s" % | |
448 (self.indent, path, value, comment)) | |
449 continue | |
450 def_match = DEF_PATTERN.match(line) | |
451 if def_match: | |
452 # Custom definitions are deprecated. | |
453 continue | |
454 prefix_match = PREFIX_PATTERN.match(line) | |
455 if prefix_match: | |
456 continue | |
457 print "Malformed line: '%s'." % line | |
458 self.CloseSection() | |
459 self.CloseGlobal() | |
460 result = self.out.getvalue() | |
461 self.out.close() | |
462 return result | |
OLD | NEW |