Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: tools/test.py

Issue 8223016: Style cleanups for test.py (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/testing/test_runner.py » ('j') | tools/testing/test_runner.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
4 # for details. All rights reserved. Use of this source code is governed by a 4 # for details. All rights reserved. Use of this source code is governed by a
5 # BSD-style license that can be found in the LICENSE file. 5 # BSD-style license that can be found in the LICENSE file.
6 # 6 #
7 7
8 """Test driver for the Dart project used by continuous build and developers."""
9
10
8 import imp 11 import imp
9 import optparse 12 import optparse
10 import os 13 import os
11 from os.path import join, dirname, abspath, basename, isdir, exists, realpath 14 import Queue
12 import platform
13 import re 15 import re
14 import select
15 import signal
16 import subprocess
17 import sys 16 import sys
18 import tempfile 17 import threading
19 import time 18 import time
20 import threading 19 import urllib
21 import traceback
22 from Queue import Queue, Empty
23 20
24 import testing 21 import testing
25 import testing.test_runner 22 from testing import test_runner
26 import utils 23 import utils
27 24
28 25
29 TIMEOUT_SECS = 60 26 TIMEOUT_SECS = 60
30 VERBOSE = False
31 ARCH_GUESS = utils.GuessArchitecture() 27 ARCH_GUESS = utils.GuessArchitecture()
32 OS_GUESS = utils.GuessOS() 28 OS_GUESS = utils.GuessOS()
33 BUILT_IN_TESTS = ['dartc', 'vm', 'dart', 'corelib', 'language', 'co19', 29 BUILT_IN_TESTS = ['dartc', 'vm', 'dart', 'corelib', 'language', 'co19',
34 'samples', 'isolate', 'stub-generator', 'client'] 30 'samples', 'isolate', 'stub-generator', 'client']
35 31
36 # Patterns for matching test options in .dart files. 32 # Patterns for matching test options in .dart files.
37 VM_OPTIONS_PATTERN = re.compile(r"// VMOptions=(.*)") 33 VM_OPTIONS_PATTERN = re.compile(r'// VMOptions=(.*)')
38 DART_OPTIONS_PATTERN = re.compile(r"// DartOptions=(.*)") 34 DART_OPTIONS_PATTERN = re.compile(r'// DartOptions=(.*)')
39 ISOLATE_STUB_PATTERN = re.compile(r"// IsolateStubs=(.*)") 35 ISOLATE_STUB_PATTERN = re.compile(r'// IsolateStubs=(.*)')
40 36
41 # --------------------------------------------- 37 # ---------------------------------------------
42 # --- P r o g r e s s I n d i c a t o r s --- 38 # --- P r o g r e s s I n d i c a t o r s ---
43 # --------------------------------------------- 39 # ---------------------------------------------
44 40
45 41
42 class Error(Exception):
43 pass
44
45
46 class ProgressIndicator(object): 46 class ProgressIndicator(object):
47 """Base class for displaying the progress of the test run."""
47 48
48 def __init__(self, cases, context): 49 def __init__(self, cases, context):
49 self.abort = False 50 self.abort = False
50 self.terminate = False 51 self.terminate = False
51 self.cases = cases 52 self.cases = cases
52 self.queue = Queue(len(cases)) 53 self.queue = Queue.Queue(len(cases))
53 self.batch_queues = {}; 54 self.batch_queues = {}
54 self.context = context 55 self.context = context
55 56
56 # Extract batchable cases. 57 # Extract batchable cases.
57 found_cmds = {} 58 found_cmds = {}
58 batch_cases = []
59 for case in cases: 59 for case in cases:
60 cmd = case.case.GetCommand()[0] 60 cmd = case.case.GetCommand()[0]
61 if not utils.IsWindows(): 61 if not utils.IsWindows():
62 # Diagnostic check for executable (if an absolute pathname) 62 # Diagnostic check for executable (if an absolute pathname)
63 if not cmd in found_cmds: 63 if not cmd in found_cmds:
64 if os.path.isabs(cmd) and not os.path.isfile(cmd): 64 if os.path.isabs(cmd) and not os.path.isfile(cmd):
65 msg = "Can't find command %s\n" % cmd \ 65 msg = "Can't find command %s\n" % cmd
66 + "(Did you build first? " \ 66 msg += '(Did you build first? '
67 + "Are you running in the correct directory?)" 67 msg += 'Are you running in the correct directory?)'
68 raise Exception(msg) 68 raise Exception(msg)
69 else: 69 else:
70 found_cmds[cmd] = 1 70 found_cmds[cmd] = 1
71 71
72 if case.case.IsBatchable(): 72 if case.case.IsBatchable():
73 if not self.batch_queues.has_key(cmd): 73 if not cmd in self.batch_queues:
74 self.batch_queues[cmd] = Queue(len(cases)) 74 self.batch_queues[cmd] = Queue.Queue(len(cases))
75 self.batch_queues[cmd].put(case) 75 self.batch_queues[cmd].put(case)
76 else: 76 else:
77 self.queue.put_nowait(case) 77 self.queue.put_nowait(case)
78 78
79 self.succeeded = 0 79 self.succeeded = 0
80 self.remaining = len(cases) 80 self.remaining = len(cases)
81 self.total = len(cases) 81 self.total = len(cases)
82 self.failed = [ ] 82 self.failed = []
83 self.crashed = 0 83 self.crashed = 0
84 self.lock = threading.Lock() 84 self.lock = threading.Lock()
85 85
86 def PrintFailureHeader(self, test): 86 def PrintFailureHeader(self, test):
87 if test.IsNegative(): 87 if test.IsNegative():
88 negative_marker = '[negative] ' 88 negative_marker = '[negative] '
89 else: 89 else:
90 negative_marker = '' 90 negative_marker = ''
91 print "=== %(label)s %(negative)s===" % { 91 print '=== %(label)s %(negative)s===' % {
92 'label': test.GetLabel(), 92 'label': test.GetLabel(),
93 'negative': negative_marker 93 'negative': negative_marker
94 } 94 }
95 print "Path: %s" % "/".join(test.path) 95 print 'Path: %s' % '/'.join(test.path)
96 96
97 def Run(self, tasks): 97 def Run(self, tasks):
98 """Starts tests and keeps running until queues are drained."""
98 self.Starting() 99 self.Starting()
99 100
100 # Scale the number of tasks to the nubmer of CPUs on the machine 101 # Scale the number of tasks to the nubmer of CPUs on the machine
101 if tasks == testing.USE_DEFAULT_CPUS: 102 if tasks == testing.USE_DEFAULT_CPUS:
102 tasks = testing.HOST_CPUS 103 tasks = testing.HOST_CPUS
103 104
104 # TODO(zundel): Refactor BatchSingle method and TestRunner to 105 # TODO(zundel): Refactor BatchSingle method and TestRunner to
105 # share code and simplify this method. 106 # share code and simplify this method.
106 107
107 # Start the non-batchable items first - there are some long running 108 # Start the non-batchable items first - there are some long running
108 # jobs we don't want to wait on at the end. 109 # jobs we don't want to wait on at the end.
109 threads = [] 110 threads = []
110 # Spawn N-1 threads and then use this thread as the last one. 111 # Spawn N-1 threads and then use this thread as the last one.
111 # That way -j1 avoids threading altogether which is a nice fallback 112 # That way -j1 avoids threading altogether which is a nice fallback
112 # in case of threading problems. 113 # in case of threading problems.
113 for i in xrange(tasks - 1): 114 for unused_i in xrange(tasks - 1):
114 thread = threading.Thread(target=self.RunSingle, args=[]) 115 thread = threading.Thread(target=self.RunSingle, args=[])
115 threads.append(thread) 116 threads.append(thread)
116 thread.start() 117 thread.start()
117 118
118
119 # Next, crank up the batchable tasks. Note that this will start 119 # Next, crank up the batchable tasks. Note that this will start
120 # 'tasks' more threads, but the assumption is that if batching is 120 # 'tasks' more threads, but the assumption is that if batching is
121 # enabled that almost all tests are batchable. 121 # enabled that almost all tests are batchable.
122 for (cmd, queue) in self.batch_queues.items(): 122 for (cmd, queue) in self.batch_queues.items():
123 if not queue.empty(): 123 if not queue.empty():
124 batch_len = queue.qsize();
125 batch_tester = None 124 batch_tester = None
126 try: 125 try:
127 batch_tester = testing.test_runner.BatchRunner(queue, tasks, self, 126 batch_tester = test_runner.BatchRunner(queue, tasks, self,
128 [cmd, '-batch']) 127 [cmd, '-batch'])
129 except Exception, e: 128 except:
130 print "Aborting batch test for " + cmd + ". Problem on startup." 129 print 'Aborting batch test for ' + cmd + '. Problem on startup.'
131 if batch_tester: batch_tester.Shutdown() 130 if batch_tester: batch_tester.Shutdown()
132 raise 131 raise
133 132
134 try: 133 try:
135 batch_tester.WaitForCompletion() 134 batch_tester.WaitForCompletion()
136 except: 135 except:
137 print "Aborting batch cmd " + cmd + "while waiting for completion." 136 print 'Aborting batch cmd ' + cmd + 'while waiting for completion.'
138 if batch_tester: batch_tester.Shutdown() 137 if batch_tester: batch_tester.Shutdown()
139 raise 138 raise
140 139
141 try: 140 try:
142 self.RunSingle() 141 self.RunSingle()
143 if self.abort: 142 if self.abort:
144 raise Exception("Aborted") 143 raise Error('Aborted')
145 # Wait for the remaining non-batched threads. 144 # Wait for the remaining non-batched threads.
146 for thread in threads: 145 for thread in threads:
147 # Use a timeout so that signals (ctrl-c) will be processed. 146 # Use a timeout so that signals (ctrl-c) will be processed.
148 thread.join(timeout=10000000) 147 thread.join(timeout=10000000)
149 if self.abort: 148 if self.abort:
150 raise Exception("Aborted") 149 raise Error('Aborted')
151 except Exception, e: 150 except:
152 # If there's an exception we schedule an interruption for any 151 # If there's an exception we schedule an interruption for any
153 # remaining threads. 152 # remaining threads.
154 self.terminate = True 153 self.terminate = True
155 # ...and then reraise the exception to bail out 154 # ...and then reraise the exception to bail out
156 raise 155 raise
157 156
158 self.Done() 157 self.Done()
159 return not self.failed 158 return not self.failed
160 159
161 def RunSingle(self): 160 def RunSingle(self):
162 while not self.terminate: 161 while not self.terminate:
163 try: 162 try:
164 test = self.queue.get_nowait() 163 test = self.queue.get_nowait()
165 except Empty: 164 except Queue.Empty:
166 return 165 return
167 case = test.case 166 case = test.case
168 with self.lock: 167 with self.lock:
169 self.AboutToRun(case) 168 self.AboutToRun(case)
170 try: 169 try:
171 start = time.time() 170 start = time.time()
172 output = case.Run() 171 output = case.Run()
173 case.duration = (time.time() - start) 172 case.duration = (time.time() - start)
174 except KeyboardInterrupt: 173 except KeyboardInterrupt:
175 self.abort = True 174 self.abort = True
176 self.terminate = True 175 self.terminate = True
177 raise 176 raise
178 except IOError, e: 177 except IOError:
179 self.abort = True 178 self.abort = True
180 self.terminate = True 179 self.terminate = True
181 raise 180 raise
182 if self.terminate: 181 if self.terminate:
183 return 182 return
184 with self.lock: 183 with self.lock:
185 if output.UnexpectedOutput(): 184 if output.UnexpectedOutput():
186 self.failed.append(output) 185 self.failed.append(output)
187 if output.HasCrashed(): 186 if output.HasCrashed():
188 self.crashed += 1 187 self.crashed += 1
189 else: 188 else:
190 self.succeeded += 1 189 self.succeeded += 1
191 self.remaining -= 1 190 self.remaining -= 1
192 self.HasRun(output) 191 self.HasRun(output)
193 192
194 193
195 def EscapeCommand(command): 194 def EscapeCommand(command):
196 parts = [] 195 parts = []
197 for part in command: 196 for part in command:
198 if ' ' in part: 197 if ' ' in part:
199 # Escape spaces. We may need to escape more characters for this 198 # Escape spaces. We may need to escape more characters for this
200 # to work properly. 199 # to work properly.
201 parts.append('"%s"' % part) 200 parts.append('"%s"' % part)
202 else: 201 else:
203 parts.append(part) 202 parts.append(part)
204 return " ".join(parts) 203 return ' '.join(parts)
205 204
206 205
207 class SimpleProgressIndicator(ProgressIndicator): 206 class SimpleProgressIndicator(ProgressIndicator):
207 """Base class for printing output of each test separately."""
208 208
209 def Starting(self): 209 def Starting(self):
210 """Called at the beginning before any tests are run."""
210 print 'Running %i tests' % len(self.cases) 211 print 'Running %i tests' % len(self.cases)
211 212
212 def Done(self): 213 def Done(self):
214 """Called when all tests are complete."""
213 print 215 print
214 for failed in self.failed: 216 for failed in self.failed:
215 self.PrintFailureHeader(failed.test) 217 self.PrintFailureHeader(failed.test)
216 if failed.output.stderr: 218 if failed.output.stderr:
217 print "--- stderr ---" 219 print '--- stderr ---'
218 print failed.output.stderr.strip() 220 print failed.output.stderr.strip()
219 if failed.output.stdout: 221 if failed.output.stdout:
220 print "--- stdout ---" 222 print '--- stdout ---'
221 print failed.output.stdout.strip() 223 print failed.output.stdout.strip()
222 print "Command: %s" % EscapeCommand(failed.command) 224 print 'Command: %s' % EscapeCommand(failed.command)
223 if failed.HasCrashed(): 225 if failed.HasCrashed():
224 print "--- CRASHED ---" 226 print '--- CRASHED ---'
225 if failed.HasTimedOut(): 227 if failed.HasTimedOut():
226 print "--- TIMEOUT ---" 228 print '--- TIMEOUT ---'
227 if len(self.failed) == 0: 229 if not self.failed:
228 print "===" 230 print '==='
229 print "=== All tests succeeded" 231 print '=== All tests succeeded'
230 print "===" 232 print '==='
231 else: 233 else:
232 print 234 print
233 print "===" 235 print '==='
234 if len(self.failed) == 1: 236 if len(self.failed) == 1:
235 print "=== 1 test failed" 237 print '=== 1 test failed'
236 else: 238 else:
237 print "=== %i tests failed" % len(self.failed) 239 print '=== %i tests failed' % len(self.failed)
238 if self.crashed > 0: 240 if self.crashed > 0:
239 if self.crashed == 1: 241 if self.crashed == 1:
240 print "=== 1 test CRASHED" 242 print '=== 1 test CRASHED'
241 else: 243 else:
242 print "=== %i tests CRASHED" % self.crashed 244 print '=== %i tests CRASHED' % self.crashed
243 print "===" 245 print '==='
244 246
245 247
246 class VerboseProgressIndicator(SimpleProgressIndicator): 248 class VerboseProgressIndicator(SimpleProgressIndicator):
249 """Print verbose information about each test that is run."""
247 250
248 def AboutToRun(self, case): 251 def AboutToRun(self, case):
252 """Called before each test case is run."""
249 print 'Starting %s...' % case.GetLabel() 253 print 'Starting %s...' % case.GetLabel()
250 sys.stdout.flush() 254 sys.stdout.flush()
251 255
252 def HasRun(self, output): 256 def HasRun(self, output):
257 """Called after each test case is run."""
253 if output.UnexpectedOutput(): 258 if output.UnexpectedOutput():
254 if output.HasCrashed(): 259 if output.HasCrashed():
255 outcome = 'CRASH' 260 outcome = 'CRASH'
256 else: 261 else:
257 outcome = 'FAIL' 262 outcome = 'FAIL'
258 else: 263 else:
259 outcome = 'PASS' 264 outcome = 'PASS'
260 print 'Done running %s: %s' % (output.test.GetLabel(), outcome) 265 print 'Done running %s: %s' % (output.test.GetLabel(), outcome)
261 266
262 267
263 class OneLineProgressIndicator(SimpleProgressIndicator): 268 class OneLineProgressIndicator(SimpleProgressIndicator):
269 """Results of each test is printed like a report, on a line by itself."""
264 270
265 def AboutToRun(self, case): 271 def AboutToRun(self, case):
272 """Called before each test case is run."""
266 pass 273 pass
267 274
268 def HasRun(self, output): 275 def HasRun(self, output):
276 """Called after each test case is run."""
269 if output.UnexpectedOutput(): 277 if output.UnexpectedOutput():
270 if output.HasCrashed(): 278 if output.HasCrashed():
271 outcome = 'CRASH' 279 outcome = 'CRASH'
272 else: 280 else:
273 outcome = 'FAIL' 281 outcome = 'FAIL'
274 else: 282 else:
275 outcome = 'pass' 283 outcome = 'pass'
276 print 'Done %s: %s' % (output.test.GetLabel(), outcome) 284 print 'Done %s: %s' % (output.test.GetLabel(), outcome)
277 285
278 286
279 class OneLineProgressIndicatorForBuildBot(OneLineProgressIndicator): 287 class OneLineProgressIndicatorForBuildBot(OneLineProgressIndicator):
280 288
281 def HasRun(self, output): 289 def HasRun(self, output):
290 """Called after each test case is run."""
282 super(OneLineProgressIndicatorForBuildBot, self).HasRun(output) 291 super(OneLineProgressIndicatorForBuildBot, self).HasRun(output)
283 percent = (((self.total - self.remaining) * 100) // self.total) 292 percent = (((self.total - self.remaining) * 100) // self.total)
284 print '@@@STEP_CLEAR@@@' 293 print '@@@STEP_CLEAR@@@'
285 print '@@@STEP_TEXT@ %3d%% +%d -%d @@@' % ( 294 print '@@@STEP_TEXT@ %3d%% +%d -%d @@@' % (
286 percent, self.succeeded, len(self.failed)) 295 percent, self.succeeded, len(self.failed))
287 296
288 297
289 class CompactProgressIndicator(ProgressIndicator): 298 class CompactProgressIndicator(ProgressIndicator):
299 """Continuously updates a single line w/ a summary of progress of the run."""
290 300
291 def __init__(self, cases, context, templates): 301 def __init__(self, cases, context, templates):
292 super(CompactProgressIndicator, self).__init__(cases, context) 302 super(CompactProgressIndicator, self).__init__(cases, context)
293 self.templates = templates 303 self.templates = templates
294 self.last_status_length = 0 304 self.last_status_length = 0
295 self.start_time = time.time() 305 self.start_time = time.time()
296 306
297 def Starting(self): 307 def Starting(self):
308 """Called at the beginning before any tests are run."""
298 pass 309 pass
299 310
300 def Done(self): 311 def Done(self):
301 self.PrintProgress('Done') 312 """Called when all tests are complete."""
313 self._PrintProgress('Done')
302 314
303 def AboutToRun(self, case): 315 def AboutToRun(self, case):
304 self.PrintProgress(case.GetLabel()) 316 """Called before each test case is run."""
317 self._PrintProgress(case.GetLabel())
305 318
306 def HasRun(self, output): 319 def HasRun(self, output):
320 """Called after each test case is run."""
307 if output.UnexpectedOutput(): 321 if output.UnexpectedOutput():
308 self.ClearLine(self.last_status_length) 322 self.ClearLine(self.last_status_length)
309 self.PrintFailureHeader(output.test) 323 self.PrintFailureHeader(output.test)
310 stdout = output.output.stdout.strip() 324 stdout = output.output.stdout.strip()
311 if len(stdout): 325 if stdout:
312 print self.templates['stdout'] % stdout 326 print self.templates['stdout'] % stdout
313 stderr = output.output.stderr.strip() 327 stderr = output.output.stderr.strip()
314 if len(stderr): 328 if stderr:
315 print self.templates['stderr'] % stderr 329 print self.templates['stderr'] % stderr
316 print "Command: %s" % EscapeCommand(output.command) 330 print 'Command: %s' % EscapeCommand(output.command)
317 if output.HasCrashed(): 331 if output.HasCrashed():
318 print "--- CRASHED ---" 332 print '--- CRASHED ---'
319 if output.HasTimedOut(): 333 if output.HasTimedOut():
320 print "--- TIMEOUT ---" 334 print '--- TIMEOUT ---'
321 335
322 def Truncate(self, str, length): 336 def _Truncate(self, buf, length):
323 if length and (len(str) > (length - 3)): 337 """Truncate a line if it exceeds length, substituting an ellipsis..."""
324 return str[:(length-3)] + "..." 338 if length and (len(buf) > (length - 3)):
339 return buf[:(length-3)] + '...'
325 else: 340 else:
326 return str 341 return buf
327 342
328 def PrintProgress(self, name): 343 def _PrintProgress(self, name):
344 """Refresh the display."""
329 self.ClearLine(self.last_status_length) 345 self.ClearLine(self.last_status_length)
330 elapsed = time.time() - self.start_time 346 elapsed = time.time() - self.start_time
331 status = self.templates['status_line'] % { 347 status = self.templates['status_line'] % {
332 'passed': self.succeeded, 348 'passed': self.succeeded,
333 'percent': (((self.total - self.remaining) * 100) // self.total), 349 'percent': (((self.total - self.remaining) * 100) // self.total),
334 'failed': len(self.failed), 350 'failed': len(self.failed),
335 'test': name, 351 'test': name,
336 'mins': int(elapsed) / 60, 352 'mins': int(elapsed) / 60,
337 'secs': int(elapsed) % 60 353 'secs': int(elapsed) % 60
338 } 354 }
339 status = self.Truncate(status, 78) 355 status = self._Truncate(status, 78)
340 self.last_status_length = len(status) 356 self.last_status_length = len(status)
341 print status, 357 print status,
342 sys.stdout.flush() 358 sys.stdout.flush()
343 359
360 def ClearLine(self, last_line_length):
361 """Erase the current line w/ a linefeed and overwriting with spaces."""
362 print ('\r' + (' ' * last_line_length) + '\r'),
363
344 364
345 class MonochromeProgressIndicator(CompactProgressIndicator): 365 class MonochromeProgressIndicator(CompactProgressIndicator):
366 """A CompactProgressIndicator with no color."""
346 367
347 def __init__(self, cases, context): 368 def __init__(self, cases, context):
348 templates = { 369 templates = {
349 'status_line': "[%(mins)02i:%(secs)02i|%%%(percent) 4d|+%(passed) 4d|-%(fa iled) 4d]: %(test)s", 370 'status_line': '[%(mins)02i:%(secs)02i|%%%(percent) '
350 'stdout': '%s', 371 '4d|+%(passed) 4d|-%(failed) 4d]: %(test)s',
351 'stderr': '%s', 372 'stdout': '%s',
352 'clear': lambda last_line_length: ("\r" + (" " * last_line_length) + "\r") , 373 'stderr': '%s',
353 'max_length': 78 374 'clear': lambda last_line_len: self.ClearLine(last_line_len),
375 'max_length': 78
354 } 376 }
355 super(MonochromeProgressIndicator, self).__init__(cases, context, templates) 377 super(MonochromeProgressIndicator, self).__init__(cases,
378 context,
379 templates)
356 380
357 def ClearLine(self, last_line_length):
358 print ("\r" + (" " * last_line_length) + "\r"),
359 381
360 class ColorProgressIndicator(CompactProgressIndicator): 382 class ColorProgressIndicator(CompactProgressIndicator):
383 """A CompactProgressIndicator with pretty colors."""
361 384
362 def __init__(self, cases, context): 385 def __init__(self, cases, context):
363 templates = { 386 templates = {
364 'status_line': ("[%(mins)02i:%(secs)02i|%%%(percent) 4d|" 387 'status_line': ('[%(mins)02i:%(secs)02i|%%%(percent) 4d|'
365 "\033[32m+%(passed) 4d" 388 '\033[32m+%(passed) 4d'
366 "\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s"), 389 '\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s'),
367 'stdout': '%s', 390 'stdout': '%s',
368 'stderr': '%s', 391 'stderr': '%s',
369 'clear': lambda last_line_length: ("\r" + (" " * last_line_length) + "\r") , 392 'clear': lambda last_line_len: self.ClearLine(last_line_len),
370 'max_length': 78 393 'max_length': 78
371 } 394 }
372 super(ColorProgressIndicator, self).__init__(cases, context, templates) 395 super(ColorProgressIndicator, self).__init__(cases, context, templates)
373 396
374 def ClearLine(self, last_line_length):
375 print ("\r" + (" " * last_line_length) + "\r"),
376
377 397
378 PROGRESS_INDICATORS = { 398 PROGRESS_INDICATORS = {
379 'verbose': VerboseProgressIndicator, 399 'verbose': VerboseProgressIndicator,
380 'mono': MonochromeProgressIndicator, 400 'mono': MonochromeProgressIndicator,
381 'color': ColorProgressIndicator, 401 'color': ColorProgressIndicator,
382 'line': OneLineProgressIndicator, 402 'line': OneLineProgressIndicator,
383 'buildbot': OneLineProgressIndicatorForBuildBot 403 'buildbot': OneLineProgressIndicatorForBuildBot
384 } 404 }
385 405
386 406
387 # ------------------------- 407 # -------------------------
388 # --- F r a m e w o r k --- 408 # --- F r a m e w o r k ---
389 # ------------------------- 409 # -------------------------
390 410
391 411
392 class CommandOutput(object):
393
394 def __init__(self, pid, exit_code, timed_out, stdout, stderr):
395 self.pid = pid
396 self.exit_code = exit_code
397 self.timed_out = timed_out
398 self.stdout = stdout
399 self.stderr = stderr
400 self.failed = None
401
402
403 class TestCase(object): 412 class TestCase(object):
413 """A single test case, like running 'dart' on a single .dart file."""
404 414
405 def __init__(self, context, path): 415 def __init__(self, context, path):
406 self.path = path 416 self.path = path
407 self.context = context 417 self.context = context
408 self.duration = None 418 self.duration = None
409 self.arch = [] 419 self.arch = []
410 420
411 def IsBatchable(self): 421 def IsBatchable(self):
412 if self.context.use_batch: 422 if self.context.use_batch:
413 if self.arch and 'dartc' in self.arch: 423 if self.arch and 'dartc' in self.arch:
(...skipping 10 matching lines...) Expand all
424 if output.failed is None: 434 if output.failed is None:
425 output.failed = self.IsFailureOutput(output) 435 output.failed = self.IsFailureOutput(output)
426 return output.failed 436 return output.failed
427 437
428 def IsFailureOutput(self, output): 438 def IsFailureOutput(self, output):
429 return output.exit_code != 0 439 return output.exit_code != 0
430 440
431 def RunCommand(self, command, cwd=None, cleanup=True): 441 def RunCommand(self, command, cwd=None, cleanup=True):
432 full_command = self.context.processor(command) 442 full_command = self.context.processor(command)
433 try: 443 try:
434 output = Execute(full_command, self.context, self.context.timeout, cwd) 444 output = test_runner.Execute(full_command, self.context,
445 self.context.timeout, cwd)
435 except OSError as e: 446 except OSError as e:
436 raise utils.ToolError("%s: %s" % (full_command[0], e.strerror)) 447 raise utils.ToolError('%s: %s' % (full_command[0], e.strerror))
437 test_output = TestOutput(self, full_command, output) 448 test_output = test_runner.TestOutput(self, full_command, output)
438 if cleanup: self.Cleanup() 449 if cleanup: self.Cleanup()
439 return test_output 450 return test_output
440 451
441 def BeforeRun(self): 452 def BeforeRun(self):
442 pass 453 pass
443 454
444 def AfterRun(self): 455 def AfterRun(self):
445 pass 456 pass
446 457
447 def Run(self): 458 def Run(self):
448 self.BeforeRun() 459 self.BeforeRun()
449 cmd = self.GetCommand() 460 cmd = self.GetCommand()
450 try: 461 try:
451 result = self.RunCommand(cmd) 462 result = self.RunCommand(cmd)
452 finally: 463 finally:
453 self.AfterRun() 464 self.AfterRun()
454 return result 465 return result
455 466
456 def Cleanup(self): 467 def Cleanup(self):
457 return 468 return
458 469
459 470
460 class TestOutput(object):
461
462 def __init__(self, test, command, output):
463 self.test = test
464 self.command = command
465 self.output = output
466
467 def UnexpectedOutput(self):
468 if self.HasCrashed():
469 outcome = testing.CRASH
470 elif self.HasTimedOut():
471 outcome = testing.TIMEOUT
472 elif self.HasFailed():
473 outcome = testing.FAIL
474 else:
475 outcome = testing.PASS
476 return not outcome in self.test.outcomes
477
478 def HasCrashed(self):
479 if utils.IsWindows():
480 if self.output.exit_code == 3:
481 # The VM uses std::abort to terminate on asserts.
482 # std::abort terminates with exit code 3 on Windows.
483 return True
484 return 0x80000000 & self.output.exit_code and not (0x3FFFFF00 & self.outpu t.exit_code)
485 else:
486 # Timed out tests will have exit_code -signal.SIGTERM.
487 if self.output.timed_out:
488 return False
489 if self.output.exit_code == 253:
490 # The Java dartc runners exit 253 in case of unhandled exceptions.
491 return True
492 return self.output.exit_code < 0
493
494 def HasTimedOut(self):
495 return self.output.timed_out;
496
497 def HasFailed(self):
498 execution_failed = self.test.DidFail(self.output)
499 if self.test.IsNegative():
500 return not execution_failed
501 else:
502 return execution_failed
503
504
505 def KillProcessWithID(pid):
506 if utils.IsWindows():
507 os.popen('taskkill /T /F /PID %d' % pid)
508 else:
509 os.kill(pid, signal.SIGTERM)
510
511
512 MAX_SLEEP_TIME = 0.1
513 INITIAL_SLEEP_TIME = 0.0001
514 SLEEP_TIME_FACTOR = 1.25
515
516 SEM_INVALID_VALUE = -1
517 SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h
518
519 def Win32SetErrorMode(mode):
520 prev_error_mode = SEM_INVALID_VALUE
521 try:
522 import ctypes
523 prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode);
524 except ImportError:
525 pass
526 return prev_error_mode
527
528 def RunProcess(context, timeout, args, **rest):
529 if context.verbose: print "#", " ".join(args)
530 popen_args = args
531 prev_error_mode = SEM_INVALID_VALUE;
532 if utils.IsWindows():
533 popen_args = '"' + subprocess.list2cmdline(args) + '"'
534 if context.suppress_dialogs:
535 # Try to change the error mode to avoid dialogs on fatal errors. Don't
536 # touch any existing error mode flags by merging the existing error mode.
537 # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx.
538 error_mode = SEM_NOGPFAULTERRORBOX;
539 prev_error_mode = Win32SetErrorMode(error_mode);
540 Win32SetErrorMode(error_mode | prev_error_mode);
541 process = subprocess.Popen(
542 shell = utils.IsWindows(),
543 args = popen_args,
544 **rest
545 )
546 if utils.IsWindows() and context.suppress_dialogs and prev_error_mode != SEM_I NVALID_VALUE:
547 Win32SetErrorMode(prev_error_mode)
548 # Compute the end time - if the process crosses this limit we
549 # consider it timed out.
550 if timeout is None: end_time = None
551 else: end_time = time.time() + timeout
552 timed_out = False
553 # Repeatedly check the exit code from the process in a
554 # loop and keep track of whether or not it times out.
555 exit_code = None
556 sleep_time = INITIAL_SLEEP_TIME
557 while exit_code is None:
558 if (not end_time is None) and (time.time() >= end_time):
559 # Kill the process and wait for it to exit.
560 KillProcessWithID(process.pid)
561 # Drain the output pipe from the process to avoid deadlock
562 process.communicate()
563 exit_code = process.wait()
564 timed_out = True
565 else:
566 exit_code = process.poll()
567 time.sleep(sleep_time)
568 sleep_time = sleep_time * SLEEP_TIME_FACTOR
569 if sleep_time > MAX_SLEEP_TIME:
570 sleep_time = MAX_SLEEP_TIME
571 return (process, exit_code, timed_out)
572
573
574 def PrintError(str):
575 sys.stderr.write(str)
576 sys.stderr.write('\n')
577
578
579 def CheckedUnlink(name):
580 try:
581 os.unlink(name)
582 except OSError, e:
583 PrintError("os.unlink() " + str(e))
584
585
586 def Execute(args, context, timeout=None, cwd=None):
587 (fd_out, outname) = tempfile.mkstemp()
588 (fd_err, errname) = tempfile.mkstemp()
589 (process, exit_code, timed_out) = RunProcess(
590 context,
591 timeout,
592 args = args,
593 stdout = fd_out,
594 stderr = fd_err,
595 cwd = cwd
596 )
597 os.close(fd_out)
598 os.close(fd_err)
599 output = file(outname).read()
600 errors = file(errname).read()
601 CheckedUnlink(outname)
602 CheckedUnlink(errname)
603 result = CommandOutput(process.pid, exit_code, timed_out, output, errors)
604 return result
605
606
607 class TestConfiguration(object): 471 class TestConfiguration(object):
472 """Test configurations give test.py the list of tests, e.g. listing a dir."""
608 473
609 def __init__(self, context, root): 474 def __init__(self, context, root):
610 self.context = context 475 self.context = context
611 self.root = root 476 self.root = root
612 477
613 def Contains(self, path, file): 478 def Contains(self, path, filename):
614 if len(path) > len(file): 479 """Returns True if the given path regexp matches the passed filename."""
480
481 if len(path) > len(filename):
615 return False 482 return False
616 for i in xrange(len(path)): 483 for i in xrange(len(path)):
617 if not path[i].match(file[i]): 484 try:
618 return False 485 if not path[i].match(filename[i]):
486 return False
487 except:
488 print 'Invalid regexp %s in .status file. ' % '/'.join(path)
489 print 'Try escaping special characters with \\'
490 raise
491
619 return True 492 return True
620 493
621 def GetTestStatus(self, sections, defs): 494 def GetTestStatus(self, sections, defs):
622 pass 495 pass
623 496
624 497
625 class TestSuite(object): 498 class TestSuite(object):
626 499
627 def __init__(self, name): 500 def __init__(self, name):
628 self.name = name 501 self.name = name
629 502
630 def GetName(self): 503 def GetName(self):
631 return self.name 504 return self.name
632 505
633 506
634 class TestRepository(TestSuite): 507 class TestRepository(TestSuite):
508 """A collection of test configurations."""
635 509
636 def __init__(self, path): 510 def __init__(self, path):
637 normalized_path = abspath(path) 511 normalized_path = os.path.abspath(path)
638 super(TestRepository, self).__init__(basename(normalized_path)) 512 super(TestRepository, self).__init__(os.path.basename(normalized_path))
639 self.path = normalized_path 513 self.path = normalized_path
640 self.is_loaded = False 514 self.is_loaded = False
641 self.config = None 515 self.config = None
642 516
643 def GetConfiguration(self, context): 517 def GetConfiguration(self, context):
518 """Retrieve a TestConfiguration subclass for this set of tests."""
644 if self.is_loaded: 519 if self.is_loaded:
645 return self.config 520 return self.config
646 self.is_loaded = True 521 self.is_loaded = True
647 file = None 522 filename = None
648 try: 523 try:
649 (file, pathname, description) = imp.find_module('testcfg', [ self.path ]) 524 (filename, pathname, description) = imp.find_module(
650 module = imp.load_module('testcfg', file, pathname, description) 525 'testcfg', [self.path])
526 module = imp.load_module('testcfg', filename, pathname, description)
651 self.config = module.GetConfiguration(context, self.path) 527 self.config = module.GetConfiguration(context, self.path)
652 finally: 528 finally:
653 if file: 529 if filename:
654 file.close() 530 filename.close()
655 return self.config 531 return self.config
656 532
657 def ListTests(self, current_path, path, context, mode, arch): 533 def ListTests(self, current_path, path, context, mode, arch):
658 return self.GetConfiguration(context).ListTests(current_path, 534 return self.GetConfiguration(context).ListTests(current_path,
659 path, 535 path,
660 mode, 536 mode,
661 arch) 537 arch)
662 538
663 def GetTestStatus(self, context, sections, defs): 539 def GetTestStatus(self, context, sections, defs):
664 self.GetConfiguration(context).GetTestStatus(sections, defs) 540 self.GetConfiguration(context).GetTestStatus(sections, defs)
665 541
666 542
667 class LiteralTestSuite(TestSuite): 543 class LiteralTestSuite(TestSuite):
544 """Represents one set of tests."""
668 545
669 def __init__(self, tests): 546 def __init__(self, tests):
670 super(LiteralTestSuite, self).__init__('root') 547 super(LiteralTestSuite, self).__init__('root')
671 self.tests = tests 548 self.tests = tests
672 549
673 def ListTests(self, current_path, path, context, mode, arch): 550 def ListTests(self, current_path, path, context, mode, arch):
674 name = path[0] 551 name = path[0]
675 result = [ ] 552 result = []
676 for test in self.tests: 553 for test in self.tests:
677 test_name = test.GetName() 554 test_name = test.GetName()
678 if name.match(test_name): 555 if name.match(test_name):
679 full_path = current_path + [test_name] 556 full_path = current_path + [test_name]
680 result += test.ListTests(full_path, path, context, mode, arch) 557 result += test.ListTests(full_path, path, context, mode, arch)
681 return result 558 return result
682 559
683 def GetTestStatus(self, context, sections, defs): 560 def GetTestStatus(self, context, sections, defs):
684 for test in self.tests: 561 for test in self.tests:
685 test.GetTestStatus(context, sections, defs) 562 test.GetTestStatus(context, sections, defs)
686 563
564
687 class Context(object): 565 class Context(object):
566 """A way to send global context for the test run to each test case."""
688 567
689 def __init__(self, workspace, verbose, os, timeout, 568 def __init__(self, workspace, verbose, os_def, timeout,
ahe 2011/10/11 07:55:34 I'm not sure what os_def means. Would you mind not
zundel 2011/10/11 22:45:44 os conflicts with import os, so I renamed it. I'l
690 processor, suppress_dialogs, executable, flags, 569 processor, suppress_dialogs, executable, flags,
691 keep_temporary_files, use_batch): 570 keep_temporary_files, use_batch):
692 self.workspace = workspace 571 self.workspace = workspace
693 self.verbose = verbose 572 self.verbose = verbose
694 self.os = os 573 self.os = os_def
695 self.timeout = timeout 574 self.timeout = timeout
696 self.processor = processor 575 self.processor = processor
697 self.suppress_dialogs = suppress_dialogs 576 self.suppress_dialogs = suppress_dialogs
698 self.executable = executable 577 self.executable = executable
699 self.flags = flags 578 self.flags = flags
700 self.keep_temporary_files = keep_temporary_files 579 self.keep_temporary_files = keep_temporary_files
701 self.use_batch = use_batch == "true" 580 self.use_batch = use_batch == 'true'
702 581
703 def GetBuildRoot(self, mode, arch): 582 def GetBuildRoot(self, mode, arch):
583 """The top level directory containing compiler, runtime, tools..."""
704 result = utils.GetBuildRoot(self.os, mode, arch) 584 result = utils.GetBuildRoot(self.os, mode, arch)
705 return result 585 return result
706 586
707 def GetBuildConf(self, mode, arch): 587 def GetBuildConf(self, mode, arch):
708 result = utils.GetBuildConf(mode, arch) 588 result = utils.GetBuildConf(mode, arch)
709 return result 589 return result
710 590
711 def GetExecutable(self, mode, arch, name): 591 def GetExecutable(self, mode, arch, name):
592 """Returns the name of the executable used to run the test."""
712 if self.executable is not None: 593 if self.executable is not None:
713 return self.executable 594 return self.executable
714 path = abspath(join(self.GetBuildRoot(mode, arch), name)) 595 path = os.path.abspath(os.path.join(self.GetBuildRoot(mode, arch), name))
715 if utils.IsWindows() and not path.endswith('.exe'): 596 if utils.IsWindows() and not path.endswith('.exe'):
716 return path + '.exe' 597 return path + '.exe'
717 else: 598 else:
718 return path 599 return path
719 600
720 def GetDart(self, mode, arch): 601 def GetDart(self, mode, arch):
602 """Returns the path to the Dart test runner (executes the .dart file)."""
721 if arch == 'dartc': 603 if arch == 'dartc':
722 command = [ abspath(join(self.GetBuildRoot(mode, arch), 604 command = [os.path.abspath(
723 'compiler', 'bin', 'dartc_test')) ] 605 os.path.join(self.GetBuildRoot(mode, arch),
606 'compiler', 'bin', 'dartc_test'))]
724 else: 607 else:
725 command = [ self.GetExecutable(mode, arch, 'dart_bin') ] 608 command = [self.GetExecutable(mode, arch, 'dart_bin')]
726 609
727 return command 610 return command
728 611
729 def GetDartC(self, mode, arch): 612 def GetDartC(self, mode, arch):
730 dartc = abspath(os.path.join(self.GetBuildRoot(mode, arch), 613 """Returns the path to the Dart --> JS compiler."""
731 'compiler', 'bin', 'dartc')) 614 dartc = os.path.abspath(os.path.join(
615 self.GetBuildRoot(mode, arch), 'compiler', 'bin', 'dartc'))
732 if utils.IsWindows(): dartc += '.exe' 616 if utils.IsWindows(): dartc += '.exe'
733 command = [ dartc ] 617 command = [dartc]
734 618
735 # Add the flags from the context to the command line. 619 # Add the flags from the context to the command line.
736 command += self.flags 620 command += self.flags
737 return command 621 return command
738 622
739 def GetRunTests(self, mode, arch): 623 def GetRunTests(self, mode, arch):
740 return [ self.GetExecutable(mode, arch, 'run_vm_tests') ] 624 return [self.GetExecutable(mode, arch, 'run_vm_tests')]
625
741 626
742 def RunTestCases(cases_to_run, progress, tasks, context): 627 def RunTestCases(cases_to_run, progress, tasks, context):
628 """Chooses a progress indicator and then starts the tests."""
743 progress = PROGRESS_INDICATORS[progress](cases_to_run, context) 629 progress = PROGRESS_INDICATORS[progress](cases_to_run, context)
744 return progress.Run(tasks) 630 return progress.Run(tasks)
745 631
746 632
747 # ------------------------------------------- 633 # -------------------------------------------
748 # --- T e s t C o n f i g u r a t i o n --- 634 # --- T e s t C o n f i g u r a t i o n ---
749 # ------------------------------------------- 635 # -------------------------------------------
750 636
637
751 class Expression(object): 638 class Expression(object):
752 pass 639 pass
753 640
754 641
755 class Constant(Expression): 642 class Constant(Expression):
756 643
757 def __init__(self, value): 644 def __init__(self, value):
645 super(Constant, self).__init__()
758 self.value = value 646 self.value = value
759 647
760 def Evaluate(self, env, defs): 648 def Evaluate(self, unused_env, unused_defs):
761 return self.value 649 return self.value
762 650
763 651
764 class Variable(Expression): 652 class Variable(Expression):
765 653
766 def __init__(self, name): 654 def __init__(self, name):
655 super(Variable, self).__init__()
767 self.name = name 656 self.name = name
768 657
769 def GetOutcomes(self, env, defs): 658 def GetOutcomes(self, env, unused_defs):
770 if self.name in env: return ListSet([env[self.name]]) 659 if self.name in env:
660 return ListSet([env[self.name]])
771 else: return Nothing() 661 else: return Nothing()
772 662
773 663
774 class Outcome(Expression): 664 class Outcome(Expression):
775 665
776 def __init__(self, name): 666 def __init__(self, name):
667 super(Outcome, self).__init__()
777 self.name = name 668 self.name = name
778 669
779 def GetOutcomes(self, env, defs): 670 def GetOutcomes(self, env, defs):
780 if self.name in defs: 671 if self.name in defs:
781 return defs[self.name].GetOutcomes(env, defs) 672 return defs[self.name].GetOutcomes(env, defs)
782 else: 673 else:
783 return ListSet([self.name]) 674 return ListSet([self.name])
784 675
785 676
786 class Set(object): 677 class Set(object):
678 """An abstract set class used to hold Rules."""
787 pass 679 pass
788 680
789 681
790 class ListSet(Set): 682 class ListSet(Set):
683 """A set that uses lists for storage."""
791 684
792 def __init__(self, elms): 685 def __init__(self, elms):
686 super(ListSet, self).__init__()
793 self.elms = elms 687 self.elms = elms
794 688
795 def __str__(self): 689 def __str__(self):
796 return "ListSet%s" % str(self.elms) 690 return 'ListSet%s' % str(self.elms)
797 691
798 def Intersect(self, that): 692 def Intersect(self, that):
799 if not isinstance(that, ListSet): 693 if not isinstance(that, ListSet):
800 return that.Intersect(self) 694 return that.Intersect(self)
801 return ListSet([ x for x in self.elms if x in that.elms ]) 695 return ListSet([x for x in self.elms if x in that.elms])
802 696
803 def Union(self, that): 697 def Union(self, that):
804 if not isinstance(that, ListSet): 698 if not isinstance(that, ListSet):
805 return that.Union(self) 699 return that.Union(self)
806 return ListSet(self.elms + [ x for x in that.elms if x not in self.elms ]) 700 return ListSet(self.elms +
701 [x for x in that.elms if x not in self.elms])
807 702
808 def IsEmpty(self): 703 def IsEmpty(self):
809 return len(self.elms) == 0 704 return not self.elms
810 705
811 706
812 class Everything(Set): 707 class Everything(Set):
708 """A set that represents all possible values."""
813 709
814 def Intersect(self, that): 710 def Intersect(self, that):
815 return that 711 return that
816 712
817 def Union(self, that): 713 def Union(self, unused_that):
818 return self 714 return self
819 715
820 def IsEmpty(self): 716 def IsEmpty(self):
821 return False 717 return False
822 718
823 719
824 class Nothing(Set): 720 class Nothing(Set):
825 721
826 def Intersect(self, that): 722 def Intersect(self, unused_that):
827 return self 723 return self
828 724
829 def Union(self, that): 725 def Union(self, that):
830 return that 726 return that
831 727
832 def IsEmpty(self): 728 def IsEmpty(self):
833 return True 729 return True
834 730
835 731
836 class Operation(Expression): 732 class Operation(Expression):
733 """A conditional expression. e.g. ($arch == ia32)."""
837 734
838 def __init__(self, left, op, right): 735 def __init__(self, left, op, right):
736 super(Operation, self).__init__()
839 self.left = left 737 self.left = left
840 self.op = op 738 self.op = op
841 self.right = right 739 self.right = right
842 740
843 def Evaluate(self, env, defs): 741 def Evaluate(self, env, defs):
742 """Evaluates expression in the .status file. e.g. ($arch == ia32)."""
743
844 if self.op == '||' or self.op == ',': 744 if self.op == '||' or self.op == ',':
845 return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs) 745 return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs)
846 elif self.op == 'if': 746 elif self.op == 'if':
847 return False 747 return False
848 elif self.op == '==': 748 elif self.op == '==':
849 inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes( env, defs)) 749 outcomes = self.left.GetOutcomes(env, defs)
750 inter = outcomes.Intersect(self.right.GetOutcomes(env, defs))
850 return not inter.IsEmpty() 751 return not inter.IsEmpty()
851 else: 752 else:
852 assert self.op == '&&' 753 assert self.op == '&&'
853 return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs) 754 return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs)
854 755
855 def GetOutcomes(self, env, defs): 756 def GetOutcomes(self, env, defs):
856 if self.op == '||' or self.op == ',': 757 if self.op == '||' or self.op == ',':
857 return self.left.GetOutcomes(env, defs).Union(self.right.GetOutcomes(env, defs)) 758 outcomes = self.left.GetOutcomes(env, defs)
759 return outcomes.Union(self.right.GetOutcomes(env, defs))
858 elif self.op == 'if': 760 elif self.op == 'if':
859 if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs) 761 if self.right.Evaluate(env, defs):
762 return self.left.GetOutcomes(env, defs)
860 else: return Nothing() 763 else: return Nothing()
861 else: 764 else:
862 assert self.op == '&&' 765 assert self.op == '&&'
863 return self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(e nv, defs)) 766 outcomes = self.left.GetOutcomes(env, defs)
767 return outcomes.Intersect(self.right.GetOutcomes(env, defs))
864 768
865 769
866 def IsAlpha(str): 770 def IsAlpha(buf):
867 for char in str: 771 """Returns True if the entire string is alphanumeric."""
772 for char in buf:
868 if not (char.isalpha() or char.isdigit() or char == '_'): 773 if not (char.isalpha() or char.isdigit() or char == '_'):
869 return False 774 return False
870 return True 775 return True
871 776
872 777
873 class Tokenizer(object): 778 class Tokenizer(object):
874 """A simple string tokenizer that chops expressions into variables, 779 """Tokenizer that chops expressions into variables, parens and operators."""
875 parens and operators"""
876 780
877 def __init__(self, expr): 781 def __init__(self, expr):
878 self.index = 0 782 self.index = 0
879 self.expr = expr 783 self.expr = expr
880 self.length = len(expr) 784 self.length = len(expr)
881 self.tokens = None 785 self.tokens = None
882 786
883 def Current(self, length = 1): 787 def Current(self, length=1):
884 if not self.HasMore(length): return "" 788 if not self.HasMore(length): return ''
885 return self.expr[self.index:self.index+length] 789 return self.expr[self.index:self.index+length]
886 790
887 def HasMore(self, length = 1): 791 def HasMore(self, length=1):
888 return self.index < self.length + (length - 1) 792 return self.index < self.length + (length - 1)
889 793
890 def Advance(self, count = 1): 794 def Advance(self, count=1):
891 self.index = self.index + count 795 self.index += count
892 796
893 def AddToken(self, token): 797 def AddToken(self, token):
894 self.tokens.append(token) 798 self.tokens.append(token)
895 799
896 def SkipSpaces(self): 800 def SkipSpaces(self):
897 while self.HasMore() and self.Current().isspace(): 801 while self.HasMore() and self.Current().isspace():
898 self.Advance() 802 self.Advance()
899 803
900 def Tokenize(self): 804 def Tokenize(self):
901 self.tokens = [ ] 805 """Lexical analysis of an expression in a .status file.
806
807 Example:
808 [ $mode == debug && ($arch == chromium || $arch == dartc) ]
809
810 Args:
811 None.
812
813 Returns:
814 A list of tokens on success, None on failure.
815 """
816
817 self.tokens = []
902 while self.HasMore(): 818 while self.HasMore():
903 self.SkipSpaces() 819 self.SkipSpaces()
904 if not self.HasMore(): 820 if not self.HasMore():
905 return None 821 return None
906 if self.Current() == '(': 822 if self.Current() == '(':
907 self.AddToken('(') 823 self.AddToken('(')
908 self.Advance() 824 self.Advance()
909 elif self.Current() == ')': 825 elif self.Current() == ')':
910 self.AddToken(')') 826 self.AddToken(')')
911 self.Advance() 827 self.Advance()
912 elif self.Current() == '$': 828 elif self.Current() == '$':
913 self.AddToken('$') 829 self.AddToken('$')
914 self.Advance() 830 self.Advance()
915 elif self.Current() == ',': 831 elif self.Current() == ',':
916 self.AddToken(',') 832 self.AddToken(',')
917 self.Advance() 833 self.Advance()
918 elif IsAlpha(self.Current()): 834 elif IsAlpha(self.Current()):
919 buf = "" 835 buf = ''
920 while self.HasMore() and IsAlpha(self.Current()): 836 while self.HasMore() and IsAlpha(self.Current()):
921 buf += self.Current() 837 buf += self.Current()
922 self.Advance() 838 self.Advance()
923 self.AddToken(buf) 839 self.AddToken(buf)
924 elif self.Current(2) == '&&': 840 elif self.Current(2) == '&&':
925 self.AddToken('&&') 841 self.AddToken('&&')
926 self.Advance(2) 842 self.Advance(2)
927 elif self.Current(2) == '||': 843 elif self.Current(2) == '||':
928 self.AddToken('||') 844 self.AddToken('||')
929 self.Advance(2) 845 self.Advance(2)
930 elif self.Current(2) == '==': 846 elif self.Current(2) == '==':
931 self.AddToken('==') 847 self.AddToken('==')
932 self.Advance(2) 848 self.Advance(2)
933 else: 849 else:
934 return None 850 return None
935 return self.tokens 851 return self.tokens
936 852
937 853
938 class Scanner(object): 854 class Scanner(object):
939 """A simple scanner that can serve out tokens from a given list""" 855 """A simple scanner that can serve out tokens from a given list."""
940 856
941 def __init__(self, tokens): 857 def __init__(self, tokens):
942 self.tokens = tokens 858 self.tokens = tokens
943 self.length = len(tokens) 859 self.length = len(tokens)
944 self.index = 0 860 self.index = 0
945 861
946 def HasMore(self): 862 def HasMore(self):
947 return self.index < self.length 863 return self.index < self.length
948 864
949 def Current(self): 865 def Current(self):
950 return self.tokens[self.index] 866 return self.tokens[self.index]
951 867
952 def Advance(self): 868 def Advance(self):
953 self.index = self.index + 1 869 self.index += 1
954 870
955 871
956 def ParseAtomicExpression(scan): 872 def ParseAtomicExpression(scan):
957 if scan.Current() == "true": 873 """Parse an single (non recursive) expression in a .status file."""
874
875 if scan.Current() == 'true':
958 scan.Advance() 876 scan.Advance()
959 return Constant(True) 877 return Constant(True)
960 elif scan.Current() == "false": 878 elif scan.Current() == 'false':
961 scan.Advance() 879 scan.Advance()
962 return Constant(False) 880 return Constant(False)
963 elif IsAlpha(scan.Current()): 881 elif IsAlpha(scan.Current()):
964 name = scan.Current() 882 name = scan.Current()
965 scan.Advance() 883 scan.Advance()
966 return Outcome(name.lower()) 884 return Outcome(name.lower())
967 elif scan.Current() == '$': 885 elif scan.Current() == '$':
968 scan.Advance() 886 scan.Advance()
969 if not IsAlpha(scan.Current()): 887 if not IsAlpha(scan.Current()):
970 return None 888 return None
971 name = scan.Current() 889 name = scan.Current()
972 scan.Advance() 890 scan.Advance()
973 return Variable(name.lower()) 891 return Variable(name.lower())
974 elif scan.Current() == '(': 892 elif scan.Current() == '(':
975 scan.Advance() 893 scan.Advance()
976 result = ParseLogicalExpression(scan) 894 result = ParseLogicalExpression(scan)
977 if (not result) or (scan.Current() != ')'): 895 if (not result) or (scan.Current() != ')'):
978 return None 896 return None
979 scan.Advance() 897 scan.Advance()
980 return result 898 return result
981 else: 899 else:
982 return None 900 return None
983 901
984 902
985 BINARIES = ['==']
986 def ParseOperatorExpression(scan): 903 def ParseOperatorExpression(scan):
904 """Parse an expression that has operators."""
987 left = ParseAtomicExpression(scan) 905 left = ParseAtomicExpression(scan)
988 if not left: return None 906 if not left: return None
989 while scan.HasMore() and (scan.Current() in BINARIES): 907 while scan.HasMore() and (scan.Current() in ['==']):
990 op = scan.Current() 908 op = scan.Current()
991 scan.Advance() 909 scan.Advance()
992 right = ParseOperatorExpression(scan) 910 right = ParseOperatorExpression(scan)
993 if not right: 911 if not right:
994 return None 912 return None
995 left = Operation(left, op, right) 913 left = Operation(left, op, right)
996 return left 914 return left
997 915
998 916
999 def ParseConditionalExpression(scan): 917 def ParseConditionalExpression(scan):
1000 left = ParseOperatorExpression(scan) 918 left = ParseOperatorExpression(scan)
1001 if not left: return None 919 if not left: return None
1002 while scan.HasMore() and (scan.Current() == 'if'): 920 while scan.HasMore() and (scan.Current() == 'if'):
1003 scan.Advance() 921 scan.Advance()
1004 right = ParseOperatorExpression(scan) 922 right = ParseOperatorExpression(scan)
1005 if not right: 923 if not right:
1006 return None 924 return None
1007 left= Operation(left, 'if', right) 925 left = Operation(left, 'if', right)
1008 return left 926 return left
1009 927
1010 928
1011 LOGICALS = ["&&", "||", ","]
1012 def ParseLogicalExpression(scan): 929 def ParseLogicalExpression(scan):
930 """Parse a binary expression separated by boolean operators."""
1013 left = ParseConditionalExpression(scan) 931 left = ParseConditionalExpression(scan)
1014 if not left: return None 932 if not left: return None
1015 while scan.HasMore() and (scan.Current() in LOGICALS): 933 while scan.HasMore() and (scan.Current() in ['&&', '||', ',']):
1016 op = scan.Current() 934 op = scan.Current()
1017 scan.Advance() 935 scan.Advance()
1018 right = ParseConditionalExpression(scan) 936 right = ParseConditionalExpression(scan)
1019 if not right: 937 if not right:
1020 return None 938 return None
1021 left = Operation(left, op, right) 939 left = Operation(left, op, right)
1022 return left 940 return left
1023 941
1024 942
1025 def ParseCondition(expr): 943 def ParseCondition(expr):
1026 """Parses a logical expression into an Expression object""" 944 """Parses a boolean expression into an Expression object."""
1027 tokens = Tokenizer(expr).Tokenize() 945 tokens = Tokenizer(expr).Tokenize()
1028 if not tokens: 946 if not tokens:
1029 print "Malformed expression: '%s'" % expr 947 print 'Malformed expression: "%s"' % expr
1030 return None 948 return None
1031 scan = Scanner(tokens) 949 scan = Scanner(tokens)
1032 ast = ParseLogicalExpression(scan) 950 ast = ParseLogicalExpression(scan)
1033 if not ast: 951 if not ast:
1034 print "Malformed expression: '%s'" % expr 952 print 'Malformed expression: "%s"' % expr
1035 return None 953 return None
1036 if scan.HasMore(): 954 if scan.HasMore():
1037 print "Malformed expression: '%s'" % expr 955 print 'Malformed expression: "%s"' % expr
1038 return None 956 return None
1039 return ast 957 return ast
1040 958
1041 959
1042 class ClassifiedTest(object): 960 class ClassifiedTest(object):
1043 961
1044 def __init__(self, case, outcomes): 962 def __init__(self, case, outcomes):
1045 self.case = case 963 self.case = case
1046 self.outcomes = outcomes 964 self.outcomes = outcomes
1047 965
1048 966
1049 class Configuration(object): 967 class Configuration(object):
1050 """The parsed contents of a configuration file""" 968 """The parsed contents of a configuration file."""
1051 969
1052 def __init__(self, sections, defs): 970 def __init__(self, sections, defs):
1053 self.sections = sections 971 self.sections = sections
1054 self.defs = defs 972 self.defs = defs
1055 973
1056 def ClassifyTests(self, cases, env): 974 def ClassifyTests(self, cases, env):
1057 sections = [s for s in self.sections if s.condition.Evaluate(env, self.defs) ] 975 """Matches a test case with the test prefixes requested on the cmdline?
ahe 2011/10/11 07:55:34 ? -> .
zundel 2011/10/11 22:45:44 Done.
976
977 This 'wraps' each TestCase object with some meta information
978 about the test.
979
980 Args:
981 cases: list of TestCase objects to classify.
982 env: dictionary containing values for 'mode': mode,
983 'system' and 'arch'.
984
985 Returns:
986 A triplet of (result, rules, expected_outcomes).
987 """
988 sections = [s for s in self.sections
989 if s.condition.Evaluate(env, self.defs)]
1058 all_rules = reduce(list.__add__, [s.rules for s in sections], []) 990 all_rules = reduce(list.__add__, [s.rules for s in sections], [])
1059 unused_rules = set(all_rules) 991 unused_rules = set(all_rules)
1060 result = [ ] 992 result = []
1061 all_outcomes = set([]) 993 all_outcomes = set([])
1062 for case in cases: 994 for case in cases:
1063 matches = [ r for r in all_rules if r.Contains(case.path) ] 995 matches = [r for r in all_rules if r.Contains(case.path)]
1064 outcomes = set([]) 996 outcomes = set([])
1065 for rule in matches: 997 for rule in matches:
1066 outcomes = outcomes.union(rule.GetOutcomes(env, self.defs)) 998 outcomes = outcomes.union(rule.GetOutcomes(env, self.defs))
1067 unused_rules.discard(rule) 999 unused_rules.discard(rule)
1068 if not outcomes: 1000 if not outcomes:
1069 outcomes = [testing.PASS] 1001 outcomes = [testing.PASS]
1070 case.outcomes = outcomes 1002 case.outcomes = outcomes
1071 all_outcomes = all_outcomes.union(outcomes) 1003 all_outcomes = all_outcomes.union(outcomes)
1072 result.append(ClassifiedTest(case, outcomes)) 1004 result.append(ClassifiedTest(case, outcomes))
1073 return (result, list(unused_rules), all_outcomes) 1005 return (result, list(unused_rules), all_outcomes)
1074 1006
1075 1007
1076 class Section(object): 1008 class Section(object):
1077 """A section of the configuration file. Sections are enabled or 1009 """A section of the configuration file.
1078 disabled prior to running the tests, based on their conditions""" 1010
1011 Sections are enabled or disabled prior to running the tests,
1012 based on their conditions.
1013 """
1079 1014
1080 def __init__(self, condition): 1015 def __init__(self, condition):
1081 self.condition = condition 1016 self.condition = condition
1082 self.rules = [ ] 1017 self.rules = []
1083 1018
1084 def AddRule(self, rule): 1019 def AddRule(self, rule):
1085 self.rules.append(rule) 1020 self.rules.append(rule)
1086 1021
1087 1022
1088 class Rule(object): 1023 class Rule(object):
1089 """A single rule that specifies the expected outcome for a single 1024 """A single rule that specifies the expected outcome for a single test."""
1090 test."""
1091 1025
1092 def __init__(self, raw_path, path, value): 1026 def __init__(self, raw_path, path, value):
1093 self.raw_path = raw_path 1027 self.raw_path = raw_path
1094 self.path = path 1028 self.path = path
1095 self.value = value 1029 self.value = value
1096 1030
1097 def GetOutcomes(self, env, defs): 1031 def GetOutcomes(self, env, defs):
1098 set = self.value.GetOutcomes(env, defs) 1032 outcomes = self.value.GetOutcomes(env, defs)
1099 assert isinstance(set, ListSet) 1033 assert isinstance(outcomes, ListSet)
1100 return set.elms 1034 return outcomes.elms
1101 1035
1102 def Contains(self, path): 1036 def Contains(self, path):
1037 """Returns True if the specified path matches this rule (regexp)."""
1103 if len(self.path) > len(path): 1038 if len(self.path) > len(path):
1104 return False 1039 return False
1105 for i in xrange(len(self.path)): 1040 for i in xrange(len(self.path)):
1106 try: 1041 try:
1107 if not self.path[i].match(path[i]): 1042 if not self.path[i].match(path[i]):
1108 return False 1043 return False
1109 except: 1044 except:
1110 print "Invalid reguar expression %s in .status file. " % '/'.join(path) 1045 print 'Invalid regexp %s in .status file. ' % '/'.join(path)
1111 print "Try escaping special characters with \\" 1046 print 'Try escaping special characters with \\'
1112 raise 1047 raise
1113 return True 1048 return True
1114 1049
1115 1050
1116 HEADER_PATTERN = re.compile(r'\[([^]]+)\]') 1051 HEADER_PATTERN = re.compile(r'\[([^]]+)\]')
1117 RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)') 1052 RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)')
1118 DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$') 1053 DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$')
1119 PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$') 1054 PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$')
1120 1055
1121 1056
1122 def ReadConfigurationInto(path, sections, defs): 1057 def ReadConfigurationInto(path, sections, defs):
1058 """Parses a .status file into specified sections and defs arguments."""
1123 current_section = Section(Constant(True)) 1059 current_section = Section(Constant(True))
1124 sections.append(current_section) 1060 sections.append(current_section)
1125 prefix = [] 1061 prefix = []
1126 for line in utils.ReadLinesFrom(path): 1062 for line in utils.ReadLinesFrom(path):
1127 header_match = HEADER_PATTERN.match(line) 1063 header_match = HEADER_PATTERN.match(line)
1128 if header_match: 1064 if header_match:
1129 condition_str = header_match.group(1).strip() 1065 condition_str = header_match.group(1).strip()
1130 condition = ParseCondition(condition_str) 1066 condition = ParseCondition(condition_str)
1131 new_section = Section(condition) 1067 new_section = Section(condition)
1132 sections.append(new_section) 1068 sections.append(new_section)
1133 current_section = new_section 1069 current_section = new_section
1134 continue 1070 continue
1135 rule_match = RULE_PATTERN.match(line) 1071 rule_match = RULE_PATTERN.match(line)
1136 if rule_match: 1072 if rule_match:
1137 path = prefix + SplitPath(rule_match.group(1).strip()) 1073 path = prefix + _SplitPath(rule_match.group(1).strip())
1138 value_str = rule_match.group(2).strip() 1074 value_str = rule_match.group(2).strip()
1139 value = ParseCondition(value_str) 1075 value = ParseCondition(value_str)
1140 if not value: 1076 if not value:
1141 return False 1077 return False
1142 current_section.AddRule(Rule(rule_match.group(1), path, value)) 1078 current_section.AddRule(Rule(rule_match.group(1), path, value))
1143 continue 1079 continue
1144 def_match = DEF_PATTERN.match(line) 1080 def_match = DEF_PATTERN.match(line)
1145 if def_match: 1081 if def_match:
1146 name = def_match.group(1).lower() 1082 name = def_match.group(1).lower()
1147 value = ParseCondition(def_match.group(2).strip()) 1083 value = ParseCondition(def_match.group(2).strip())
1148 if not value: 1084 if not value:
1149 return False 1085 return False
1150 defs[name] = value 1086 defs[name] = value
1151 continue 1087 continue
1152 prefix_match = PREFIX_PATTERN.match(line) 1088 prefix_match = PREFIX_PATTERN.match(line)
1153 if prefix_match: 1089 if prefix_match:
1154 prefix = SplitPath(prefix_match.group(1).strip()) 1090 prefix = _SplitPath(prefix_match.group(1).strip())
1155 continue 1091 continue
1156 print "Malformed line: '%s'." % line 1092 print 'Malformed line: "%s".' % line
1157 return False 1093 return False
1158 return True 1094 return True
1159 1095
1160 1096
1161 # --------------- 1097 # ---------------
1162 # --- M a i n --- 1098 # --- M a i n ---
1163 # --------------- 1099 # ---------------
1164 1100
1165 1101
1166 def BuildOptions(): 1102 def BuildOptions():
1103 """Confiigures the Python optparse library with the cmdline for test.py."""
ahe 2011/10/11 07:55:34 Confiigures -> Configures
zundel 2011/10/11 22:45:44 Done.
1167 result = optparse.OptionParser() 1104 result = optparse.OptionParser()
1168 result.add_option("-m", "--mode", 1105 result.add_option(
1169 help="The test modes in which to run (comma-separated)", 1106 '-m', '--mode',
1107 help='The test modes in which to run (comma-separated)',
1170 metavar='[all,debug,release]', 1108 metavar='[all,debug,release]',
1171 default='debug') 1109 default='debug')
1172 result.add_option("-v", "--verbose", 1110 result.add_option(
1173 help="Verbose output", 1111 '-v', '--verbose',
1112 help='Verbose output',
1174 default=False, 1113 default=False,
1175 action="store_true") 1114 action='store_true')
1176 result.add_option("-p", "--progress", 1115 result.add_option(
1177 help="The style of progress indicator (verbose, line, color, mono)", 1116 '-p', '--progress',
1117 help='The style of progress indicator (verbose, line, color, mono)',
1178 choices=PROGRESS_INDICATORS.keys(), 1118 choices=PROGRESS_INDICATORS.keys(),
1179 default=None) 1119 default=None)
1180 result.add_option("--report", 1120 result.add_option(
1181 help="Print a summary of the tests to be run", 1121 '--report',
1122 help='Print a summary of the tests to be run',
1182 default=False, 1123 default=False,
1183 action="store_true") 1124 action='store_true')
1184 result.add_option("--list", 1125 result.add_option(
1185 help="List all the tests, but don't run them", 1126 '--list',
1127 help='List all the tests, but don\'t run them',
1186 default=False, 1128 default=False,
1187 action="store_true") 1129 action='store_true')
1188 result.add_option("-s", "--suite", 1130 result.add_option(
1189 help="A test suite", 1131 '-s', '--suite',
1132 help='A test suite',
1190 default=[], 1133 default=[],
1191 action="append") 1134 action='append')
1192 result.add_option("-t", "--timeout", 1135 result.add_option(
1193 help="Timeout in seconds", 1136 '-t', '--timeout',
1137 help='Timeout in seconds',
1194 default=None, 1138 default=None,
1195 type="int") 1139 type='int')
1196 result.add_option("--checked", 1140 result.add_option(
1197 help="Run tests in checked mode", 1141 '--checked',
1142 help='Run tests in checked mode',
1198 default=False, 1143 default=False,
1199 action="store_true") 1144 action='store_true')
1200 result.add_option("--flag", 1145 result.add_option(
1201 help="Pass this additional flag to the VM", 1146 '--flag',
1147 help='Pass this additional flag to the VM',
1202 default=[], 1148 default=[],
1203 action="append") 1149 action='append')
1204 result.add_option("--arch", 1150 result.add_option(
1205 help="The architecture to run tests for", 1151 '--arch',
1206 metavar="[all,ia32,x64,simarm,arm,dartc]", 1152 help='The architecture to run tests for',
1153 metavar='[all,ia32,x64,simarm,arm,dartc]',
1207 default=ARCH_GUESS) 1154 default=ARCH_GUESS)
1208 result.add_option("--os", 1155 result.add_option(
1209 help="The OS to run tests on", 1156 '--os',
1157 help='The OS to run tests on',
1210 default=OS_GUESS) 1158 default=OS_GUESS)
1211 result.add_option("--valgrind", 1159 result.add_option(
1212 help="Run tests through valgrind", 1160 '--valgrind',
1161 help='Run tests through valgrind',
1213 default=False, 1162 default=False,
1214 action="store_true") 1163 action='store_true')
1215 result.add_option("-j", "--tasks", 1164 result.add_option(
1216 help="The number of parallel tasks to run", 1165 '-j', '--tasks',
1166 help='The number of parallel tasks to run',
1217 metavar=testing.HOST_CPUS, 1167 metavar=testing.HOST_CPUS,
1218 default=testing.USE_DEFAULT_CPUS, 1168 default=testing.USE_DEFAULT_CPUS,
1219 type="int") 1169 type='int')
1220 result.add_option("--time", 1170 result.add_option(
1221 help="Print timing information after running", 1171 '--time',
1172 help='Print timing information after running',
1222 default=False, 1173 default=False,
1223 action="store_true") 1174 action='store_true')
1224 result.add_option("--executable", 1175 result.add_option(
1225 help="The executable with which to run the tests", 1176 '--executable',
1177 help='The executable with which to run the tests',
1226 default=None) 1178 default=None)
1227 result.add_option("--keep_temporary_files", 1179 result.add_option(
1228 help="Do not delete temporary files after running the tests", 1180 '--keep_temporary_files',
1181 help='Do not delete temporary files after running the tests',
1229 default=False, 1182 default=False,
1230 action="store_true") 1183 action='store_true')
1231 result.add_option("--batch", 1184 result.add_option(
1232 help="Run multiple tests for dartc architecture in a single vm", 1185 '--batch',
1233 choices=["true","false"], 1186 help='Run multiple tests for dartc architecture in a single vm',
1234 default="true", 1187 choices=['true', 'false'],
1235 type="choice"); 1188 default='true',
1236 result.add_option("--optimize", 1189 type='choice')
1237 help="Invoke dart compiler with --optimize flag", 1190 result.add_option(
1191 '--optimize',
1192 help='Invoke dart compiler with --optimize flag',
1238 default=False, 1193 default=False,
1239 action="store_true") 1194 action='store_true')
1240
1241 return result 1195 return result
1242 1196
1243 1197
1244 def ProcessOptions(options): 1198 def ProcessOptions(options):
1245 global VERBOSE 1199 """Process command line options."""
1246 VERBOSE = options.verbose
1247 if options.arch == 'all': 1200 if options.arch == 'all':
1248 options.arch = 'ia32,x64,simarm' 1201 options.arch = 'ia32,x64,simarm'
1249 if options.mode == 'all': 1202 if options.mode == 'all':
1250 options.mode = 'debug,release' 1203 options.mode = 'debug,release'
1251 # By default we run with a higher timeout setting in when running on 1204 # By default we run with a higher timeout setting in when running on
1252 # a simulated architecture and in debug mode. 1205 # a simulated architecture and in debug mode.
1253 if not options.timeout: 1206 if not options.timeout:
1254 options.timeout = TIMEOUT_SECS 1207 options.timeout = TIMEOUT_SECS
1255 if 'dartc' in options.arch: options.timeout *= 4 1208 if 'dartc' in options.arch:
1256 elif 'chromium' in options.arch: options.timeout *= 4 1209 options.timeout *= 4
1257 elif 'dartium' in options.arch: options.timeout *= 4 1210 elif 'chromium' in options.arch:
1258 elif 'debug' in options.mode: options.timeout *= 2 1211 options.timeout *= 4
1259 # TODO(zundel): is arch 'sim' out of date? 1212 elif 'dartium' in options.arch:
1260 if 'sim' in options.arch: options.timeout *= 4 1213 options.timeout *= 4
1214 elif 'debug' in options.mode:
1215 options.timeout *= 2
1261 options.mode = options.mode.split(',') 1216 options.mode = options.mode.split(',')
1262 options.arch = options.arch.split(',') 1217 options.arch = options.arch.split(',')
1263 for mode in options.mode: 1218 for mode in options.mode:
1264 if not mode in ['debug', 'release']: 1219 if not mode in ['debug', 'release']:
1265 print "Unknown mode %s" % mode 1220 print 'Unknown mode %s' % mode
1266 return False 1221 return False
1267 for arch in options.arch: 1222 for arch in options.arch:
1268 if not arch in ['ia32', 'x64', 'simarm', 'arm', 'dartc', 'dartium', 1223 if not arch in ['ia32', 'x64', 'simarm', 'arm', 'dartc', 'dartium',
1269 'chromium']: 1224 'chromium']:
1270 print "Unknown arch %s" % arch 1225 print 'Unknown arch %s' % arch
1271 return False 1226 return False
1272 options.flags = [] 1227 options.flags = []
1273 if (arch == 'dartc' or arch == 'chromium') and mode == 'release': 1228 if (arch == 'dartc' or arch == 'chromium') and mode == 'release':
1274 options.flags.append('--optimize') 1229 options.flags.append('--optimize')
1275 options.flags.append('--ignore-unrecognized-flags') 1230 options.flags.append('--ignore-unrecognized-flags')
1276 if options.checked: 1231 if options.checked:
1277 options.flags.append('--enable_asserts') 1232 options.flags.append('--enable_asserts')
1278 options.flags.append('--enable_type_checks') 1233 options.flags.append('--enable_type_checks')
1279 if options.optimize: 1234 if options.optimize:
1280 options.flags.append('--optimize') 1235 options.flags.append('--optimize')
1281 for flag in options.flag: 1236 for flag in options.flag:
1282 options.flags.append(flag) 1237 options.flags.append(flag)
1283 if options.verbose: 1238 if options.verbose:
1284 print "Flags on the command line:" 1239 print 'Flags on the command line:'
1285 for x in options.flags: 1240 for x in options.flags:
1286 print x 1241 print x
1287 # If the user hasn't specified the progress indicator, we pick 1242 # If the user hasn't specified the progress indicator, we pick
1288 # a good one depending on the setting of the verbose option. 1243 # a good one depending on the setting of the verbose option.
1289 if not options.progress: 1244 if not options.progress:
1290 if options.verbose: options.progress = 'verbose' 1245 if options.verbose: options.progress = 'verbose'
1291 else: options.progress = 'mono' 1246 else: options.progress = 'mono'
1292 # Options for future use. Such as Windows runner support. 1247 # Options for future use. Such as Windows runner support.
1293 options.suppress_dialogs = True 1248 options.suppress_dialogs = True
1294 options.special_command = None 1249 options.special_command = None
1295 return True 1250 return True
1296 1251
1297 1252
1298 REPORT_TEMPLATE = """\ 1253 REPORT_TEMPLATE = """\
1299 Total: %(total)i tests 1254 Total: %(total)i tests
1300 * %(skipped)4d tests will be skipped 1255 * %(skipped)4d tests will be skipped
1301 * %(nocrash)4d tests are expected to be flaky but not crash 1256 * %(nocrash)4d tests are expected to be flaky but not crash
1302 * %(pass)4d tests are expected to pass 1257 * %(pass)4d tests are expected to pass
1303 * %(fail_ok)4d tests are expected to fail that we won't fix 1258 * %(fail_ok)4d tests are expected to fail that we won't fix
1304 * %(fail)4d tests are expected to fail that we should fix 1259 * %(fail)4d tests are expected to fail that we should fix
1305 * %(crash)4d tests are expected to crash that we should fix 1260 * %(crash)4d tests are expected to crash that we should fix
1306 * %(batched)4d tests are running in batch mode\ 1261 * %(batched)4d tests are running in batch mode\
1307 """ 1262 """
1308 1263
1264
1309 def PrintReport(cases): 1265 def PrintReport(cases):
1310 """Print a breakdown of which tests are marked pass/skip/fail """ 1266 """Print a breakdown of which tests are marked pass/skip/fail."""
1267
1311 def IsFlaky(o): 1268 def IsFlaky(o):
1312 return ((testing.PASS in o) and (testing.FAIL in o) 1269 return ((testing.PASS in o) and (testing.FAIL in o)
1313 and (not testing.CRASH in o) and (not testing.OKAY in o)) 1270 and (not testing.CRASH in o) and (not testing.OKAY in o))
1271
1314 def IsFailOk(o): 1272 def IsFailOk(o):
1315 return (len(o) == 2) and (testing.FAIL in o) and (testing.OKAY in o) 1273 return (len(o) == 2) and (testing.FAIL in o) and (testing.OKAY in o)
ahe 2011/10/11 07:55:34 Add newline.
zundel 2011/10/11 22:45:44 Done.
1316 unskipped = [c for c in cases if not testing.SKIP in c.outcomes] 1274 unskipped = [c for c in cases if not testing.SKIP in c.outcomes]
1317 print REPORT_TEMPLATE % { 1275 print REPORT_TEMPLATE % {
1318 'total': len(cases), 1276 'total': len(cases),
1319 'skipped': len(cases) - len(unskipped), 1277 'skipped': len(cases) - len(unskipped),
1320 'nocrash': len([t for t in unskipped if IsFlaky(t.outcomes)]), 1278 'nocrash': len([t for t in unskipped if IsFlaky(t.outcomes)]),
1321 'pass': len([t for t in unskipped if list(t.outcomes) == [testing.PASS]]), 1279 'pass': len([t for t in unskipped
1322 'fail_ok': len([t for t in unskipped if IsFailOk(t.outcomes)]), 1280 if list(t.outcomes) == [testing.PASS]]),
1323 'fail': len([t for t in unskipped if list(t.outcomes) == [testing.FAIL]]), 1281 'fail_ok': len([t for t in unskipped
1324 'crash': len([t for t in unskipped if list(t.outcomes) == [testing.CRASH]]), 1282 if IsFailOk(t.outcomes)]),
1325 'batched' : len([t for t in unskipped if t.case.IsBatchable()]) 1283 'fail': len([t for t in unskipped
1284 if list(t.outcomes) == [testing.FAIL]]),
1285 'crash': len([t for t in unskipped
1286 if list(t.outcomes) == [testing.CRASH]]),
1287 'batched': len([t for t in unskipped if t.case.IsBatchable()])
1326 } 1288 }
1327 1289
1328 1290
1329 def PrintTests(cases): 1291 def PrintTests(cases):
1292 """Print a table of the tests to be run (--list cmdline option)."""
1330 has_errors = False 1293 has_errors = False
1331 for case in cases: 1294 for case in cases:
1332 try: 1295 try:
1333 case.case.GetCommand() 1296 case.case.GetCommand()
1334 except: 1297 except:
1298 # Python can throw an exception while parsing the .dart file.
1299 # We don't want to end the program.
ahe 2011/10/11 07:55:34 We should probably improve this. It is a bit of a
zundel 2011/10/11 22:45:44 Added TODO
1335 sys.stderr.write(case.case.filename + '\n') 1300 sys.stderr.write(case.case.filename + '\n')
1336 has_errors = True 1301 has_errors = True
1337 if has_errors: 1302 if has_errors:
1338 raise Exception('Errors in above files') 1303 raise Exception('Errors in above files')
1339 for case in [c for c in cases if not testing.SKIP in c.outcomes]: 1304 for case in [c for c in cases if not testing.SKIP in c.outcomes]:
1340 print "%s\t%s\t%s\t%s" %('/'.join(case.case.path), 1305 print '%s\t%s\t%s\t%s' %('/'.join(case.case.path),
1341 ','.join(case.outcomes), 1306 ','.join(case.outcomes),
1342 case.case.IsNegative(), 1307 case.case.IsNegative(),
1343 '\t'.join(case.case.GetCommand()[1:])) 1308 '\t'.join(case.case.GetCommand()[1:]))
1344 1309
1345 1310
1346 class Pattern(object): 1311 class Pattern(object):
1312 """Convenience class to hold a compiled re pattern."""
1347 1313
1348 def __init__(self, pattern): 1314 def __init__(self, pattern):
1349 self.pattern = pattern 1315 self.pattern = pattern
1350 self.compiled = None 1316 self.compiled = None
1351 1317
1352 def match(self, str): 1318 def match(self, buf):
1353 if not self.compiled: 1319 if not self.compiled:
1354 pattern = "^" + self.pattern.replace('*', '.*') + "$" 1320 pattern = '^' + self.pattern.replace('*', '.*') + '$'
ahe 2011/10/11 07:55:34 I think the style guide prefer: '^%s$' % self.patt
zundel 2011/10/11 22:45:44 Done.
1355 self.compiled = re.compile(pattern) 1321 self.compiled = re.compile(pattern)
1356 return self.compiled.match(str) 1322 return self.compiled.match(buf)
1357 1323
1358 def __str__(self): 1324 def __str__(self):
1359 return self.pattern 1325 return self.pattern
1360 1326
1361 1327
1362 def SplitPath(s): 1328 def _SplitPath(s):
1363 stripped = [ c.strip() for c in s.split('/') ] 1329 """Split a path into directories - opposite of os.path.join()?"""
1364 return [ Pattern(s) for s in stripped if len(s) > 0 ] 1330 stripped = [c.strip() for c in s.split('/')]
1331 return [Pattern(s) for s in stripped if s]
1365 1332
1366 1333
1367 def GetSpecialCommandProcessor(value): 1334 def GetSpecialCommandProcessor(value):
1368 if (not value) or (value.find('@') == -1): 1335 if (not value) or (value.find('@') == -1):
1336
1369 def ExpandCommand(args): 1337 def ExpandCommand(args):
1370 return args 1338 return args
1339
1371 return ExpandCommand 1340 return ExpandCommand
1372 else: 1341 else:
1373 pos = value.find('@') 1342 pos = value.find('@')
1374 import urllib
1375 prefix = urllib.unquote(value[:pos]).split() 1343 prefix = urllib.unquote(value[:pos]).split()
1376 suffix = urllib.unquote(value[pos+1:]).split() 1344 suffix = urllib.unquote(value[pos+1:]).split()
1345
1377 def ExpandCommand(args): 1346 def ExpandCommand(args):
1378 return prefix + args + suffix 1347 return prefix + args + suffix
1348
1379 return ExpandCommand 1349 return ExpandCommand
1380 1350
1381 1351
1382 def GetSuites(test_root): 1352 def GetSuites(test_root):
1383 def IsSuite(path): 1353 def IsSuite(path):
1384 return isdir(path) and exists(join(path, 'testcfg.py')) 1354 return os.path.isdir(path) and os.path.exists(
1385 return [ f for f in os.listdir(test_root) if IsSuite(join(test_root, f)) ] 1355 os.path.join(path, 'testcfg.py'))
1356 return [f for f in os.listdir(test_root) if IsSuite(
1357 os.path.join(test_root, f))]
1386 1358
1387 1359
1388 def FormatTime(d): 1360 def FormatTime(d):
1389 millis = round(d * 1000) % 1000 1361 millis = round(d * 1000) % 1000
1390 return time.strftime("%M:%S.", time.gmtime(d)) + ("%03i" % millis) 1362 return time.strftime('%M:%S.', time.gmtime(d)) + ('%03i' % millis)
1391 1363
1392 1364
1393 def Main(): 1365 def Main():
1366 """Main loop."""
1394 utils.ConfigureJava() 1367 utils.ConfigureJava()
1395 parser = BuildOptions() 1368 parser = BuildOptions()
1396 (options, args) = parser.parse_args() 1369 (options, args) = parser.parse_args()
1397 if not ProcessOptions(options): 1370 if not ProcessOptions(options):
1398 parser.print_help() 1371 parser.print_help()
1399 return 1 1372 return 1
1400 1373
1401 client = abspath(join(dirname(sys.argv[0]), '..')) 1374 client = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..'))
1402 repositories = [] 1375 repositories = []
1403 for component in os.listdir(client) + ['.']: 1376 for component in os.listdir(client) + ['.']:
1404 test_path = join(client, component, 'tests') 1377 test_path = os.path.join(client, component, 'tests')
1405 if exists(test_path) and isdir(test_path): 1378 if os.path.exists(test_path) and os.path.isdir(test_path):
1406 suites = GetSuites(test_path) 1379 suites = GetSuites(test_path)
1407 repositories += [TestRepository(join(test_path, name)) for name in suites] 1380 repositories += [TestRepository(os.path.join(test_path, name))
1381 for name in suites]
1408 repositories += [TestRepository(a) for a in options.suite] 1382 repositories += [TestRepository(a) for a in options.suite]
1409 1383
1410 root = LiteralTestSuite(repositories) 1384 root = LiteralTestSuite(repositories)
1411 if len(args) == 0: 1385 if args:
1412 paths = [SplitPath(t) for t in BUILT_IN_TESTS] 1386 paths = []
1387 for arg in args:
1388 path = _SplitPath(arg)
1389 paths.append(path)
1413 else: 1390 else:
1414 paths = [ ] 1391 paths = [_SplitPath(t) for t in BUILT_IN_TESTS]
1415 for arg in args:
1416 path = SplitPath(arg)
1417 paths.append(path)
1418 1392
1419 # Check for --valgrind option. If enabled, we overwrite the special 1393 # Check for --valgrind option. If enabled, we overwrite the special
1420 # command flag with a command that uses the tools/valgrind.py script. 1394 # command flag with a command that uses the tools/valgrind.py script.
1421 if options.valgrind: 1395 if options.valgrind:
1422 run_valgrind = join(client, 'runtime', 'tools', 'valgrind.py') 1396 run_valgrind = os.path.join(client, 'runtime', 'tools', 'valgrind.py')
1423 options.special_command = "python -u " + run_valgrind + " @" 1397 options.special_command = 'python -u ' + run_valgrind + ' @'
1424 1398
1425 context = Context(client, 1399 context = Context(client,
1426 VERBOSE, 1400 options.verbose,
1427 options.os, 1401 options.os,
1428 options.timeout, 1402 options.timeout,
1429 GetSpecialCommandProcessor(options.special_command), 1403 GetSpecialCommandProcessor(options.special_command),
1430 options.suppress_dialogs, 1404 options.suppress_dialogs,
1431 options.executable, 1405 options.executable,
1432 options.flags, 1406 options.flags,
1433 options.keep_temporary_files, 1407 options.keep_temporary_files,
1434 options.batch) 1408 options.batch)
1435 1409
1436 # Get status for tests 1410 # Get status for tests
1437 sections = [ ] 1411 sections = []
1438 defs = { } 1412 defs = {}
1439 root.GetTestStatus(context, sections, defs) 1413 root.GetTestStatus(context, sections, defs)
1440 config = Configuration(sections, defs) 1414 config = Configuration(sections, defs)
1441 1415
1442 # List the tests 1416 # List the tests
1443 all_cases = [ ] 1417 all_cases = []
1444 all_unused = [ ] 1418 all_unused = []
1445 unclassified_tests = [ ]
1446 globally_unused_rules = None 1419 globally_unused_rules = None
1447 for path in paths: 1420 for path in paths:
1448 for mode in options.mode: 1421 for mode in options.mode:
1449 for arch in options.arch: 1422 for arch in options.arch:
1450 env = { 1423 env = {
1451 'mode': mode, 1424 'mode': mode,
1452 'system': utils.GuessOS(), 1425 'system': utils.GuessOS(),
1453 'arch': arch, 1426 'arch': arch,
1454 } 1427 }
1455 test_list = root.ListTests([], path, context, mode, arch) 1428 test_list = root.ListTests([], path, context, mode, arch)
1456 unclassified_tests += test_list 1429 (cases, unused_rules, unused_outcomes) = config.ClassifyTests(
1457 (cases, unused_rules, all_outcomes) = config.ClassifyTests(test_list, en v) 1430 test_list, env)
1458 if globally_unused_rules is None: 1431 if globally_unused_rules is None:
1459 globally_unused_rules = set(unused_rules) 1432 globally_unused_rules = set(unused_rules)
1460 else: 1433 else:
1461 globally_unused_rules = globally_unused_rules.intersection(unused_rule s) 1434 globally_unused_rules = (
1435 globally_unused_rules.intersection(unused_rules))
1462 all_cases += cases 1436 all_cases += cases
1463 all_unused.append(unused_rules) 1437 all_unused.append(unused_rules)
1464 1438
1465 if options.report: 1439 if options.report:
1466 PrintReport(all_cases) 1440 PrintReport(all_cases)
1467 1441
1468 if options.list: 1442 if options.list:
1469 PrintTests(all_cases) 1443 PrintTests(all_cases)
1470 return 0; 1444 return 0
1471 1445
1472 result = None 1446 result = None
1447
1473 def DoSkip(case): 1448 def DoSkip(case):
1474 return testing.SKIP in case.outcomes or testing.SLOW in case.outcomes 1449 return testing.SKIP in case.outcomes or testing.SLOW in case.outcomes
1475 cases_to_run = [ c for c in all_cases if not DoSkip(c) ] 1450
1451 cases_to_run = [c for c in all_cases if not DoSkip(c)]
1476 # Creating test cases may generate temporary files. Make sure 1452 # Creating test cases may generate temporary files. Make sure
1477 # Skipped tests clean up these files. 1453 # Skipped tests clean up these files.
1478 for c in all_cases: 1454 for c in all_cases:
1479 if DoSkip(c): c.case.Cleanup() 1455 if DoSkip(c): c.case.Cleanup()
1480 1456
1481 if len(cases_to_run) == 0: 1457 if cases_to_run:
1482 print "No tests to run."
1483 return 0
1484 else:
1485 try: 1458 try:
1486 start = time.time() 1459 start = time.time()
1487 if RunTestCases(cases_to_run, options.progress, options.tasks, 1460 if RunTestCases(cases_to_run, options.progress, options.tasks,
1488 context): 1461 context):
1489 result = 0 1462 result = 0
1490 else: 1463 else:
1491 result = 1 1464 result = 1
1492 duration = time.time() - start 1465 duration = time.time() - start
1493 except KeyboardInterrupt: 1466 except KeyboardInterrupt:
1494 print "Exiting on KeyboardInterrupt" 1467 print 'Exiting on KeyboardInterrupt'
1495 return 1 1468 return 1
1469 else:
1470 print 'No tests to run.'
1471 return 0
1496 1472
1497 if options.time: 1473 if options.time:
1498 print 1474 print
1499 print "--- Total time: %s ---" % FormatTime(duration) 1475 print '--- Total time: %s ---' % FormatTime(duration)
1500 timed_tests = [ t.case for t in cases_to_run if not t.case.duration is None ] 1476 timed_tests = [t.case for t in cases_to_run if not t.case.duration is None]
1501 timed_tests.sort(lambda a, b: a.CompareTime(b)) 1477 timed_tests.sort(lambda a, b: a.CompareTime(b))
1502 index = 1 1478 index = 1
1503 for entry in timed_tests[:20]: 1479 for entry in timed_tests[:20]:
1504 t = FormatTime(entry.duration) 1480 t = FormatTime(entry.duration)
1505 print "%4i (%s) %s" % (index, t, entry.GetLabel()) 1481 print '%4i (%s) %s' % (index, t, entry.GetLabel())
1506 index += 1 1482 index += 1
1507 1483
1508 return result 1484 return result
1509 1485
1510 1486
1511 if __name__ == '__main__': 1487 if __name__ == '__main__':
1512 sys.exit(Main()) 1488 sys.exit(Main())
OLDNEW
« no previous file with comments | « no previous file | tools/testing/test_runner.py » ('j') | tools/testing/test_runner.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698