My favorites | Sign in
v8
Project Home Downloads Wiki Issues Source Code Search
Checkout   Browse   Changes  
Changes to /trunk/tools/test.py
r12227 vs. r12706 Compare: vs.  Format:
Revision r12706
Go to: 
Project members, sign in to write a code review
/trunk/tools/test.py   r12227 /trunk/tools/test.py   r12706
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2012 the V8 project authors. All rights reserved. 3 # Copyright 2012 the V8 project authors. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without 4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are 5 # modification, are permitted provided that the following conditions are
6 # met: 6 # met:
7 # 7 #
8 # * Redistributions of source code must retain the above copyright 8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer. 9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above 10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following 11 # copyright notice, this list of conditions and the following
12 # disclaimer in the documentation and/or other materials provided 12 # disclaimer in the documentation and/or other materials provided
13 # with the distribution. 13 # with the distribution.
14 # * Neither the name of Google Inc. nor the names of its 14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived 15 # contributors may be used to endorse or promote products derived
16 # from this software without specific prior written permission. 16 # from this software without specific prior written permission.
17 # 17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 29
30 30
31 import imp 31 import imp
32 import optparse 32 import optparse
33 import os 33 import os
34 from os.path import join, dirname, abspath, basename, isdir, exists 34 from os.path import join, dirname, abspath, basename, isdir, exists
35 import platform 35 import platform
36 import re 36 import re
37 import signal 37 import signal
38 import subprocess 38 import subprocess
39 import sys 39 import sys
40 import tempfile 40 import tempfile
41 import time 41 import time
42 import threading 42 import threading
43 import utils 43 import utils
44 from Queue import Queue, Empty 44 from Queue import Queue, Empty
45 45
46 46
47 VERBOSE = False 47 VERBOSE = False
48 48
49 49
50 # --------------------------------------------- 50 # ---------------------------------------------
51 # --- P r o g r e s s I n d i c a t o r s --- 51 # --- P r o g r e s s I n d i c a t o r s ---
52 # --------------------------------------------- 52 # ---------------------------------------------
53 53
54 54
55 class ProgressIndicator(object): 55 class ProgressIndicator(object):
56 56
57 def __init__(self, cases): 57 def __init__(self, cases):
58 self.cases = cases 58 self.cases = cases
59 self.queue = Queue(len(cases)) 59 self.queue = Queue(len(cases))
60 for case in cases: 60 for case in cases:
61 self.queue.put_nowait(case) 61 self.queue.put_nowait(case)
62 self.succeeded = 0 62 self.succeeded = 0
63 self.remaining = len(cases) 63 self.remaining = len(cases)
64 self.total = len(cases) 64 self.total = len(cases)
65 self.failed = [ ] 65 self.failed = [ ]
66 self.crashed = 0 66 self.crashed = 0
67 self.terminate = False 67 self.terminate = False
68 self.lock = threading.Lock() 68 self.lock = threading.Lock()
69 69
70 def PrintFailureHeader(self, test): 70 def PrintFailureHeader(self, test):
71 if test.IsNegative(): 71 if test.IsNegative():
72 negative_marker = '[negative] ' 72 negative_marker = '[negative] '
73 else: 73 else:
74 negative_marker = '' 74 negative_marker = ''
75 print "=== %(label)s %(negative)s===" % { 75 print "=== %(label)s %(negative)s===" % {
76 'label': test.GetLabel(), 76 'label': test.GetLabel(),
77 'negative': negative_marker 77 'negative': negative_marker
78 } 78 }
79 print "Path: %s" % "/".join(test.path) 79 print "Path: %s" % "/".join(test.path)
80 80
81 def Run(self, tasks): 81 def Run(self, tasks):
82 self.Starting() 82 self.Starting()
83 threads = [] 83 threads = []
84 # Spawn N-1 threads and then use this thread as the last one. 84 # Spawn N-1 threads and then use this thread as the last one.
85 # That way -j1 avoids threading altogether which is a nice fallback 85 # That way -j1 avoids threading altogether which is a nice fallback
86 # in case of threading problems. 86 # in case of threading problems.
87 for i in xrange(tasks - 1): 87 for i in xrange(tasks - 1):
88 thread = threading.Thread(target=self.RunSingle, args=[]) 88 thread = threading.Thread(target=self.RunSingle, args=[])
89 threads.append(thread) 89 threads.append(thread)
90 thread.start() 90 thread.start()
91 try: 91 try:
92 self.RunSingle() 92 self.RunSingle()
93 # Wait for the remaining threads 93 # Wait for the remaining threads
94 for thread in threads: 94 for thread in threads:
95 # Use a timeout so that signals (ctrl-c) will be processed. 95 # Use a timeout so that signals (ctrl-c) will be processed.
96 thread.join(timeout=10000000) 96 thread.join(timeout=10000000)
97 except Exception, e: 97 except Exception, e:
98 # If there's an exception we schedule an interruption for any 98 # If there's an exception we schedule an interruption for any
99 # remaining threads. 99 # remaining threads.
100 self.terminate = True 100 self.terminate = True
101 # ...and then reraise the exception to bail out 101 # ...and then reraise the exception to bail out
102 raise 102 raise
103 self.Done() 103 self.Done()
104 return not self.failed 104 return not self.failed
105 105
106 def RunSingle(self): 106 def RunSingle(self):
107 while not self.terminate: 107 while not self.terminate:
108 try: 108 try:
109 test = self.queue.get_nowait() 109 test = self.queue.get_nowait()
110 except Empty: 110 except Empty:
111 return 111 return
112 case = test.case 112 case = test.case
113 self.lock.acquire() 113 self.lock.acquire()
114 self.AboutToRun(case) 114 self.AboutToRun(case)
115 self.lock.release() 115 self.lock.release()
116 try: 116 try:
117 start = time.time() 117 start = time.time()
118 output = case.Run() 118 output = case.Run()
119 case.duration = (time.time() - start) 119 case.duration = (time.time() - start)
120 except BreakNowException: 120 except BreakNowException:
121 self.terminate = True 121 self.terminate = True
122 except IOError, e: 122 except IOError, e:
123 assert self.terminate 123 assert self.terminate
124 return 124 return
125 if self.terminate: 125 if self.terminate:
126 return 126 return
127 self.lock.acquire() 127 self.lock.acquire()
128 if output.UnexpectedOutput(): 128 if output.UnexpectedOutput():
129 self.failed.append(output) 129 self.failed.append(output)
130 if output.HasCrashed(): 130 if output.HasCrashed():
131 self.crashed += 1 131 self.crashed += 1
132 else: 132 else:
133 self.succeeded += 1 133 self.succeeded += 1
134 self.remaining -= 1 134 self.remaining -= 1
135 self.HasRun(output) 135 self.HasRun(output)
136 self.lock.release() 136 self.lock.release()
137 137
138 138
139 def EscapeCommand(command): 139 def EscapeCommand(command):
140 parts = [] 140 parts = []
141 for part in command: 141 for part in command:
142 if ' ' in part: 142 if ' ' in part:
143 # Escape spaces and double quotes. We may need to escape more characters 143 # Escape spaces and double quotes. We may need to escape more characters
144 # for this to work properly. 144 # for this to work properly.
145 parts.append('"%s"' % part.replace('"', '\\"')) 145 parts.append('"%s"' % part.replace('"', '\\"'))
146 else: 146 else:
147 parts.append(part) 147 parts.append(part)
148 return " ".join(parts) 148 return " ".join(parts)
149 149
150 150
151 class SimpleProgressIndicator(ProgressIndicator): 151 class SimpleProgressIndicator(ProgressIndicator):
152 152
153 def Starting(self): 153 def Starting(self):
154 print 'Running %i tests' % len(self.cases) 154 print 'Running %i tests' % len(self.cases)
155 155
156 def Done(self): 156 def Done(self):
157 print 157 print
158 for failed in self.failed: 158 for failed in self.failed:
159 self.PrintFailureHeader(failed.test) 159 self.PrintFailureHeader(failed.test)
160 if failed.output.stderr: 160 if failed.output.stderr:
161 print "--- stderr ---" 161 print "--- stderr ---"
162 print failed.output.stderr.strip() 162 print failed.output.stderr.strip()
163 if failed.output.stdout: 163 if failed.output.stdout:
164 print "--- stdout ---" 164 print "--- stdout ---"
165 print failed.output.stdout.strip() 165 print failed.output.stdout.strip()
166 print "Command: %s" % EscapeCommand(failed.command) 166 print "Command: %s" % EscapeCommand(failed.command)
167 if failed.HasCrashed(): 167 if failed.HasCrashed():
168 print "--- CRASHED ---" 168 print "--- CRASHED ---"
169 if failed.HasTimedOut(): 169 if failed.HasTimedOut():
170 print "--- TIMEOUT ---" 170 print "--- TIMEOUT ---"
171 if len(self.failed) == 0: 171 if len(self.failed) == 0:
172 print "===" 172 print "==="
173 print "=== All tests succeeded" 173 print "=== All tests succeeded"
174 print "===" 174 print "==="
175 else: 175 else:
176 print 176 print
177 print "===" 177 print "==="
178 print "=== %i tests failed" % len(self.failed) 178 print "=== %i tests failed" % len(self.failed)
179 if self.crashed > 0: 179 if self.crashed > 0:
180 print "=== %i tests CRASHED" % self.crashed 180 print "=== %i tests CRASHED" % self.crashed
181 print "===" 181 print "==="
182 182
183 183
184 class VerboseProgressIndicator(SimpleProgressIndicator): 184 class VerboseProgressIndicator(SimpleProgressIndicator):
185 185
186 def AboutToRun(self, case): 186 def AboutToRun(self, case):
187 print 'Starting %s...' % case.GetLabel() 187 print 'Starting %s...' % case.GetLabel()
188 sys.stdout.flush() 188 sys.stdout.flush()
189 189
190 def HasRun(self, output): 190 def HasRun(self, output):
191 if output.UnexpectedOutput(): 191 if output.UnexpectedOutput():
192 if output.HasCrashed(): 192 if output.HasCrashed():
193 outcome = 'CRASH' 193 outcome = 'CRASH'
194 else: 194 else:
195 outcome = 'FAIL' 195 outcome = 'FAIL'
196 else: 196 else:
197 outcome = 'pass' 197 outcome = 'pass'
198 print 'Done running %s: %s' % (output.test.GetLabel(), outcome) 198 print 'Done running %s: %s' % (output.test.GetLabel(), outcome)
199 199
200 200
201 class DotsProgressIndicator(SimpleProgressIndicator): 201 class DotsProgressIndicator(SimpleProgressIndicator):
202 202
203 def AboutToRun(self, case): 203 def AboutToRun(self, case):
204 pass 204 pass
205 205
206 def HasRun(self, output): 206 def HasRun(self, output):
207 total = self.succeeded + len(self.failed) 207 total = self.succeeded + len(self.failed)
208 if (total > 1) and (total % 50 == 1): 208 if (total > 1) and (total % 50 == 1):
209 sys.stdout.write('\n') 209 sys.stdout.write('\n')
210 if output.UnexpectedOutput(): 210 if output.UnexpectedOutput():
211 if output.HasCrashed(): 211 if output.HasCrashed():
212 sys.stdout.write('C') 212 sys.stdout.write('C')
213 sys.stdout.flush() 213 sys.stdout.flush()
214 elif output.HasTimedOut(): 214 elif output.HasTimedOut():
215 sys.stdout.write('T') 215 sys.stdout.write('T')
216 sys.stdout.flush() 216 sys.stdout.flush()
217 else: 217 else:
218 sys.stdout.write('F') 218 sys.stdout.write('F')
219 sys.stdout.flush() 219 sys.stdout.flush()
220 else: 220 else:
221 sys.stdout.write('.') 221 sys.stdout.write('.')
222 sys.stdout.flush() 222 sys.stdout.flush()
223 223
224 224
225 class CompactProgressIndicator(ProgressIndicator): 225 class CompactProgressIndicator(ProgressIndicator):
226 226
227 def __init__(self, cases, templates): 227 def __init__(self, cases, templates):
228 super(CompactProgressIndicator, self).__init__(cases) 228 super(CompactProgressIndicator, self).__init__(cases)
229 self.templates = templates 229 self.templates = templates
230 self.last_status_length = 0 230 self.last_status_length = 0
231 self.start_time = time.time() 231 self.start_time = time.time()
232 232
233 def Starting(self): 233 def Starting(self):
234 pass 234 pass
235 235
236 def Done(self): 236 def Done(self):
237 self.PrintProgress('Done') 237 self.PrintProgress('Done')
238 238
239 def AboutToRun(self, case): 239 def AboutToRun(self, case):
240 self.PrintProgress(case.GetLabel()) 240 self.PrintProgress(case.GetLabel())
241 241
242 def HasRun(self, output): 242 def HasRun(self, output):
243 if output.UnexpectedOutput(): 243 if output.UnexpectedOutput():
244 self.ClearLine(self.last_status_length) 244 self.ClearLine(self.last_status_length)
245 self.PrintFailureHeader(output.test) 245 self.PrintFailureHeader(output.test)
246 stdout = output.output.stdout.strip() 246 stdout = output.output.stdout.strip()
247 if len(stdout): 247 if len(stdout):
248 print self.templates['stdout'] % stdout 248 print self.templates['stdout'] % stdout
249 stderr = output.output.stderr.strip() 249 stderr = output.output.stderr.strip()
250 if len(stderr): 250 if len(stderr):
251 print self.templates['stderr'] % stderr 251 print self.templates['stderr'] % stderr
252 print "Command: %s" % EscapeCommand(output.command) 252 print "Command: %s" % EscapeCommand(output.command)
253 if output.HasCrashed(): 253 if output.HasCrashed():
254 print "--- CRASHED ---" 254 print "--- CRASHED ---"
255 if output.HasTimedOut(): 255 if output.HasTimedOut():
256 print "--- TIMEOUT ---" 256 print "--- TIMEOUT ---"
257 257
258 def Truncate(self, str, length): 258 def Truncate(self, str, length):
259 if length and (len(str) > (length - 3)): 259 if length and (len(str) > (length - 3)):
260 return str[:(length-3)] + "..." 260 return str[:(length-3)] + "..."
261 else: 261 else:
262 return str 262 return str
263 263
264 def PrintProgress(self, name): 264 def PrintProgress(self, name):
265 self.ClearLine(self.last_status_length) 265 self.ClearLine(self.last_status_length)
266 elapsed = time.time() - self.start_time 266 elapsed = time.time() - self.start_time
267 status = self.templates['status_line'] % { 267 status = self.templates['status_line'] % {
268 'passed': self.succeeded, 268 'passed': self.succeeded,
269 'remaining': (((self.total - self.remaining) * 100) // self.total), 269 'remaining': (((self.total - self.remaining) * 100) // self.total),
270 'failed': len(self.failed), 270 'failed': len(self.failed),
271 'test': name, 271 'test': name,
272 'mins': int(elapsed) / 60, 272 'mins': int(elapsed) / 60,
273 'secs': int(elapsed) % 60 273 'secs': int(elapsed) % 60
274 } 274 }
275 status = self.Truncate(status, 78) 275 status = self.Truncate(status, 78)
276 self.last_status_length = len(status) 276 self.last_status_length = len(status)
277 print status, 277 print status,
278 sys.stdout.flush() 278 sys.stdout.flush()
279 279
280 280
281 class ColorProgressIndicator(CompactProgressIndicator): 281 class ColorProgressIndicator(CompactProgressIndicator):
282 282
283 def __init__(self, cases): 283 def __init__(self, cases):
284 templates = { 284 templates = {
285 'status_line': "[%(mins)02i:%(secs)02i|\033[34m%%%(remaining) 4d\033[0m|\033[32m+%(passed) 4d\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s", 285 'status_line': "[%(mins)02i:%(secs)02i|\033[34m%%%(remaining) 4d\033[0m|\033[32m+%(passed) 4d\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s",
286 'stdout': "\033[1m%s\033[0m", 286 'stdout': "\033[1m%s\033[0m",
287 'stderr': "\033[31m%s\033[0m", 287 'stderr': "\033[31m%s\033[0m",
288 } 288 }
289 super(ColorProgressIndicator, self).__init__(cases, templates) 289 super(ColorProgressIndicator, self).__init__(cases, templates)
290 290
291 def ClearLine(self, last_line_length): 291 def ClearLine(self, last_line_length):
292 print "\033[1K\r", 292 print "\033[1K\r",
293 293
294 294
295 class MonochromeProgressIndicator(CompactProgressIndicator): 295 class MonochromeProgressIndicator(CompactProgressIndicator):
296 296
297 def __init__(self, cases): 297 def __init__(self, cases):
298 templates = { 298 templates = {
299 'status_line': "[%(mins)02i:%(secs)02i|%%%(remaining) 4d|+%(passed) 4d|-%(failed) 4d]: %(test)s", 299 'status_line': "[%(mins)02i:%(secs)02i|%%%(remaining) 4d|+%(passed) 4d|-%(failed) 4d]: %(test)s",
300 'stdout': '%s', 300 'stdout': '%s',
301 'stderr': '%s', 301 'stderr': '%s',
302 } 302 }
303 super(MonochromeProgressIndicator, self).__init__(cases, templates) 303 super(MonochromeProgressIndicator, self).__init__(cases, templates)
304 304
305 def ClearLine(self, last_line_length): 305 def ClearLine(self, last_line_length):
306 print ("\r" + (" " * last_line_length) + "\r"), 306 print ("\r" + (" " * last_line_length) + "\r"),
307 307
308 308
309 PROGRESS_INDICATORS = { 309 PROGRESS_INDICATORS = {
310 'verbose': VerboseProgressIndicator, 310 'verbose': VerboseProgressIndicator,
311 'dots': DotsProgressIndicator, 311 'dots': DotsProgressIndicator,
312 'color': ColorProgressIndicator, 312 'color': ColorProgressIndicator,
313 'mono': MonochromeProgressIndicator 313 'mono': MonochromeProgressIndicator
314 } 314 }
315 315
316 316
317 # ------------------------- 317 # -------------------------
318 # --- F r a m e w o r k --- 318 # --- F r a m e w o r k ---
319 # ------------------------- 319 # -------------------------
320 320
321 class BreakNowException(Exception): 321 class BreakNowException(Exception):
322 def __init__(self, value): 322 def __init__(self, value):
323 self.value = value 323 self.value = value
324 def __str__(self): 324 def __str__(self):
325 return repr(self.value) 325 return repr(self.value)
326 326
327 327
328 class CommandOutput(object): 328 class CommandOutput(object):
329 329
330 def __init__(self, exit_code, timed_out, stdout, stderr): 330 def __init__(self, exit_code, timed_out, stdout, stderr):
331 self.exit_code = exit_code 331 self.exit_code = exit_code
332 self.timed_out = timed_out 332 self.timed_out = timed_out
333 self.stdout = stdout 333 self.stdout = stdout
334 self.stderr = stderr 334 self.stderr = stderr
335 self.failed = None 335 self.failed = None
336 336
337 337
338 class TestCase(object): 338 class TestCase(object):
339 339
340 def __init__(self, context, path, mode): 340 def __init__(self, context, path, mode):
341 self.path = path 341 self.path = path
342 self.context = context 342 self.context = context
343 self.duration = None 343 self.duration = None
344 self.mode = mode 344 self.mode = mode
345 345
346 def IsNegative(self): 346 def IsNegative(self):
347 return False 347 return False
348 348
349 def TestsIsolates(self): 349 def TestsIsolates(self):
350 return False 350 return False
351 351
352 def CompareTime(self, other): 352 def CompareTime(self, other):
353 return cmp(other.duration, self.duration) 353 return cmp(other.duration, self.duration)
354 354
355 def DidFail(self, output): 355 def DidFail(self, output):
356 if output.failed is None: 356 if output.failed is None:
357 output.failed = self.IsFailureOutput(output) 357 output.failed = self.IsFailureOutput(output)
358 return output.failed 358 return output.failed
359 359
360 def IsFailureOutput(self, output): 360 def IsFailureOutput(self, output):
361 return output.exit_code != 0 361 return output.exit_code != 0
362 362
363 def GetSource(self): 363 def GetSource(self):
364 return "(no source available)" 364 return "(no source available)"
365 365
366 def RunCommand(self, command): 366 def RunCommand(self, command):
367 full_command = self.context.processor(command) 367 full_command = self.context.processor(command)
368 output = Execute(full_command, 368 output = Execute(full_command,
369 self.context, 369 self.context,
370 self.context.GetTimeout(self, self.mode)) 370 self.context.GetTimeout(self, self.mode))
371 self.Cleanup() 371 self.Cleanup()
372 return TestOutput(self, 372 return TestOutput(self,
373 full_command, 373 full_command,
374 output, 374 output,
375 self.context.store_unexpected_output) 375 self.context.store_unexpected_output)
376 376
377 def BeforeRun(self): 377 def BeforeRun(self):
378 pass 378 pass
379 379
380 def AfterRun(self, result): 380 def AfterRun(self, result):
381 pass 381 pass
382 382
383 def GetCustomFlags(self, mode): 383 def GetCustomFlags(self, mode):
384 return None 384 return None
385 385
386 def Run(self): 386 def Run(self):
387 self.BeforeRun() 387 self.BeforeRun()
388 result = None 388 result = None
389 try: 389 try:
390 result = self.RunCommand(self.GetCommand()) 390 result = self.RunCommand(self.GetCommand())
391 except: 391 except:
392 self.terminate = True 392 self.terminate = True
393 raise BreakNowException("User pressed CTRL+C or IO went wrong") 393 raise BreakNowException("User pressed CTRL+C or IO went wrong")
394 finally: 394 finally:
395 self.AfterRun(result) 395 self.AfterRun(result)
396 return result 396 return result
397 397
398 def Cleanup(self): 398 def Cleanup(self):
399 return 399 return
400 400
401 401
402 class TestOutput(object): 402 class TestOutput(object):
403 403
404 def __init__(self, test, command, output, store_unexpected_output): 404 def __init__(self, test, command, output, store_unexpected_output):
405 self.test = test 405 self.test = test
406 self.command = command 406 self.command = command
407 self.output = output 407 self.output = output
408 self.store_unexpected_output = store_unexpected_output 408 self.store_unexpected_output = store_unexpected_output
409 409
410 def UnexpectedOutput(self): 410 def UnexpectedOutput(self):
411 if self.HasCrashed(): 411 if self.HasCrashed():
412 outcome = CRASH 412 outcome = CRASH
413 elif self.HasTimedOut(): 413 elif self.HasTimedOut():
414 outcome = TIMEOUT 414 outcome = TIMEOUT
415 elif self.HasFailed(): 415 elif self.HasFailed():
416 outcome = FAIL 416 outcome = FAIL
417 else: 417 else:
418 outcome = PASS 418 outcome = PASS
419 return not outcome in self.test.outcomes 419 return not outcome in self.test.outcomes
420 420
421 def HasPreciousOutput(self): 421 def HasPreciousOutput(self):
422 return self.UnexpectedOutput() and self.store_unexpected_output 422 return self.UnexpectedOutput() and self.store_unexpected_output
423 423
424 def HasCrashed(self): 424 def HasCrashed(self):
425 if utils.IsWindows(): 425 if utils.IsWindows():
426 return 0x80000000 & self.output.exit_code and not (0x3FFFFF00 & self.output.exit_code) 426 return 0x80000000 & self.output.exit_code and not (0x3FFFFF00 & self.output.exit_code)
427 else: 427 else:
428 # Timed out tests will have exit_code -signal.SIGTERM. 428 # Timed out tests will have exit_code -signal.SIGTERM.
429 if self.output.timed_out: 429 if self.output.timed_out:
430 return False 430 return False
431 return self.output.exit_code < 0 and \ 431 return self.output.exit_code < 0 and \
432 self.output.exit_code != -signal.SIGABRT 432 self.output.exit_code != -signal.SIGABRT
433 433
434 def HasTimedOut(self): 434 def HasTimedOut(self):
435 return self.output.timed_out 435 return self.output.timed_out
436 436
437 def HasFailed(self): 437 def HasFailed(self):
438 execution_failed = self.test.DidFail(self.output) 438 execution_failed = self.test.DidFail(self.output)
439 if self.test.IsNegative(): 439 if self.test.IsNegative():
440 return not execution_failed 440 return not execution_failed
441 else: 441 else:
442 return execution_failed 442 return execution_failed
443 443
444 444
445 def KillProcessWithID(pid): 445 def KillProcessWithID(pid):
446 if utils.IsWindows(): 446 if utils.IsWindows():
447 os.popen('taskkill /T /F /PID %d' % pid) 447 os.popen('taskkill /T /F /PID %d' % pid)
448 else: 448 else:
449 os.kill(pid, signal.SIGTERM) 449 os.kill(pid, signal.SIGTERM)
450 450
451 451
452 MAX_SLEEP_TIME = 0.1 452 MAX_SLEEP_TIME = 0.1
453 INITIAL_SLEEP_TIME = 0.0001 453 INITIAL_SLEEP_TIME = 0.0001
454 SLEEP_TIME_FACTOR = 1.25 454 SLEEP_TIME_FACTOR = 1.25
455 455
456 SEM_INVALID_VALUE = -1 456 SEM_INVALID_VALUE = -1
457 SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h 457 SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h
458 458
459 def Win32SetErrorMode(mode): 459 def Win32SetErrorMode(mode):
460 prev_error_mode = SEM_INVALID_VALUE 460 prev_error_mode = SEM_INVALID_VALUE
461 try: 461 try:
462 import ctypes 462 import ctypes
463 prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode) 463 prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode)
464 except ImportError: 464 except ImportError:
465 pass 465 pass
466 return prev_error_mode 466 return prev_error_mode
467 467
468 def RunProcess(context, timeout, args, **rest): 468 def RunProcess(context, timeout, args, **rest):
469 if context.verbose: print "#", " ".join(args) 469 if context.verbose: print "#", " ".join(args)
470 popen_args = args 470 popen_args = args
471 prev_error_mode = SEM_INVALID_VALUE 471 prev_error_mode = SEM_INVALID_VALUE
472 if utils.IsWindows(): 472 if utils.IsWindows():
473 popen_args = subprocess.list2cmdline(args) 473 popen_args = subprocess.list2cmdline(args)
474 if context.suppress_dialogs: 474 if context.suppress_dialogs:
475 # Try to change the error mode to avoid dialogs on fatal errors. Don't 475 # Try to change the error mode to avoid dialogs on fatal errors. Don't
476 # touch any existing error mode flags by merging the existing error mode. 476 # touch any existing error mode flags by merging the existing error mode.
477 # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx. 477 # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx.
478 error_mode = SEM_NOGPFAULTERRORBOX 478 error_mode = SEM_NOGPFAULTERRORBOX
479 prev_error_mode = Win32SetErrorMode(error_mode) 479 prev_error_mode = Win32SetErrorMode(error_mode)
480 Win32SetErrorMode(error_mode | prev_error_mode) 480 Win32SetErrorMode(error_mode | prev_error_mode)
481 process = subprocess.Popen( 481 process = subprocess.Popen(
482 shell = utils.IsWindows(), 482 shell = utils.IsWindows(),
483 args = popen_args, 483 args = popen_args,
484 **rest 484 **rest
485 ) 485 )
486 if utils.IsWindows() and context.suppress_dialogs and prev_error_mode != SEM_INVALID_VALUE: 486 if utils.IsWindows() and context.suppress_dialogs and prev_error_mode != SEM_INVALID_VALUE:
487 Win32SetErrorMode(prev_error_mode) 487 Win32SetErrorMode(prev_error_mode)
488 # Compute the end time - if the process crosses this limit we 488 # Compute the end time - if the process crosses this limit we
489 # consider it timed out. 489 # consider it timed out.
490 if timeout is None: end_time = None 490 if timeout is None: end_time = None
491 else: end_time = time.time() + timeout 491 else: end_time = time.time() + timeout
492 timed_out = False 492 timed_out = False
493 # Repeatedly check the exit code from the process in a 493 # Repeatedly check the exit code from the process in a
494 # loop and keep track of whether or not it times out. 494 # loop and keep track of whether or not it times out.
495 exit_code = None 495 exit_code = None
496 sleep_time = INITIAL_SLEEP_TIME 496 sleep_time = INITIAL_SLEEP_TIME
497 while exit_code is None: 497 while exit_code is None:
498 if (not end_time is None) and (time.time() >= end_time): 498 if (not end_time is None) and (time.time() >= end_time):
499 # Kill the process and wait for it to exit. 499 # Kill the process and wait for it to exit.
500 KillProcessWithID(process.pid) 500 KillProcessWithID(process.pid)
501 exit_code = process.wait() 501 exit_code = process.wait()
502 timed_out = True 502 timed_out = True
503 else: 503 else:
504 exit_code = process.poll() 504 exit_code = process.poll()
505 time.sleep(sleep_time) 505 time.sleep(sleep_time)
506 sleep_time = sleep_time * SLEEP_TIME_FACTOR 506 sleep_time = sleep_time * SLEEP_TIME_FACTOR
507 if sleep_time > MAX_SLEEP_TIME: 507 if sleep_time > MAX_SLEEP_TIME:
508 sleep_time = MAX_SLEEP_TIME 508 sleep_time = MAX_SLEEP_TIME
509 return (process, exit_code, timed_out) 509 return (process, exit_code, timed_out)
510 510
511 511
512 def PrintError(str): 512 def PrintError(str):
513 sys.stderr.write(str) 513 sys.stderr.write(str)
514 sys.stderr.write('\n') 514 sys.stderr.write('\n')
515 515
516 516
517 def CheckedUnlink(name): 517 def CheckedUnlink(name):
518 # On Windows, when run with -jN in parallel processes, 518 # On Windows, when run with -jN in parallel processes,
519 # OS often fails to unlink the temp file. Not sure why. 519 # OS often fails to unlink the temp file. Not sure why.
520 # Need to retry. 520 # Need to retry.
521 # Idea from https://bugs.webkit.org/attachment.cgi?id=75982&action=prettypatch 521 # Idea from https://bugs.webkit.org/attachment.cgi?id=75982&action=prettypatch
522 retry_count = 0 522 retry_count = 0
523 while retry_count < 30: 523 while retry_count < 30:
524 try: 524 try:
525 os.unlink(name) 525 os.unlink(name)
526 return 526 return
527 except OSError, e: 527 except OSError, e:
528 retry_count += 1 528 retry_count += 1
529 time.sleep(retry_count * 0.1) 529 time.sleep(retry_count * 0.1)
530 PrintError("os.unlink() " + str(e)) 530 PrintError("os.unlink() " + str(e))
531 531
532 def Execute(args, context, timeout=None): 532 def Execute(args, context, timeout=None):
533 (fd_out, outname) = tempfile.mkstemp() 533 (fd_out, outname) = tempfile.mkstemp()
534 (fd_err, errname) = tempfile.mkstemp() 534 (fd_err, errname) = tempfile.mkstemp()
535 (process, exit_code, timed_out) = RunProcess( 535 (process, exit_code, timed_out) = RunProcess(
536 context, 536 context,
537 timeout, 537 timeout,
538 args = args, 538 args = args,
539 stdout = fd_out, 539 stdout = fd_out,
540 stderr = fd_err, 540 stderr = fd_err,
541 ) 541 )
542 os.close(fd_out) 542 os.close(fd_out)
543 os.close(fd_err) 543 os.close(fd_err)
544 output = file(outname).read() 544 output = file(outname).read()
545 errors = file(errname).read() 545 errors = file(errname).read()
546 CheckedUnlink(outname) 546 CheckedUnlink(outname)
547 CheckedUnlink(errname) 547 CheckedUnlink(errname)
548 return CommandOutput(exit_code, timed_out, output, errors) 548 return CommandOutput(exit_code, timed_out, output, errors)
549 549
550 550
551 def ExecuteNoCapture(args, context, timeout=None): 551 def ExecuteNoCapture(args, context, timeout=None):
552 (process, exit_code, timed_out) = RunProcess( 552 (process, exit_code, timed_out) = RunProcess(
553 context, 553 context,
554 timeout, 554 timeout,
555 args = args, 555 args = args,
556 ) 556 )
557 return CommandOutput(exit_code, False, "", "") 557 return CommandOutput(exit_code, False, "", "")
558 558
559 559
560 def CarCdr(path): 560 def CarCdr(path):
561 if len(path) == 0: 561 if len(path) == 0:
562 return (None, [ ]) 562 return (None, [ ])
563 else: 563 else:
564 return (path[0], path[1:]) 564 return (path[0], path[1:])
565 565
566 566
567 # Use this to run several variants of the tests, e.g.: 567 # Use this to run several variants of the tests, e.g.:
568 # VARIANT_FLAGS = [[], ['--always_compact', '--noflush_code']] 568 # VARIANT_FLAGS = [[], ['--always_compact', '--noflush_code']]
569 VARIANT_FLAGS = [[], 569 VARIANT_FLAGS = [[],
570 ['--stress-opt', '--always-opt'], 570 ['--stress-opt', '--always-opt'],
571 ['--nocrankshaft']] 571 ['--nocrankshaft']]
572 572
573 573
574 class TestConfiguration(object): 574 class TestConfiguration(object):
575 575
576 def __init__(self, context, root): 576 def __init__(self, context, root):
577 self.context = context 577 self.context = context
578 self.root = root 578 self.root = root
579 579
580 def Contains(self, path, file): 580 def Contains(self, path, file):
581 if len(path) > len(file): 581 if len(path) > len(file):
582 return False 582 return False
583 for i in xrange(len(path)): 583 for i in xrange(len(path)):
584 if not path[i].match(file[i]): 584 if not path[i].match(file[i]):
585 return False 585 return False
586 return True 586 return True
587 587
588 def GetTestStatus(self, sections, defs): 588 def GetTestStatus(self, sections, defs):
589 pass 589 pass
590 590
591 def VariantFlags(self): 591 def VariantFlags(self):
592 return VARIANT_FLAGS 592 return VARIANT_FLAGS
593 593
594 594
595 595
596 596
597 class TestSuite(object): 597 class TestSuite(object):
598 598
599 def __init__(self, name): 599 def __init__(self, name):
600 self.name = name 600 self.name = name
601 601
602 def GetName(self): 602 def GetName(self):
603 return self.name 603 return self.name
604 604
605 605
606 class TestRepository(TestSuite): 606 class TestRepository(TestSuite):
607 607
608 def __init__(self, path): 608 def __init__(self, path):
609 normalized_path = abspath(path) 609 normalized_path = abspath(path)
610 super(TestRepository, self).__init__(basename(normalized_path)) 610 super(TestRepository, self).__init__(basename(normalized_path))
611 self.path = normalized_path 611 self.path = normalized_path
612 self.is_loaded = False 612 self.is_loaded = False
613 self.config = None 613 self.config = None
614 614
615 def GetConfiguration(self, context): 615 def GetConfiguration(self, context):
616 if self.is_loaded: 616 if self.is_loaded:
617 return self.config 617 return self.config
618 self.is_loaded = True 618 self.is_loaded = True
619 file = None 619 file = None
620 try: 620 try:
621 (file, pathname, description) = imp.find_module('testcfg', [ self.path ]) 621 (file, pathname, description) = imp.find_module('testcfg', [ self.path ])
622 module = imp.load_module('testcfg', file, pathname, description) 622 module = imp.load_module('testcfg', file, pathname, description)
623 self.config = module.GetConfiguration(context, self.path) 623 self.config = module.GetConfiguration(context, self.path)
624 finally: 624 finally:
625 if file: 625 if file:
626 file.close() 626 file.close()
627 return self.config 627 return self.config
628 628
629 def GetBuildRequirements(self, path, context): 629 def GetBuildRequirements(self, path, context):
630 return self.GetConfiguration(context).GetBuildRequirements() 630 return self.GetConfiguration(context).GetBuildRequirements()
631 631
632 def DownloadData(self, context): 632 def DownloadData(self, context):
633 config = self.GetConfiguration(context) 633 config = self.GetConfiguration(context)
634 if 'DownloadData' in dir(config): 634 if 'DownloadData' in dir(config):
635 config.DownloadData() 635 config.DownloadData()
636 636
637 def AddTestsToList(self, result, current_path, path, context, mode): 637 def AddTestsToList(self, result, current_path, path, context, mode):
638 config = self.GetConfiguration(context) 638 config = self.GetConfiguration(context)
639 for v in config.VariantFlags(): 639 for v in config.VariantFlags():
640 tests = config.ListTests(current_path, path, mode, v) 640 tests = config.ListTests(current_path, path, mode, v)
641 for t in tests: t.variant_flags = v 641 for t in tests: t.variant_flags = v
642 result += tests 642 result += tests
643 643
644 def GetTestStatus(self, context, sections, defs): 644 def GetTestStatus(self, context, sections, defs):
645 self.GetConfiguration(context).GetTestStatus(sections, defs) 645 self.GetConfiguration(context).GetTestStatus(sections, defs)
646 646
647 647
648 class LiteralTestSuite(TestSuite): 648 class LiteralTestSuite(TestSuite):
649 649
650 def __init__(self, tests): 650 def __init__(self, tests):
651 super(LiteralTestSuite, self).__init__('root') 651 super(LiteralTestSuite, self).__init__('root')
652 self.tests = tests 652 self.tests = tests
653 653
654 def GetBuildRequirements(self, path, context): 654 def GetBuildRequirements(self, path, context):
655 (name, rest) = CarCdr(path) 655 (name, rest) = CarCdr(path)
656 result = [ ] 656 result = [ ]
657 for test in self.tests: 657 for test in self.tests:
658 if not name or name.match(test.GetName()): 658 if not name or name.match(test.GetName()):
659 result += test.GetBuildRequirements(rest, context) 659 result += test.GetBuildRequirements(rest, context)
660 return result 660 return result
661 661
662 def DownloadData(self, path, context): 662 def DownloadData(self, path, context):
663 (name, rest) = CarCdr(path) 663 (name, rest) = CarCdr(path)
664 for test in self.tests: 664 for test in self.tests:
665 if not name or name.match(test.GetName()): 665 if not name or name.match(test.GetName()):
666 test.DownloadData(context) 666 test.DownloadData(context)
667 667
668 def ListTests(self, current_path, path, context, mode, variant_flags): 668 def ListTests(self, current_path, path, context, mode, variant_flags):
669 (name, rest) = CarCdr(path) 669 (name, rest) = CarCdr(path)
670 result = [ ] 670 result = [ ]
671 for test in self.tests: 671 for test in self.tests:
672 test_name = test.GetName() 672 test_name = test.GetName()
673 if not name or name.match(test_name): 673 if not name or name.match(test_name):
674 full_path = current_path + [test_name] 674 full_path = current_path + [test_name]
675 test.AddTestsToList(result, full_path, path, context, mode) 675 test.AddTestsToList(result, full_path, path, context, mode)
676 return result 676 return result
677 677
678 def GetTestStatus(self, context, sections, defs): 678 def GetTestStatus(self, context, sections, defs):
679 for test in self.tests: 679 for test in self.tests:
680 test.GetTestStatus(context, sections, defs) 680 test.GetTestStatus(context, sections, defs)
681 681
682 682
683 SUFFIX = { 683 SUFFIX = {
684 'debug' : '_g', 684 'debug' : '_g',
685 'release' : '' } 685 'release' : '' }
686 FLAGS = { 686 FLAGS = {
687 'debug' : ['--nobreak-on-abort', '--enable-slow-asserts', '--debug-code', '--verify-heap'], 687 'debug' : ['--nobreak-on-abort', '--nodead-code-elimination',
688 'release' : ['--nobreak-on-abort']} 688 '--enable-slow-asserts', '--debug-code', '--verify-heap'],
689 'release' : ['--nobreak-on-abort', '--nodead-code-elimination']}
689 TIMEOUT_SCALEFACTOR = { 690 TIMEOUT_SCALEFACTOR = {
690 'debug' : 4, 691 'debug' : 4,
691 'release' : 1 } 692 'release' : 1 }
692 693
693 694
694 class Context(object): 695 class Context(object):
695 696
696 def __init__(self, workspace, buildspace, verbose, vm, timeout, processor, suppress_dialogs, store_unexpected_output): 697 def __init__(self, workspace, buildspace, verbose, vm, timeout, processor, suppress_dialogs, store_unexpected_output):
697 self.workspace = workspace 698 self.workspace = workspace
698 self.buildspace = buildspace 699 self.buildspace = buildspace
699 self.verbose = verbose 700 self.verbose = verbose
700 self.vm_root = vm 701 self.vm_root = vm
701 self.timeout = timeout 702 self.timeout = timeout
702 self.processor = processor 703 self.processor = processor
703 self.suppress_dialogs = suppress_dialogs 704 self.suppress_dialogs = suppress_dialogs
704 self.store_unexpected_output = store_unexpected_output 705 self.store_unexpected_output = store_unexpected_output
705 706
706 def GetVm(self, mode): 707 def GetVm(self, mode):
707 name = self.vm_root + SUFFIX[mode] 708 name = self.vm_root + SUFFIX[mode]
708 if utils.IsWindows() and not name.endswith('.exe'): 709 if utils.IsWindows() and not name.endswith('.exe'):
709 name = name + '.exe' 710 name = name + '.exe'
710 return name 711 return name
711 712
712 def GetVmCommand(self, testcase, mode): 713 def GetVmCommand(self, testcase, mode):
713 return [self.GetVm(mode)] + self.GetVmFlags(testcase, mode) 714 return [self.GetVm(mode)] + self.GetVmFlags(testcase, mode)
714 715
715 def GetVmFlags(self, testcase, mode): 716 def GetVmFlags(self, testcase, mode):
716 flags = testcase.GetCustomFlags(mode) 717 flags = testcase.GetCustomFlags(mode)
717 if flags is None: 718 if flags is None:
718 flags = FLAGS[mode] 719 flags = FLAGS[mode]
719 return testcase.variant_flags + flags 720 return testcase.variant_flags + flags
720 721
721 def GetTimeout(self, testcase, mode): 722 def GetTimeout(self, testcase, mode):
722 result = self.timeout * TIMEOUT_SCALEFACTOR[mode] 723 result = self.timeout * TIMEOUT_SCALEFACTOR[mode]
723 if '--stress-opt' in self.GetVmFlags(testcase, mode): 724 if '--stress-opt' in self.GetVmFlags(testcase, mode):
724 return result * 4 725 return result * 4
725 else: 726 else:
726 return result 727 return result
727 728
728 def RunTestCases(cases_to_run, progress, tasks): 729 def RunTestCases(cases_to_run, progress, tasks):
729 progress = PROGRESS_INDICATORS[progress](cases_to_run) 730 progress = PROGRESS_INDICATORS[progress](cases_to_run)
730 result = 0 731 result = 0
731 try: 732 try:
732 result = progress.Run(tasks) 733 result = progress.Run(tasks)
733 except Exception, e: 734 except Exception, e:
734 print "\n", e 735 print "\n", e
735 return result 736 return result
736 737
737 738
738 def BuildRequirements(context, requirements, mode, scons_flags): 739 def BuildRequirements(context, requirements, mode, scons_flags):
739 command_line = (['scons', '-Y', context.workspace, 'mode=' + ",".join(mode)] 740 command_line = (['scons', '-Y', context.workspace, 'mode=' + ",".join(mode)]
740 + requirements 741 + requirements
741 + scons_flags) 742 + scons_flags)
742 output = ExecuteNoCapture(command_line, context) 743 output = ExecuteNoCapture(command_line, context)
743 return output.exit_code == 0 744 return output.exit_code == 0
744 745
745 746
746 # ------------------------------------------- 747 # -------------------------------------------
747 # --- T e s t C o n f i g u r a t i o n --- 748 # --- T e s t C o n f i g u r a t i o n ---
748 # ------------------------------------------- 749 # -------------------------------------------
749 750
750 751
751 SKIP = 'skip' 752 SKIP = 'skip'
752 FAIL = 'fail' 753 FAIL = 'fail'
753 PASS = 'pass' 754 PASS = 'pass'
754 OKAY = 'okay' 755 OKAY = 'okay'
755 TIMEOUT = 'timeout' 756 TIMEOUT = 'timeout'
756 CRASH = 'crash' 757 CRASH = 'crash'
757 SLOW = 'slow' 758 SLOW = 'slow'
758 759
759 760
760 class Expression(object): 761 class Expression(object):
761 pass 762 pass
762 763
763 764
764 class Constant(Expression): 765 class Constant(Expression):
765 766
766 def __init__(self, value): 767 def __init__(self, value):
767 self.value = value 768 self.value = value
768 769
769 def Evaluate(self, env, defs): 770 def Evaluate(self, env, defs):
770 return self.value 771 return self.value
771 772
772 773
773 class Variable(Expression): 774 class Variable(Expression):
774 775
775 def __init__(self, name): 776 def __init__(self, name):
776 self.name = name 777 self.name = name
777 778
778 def GetOutcomes(self, env, defs): 779 def GetOutcomes(self, env, defs):
779 if self.name in env: return ListSet([env[self.name]]) 780 if self.name in env: return ListSet([env[self.name]])
780 else: return Nothing() 781 else: return Nothing()
781 782
782 def Evaluate(self, env, defs): 783 def Evaluate(self, env, defs):
783 return env[self.name] 784 return env[self.name]
784 785
785 786
786 class Outcome(Expression): 787 class Outcome(Expression):
787 788
788 def __init__(self, name): 789 def __init__(self, name):
789 self.name = name 790 self.name = name
790 791
791 def GetOutcomes(self, env, defs): 792 def GetOutcomes(self, env, defs):
792 if self.name in defs: 793 if self.name in defs:
793 return defs[self.name].GetOutcomes(env, defs) 794 return defs[self.name].GetOutcomes(env, defs)
794 else: 795 else:
795 return ListSet([self.name]) 796 return ListSet([self.name])
796 797
797 798
798 class Set(object): 799 class Set(object):
799 pass 800 pass
800 801
801 802
802 class ListSet(Set): 803 class ListSet(Set):
803 804
804 def __init__(self, elms): 805 def __init__(self, elms):
805 self.elms = elms 806 self.elms = elms
806 807
807 def __str__(self): 808 def __str__(self):
808 return "ListSet%s" % str(self.elms) 809 return "ListSet%s" % str(self.elms)
809 810
810 def Intersect(self, that): 811 def Intersect(self, that):
811 if not isinstance(that, ListSet): 812 if not isinstance(that, ListSet):
812 return that.Intersect(self) 813 return that.Intersect(self)
813 return ListSet([ x for x in self.elms if x in that.elms ]) 814 return ListSet([ x for x in self.elms if x in that.elms ])
814 815
815 def Union(self, that): 816 def Union(self, that):
816 if not isinstance(that, ListSet): 817 if not isinstance(that, ListSet):
817 return that.Union(self) 818 return that.Union(self)
818 return ListSet(self.elms + [ x for x in that.elms if x not in self.elms ]) 819 return ListSet(self.elms + [ x for x in that.elms if x not in self.elms ])
819 820
820 def IsEmpty(self): 821 def IsEmpty(self):
821 return len(self.elms) == 0 822 return len(self.elms) == 0
822 823
823 824
824 class Everything(Set): 825 class Everything(Set):
825 826
826 def Intersect(self, that): 827 def Intersect(self, that):
827 return that 828 return that
828 829
829 def Union(self, that): 830 def Union(self, that):
830 return self 831 return self
831 832
832 def IsEmpty(self): 833 def IsEmpty(self):
833 return False 834 return False
834 835
835 836
836 class Nothing(Set): 837 class Nothing(Set):
837 838
838 def Intersect(self, that): 839 def Intersect(self, that):
839 return self 840 return self
840 841
841 def Union(self, that): 842 def Union(self, that):
842 return that 843 return that
843 844
844 def IsEmpty(self): 845 def IsEmpty(self):
845 return True 846 return True
846 847
847 848
848 class Operation(Expression): 849 class Operation(Expression):
849 850
850 def __init__(self, left, op, right): 851 def __init__(self, left, op, right):
851 self.left = left 852 self.left = left
852 self.op = op 853 self.op = op
853 self.right = right 854 self.right = right
854 855
855 def Evaluate(self, env, defs): 856 def Evaluate(self, env, defs):
856 if self.op == '||' or self.op == ',': 857 if self.op == '||' or self.op == ',':
857 return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs) 858 return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs)
858 elif self.op == 'if': 859 elif self.op == 'if':
859 return False 860 return False
860 elif self.op == '==': 861 elif self.op == '==':
861 inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs)) 862 inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs))
862 return not inter.IsEmpty() 863 return not inter.IsEmpty()
863 elif self.op == '!=': 864 elif self.op == '!=':
864 inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs)) 865 inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs))
865 return inter.IsEmpty() 866 return inter.IsEmpty()
866 else: 867 else:
867 assert self.op == '&&' 868 assert self.op == '&&'
868 return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs) 869 return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs)
869 870
870 def GetOutcomes(self, env, defs): 871 def GetOutcomes(self, env, defs):
871 if self.op == '||' or self.op == ',': 872 if self.op == '||' or self.op == ',':
872 return self.left.GetOutcomes(env, defs).Union(self.right.GetOutcomes(env, defs)) 873 return self.left.GetOutcomes(env, defs).Union(self.right.GetOutcomes(env, defs))
873 elif self.op == 'if': 874 elif self.op == 'if':
874 if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs) 875 if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs)
875 else: return Nothing() 876 else: return Nothing()
876 else: 877 else:
877 assert self.op == '&&' 878 assert self.op == '&&'
878 return self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs)) 879 return self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs))
879 880
880 881
881 def IsAlpha(str): 882 def IsAlpha(str):
882 for char in str: 883 for char in str:
883 if not (char.isalpha() or char.isdigit() or char == '_'): 884 if not (char.isalpha() or char.isdigit() or char == '_'):
884 return False 885 return False
885 return True 886 return True
886 887
887 888
888 class Tokenizer(object): 889 class Tokenizer(object):
889 """A simple string tokenizer that chops expressions into variables, 890 """A simple string tokenizer that chops expressions into variables,
890 parens and operators""" 891 parens and operators"""
891 892
892 def __init__(self, expr): 893 def __init__(self, expr):
893 self.index = 0 894 self.index = 0
894 self.expr = expr 895 self.expr = expr
895 self.length = len(expr) 896 self.length = len(expr)
896 self.tokens = None 897 self.tokens = None
897 898
898 def Current(self, length = 1): 899 def Current(self, length = 1):
899 if not self.HasMore(length): return "" 900 if not self.HasMore(length): return ""
900 return self.expr[self.index:self.index+length] 901 return self.expr[self.index:self.index+length]
901 902
902 def HasMore(self, length = 1): 903 def HasMore(self, length = 1):
903 return self.index < self.length + (length - 1) 904 return self.index < self.length + (length - 1)
904 905
905 def Advance(self, count = 1): 906 def Advance(self, count = 1):
906 self.index = self.index + count 907 self.index = self.index + count
907 908
908 def AddToken(self, token): 909 def AddToken(self, token):
909 self.tokens.append(token) 910 self.tokens.append(token)
910 911
911 def SkipSpaces(self): 912 def SkipSpaces(self):
912 while self.HasMore() and self.Current().isspace(): 913 while self.HasMore() and self.Current().isspace():
913 self.Advance() 914 self.Advance()
914 915
915 def Tokenize(self): 916 def Tokenize(self):
916 self.tokens = [ ] 917 self.tokens = [ ]
917 while self.HasMore(): 918 while self.HasMore():
918 self.SkipSpaces() 919 self.SkipSpaces()
919 if not self.HasMore(): 920 if not self.HasMore():
920 return None 921 return None
921 if self.Current() == '(': 922 if self.Current() == '(':
922 self.AddToken('(') 923 self.AddToken('(')
923 self.Advance() 924 self.Advance()
924 elif self.Current() == ')': 925 elif self.Current() == ')':
925 self.AddToken(')') 926 self.AddToken(')')
926 self.Advance() 927 self.Advance()
927 elif self.Current() == '$': 928 elif self.Current() == '$':
928 self.AddToken('$') 929 self.AddToken('$')
929 self.Advance() 930 self.Advance()
930 elif self.Current() == ',': 931 elif self.Current() == ',':
931 self.AddToken(',') 932 self.AddToken(',')
932 self.Advance() 933 self.Advance()
933 elif IsAlpha(self.Current()): 934 elif IsAlpha(self.Current()):
934 buf = "" 935 buf = ""
935 while self.HasMore() and IsAlpha(self.Current()): 936 while self.HasMore() and IsAlpha(self.Current()):
936 buf += self.Current() 937 buf += self.Current()
937 self.Advance() 938 self.Advance()
938 self.AddToken(buf) 939 self.AddToken(buf)
939 elif self.Current(2) == '&&': 940 elif self.Current(2) == '&&':
940 self.AddToken('&&') 941 self.AddToken('&&')
941 self.Advance(2) 942 self.Advance(2)
942 elif self.Current(2) == '||': 943 elif self.Current(2) == '||':
943 self.AddToken('||') 944 self.AddToken('||')
944 self.Advance(2) 945 self.Advance(2)
945 elif self.Current(2) == '==': 946 elif self.Current(2) == '==':
946 self.AddToken('==') 947 self.AddToken('==')
947 self.Advance(2) 948 self.Advance(2)
948 elif self.Current(2) == '!=': 949 elif self.Current(2) == '!=':
949 self.AddToken('!=') 950 self.AddToken('!=')
950 self.Advance(2) 951 self.Advance(2)
951 else: 952 else:
952 return None 953 return None
953 return self.tokens 954 return self.tokens
954 955
955 956
956 class Scanner(object): 957 class Scanner(object):
957 """A simple scanner that can serve out tokens from a given list""" 958 """A simple scanner that can serve out tokens from a given list"""
958 959
959 def __init__(self, tokens): 960 def __init__(self, tokens):
960 self.tokens = tokens 961 self.tokens = tokens
961 self.length = len(tokens) 962 self.length = len(tokens)
962 self.index = 0 963 self.index = 0
963 964
964 def HasMore(self): 965 def HasMore(self):
965 return self.index < self.length 966 return self.index < self.length
966 967
967 def Current(self): 968 def Current(self):
968 return self.tokens[self.index] 969 return self.tokens[self.index]
969 970
970 def Advance(self): 971 def Advance(self):
971 self.index = self.index + 1 972 self.index = self.index + 1
972 973
973 974
974 def ParseAtomicExpression(scan): 975 def ParseAtomicExpression(scan):
975 if scan.Current() == "true": 976 if scan.Current() == "true":
976 scan.Advance() 977 scan.Advance()
977 return Constant(True) 978 return Constant(True)
978 elif scan.Current() == "false": 979 elif scan.Current() == "false":
979 scan.Advance() 980 scan.Advance()
980 return Constant(False) 981 return Constant(False)
981 elif IsAlpha(scan.Current()): 982 elif IsAlpha(scan.Current()):
982 name = scan.Current() 983 name = scan.Current()
983 scan.Advance() 984 scan.Advance()
984 return Outcome(name.lower()) 985 return Outcome(name.lower())
985 elif scan.Current() == '$': 986 elif scan.Current() == '$':
986 scan.Advance() 987 scan.Advance()
987 if not IsAlpha(scan.Current()): 988 if not IsAlpha(scan.Current()):
988 return None 989 return None
989 name = scan.Current() 990 name = scan.Current()
990 scan.Advance() 991 scan.Advance()
991 return Variable(name.lower()) 992 return Variable(name.lower())
992 elif scan.Current() == '(': 993 elif scan.Current() == '(':
993 scan.Advance() 994 scan.Advance()
994 result = ParseLogicalExpression(scan) 995 result = ParseLogicalExpression(scan)
995 if (not result) or (scan.Current() != ')'): 996 if (not result) or (scan.Current() != ')'):
996 return None 997 return None
997 scan.Advance() 998 scan.Advance()
998 return result 999 return result
999 else: 1000 else:
1000 return None 1001 return None
1001 1002
1002 1003
1003 BINARIES = ['==', '!='] 1004 BINARIES = ['==', '!=']
1004 def ParseOperatorExpression(scan): 1005 def ParseOperatorExpression(scan):
1005 left = ParseAtomicExpression(scan) 1006 left = ParseAtomicExpression(scan)
1006 if not left: return None 1007 if not left: return None
1007 while scan.HasMore() and (scan.Current() in BINARIES): 1008 while scan.HasMore() and (scan.Current() in BINARIES):
1008 op = scan.Current() 1009 op = scan.Current()
1009 scan.Advance() 1010 scan.Advance()
1010 right = ParseOperatorExpression(scan) 1011 right = ParseOperatorExpression(scan)
1011 if not right: 1012 if not right:
1012 return None 1013 return None
1013 left = Operation(left, op, right) 1014 left = Operation(left, op, right)
1014 return left 1015 return left
1015 1016
1016 1017
1017 def ParseConditionalExpression(scan): 1018 def ParseConditionalExpression(scan):
1018 left = ParseOperatorExpression(scan) 1019 left = ParseOperatorExpression(scan)
1019 if not left: return None 1020 if not left: return None
1020 while scan.HasMore() and (scan.Current() == 'if'): 1021 while scan.HasMore() and (scan.Current() == 'if'):
1021 scan.Advance() 1022 scan.Advance()
1022 right = ParseOperatorExpression(scan) 1023 right = ParseOperatorExpression(scan)
1023 if not right: 1024 if not right:
1024 return None 1025 return None
1025 left = Operation(left, 'if', right) 1026 left = Operation(left, 'if', right)
1026 return left 1027 return left
1027 1028
1028 1029
1029 LOGICALS = ["&&", "||", ","] 1030 LOGICALS = ["&&", "||", ","]
1030 def ParseLogicalExpression(scan): 1031 def ParseLogicalExpression(scan):
1031 left = ParseConditionalExpression(scan) 1032 left = ParseConditionalExpression(scan)
1032 if not left: return None 1033 if not left: return None
1033 while scan.HasMore() and (scan.Current() in LOGICALS): 1034 while scan.HasMore() and (scan.Current() in LOGICALS):
1034 op = scan.Current() 1035 op = scan.Current()
1035 scan.Advance() 1036 scan.Advance()
1036 right = ParseConditionalExpression(scan) 1037 right = ParseConditionalExpression(scan)
1037 if not right: 1038 if not right:
1038 return None 1039 return None
1039 left = Operation(left, op, right) 1040 left = Operation(left, op, right)
1040 return left 1041 return left
1041 1042
1042 1043
1043 def ParseCondition(expr): 1044 def ParseCondition(expr):
1044 """Parses a logical expression into an Expression object""" 1045 """Parses a logical expression into an Expression object"""
1045 tokens = Tokenizer(expr).Tokenize() 1046 tokens = Tokenizer(expr).Tokenize()
1046 if not tokens: 1047 if not tokens:
1047 print "Malformed expression: '%s'" % expr 1048 print "Malformed expression: '%s'" % expr
1048 return None 1049 return None
1049 scan = Scanner(tokens) 1050 scan = Scanner(tokens)
1050 ast = ParseLogicalExpression(scan) 1051 ast = ParseLogicalExpression(scan)
1051 if not ast: 1052 if not ast:
1052 print "Malformed expression: '%s'" % expr 1053 print "Malformed expression: '%s'" % expr
1053 return None 1054 return None
1054 if scan.HasMore(): 1055 if scan.HasMore():
1055 print "Malformed expression: '%s'" % expr 1056 print "Malformed expression: '%s'" % expr
1056 return None 1057 return None
1057 return ast 1058 return ast
1058 1059
1059 1060
1060 class ClassifiedTest(object): 1061 class ClassifiedTest(object):
1061 1062
1062 def __init__(self, case, outcomes): 1063 def __init__(self, case, outcomes):
1063 self.case = case 1064 self.case = case
1064 self.outcomes = outcomes 1065 self.outcomes = outcomes
1065 1066
1066 def TestsIsolates(self): 1067 def TestsIsolates(self):
1067 return self.case.TestsIsolates() 1068 return self.case.TestsIsolates()
1068 1069
1069 1070
1070 class Configuration(object): 1071 class Configuration(object):
1071 """The parsed contents of a configuration file""" 1072 """The parsed contents of a configuration file"""
1072 1073
1073 def __init__(self, sections, defs): 1074 def __init__(self, sections, defs):
1074 self.sections = sections 1075 self.sections = sections
1075 self.defs = defs 1076 self.defs = defs
1076 1077
1077 def ClassifyTests(self, cases, env): 1078 def ClassifyTests(self, cases, env):
1078 sections = [s for s in self.sections if s.condition.Evaluate(env, self.defs)] 1079 sections = [s for s in self.sections if s.condition.Evaluate(env, self.defs)]
1079 all_rules = reduce(list.__add__, [s.rules for s in sections], []) 1080 all_rules = reduce(list.__add__, [s.rules for s in sections], [])
1080 unused_rules = set(all_rules) 1081 unused_rules = set(all_rules)
1081 result = [ ] 1082 result = [ ]
1082 all_outcomes = set([]) 1083 all_outcomes = set([])
1083 for case in cases: 1084 for case in cases:
1084 matches = [ r for r in all_rules if r.Contains(case.path) ] 1085 matches = [ r for r in all_rules if r.Contains(case.path) ]
1085 outcomes = set([]) 1086 outcomes = set([])
1086 for rule in matches: 1087 for rule in matches:
1087 outcomes = outcomes.union(rule.GetOutcomes(env, self.defs)) 1088 outcomes = outcomes.union(rule.GetOutcomes(env, self.defs))
1088 unused_rules.discard(rule) 1089 unused_rules.discard(rule)
1089 if not outcomes: 1090 if not outcomes:
1090 outcomes = [PASS] 1091 outcomes = [PASS]
1091 case.outcomes = outcomes 1092 case.outcomes = outcomes
1092 all_outcomes = all_outcomes.union(outcomes) 1093 all_outcomes = all_outcomes.union(outcomes)
1093 result.append(ClassifiedTest(case, outcomes)) 1094 result.append(ClassifiedTest(case, outcomes))
1094 return (result, list(unused_rules), all_outcomes) 1095 return (result, list(unused_rules), all_outcomes)
1095 1096
1096 1097
1097 class Section(object): 1098 class Section(object):
1098 """A section of the configuration file. Sections are enabled or 1099 """A section of the configuration file. Sections are enabled or
1099 disabled prior to running the tests, based on their conditions""" 1100 disabled prior to running the tests, based on their conditions"""
1100 1101
1101 def __init__(self, condition): 1102 def __init__(self, condition):
1102 self.condition = condition 1103 self.condition = condition
1103 self.rules = [ ] 1104 self.rules = [ ]
1104 1105
1105 def AddRule(self, rule): 1106 def AddRule(self, rule):
1106 self.rules.append(rule) 1107 self.rules.append(rule)
1107 1108
1108 1109
1109 class Rule(object): 1110 class Rule(object):
1110 """A single rule that specifies the expected outcome for a single 1111 """A single rule that specifies the expected outcome for a single
1111 test.""" 1112 test."""
1112 1113
1113 def __init__(self, raw_path, path, value): 1114 def __init__(self, raw_path, path, value):
1114 self.raw_path = raw_path 1115 self.raw_path = raw_path
1115 self.path = path 1116 self.path = path
1116 self.value = value 1117 self.value = value
1117 1118
1118 def GetOutcomes(self, env, defs): 1119 def GetOutcomes(self, env, defs):
1119 set = self.value.GetOutcomes(env, defs) 1120 set = self.value.GetOutcomes(env, defs)
1120 assert isinstance(set, ListSet) 1121 assert isinstance(set, ListSet)
1121 return set.elms 1122 return set.elms
1122 1123
1123 def Contains(self, path): 1124 def Contains(self, path):
1124 if len(self.path) > len(path): 1125 if len(self.path) > len(path):
1125 return False 1126 return False
1126 for i in xrange(len(self.path)): 1127 for i in xrange(len(self.path)):
1127 if not self.path[i].match(path[i]): 1128 if not self.path[i].match(path[i]):
1128 return False 1129 return False
1129 return True 1130 return True
1130 1131
1131 1132
1132 HEADER_PATTERN = re.compile(r'\[([^]]+)\]') 1133 HEADER_PATTERN = re.compile(r'\[([^]]+)\]')
1133 RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)') 1134 RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)')
1134 DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$') 1135 DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$')
1135 PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$') 1136 PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$')
1136 1137
1137 1138
1138 def ReadConfigurationInto(path, sections, defs): 1139 def ReadConfigurationInto(path, sections, defs):
1139 current_section = Section(Constant(True)) 1140 current_section = Section(Constant(True))
1140 sections.append(current_section) 1141 sections.append(current_section)
1141 prefix = [] 1142 prefix = []
1142 for line in utils.ReadLinesFrom(path): 1143 for line in utils.ReadLinesFrom(path):
1143 header_match = HEADER_PATTERN.match(line) 1144 header_match = HEADER_PATTERN.match(line)
1144 if header_match: 1145 if header_match:
1145 condition_str = header_match.group(1).strip() 1146 condition_str = header_match.group(1).strip()
1146 condition = ParseCondition(condition_str) 1147 condition = ParseCondition(condition_str)
1147 new_section = Section(condition) 1148 new_section = Section(condition)
1148 sections.append(new_section) 1149 sections.append(new_section)
1149 current_section = new_section 1150 current_section = new_section
1150 continue 1151 continue
1151 rule_match = RULE_PATTERN.match(line) 1152 rule_match = RULE_PATTERN.match(line)
1152 if rule_match: 1153 if rule_match:
1153 path = prefix + SplitPath(rule_match.group(1).strip()) 1154 path = prefix + SplitPath(rule_match.group(1).strip())
1154 value_str = rule_match.group(2).strip() 1155 value_str = rule_match.group(2).strip()
1155 value = ParseCondition(value_str) 1156 value = ParseCondition(value_str)
1156 if not value: 1157 if not value:
1157 return False 1158 return False
1158 current_section.AddRule(Rule(rule_match.group(1), path, value)) 1159 current_section.AddRule(Rule(rule_match.group(1), path, value))
1159 continue 1160 continue
1160 def_match = DEF_PATTERN.match(line) 1161 def_match = DEF_PATTERN.match(line)
1161 if def_match: 1162 if def_match:
1162 name = def_match.group(1).lower() 1163 name = def_match.group(1).lower()
1163 value = ParseCondition(def_match.group(2).strip()) 1164 value = ParseCondition(def_match.group(2).strip())
1164 if not value: 1165 if not value:
1165 return False 1166 return False
1166 defs[name] = value 1167 defs[name] = value
1167 continue 1168 continue
1168 prefix_match = PREFIX_PATTERN.match(line) 1169 prefix_match = PREFIX_PATTERN.match(line)
1169 if prefix_match: 1170 if prefix_match:
1170 prefix = SplitPath(prefix_match.group(1).strip()) 1171 prefix = SplitPath(prefix_match.group(1).strip())
1171 continue 1172 continue
1172 print "Malformed line: '%s'." % line 1173 print "Malformed line: '%s'." % line
1173 return False 1174 return False
1174 return True 1175 return True
1175 1176
1176 1177
1177 # --------------- 1178 # ---------------
1178 # --- M a i n --- 1179 # --- M a i n ---
1179 # --------------- 1180 # ---------------
1180 1181
1181 1182
1182 ARCH_GUESS = utils.GuessArchitecture() 1183 ARCH_GUESS = utils.GuessArchitecture()
1183 TIMEOUT_DEFAULT = 60; 1184 TIMEOUT_DEFAULT = 60;
1184 1185
1185 1186
1186 def BuildOptions(): 1187 def BuildOptions():
1187 result = optparse.OptionParser() 1188 result = optparse.OptionParser()
1188 result.add_option("-m", "--mode", help="The test modes in which to run (comma-separated)", 1189 result.add_option("-m", "--mode", help="The test modes in which to run (comma-separated)",
1189 default='release') 1190 default='release')
1190 result.add_option("-v", "--verbose", help="Verbose output", 1191 result.add_option("-v", "--verbose", help="Verbose output",
1191 default=False, action="store_true") 1192 default=False, action="store_true")
1192 result.add_option("-S", dest="scons_flags", help="Flag to pass through to scons", 1193 result.add_option("-S", dest="scons_flags", help="Flag to pass through to scons",
1193 default=[], action="append") 1194 default=[], action="append")
1194 result.add_option("-p", "--progress", 1195 result.add_option("-p", "--progress",
1195 help="The style of progress indicator (verbose, dots, color, mono)", 1196 help="The style of progress indicator (verbose, dots, color, mono)",
1196 choices=PROGRESS_INDICATORS.keys(), default="mono") 1197 choices=PROGRESS_INDICATORS.keys(), default="mono")
1197 result.add_option("--no-build", help="Don't build requirements", 1198 result.add_option("--no-build", help="Don't build requirements",
1198 default=False, action="store_true") 1199 default=False, action="store_true")
1199 result.add_option("--build-only", help="Only build requirements, don't run the tests", 1200 result.add_option("--build-only", help="Only build requirements, don't run the tests",
1200 default=False, action="store_true") 1201 default=False, action="store_true")
1201 result.add_option("--build-system", help="Build system in use (scons or gyp)", 1202 result.add_option("--build-system", help="Build system in use (scons or gyp)",
1202 default='scons') 1203 default='scons')
1203 result.add_option("--report", help="Print a summary of the tests to be run", 1204 result.add_option("--report", help="Print a summary of the tests to be run",
1204 default=False, action="store_true") 1205 default=False, action="store_true")
1205 result.add_option("--download-data", help="Download missing test suite data", 1206 result.add_option("--download-data", help="Download missing test suite data",
1206 default=False, action="store_true") 1207 default=False, action="store_true")
1207 result.add_option("-s", "--suite", help="A test suite", 1208 result.add_option("-s", "--suite", help="A test suite",
1208 default=[], action="append") 1209 default=[], action="append")
1209 result.add_option("-t", "--timeout", help="Timeout in seconds", 1210 result.add_option("-t", "--timeout", help="Timeout in seconds",
1210 default=-1, type="int") 1211 default=-1, type="int")
1211 result.add_option("--arch", help='The architecture to run tests for', 1212 result.add_option("--arch", help='The architecture to run tests for',
1212 default='none') 1213 default='none')
1213 result.add_option("--snapshot", help="Run the tests with snapshot turned on", 1214 result.add_option("--snapshot", help="Run the tests with snapshot turned on",
1214 default=False, action="store_true") 1215 default=False, action="store_true")
1215 result.add_option("--simulator", help="Run tests with architecture simulator", 1216 result.add_option("--simulator", help="Run tests with architecture simulator",
1216 default='none') 1217 default='none')
1217 result.add_option("--special-command", default=None) 1218 result.add_option("--special-command", default=None)
1218 result.add_option("--valgrind", help="Run tests through valgrind", 1219 result.add_option("--valgrind", help="Run tests through valgrind",
1219 default=False, action="store_true") 1220 default=False, action="store_true")
1220 result.add_option("--cat", help="Print the source of the tests", 1221 result.add_option("--cat", help="Print the source of the tests",
1221 default=False, action="store_true") 1222 default=False, action="store_true")
1222 result.add_option("--warn-unused", help="Report unused rules", 1223 result.add_option("--warn-unused", help="Report unused rules",
1223 default=False, action="store_true") 1224 default=False, action="store_true")
1224 result.add_option("-j", help="The number of parallel tasks to run", 1225 result.add_option("-j", help="The number of parallel tasks to run",
1225 default=1, type="int") 1226 default=1, type="int")
1226 result.add_option("--time", help="Print timing information after running", 1227 result.add_option("--time", help="Print timing information after running",
1227 default=False, action="store_true") 1228 default=False, action="store_true")
1228 result.add_option("--suppress-dialogs", help="Suppress Windows dialogs for crashing tests", 1229 result.add_option("--suppress-dialogs", help="Suppress Windows dialogs for crashing tests",
1229 dest="suppress_dialogs", default=True, action="store_true") 1230 dest="suppress_dialogs", default=True, action="store_true")
1230 result.add_option("--no-suppress-dialogs", help="Display Windows dialogs for crashing tests", 1231 result.add_option("--no-suppress-dialogs", help="Display Windows dialogs for crashing tests",
1231 dest="suppress_dialogs", action="store_false") 1232 dest="suppress_dialogs", action="store_false")
1232 result.add_option("--mips-arch-variant", help="mips architecture variant: mips32r1/mips32r2", default="mips32r2"); 1233 result.add_option("--mips-arch-variant", help="mips architecture variant: mips32r1/mips32r2", default="mips32r2");
1233 result.add_option("--shell", help="Path to V8 shell", default="d8") 1234 result.add_option("--shell", help="Path to V8 shell", default="d8")
1234 result.add_option("--isolates", help="Whether to test isolates", default=False, action="store_true") 1235 result.add_option("--isolates", help="Whether to test isolates", default=False, action="store_true")
1235 result.add_option("--store-unexpected-output", 1236 result.add_option("--store-unexpected-output",
1236 help="Store the temporary JS files from tests that fails", 1237 help="Store the temporary JS files from tests that fails",
1237 dest="store_unexpected_output", default=True, action="store_true") 1238 dest="store_unexpected_output", default=True, action="store_true")
1238 result.add_option("--no-store-unexpected-output", 1239 result.add_option("--no-store-unexpected-output",
1239 help="Deletes the temporary JS files from tests that fails", 1240 help="Deletes the temporary JS files from tests that fails",
1240 dest="store_unexpected_output", action="store_false") 1241 dest="store_unexpected_output", action="store_false")
1241 result.add_option("--stress-only", 1242 result.add_option("--stress-only",
1242 help="Only run tests with --always-opt --stress-opt", 1243 help="Only run tests with --always-opt --stress-opt",
1243 default=False, action="store_true") 1244 default=False, action="store_true")
1244 result.add_option("--nostress", 1245 result.add_option("--nostress",
1245 help="Don't run crankshaft --always-opt --stress-op test", 1246 help="Don't run crankshaft --always-opt --stress-op test",
1246 default=False, action="store_true") 1247 default=False, action="store_true")
1247 result.add_option("--shard-count", 1248 result.add_option("--shard-count",
1248 help="Split testsuites into this number of shards", 1249 help="Split testsuites into this number of shards",
1249 default=1, type="int") 1250 default=1, type="int")
1250 result.add_option("--shard-run", 1251 result.add_option("--shard-run",
1251 help="Run this shard from the split up tests.", 1252 help="Run this shard from the split up tests.",
1252 default=1, type="int") 1253 default=1, type="int")
1253 result.add_option("--noprof", help="Disable profiling support", 1254 result.add_option("--noprof", help="Disable profiling support",
1254 default=False) 1255 default=False)
1255 return result 1256 return result
1256 1257
1257 1258
1258 def ProcessOptions(options): 1259 def ProcessOptions(options):
1259 global VERBOSE 1260 global VERBOSE
1260 VERBOSE = options.verbose 1261 VERBOSE = options.verbose
1261 options.mode = options.mode.split(',') 1262 options.mode = options.mode.split(',')
1262 for mode in options.mode: 1263 for mode in options.mode:
1263 if not mode in ['debug', 'release']: 1264 if not mode in ['debug', 'release']:
1264 print "Unknown mode %s" % mode 1265 print "Unknown mode %s" % mode
1265 return False 1266 return False
1266 if options.simulator != 'none': 1267 if options.simulator != 'none':
1267 # Simulator argument was set. Make sure arch and simulator agree. 1268 # Simulator argument was set. Make sure arch and simulator agree.
1268 if options.simulator != options.arch: 1269 if options.simulator != options.arch:
1269 if options.arch == 'none': 1270 if options.arch == 'none':
1270 options.arch = options.simulator 1271 options.arch = options.simulator
1271 else: 1272 else:
1272 print "Architecture %s does not match sim %s" %(options.arch, options.simulator) 1273 print "Architecture %s does not match sim %s" %(options.arch, options.simulator)
1273 return False 1274 return False
1274 # Ensure that the simulator argument is handed down to scons. 1275 # Ensure that the simulator argument is handed down to scons.
1275 options.scons_flags.append("simulator=" + options.simulator) 1276 options.scons_flags.append("simulator=" + options.simulator)
1276 else: 1277 else:
1277 # If options.arch is not set by the command line and no simulator setting 1278 # If options.arch is not set by the command line and no simulator setting
1278 # was found, set the arch to the guess. 1279 # was found, set the arch to the guess.
1279 if options.arch == 'none': 1280 if options.arch == 'none':
1280 options.arch = ARCH_GUESS 1281 options.arch = ARCH_GUESS
1281 options.scons_flags.append("arch=" + options.arch) 1282 options.scons_flags.append("arch=" + options.arch)
1282 # Simulators are slow, therefore allow a longer default timeout. 1283 # Simulators are slow, therefore allow a longer default timeout.
1283 if options.timeout == -1: 1284 if options.timeout == -1:
1284 if options.arch in ['android', 'arm', 'mipsel']: 1285 if options.arch in ['android', 'arm', 'mipsel']:
1285 options.timeout = 2 * TIMEOUT_DEFAULT; 1286 options.timeout = 2 * TIMEOUT_DEFAULT;
1286 else: 1287 else:
1287 options.timeout = TIMEOUT_DEFAULT; 1288 options.timeout = TIMEOUT_DEFAULT;
1288 if options.snapshot: 1289 if options.snapshot:
1289 options.scons_flags.append("snapshot=on") 1290 options.scons_flags.append("snapshot=on")
1290 global VARIANT_FLAGS 1291 global VARIANT_FLAGS
1291 if options.mips_arch_variant: 1292 if options.mips_arch_variant:
1292 options.scons_flags.append("mips_arch_variant=" + options.mips_arch_variant) 1293 options.scons_flags.append("mips_arch_variant=" + options.mips_arch_variant)
1293 1294
1294 if options.stress_only: 1295 if options.stress_only:
1295 VARIANT_FLAGS = [['--stress-opt', '--always-opt']] 1296 VARIANT_FLAGS = [['--stress-opt', '--always-opt']]
1296 if options.nostress: 1297 if options.nostress:
1297 VARIANT_FLAGS = [[],['--nocrankshaft']] 1298 VARIANT_FLAGS = [[],['--nocrankshaft']]
1298 if options.shell.endswith("d8"): 1299 if options.shell.endswith("d8"):
1299 if options.special_command: 1300 if options.special_command:
1300 options.special_command += " --test" 1301 options.special_command += " --test"
1301 else: 1302 else:
1302 options.special_command = "@ --test" 1303 options.special_command = "@ --test"
1303 if options.noprof: 1304 if options.noprof:
1304 options.scons_flags.append("prof=off") 1305 options.scons_flags.append("prof=off")
1305 options.scons_flags.append("profilingsupport=off") 1306 options.scons_flags.append("profilingsupport=off")
1306 if options.build_system == 'gyp': 1307 if options.build_system == 'gyp':
1307 if options.build_only: 1308 if options.build_only:
1308 print "--build-only not supported for gyp, please build manually." 1309 print "--build-only not supported for gyp, please build manually."
1309 options.build_only = False 1310 options.build_only = False
1310 return True 1311 return True
1311 1312
1312 1313
1313 def DoSkip(case): 1314 def DoSkip(case):
1314 return (SKIP in case.outcomes) or (SLOW in case.outcomes) 1315 return (SKIP in case.outcomes) or (SLOW in case.outcomes)
1315 1316
1316 1317
1317 REPORT_TEMPLATE = """\ 1318 REPORT_TEMPLATE = """\
1318 Total: %(total)i tests 1319 Total: %(total)i tests
1319 * %(skipped)4d tests will be skipped 1320 * %(skipped)4d tests will be skipped
1320 * %(timeout)4d tests are expected to timeout sometimes 1321 * %(timeout)4d tests are expected to timeout sometimes
1321 * %(nocrash)4d tests are expected to be flaky but not crash 1322 * %(nocrash)4d tests are expected to be flaky but not crash
1322 * %(pass)4d tests are expected to pass 1323 * %(pass)4d tests are expected to pass
1323 * %(fail_ok)4d tests are expected to fail that we won't fix 1324 * %(fail_ok)4d tests are expected to fail that we won't fix
1324 * %(fail)4d tests are expected to fail that we should fix\ 1325 * %(fail)4d tests are expected to fail that we should fix\
1325 """ 1326 """
1326 1327
1327 def PrintReport(cases): 1328 def PrintReport(cases):
1328 def IsFlaky(o): 1329 def IsFlaky(o):
1329 return (PASS in o) and (FAIL in o) and (not CRASH in o) and (not OKAY in o) 1330 return (PASS in o) and (FAIL in o) and (not CRASH in o) and (not OKAY in o)
1330 def IsFailOk(o): 1331 def IsFailOk(o):
1331 return (len(o) == 2) and (FAIL in o) and (OKAY in o) 1332 return (len(o) == 2) and (FAIL in o) and (OKAY in o)
1332 unskipped = [c for c in cases if not DoSkip(c)] 1333 unskipped = [c for c in cases if not DoSkip(c)]
1333 print REPORT_TEMPLATE % { 1334 print REPORT_TEMPLATE % {
1334 'total': len(cases), 1335 'total': len(cases),
1335 'skipped': len(cases) - len(unskipped), 1336 'skipped': len(cases) - len(unskipped),
1336 'timeout': len([t for t in unskipped if TIMEOUT in t.outcomes]), 1337 'timeout': len([t for t in unskipped if TIMEOUT in t.outcomes]),
1337 'nocrash': len([t for t in unskipped if IsFlaky(t.outcomes)]), 1338 'nocrash': len([t for t in unskipped if IsFlaky(t.outcomes)]),
1338 'pass': len([t for t in unskipped if list(t.outcomes) == [PASS]]), 1339 'pass': len([t for t in unskipped if list(t.outcomes) == [PASS]]),
1339 'fail_ok': len([t for t in unskipped if IsFailOk(t.outcomes)]), 1340 'fail_ok': len([t for t in unskipped if IsFailOk(t.outcomes)]),
1340 'fail': len([t for t in unskipped if list(t.outcomes) == [FAIL]]) 1341 'fail': len([t for t in unskipped if list(t.outcomes) == [FAIL]])
1341 } 1342 }
1342 1343
1343 1344
1344 class Pattern(object): 1345 class Pattern(object):
1345 1346
1346 def __init__(self, pattern): 1347 def __init__(self, pattern):
1347 self.pattern = pattern 1348 self.pattern = pattern
1348 self.compiled = None 1349 self.compiled = None
1349 1350
1350 def match(self, str): 1351 def match(self, str):
1351 if not self.compiled: 1352 if not self.compiled:
1352 pattern = "^" + self.pattern.replace('*', '.*') + "$" 1353 pattern = "^" + self.pattern.replace('*', '.*') + "$"
1353 self.compiled = re.compile(pattern) 1354 self.compiled = re.compile(pattern)
1354 return self.compiled.match(str) 1355 return self.compiled.match(str)
1355 1356
1356 def __str__(self): 1357 def __str__(self):
1357 return self.pattern 1358 return self.pattern
1358 1359
1359 1360
1360 def SplitPath(s): 1361 def SplitPath(s):
1361 stripped = [ c.strip() for c in s.split('/') ] 1362 stripped = [ c.strip() for c in s.split('/') ]
1362 return [ Pattern(s) for s in stripped if len(s) > 0 ] 1363 return [ Pattern(s) for s in stripped if len(s) > 0 ]
1363 1364
1364 1365
1365 def GetSpecialCommandProcessor(value): 1366 def GetSpecialCommandProcessor(value):
1366 if (not value) or (value.find('@') == -1): 1367 if (not value) or (value.find('@') == -1):
1367 def ExpandCommand(args): 1368 def ExpandCommand(args):
1368 return args 1369 return args
1369 return ExpandCommand 1370 return ExpandCommand
1370 else: 1371 else:
1371 pos = value.find('@') 1372 pos = value.find('@')
1372 import urllib 1373 import urllib
1373 prefix = urllib.unquote(value[:pos]).split() 1374 prefix = urllib.unquote(value[:pos]).split()
1374 suffix = urllib.unquote(value[pos+1:]).split() 1375 suffix = urllib.unquote(value[pos+1:]).split()
1375 def ExpandCommand(args): 1376 def ExpandCommand(args):
1376 return prefix + args + suffix 1377 return prefix + args + suffix
1377 return ExpandCommand 1378 return ExpandCommand
1378 1379
1379 1380
1380 BUILT_IN_TESTS = ['mjsunit', 'cctest', 'message', 'preparser'] 1381 BUILT_IN_TESTS = ['mjsunit', 'cctest', 'message', 'preparser']
1381 1382
1382 1383
1383 def GetSuites(test_root): 1384 def GetSuites(test_root):
1384 def IsSuite(path): 1385 def IsSuite(path):
1385 return isdir(path) and exists(join(path, 'testcfg.py')) 1386 return isdir(path) and exists(join(path, 'testcfg.py'))
1386 return [ f for f in os.listdir(test_root) if IsSuite(join(test_root, f)) ] 1387 return [ f for f in os.listdir(test_root) if IsSuite(join(test_root, f)) ]
1387 1388
1388 1389
1389 def FormatTime(d): 1390 def FormatTime(d):
1390 millis = round(d * 1000) % 1000 1391 millis = round(d * 1000) % 1000
1391 return time.strftime("%M:%S.", time.gmtime(d)) + ("%03i" % millis) 1392 return time.strftime("%M:%S.", time.gmtime(d)) + ("%03i" % millis)
1392 1393
1393 def ShardTests(tests, options): 1394 def ShardTests(tests, options):
1394 if options.shard_count < 2: 1395 if options.shard_count < 2:
1395 return tests 1396 return tests
1396 if options.shard_run < 1 or options.shard_run > options.shard_count: 1397 if options.shard_run < 1 or options.shard_run > options.shard_count:
1397 print "shard-run not a valid number, should be in [1:shard-count]" 1398 print "shard-run not a valid number, should be in [1:shard-count]"
1398 print "defaulting back to running all tests" 1399 print "defaulting back to running all tests"
1399 return tests 1400 return tests
1400 count = 0 1401 count = 0
1401 shard = [] 1402 shard = []
1402 for test in tests: 1403 for test in tests:
1403 if count % options.shard_count == options.shard_run - 1: 1404 if count % options.shard_count == options.shard_run - 1:
1404 shard.append(test) 1405 shard.append(test)
1405 count += 1 1406 count += 1
1406 return shard 1407 return shard
1407 1408
1408 def Main(): 1409 def Main():
1409 parser = BuildOptions() 1410 parser = BuildOptions()
1410 (options, args) = parser.parse_args() 1411 (options, args) = parser.parse_args()
1411 if not ProcessOptions(options): 1412 if not ProcessOptions(options):
1412 parser.print_help() 1413 parser.print_help()
1413 return 1 1414 return 1
1414 1415
1415 workspace = abspath(join(dirname(sys.argv[0]), '..')) 1416 workspace = abspath(join(dirname(sys.argv[0]), '..'))
1416 suites = GetSuites(join(workspace, 'test')) 1417 suites = GetSuites(join(workspace, 'test'))
1417 repositories = [TestRepository(join(workspace, 'test', name)) for name in suites] 1418 repositories = [TestRepository(join(workspace, 'test', name)) for name in suites]
1418 repositories += [TestRepository(a) for a in options.suite] 1419 repositories += [TestRepository(a) for a in options.suite]
1419 1420
1420 root = LiteralTestSuite(repositories) 1421 root = LiteralTestSuite(repositories)
1421 if len(args) == 0: 1422 if len(args) == 0:
1422 paths = [SplitPath(t) for t in BUILT_IN_TESTS] 1423 paths = [SplitPath(t) for t in BUILT_IN_TESTS]
1423 else: 1424 else:
1424 paths = [ ] 1425 paths = [ ]
1425 for arg in args: 1426 for arg in args:
1426 path = SplitPath(arg) 1427 path = SplitPath(arg)
1427 paths.append(path) 1428 paths.append(path)
1428 1429
1429 # Check for --valgrind option. If enabled, we overwrite the special 1430 # Check for --valgrind option. If enabled, we overwrite the special
1430 # command flag with a command that uses the run-valgrind.py script. 1431 # command flag with a command that uses the run-valgrind.py script.
1431 if options.valgrind: 1432 if options.valgrind:
1432 run_valgrind = join(workspace, "tools", "run-valgrind.py") 1433 run_valgrind = join(workspace, "tools", "run-valgrind.py")
1433 options.special_command = "python -u " + run_valgrind + " @" 1434 options.special_command = "python -u " + run_valgrind + " @"
1434 1435
1435 if options.build_system == 'gyp': 1436 if options.build_system == 'gyp':
1436 SUFFIX['debug'] = '' 1437 SUFFIX['debug'] = ''
1437 1438
1438 shell = abspath(options.shell) 1439 shell = abspath(options.shell)
1439 buildspace = dirname(shell) 1440 buildspace = dirname(shell)
1440 1441
1441 context = Context(workspace, buildspace, VERBOSE, 1442 context = Context(workspace, buildspace, VERBOSE,
1442 shell, 1443 shell,
1443 options.timeout, 1444 options.timeout,
1444 GetSpecialCommandProcessor(options.special_command), 1445 GetSpecialCommandProcessor(options.special_command),
1445 options.suppress_dialogs, 1446 options.suppress_dialogs,
1446 options.store_unexpected_output) 1447 options.store_unexpected_output)
1447 # First build the required targets 1448 # First build the required targets
1448 if not options.no_build: 1449 if not options.no_build:
1449 reqs = [ ] 1450 reqs = [ ]
1450 for path in paths: 1451 for path in paths:
1451 reqs += root.GetBuildRequirements(path, context) 1452 reqs += root.GetBuildRequirements(path, context)
1452 reqs = list(set(reqs)) 1453 reqs = list(set(reqs))
1453 if len(reqs) > 0: 1454 if len(reqs) > 0:
1454 if options.j != 1: 1455 if options.j != 1:
1455 options.scons_flags += ['-j', str(options.j)] 1456 options.scons_flags += ['-j', str(options.j)]
1456 if not BuildRequirements(context, reqs, options.mode, options.scons_flags): 1457 if not BuildRequirements(context, reqs, options.mode, options.scons_flags):
1457 return 1 1458 return 1
1458 1459
1459 # Just return if we are only building the targets for running the tests. 1460 # Just return if we are only building the targets for running the tests.
1460 if options.build_only: 1461 if options.build_only:
1461 return 0 1462 return 0
1462 1463
1463 # Get status for tests 1464 # Get status for tests
1464 sections = [ ] 1465 sections = [ ]
1465 defs = { } 1466 defs = { }
1466 root.GetTestStatus(context, sections, defs) 1467 root.GetTestStatus(context, sections, defs)
1467 config = Configuration(sections, defs) 1468 config = Configuration(sections, defs)
1468 1469
1469 # Download missing test suite data if requested. 1470 # Download missing test suite data if requested.
1470 if options.download_data: 1471 if options.download_data:
1471 for path in paths: 1472 for path in paths:
1472 root.DownloadData(path, context) 1473 root.DownloadData(path, context)
1473 1474
1474 # List the tests 1475 # List the tests
1475 all_cases = [ ] 1476 all_cases = [ ]
1476 all_unused = [ ] 1477 all_unused = [ ]
1477 unclassified_tests = [ ] 1478 unclassified_tests = [ ]
1478 globally_unused_rules = None 1479 globally_unused_rules = None
1479 for path in paths: 1480 for path in paths:
1480 for mode in options.mode: 1481 for mode in options.mode:
1481 env = { 1482 env = {
1482 'mode': mode, 1483 'mode': mode,
1483 'system': utils.GuessOS(), 1484 'system': utils.GuessOS(),
1484 'arch': options.arch, 1485 'arch': options.arch,
1485 'simulator': options.simulator, 1486 'simulator': options.simulator,
1486 'isolates': options.isolates 1487 'isolates': options.isolates
1487 } 1488 }
1488 test_list = root.ListTests([], path, context, mode, []) 1489 test_list = root.ListTests([], path, context, mode, [])
1489 unclassified_tests += test_list 1490 unclassified_tests += test_list
1490 (cases, unused_rules, all_outcomes) = config.ClassifyTests(test_list, env) 1491 (cases, unused_rules, all_outcomes) = config.ClassifyTests(test_list, env)
1491 if globally_unused_rules is None: 1492 if globally_unused_rules is None:
1492 globally_unused_rules = set(unused_rules) 1493 globally_unused_rules = set(unused_rules)
1493 else: 1494 else:
1494 globally_unused_rules = globally_unused_rules.intersection(unused_rules) 1495 globally_unused_rules = globally_unused_rules.intersection(unused_rules)
1495 all_cases += ShardTests(cases, options) 1496 all_cases += ShardTests(cases, options)
1496 all_unused.append(unused_rules) 1497 all_unused.append(unused_rules)
1497 1498
1498 if options.cat: 1499 if options.cat:
1499 visited = set() 1500 visited = set()
1500 for test in unclassified_tests: 1501 for test in unclassified_tests:
1501 key = tuple(test.path) 1502 key = tuple(test.path)
1502 if key in visited: 1503 if key in visited:
1503 continue 1504 continue
1504 visited.add(key) 1505 visited.add(key)
1505 print "--- begin source: %s ---" % test.GetLabel() 1506 print "--- begin source: %s ---" % test.GetLabel()
1506 source = test.GetSource().strip() 1507 source = test.GetSource().strip()
1507 print source 1508 print source
1508 print "--- end source: %s ---" % test.GetLabel() 1509 print "--- end source: %s ---" % test.GetLabel()
1509 return 0 1510 return 0
1510 1511
1511 if options.warn_unused: 1512 if options.warn_unused:
1512 for rule in globally_unused_rules: 1513 for rule in globally_unused_rules:
1513 print "Rule for '%s' was not used." % '/'.join([str(s) for s in rule.path]) 1514 print "Rule for '%s' was not used." % '/'.join([str(s) for s in rule.path])
1514 1515
1515 if not options.isolates: 1516 if not options.isolates:
1516 all_cases = [c for c in all_cases if not c.TestsIsolates()] 1517 all_cases = [c for c in all_cases if not c.TestsIsolates()]
1517 1518
1518 if options.report: 1519 if options.report:
1519 PrintReport(all_cases) 1520 PrintReport(all_cases)
1520 1521
1521 result = None 1522 result = None
1522 cases_to_run = [ c for c in all_cases if not DoSkip(c) ] 1523 cases_to_run = [ c for c in all_cases if not DoSkip(c) ]
1523 if len(cases_to_run) == 0: 1524 if len(cases_to_run) == 0:
1524 print "No tests to run." 1525 print "No tests to run."
1525 return 0 1526 return 0
1526 else: 1527 else:
1527 try: 1528 try:
1528 start = time.time() 1529 start = time.time()
1529 if RunTestCases(cases_to_run, options.progress, options.j): 1530 if RunTestCases(cases_to_run, options.progress, options.j):
1530 result = 0 1531 result = 0
1531 else: 1532 else:
1532 result = 1 1533 result = 1
1533 duration = time.time() - start 1534 duration = time.time() - start
1534 except KeyboardInterrupt: 1535 except KeyboardInterrupt:
1535 print "Interrupted" 1536 print "Interrupted"
1536 return 1 1537 return 1
1537 1538
1538 if options.time: 1539 if options.time:
1539 # Write the times to stderr to make it easy to separate from the 1540 # Write the times to stderr to make it easy to separate from the
1540 # test output. 1541 # test output.
1541 print 1542 print
1542 sys.stderr.write("--- Total time: %s ---\n" % FormatTime(duration)) 1543 sys.stderr.write("--- Total time: %s ---\n" % FormatTime(duration))
1543 timed_tests = [ t.case for t in cases_to_run if not t.case.duration is None ] 1544 timed_tests = [ t.case for t in cases_to_run if not t.case.duration is None ]
1544 timed_tests.sort(lambda a, b: a.CompareTime(b)) 1545 timed_tests.sort(lambda a, b: a.CompareTime(b))
1545 index = 1 1546 index = 1
1546 for entry in timed_tests[:20]: 1547 for entry in timed_tests[:20]:
1547 t = FormatTime(entry.duration) 1548 t = FormatTime(entry.duration)
1548 sys.stderr.write("%4i (%s) %s\n" % (index, t, entry.GetLabel())) 1549 sys.stderr.write("%4i (%s) %s\n" % (index, t, entry.GetLabel()))
1549 index += 1 1550 index += 1
1550 1551
1551 return result 1552 return result
1552 1553
1553 1554
1554 if __name__ == '__main__': 1555 if __name__ == '__main__':
1555 sys.exit(Main()) 1556 sys.exit(Main())
Powered by Google Project Hosting