OLD | NEW |
---|---|
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 Loading... | |
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()) |
OLD | NEW |