Chromium Code Reviews| 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 |