 Chromium Code Reviews
 Chromium Code Reviews Issue 8223016:
  Style cleanups for test.py  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
    
  
    Issue 8223016:
  Style cleanups for test.py  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart| Index: tools/test.py | 
| diff --git a/tools/test.py b/tools/test.py | 
| index 44fcb954303b5a482a2103deb674f2ae1d02bea8..57b0febddf5b3d07def215971f670bb97433d5a1 100755 | 
| --- a/tools/test.py | 
| +++ b/tools/test.py | 
| @@ -5,73 +5,73 @@ | 
| # BSD-style license that can be found in the LICENSE file. | 
| # | 
| +"""Test driver for the Dart project used by continuous build and developers.""" | 
| + | 
| + | 
| import imp | 
| import optparse | 
| import os | 
| -from os.path import join, dirname, abspath, basename, isdir, exists, realpath | 
| -import platform | 
| +import Queue | 
| import re | 
| -import select | 
| -import signal | 
| -import subprocess | 
| import sys | 
| -import tempfile | 
| -import time | 
| import threading | 
| -import traceback | 
| -from Queue import Queue, Empty | 
| +import time | 
| +import urllib | 
| import testing | 
| -import testing.test_runner | 
| +from testing import test_runner | 
| import utils | 
| TIMEOUT_SECS = 60 | 
| -VERBOSE = False | 
| ARCH_GUESS = utils.GuessArchitecture() | 
| OS_GUESS = utils.GuessOS() | 
| BUILT_IN_TESTS = ['dartc', 'vm', 'dart', 'corelib', 'language', 'co19', | 
| 'samples', 'isolate', 'stub-generator', 'client'] | 
| # Patterns for matching test options in .dart files. | 
| -VM_OPTIONS_PATTERN = re.compile(r"// VMOptions=(.*)") | 
| -DART_OPTIONS_PATTERN = re.compile(r"// DartOptions=(.*)") | 
| -ISOLATE_STUB_PATTERN = re.compile(r"// IsolateStubs=(.*)") | 
| +VM_OPTIONS_PATTERN = re.compile(r'// VMOptions=(.*)') | 
| +DART_OPTIONS_PATTERN = re.compile(r'// DartOptions=(.*)') | 
| +ISOLATE_STUB_PATTERN = re.compile(r'// IsolateStubs=(.*)') | 
| # --------------------------------------------- | 
| # --- P r o g r e s s I n d i c a t o r s --- | 
| # --------------------------------------------- | 
| +class Error(Exception): | 
| + pass | 
| + | 
| + | 
| class ProgressIndicator(object): | 
| + """Base class for displaying the progress of the test run.""" | 
| def __init__(self, cases, context): | 
| self.abort = False | 
| self.terminate = False | 
| self.cases = cases | 
| - self.queue = Queue(len(cases)) | 
| - self.batch_queues = {}; | 
| + self.queue = Queue.Queue(len(cases)) | 
| + self.batch_queues = {} | 
| self.context = context | 
| # Extract batchable cases. | 
| found_cmds = {} | 
| - batch_cases = [] | 
| for case in cases: | 
| cmd = case.case.GetCommand()[0] | 
| if not utils.IsWindows(): | 
| # Diagnostic check for executable (if an absolute pathname) | 
| if not cmd in found_cmds: | 
| if os.path.isabs(cmd) and not os.path.isfile(cmd): | 
| - msg = "Can't find command %s\n" % cmd \ | 
| - + "(Did you build first? " \ | 
| - + "Are you running in the correct directory?)" | 
| + msg = "Can't find command %s\n" % cmd | 
| + msg += '(Did you build first? ' | 
| + msg += 'Are you running in the correct directory?)' | 
| raise Exception(msg) | 
| else: | 
| found_cmds[cmd] = 1 | 
| if case.case.IsBatchable(): | 
| - if not self.batch_queues.has_key(cmd): | 
| - self.batch_queues[cmd] = Queue(len(cases)) | 
| + if not cmd in self.batch_queues: | 
| + self.batch_queues[cmd] = Queue.Queue(len(cases)) | 
| self.batch_queues[cmd].put(case) | 
| else: | 
| self.queue.put_nowait(case) | 
| @@ -79,7 +79,7 @@ class ProgressIndicator(object): | 
| self.succeeded = 0 | 
| self.remaining = len(cases) | 
| self.total = len(cases) | 
| - self.failed = [ ] | 
| + self.failed = [] | 
| self.crashed = 0 | 
| self.lock = threading.Lock() | 
| @@ -88,13 +88,14 @@ class ProgressIndicator(object): | 
| negative_marker = '[negative] ' | 
| else: | 
| negative_marker = '' | 
| - print "=== %(label)s %(negative)s===" % { | 
| - 'label': test.GetLabel(), | 
| - 'negative': negative_marker | 
| + print '=== %(label)s %(negative)s===' % { | 
| + 'label': test.GetLabel(), | 
| + 'negative': negative_marker | 
| } | 
| - print "Path: %s" % "/".join(test.path) | 
| + print 'Path: %s' % '/'.join(test.path) | 
| def Run(self, tasks): | 
| + """Starts tests and keeps running until queues are drained.""" | 
| self.Starting() | 
| # Scale the number of tasks to the nubmer of CPUs on the machine | 
| @@ -110,45 +111,43 @@ class ProgressIndicator(object): | 
| # Spawn N-1 threads and then use this thread as the last one. | 
| # That way -j1 avoids threading altogether which is a nice fallback | 
| # in case of threading problems. | 
| - for i in xrange(tasks - 1): | 
| + for unused_i in xrange(tasks - 1): | 
| thread = threading.Thread(target=self.RunSingle, args=[]) | 
| threads.append(thread) | 
| thread.start() | 
| - | 
| # Next, crank up the batchable tasks. Note that this will start | 
| # 'tasks' more threads, but the assumption is that if batching is | 
| # enabled that almost all tests are batchable. | 
| for (cmd, queue) in self.batch_queues.items(): | 
| if not queue.empty(): | 
| - batch_len = queue.qsize(); | 
| batch_tester = None | 
| try: | 
| - batch_tester = testing.test_runner.BatchRunner(queue, tasks, self, | 
| - [cmd, '-batch']) | 
| - except Exception, e: | 
| - print "Aborting batch test for " + cmd + ". Problem on startup." | 
| + batch_tester = test_runner.BatchRunner(queue, tasks, self, | 
| + [cmd, '-batch']) | 
| + except: | 
| + print 'Aborting batch test for ' + cmd + '. Problem on startup.' | 
| if batch_tester: batch_tester.Shutdown() | 
| raise | 
| try: | 
| batch_tester.WaitForCompletion() | 
| except: | 
| - print "Aborting batch cmd " + cmd + "while waiting for completion." | 
| + print 'Aborting batch cmd ' + cmd + 'while waiting for completion.' | 
| if batch_tester: batch_tester.Shutdown() | 
| raise | 
| try: | 
| self.RunSingle() | 
| if self.abort: | 
| - raise Exception("Aborted") | 
| + raise Error('Aborted') | 
| # Wait for the remaining non-batched threads. | 
| for thread in threads: | 
| # Use a timeout so that signals (ctrl-c) will be processed. | 
| thread.join(timeout=10000000) | 
| if self.abort: | 
| - raise Exception("Aborted") | 
| - except Exception, e: | 
| + raise Error('Aborted') | 
| + except: | 
| # If there's an exception we schedule an interruption for any | 
| # remaining threads. | 
| self.terminate = True | 
| @@ -162,7 +161,7 @@ class ProgressIndicator(object): | 
| while not self.terminate: | 
| try: | 
| test = self.queue.get_nowait() | 
| - except Empty: | 
| + except Queue.Empty: | 
| return | 
| case = test.case | 
| with self.lock: | 
| @@ -175,7 +174,7 @@ class ProgressIndicator(object): | 
| self.abort = True | 
| self.terminate = True | 
| raise | 
| - except IOError, e: | 
| + except IOError: | 
| self.abort = True | 
| self.terminate = True | 
| raise | 
| @@ -201,55 +200,61 @@ def EscapeCommand(command): | 
| parts.append('"%s"' % part) | 
| else: | 
| parts.append(part) | 
| - return " ".join(parts) | 
| + return ' '.join(parts) | 
| class SimpleProgressIndicator(ProgressIndicator): | 
| + """Base class for printing output of each test separately.""" | 
| def Starting(self): | 
| + """Called at the beginning before any tests are run.""" | 
| print 'Running %i tests' % len(self.cases) | 
| def Done(self): | 
| + """Called when all tests are complete.""" | 
| for failed in self.failed: | 
| self.PrintFailureHeader(failed.test) | 
| if failed.output.stderr: | 
| - print "--- stderr ---" | 
| + print '--- stderr ---' | 
| print failed.output.stderr.strip() | 
| if failed.output.stdout: | 
| - print "--- stdout ---" | 
| + print '--- stdout ---' | 
| print failed.output.stdout.strip() | 
| - print "Command: %s" % EscapeCommand(failed.command) | 
| + print 'Command: %s' % EscapeCommand(failed.command) | 
| if failed.HasCrashed(): | 
| - print "--- CRASHED ---" | 
| + print '--- CRASHED ---' | 
| if failed.HasTimedOut(): | 
| - print "--- TIMEOUT ---" | 
| - if len(self.failed) == 0: | 
| - print "===" | 
| - print "=== All tests succeeded" | 
| - print "===" | 
| + print '--- TIMEOUT ---' | 
| + if not self.failed: | 
| + print '===' | 
| + print '=== All tests succeeded' | 
| + print '===' | 
| else: | 
| - print "===" | 
| + print '===' | 
| if len(self.failed) == 1: | 
| - print "=== 1 test failed" | 
| + print '=== 1 test failed' | 
| else: | 
| - print "=== %i tests failed" % len(self.failed) | 
| + print '=== %i tests failed' % len(self.failed) | 
| if self.crashed > 0: | 
| if self.crashed == 1: | 
| - print "=== 1 test CRASHED" | 
| + print '=== 1 test CRASHED' | 
| else: | 
| - print "=== %i tests CRASHED" % self.crashed | 
| - print "===" | 
| + print '=== %i tests CRASHED' % self.crashed | 
| + print '===' | 
| class VerboseProgressIndicator(SimpleProgressIndicator): | 
| + """Print verbose information about each test that is run.""" | 
| def AboutToRun(self, case): | 
| + """Called before each test case is run.""" | 
| print 'Starting %s...' % case.GetLabel() | 
| sys.stdout.flush() | 
| def HasRun(self, output): | 
| + """Called after each test case is run.""" | 
| if output.UnexpectedOutput(): | 
| if output.HasCrashed(): | 
| outcome = 'CRASH' | 
| @@ -261,11 +266,14 @@ class VerboseProgressIndicator(SimpleProgressIndicator): | 
| class OneLineProgressIndicator(SimpleProgressIndicator): | 
| + """Results of each test is printed like a report, on a line by itself.""" | 
| def AboutToRun(self, case): | 
| + """Called before each test case is run.""" | 
| pass | 
| def HasRun(self, output): | 
| + """Called after each test case is run.""" | 
| if output.UnexpectedOutput(): | 
| if output.HasCrashed(): | 
| outcome = 'CRASH' | 
| @@ -279,6 +287,7 @@ class OneLineProgressIndicator(SimpleProgressIndicator): | 
| class OneLineProgressIndicatorForBuildBot(OneLineProgressIndicator): | 
| def HasRun(self, output): | 
| + """Called after each test case is run.""" | 
| super(OneLineProgressIndicatorForBuildBot, self).HasRun(output) | 
| percent = (((self.total - self.remaining) * 100) // self.total) | 
| print '@@@STEP_CLEAR@@@' | 
| @@ -287,6 +296,7 @@ class OneLineProgressIndicatorForBuildBot(OneLineProgressIndicator): | 
| class CompactProgressIndicator(ProgressIndicator): | 
| + """Continuously updates a single line w/ a summary of progress of the run.""" | 
| def __init__(self, cases, context, templates): | 
| super(CompactProgressIndicator, self).__init__(cases, context) | 
| @@ -295,92 +305,102 @@ class CompactProgressIndicator(ProgressIndicator): | 
| self.start_time = time.time() | 
| def Starting(self): | 
| + """Called at the beginning before any tests are run.""" | 
| pass | 
| def Done(self): | 
| - self.PrintProgress('Done') | 
| + """Called when all tests are complete.""" | 
| + self._PrintProgress('Done') | 
| def AboutToRun(self, case): | 
| - self.PrintProgress(case.GetLabel()) | 
| + """Called before each test case is run.""" | 
| + self._PrintProgress(case.GetLabel()) | 
| def HasRun(self, output): | 
| + """Called after each test case is run.""" | 
| if output.UnexpectedOutput(): | 
| self.ClearLine(self.last_status_length) | 
| self.PrintFailureHeader(output.test) | 
| stdout = output.output.stdout.strip() | 
| - if len(stdout): | 
| + if stdout: | 
| print self.templates['stdout'] % stdout | 
| stderr = output.output.stderr.strip() | 
| - if len(stderr): | 
| + if stderr: | 
| print self.templates['stderr'] % stderr | 
| - print "Command: %s" % EscapeCommand(output.command) | 
| + print 'Command: %s' % EscapeCommand(output.command) | 
| if output.HasCrashed(): | 
| - print "--- CRASHED ---" | 
| + print '--- CRASHED ---' | 
| if output.HasTimedOut(): | 
| - print "--- TIMEOUT ---" | 
| + print '--- TIMEOUT ---' | 
| - def Truncate(self, str, length): | 
| - if length and (len(str) > (length - 3)): | 
| - return str[:(length-3)] + "..." | 
| + def _Truncate(self, buf, length): | 
| + """Truncate a line if it exceeds length, substituting an ellipsis...""" | 
| + if length and (len(buf) > (length - 3)): | 
| + return buf[:(length-3)] + '...' | 
| else: | 
| - return str | 
| + return buf | 
| - def PrintProgress(self, name): | 
| + def _PrintProgress(self, name): | 
| + """Refresh the display.""" | 
| self.ClearLine(self.last_status_length) | 
| elapsed = time.time() - self.start_time | 
| status = self.templates['status_line'] % { | 
| - 'passed': self.succeeded, | 
| - 'percent': (((self.total - self.remaining) * 100) // self.total), | 
| - 'failed': len(self.failed), | 
| - 'test': name, | 
| - 'mins': int(elapsed) / 60, | 
| - 'secs': int(elapsed) % 60 | 
| + 'passed': self.succeeded, | 
| + 'percent': (((self.total - self.remaining) * 100) // self.total), | 
| + 'failed': len(self.failed), | 
| + 'test': name, | 
| + 'mins': int(elapsed) / 60, | 
| + 'secs': int(elapsed) % 60 | 
| } | 
| - status = self.Truncate(status, 78) | 
| + status = self._Truncate(status, 78) | 
| self.last_status_length = len(status) | 
| print status, | 
| sys.stdout.flush() | 
| + def ClearLine(self, last_line_length): | 
| + """Erase the current line w/ a linefeed and overwriting with spaces.""" | 
| + print ('\r' + (' ' * last_line_length) + '\r'), | 
| + | 
| class MonochromeProgressIndicator(CompactProgressIndicator): | 
| + """A CompactProgressIndicator with no color.""" | 
| def __init__(self, cases, context): | 
| templates = { | 
| - 'status_line': "[%(mins)02i:%(secs)02i|%%%(percent) 4d|+%(passed) 4d|-%(failed) 4d]: %(test)s", | 
| - 'stdout': '%s', | 
| - 'stderr': '%s', | 
| - 'clear': lambda last_line_length: ("\r" + (" " * last_line_length) + "\r"), | 
| - 'max_length': 78 | 
| + 'status_line': '[%(mins)02i:%(secs)02i|%%%(percent) ' | 
| + '4d|+%(passed) 4d|-%(failed) 4d]: %(test)s', | 
| + 'stdout': '%s', | 
| + 'stderr': '%s', | 
| + 'clear': lambda last_line_len: self.ClearLine(last_line_len), | 
| + 'max_length': 78 | 
| } | 
| - super(MonochromeProgressIndicator, self).__init__(cases, context, templates) | 
| + super(MonochromeProgressIndicator, self).__init__(cases, | 
| + context, | 
| + templates) | 
| - def ClearLine(self, last_line_length): | 
| - print ("\r" + (" " * last_line_length) + "\r"), | 
| class ColorProgressIndicator(CompactProgressIndicator): | 
| + """A CompactProgressIndicator with pretty colors.""" | 
| def __init__(self, cases, context): | 
| templates = { | 
| - 'status_line': ("[%(mins)02i:%(secs)02i|%%%(percent) 4d|" | 
| - "\033[32m+%(passed) 4d" | 
| - "\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s"), | 
| - 'stdout': '%s', | 
| - 'stderr': '%s', | 
| - 'clear': lambda last_line_length: ("\r" + (" " * last_line_length) + "\r"), | 
| - 'max_length': 78 | 
| + 'status_line': ('[%(mins)02i:%(secs)02i|%%%(percent) 4d|' | 
| + '\033[32m+%(passed) 4d' | 
| + '\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s'), | 
| + 'stdout': '%s', | 
| + 'stderr': '%s', | 
| + 'clear': lambda last_line_len: self.ClearLine(last_line_len), | 
| + 'max_length': 78 | 
| } | 
| super(ColorProgressIndicator, self).__init__(cases, context, templates) | 
| - def ClearLine(self, last_line_length): | 
| - print ("\r" + (" " * last_line_length) + "\r"), | 
| - | 
| PROGRESS_INDICATORS = { | 
| - 'verbose': VerboseProgressIndicator, | 
| - 'mono': MonochromeProgressIndicator, | 
| - 'color': ColorProgressIndicator, | 
| - 'line': OneLineProgressIndicator, | 
| - 'buildbot': OneLineProgressIndicatorForBuildBot | 
| + 'verbose': VerboseProgressIndicator, | 
| + 'mono': MonochromeProgressIndicator, | 
| + 'color': ColorProgressIndicator, | 
| + 'line': OneLineProgressIndicator, | 
| + 'buildbot': OneLineProgressIndicatorForBuildBot | 
| } | 
| @@ -389,18 +409,8 @@ PROGRESS_INDICATORS = { | 
| # ------------------------- | 
| -class CommandOutput(object): | 
| - | 
| - def __init__(self, pid, exit_code, timed_out, stdout, stderr): | 
| - self.pid = pid | 
| - self.exit_code = exit_code | 
| - self.timed_out = timed_out | 
| - self.stdout = stdout | 
| - self.stderr = stderr | 
| - self.failed = None | 
| - | 
| - | 
| class TestCase(object): | 
| + """A single test case, like running 'dart' on a single .dart file.""" | 
| def __init__(self, context, path): | 
| self.path = path | 
| @@ -431,10 +441,11 @@ class TestCase(object): | 
| def RunCommand(self, command, cwd=None, cleanup=True): | 
| full_command = self.context.processor(command) | 
| try: | 
| - output = Execute(full_command, self.context, self.context.timeout, cwd) | 
| + output = test_runner.Execute(full_command, self.context, | 
| + self.context.timeout, cwd) | 
| except OSError as e: | 
| - raise utils.ToolError("%s: %s" % (full_command[0], e.strerror)) | 
| - test_output = TestOutput(self, full_command, output) | 
| + raise utils.ToolError('%s: %s' % (full_command[0], e.strerror)) | 
| + test_output = test_runner.TestOutput(self, full_command, output) | 
| if cleanup: self.Cleanup() | 
| return test_output | 
| @@ -457,165 +468,27 @@ class TestCase(object): | 
| return | 
| -class TestOutput(object): | 
| - | 
| - def __init__(self, test, command, output): | 
| - self.test = test | 
| - self.command = command | 
| - self.output = output | 
| - | 
| - def UnexpectedOutput(self): | 
| - if self.HasCrashed(): | 
| - outcome = testing.CRASH | 
| - elif self.HasTimedOut(): | 
| - outcome = testing.TIMEOUT | 
| - elif self.HasFailed(): | 
| - outcome = testing.FAIL | 
| - else: | 
| - outcome = testing.PASS | 
| - return not outcome in self.test.outcomes | 
| - | 
| - def HasCrashed(self): | 
| - if utils.IsWindows(): | 
| - if self.output.exit_code == 3: | 
| - # The VM uses std::abort to terminate on asserts. | 
| - # std::abort terminates with exit code 3 on Windows. | 
| - return True | 
| - return 0x80000000 & self.output.exit_code and not (0x3FFFFF00 & self.output.exit_code) | 
| - else: | 
| - # Timed out tests will have exit_code -signal.SIGTERM. | 
| - if self.output.timed_out: | 
| - return False | 
| - if self.output.exit_code == 253: | 
| - # The Java dartc runners exit 253 in case of unhandled exceptions. | 
| - return True | 
| - return self.output.exit_code < 0 | 
| - | 
| - def HasTimedOut(self): | 
| - return self.output.timed_out; | 
| - | 
| - def HasFailed(self): | 
| - execution_failed = self.test.DidFail(self.output) | 
| - if self.test.IsNegative(): | 
| - return not execution_failed | 
| - else: | 
| - return execution_failed | 
| - | 
| - | 
| -def KillProcessWithID(pid): | 
| - if utils.IsWindows(): | 
| - os.popen('taskkill /T /F /PID %d' % pid) | 
| - else: | 
| - os.kill(pid, signal.SIGTERM) | 
| - | 
| - | 
| -MAX_SLEEP_TIME = 0.1 | 
| -INITIAL_SLEEP_TIME = 0.0001 | 
| -SLEEP_TIME_FACTOR = 1.25 | 
| - | 
| -SEM_INVALID_VALUE = -1 | 
| -SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h | 
| - | 
| -def Win32SetErrorMode(mode): | 
| - prev_error_mode = SEM_INVALID_VALUE | 
| - try: | 
| - import ctypes | 
| - prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode); | 
| - except ImportError: | 
| - pass | 
| - return prev_error_mode | 
| - | 
| -def RunProcess(context, timeout, args, **rest): | 
| - if context.verbose: print "#", " ".join(args) | 
| - popen_args = args | 
| - prev_error_mode = SEM_INVALID_VALUE; | 
| - if utils.IsWindows(): | 
| - popen_args = '"' + subprocess.list2cmdline(args) + '"' | 
| - if context.suppress_dialogs: | 
| - # Try to change the error mode to avoid dialogs on fatal errors. Don't | 
| - # touch any existing error mode flags by merging the existing error mode. | 
| - # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx. | 
| - error_mode = SEM_NOGPFAULTERRORBOX; | 
| - prev_error_mode = Win32SetErrorMode(error_mode); | 
| - Win32SetErrorMode(error_mode | prev_error_mode); | 
| - process = subprocess.Popen( | 
| - shell = utils.IsWindows(), | 
| - args = popen_args, | 
| - **rest | 
| - ) | 
| - if utils.IsWindows() and context.suppress_dialogs and prev_error_mode != SEM_INVALID_VALUE: | 
| - Win32SetErrorMode(prev_error_mode) | 
| - # Compute the end time - if the process crosses this limit we | 
| - # consider it timed out. | 
| - if timeout is None: end_time = None | 
| - else: end_time = time.time() + timeout | 
| - timed_out = False | 
| - # Repeatedly check the exit code from the process in a | 
| - # loop and keep track of whether or not it times out. | 
| - exit_code = None | 
| - sleep_time = INITIAL_SLEEP_TIME | 
| - while exit_code is None: | 
| - if (not end_time is None) and (time.time() >= end_time): | 
| - # Kill the process and wait for it to exit. | 
| - KillProcessWithID(process.pid) | 
| - # Drain the output pipe from the process to avoid deadlock | 
| - process.communicate() | 
| - exit_code = process.wait() | 
| - timed_out = True | 
| - else: | 
| - exit_code = process.poll() | 
| - time.sleep(sleep_time) | 
| - sleep_time = sleep_time * SLEEP_TIME_FACTOR | 
| - if sleep_time > MAX_SLEEP_TIME: | 
| - sleep_time = MAX_SLEEP_TIME | 
| - return (process, exit_code, timed_out) | 
| - | 
| - | 
| -def PrintError(str): | 
| - sys.stderr.write(str) | 
| - sys.stderr.write('\n') | 
| - | 
| - | 
| -def CheckedUnlink(name): | 
| - try: | 
| - os.unlink(name) | 
| - except OSError, e: | 
| - PrintError("os.unlink() " + str(e)) | 
| - | 
| - | 
| -def Execute(args, context, timeout=None, cwd=None): | 
| - (fd_out, outname) = tempfile.mkstemp() | 
| - (fd_err, errname) = tempfile.mkstemp() | 
| - (process, exit_code, timed_out) = RunProcess( | 
| - context, | 
| - timeout, | 
| - args = args, | 
| - stdout = fd_out, | 
| - stderr = fd_err, | 
| - cwd = cwd | 
| - ) | 
| - os.close(fd_out) | 
| - os.close(fd_err) | 
| - output = file(outname).read() | 
| - errors = file(errname).read() | 
| - CheckedUnlink(outname) | 
| - CheckedUnlink(errname) | 
| - result = CommandOutput(process.pid, exit_code, timed_out, output, errors) | 
| - return result | 
| - | 
| - | 
| class TestConfiguration(object): | 
| + """Test configurations give test.py the list of tests, e.g. listing a dir.""" | 
| def __init__(self, context, root): | 
| self.context = context | 
| self.root = root | 
| - def Contains(self, path, file): | 
| - if len(path) > len(file): | 
| + def Contains(self, path, filename): | 
| + """Returns True if the given path regexp matches the passed filename.""" | 
| + | 
| + if len(path) > len(filename): | 
| return False | 
| for i in xrange(len(path)): | 
| - if not path[i].match(file[i]): | 
| - return False | 
| + try: | 
| + if not path[i].match(filename[i]): | 
| + return False | 
| + except: | 
| + print 'Invalid regexp %s in .status file. ' % '/'.join(path) | 
| + print 'Try escaping special characters with \\' | 
| + raise | 
| + | 
| return True | 
| def GetTestStatus(self, sections, defs): | 
| @@ -632,26 +505,29 @@ class TestSuite(object): | 
| class TestRepository(TestSuite): | 
| + """A collection of test configurations.""" | 
| def __init__(self, path): | 
| - normalized_path = abspath(path) | 
| - super(TestRepository, self).__init__(basename(normalized_path)) | 
| + normalized_path = os.path.abspath(path) | 
| + super(TestRepository, self).__init__(os.path.basename(normalized_path)) | 
| self.path = normalized_path | 
| self.is_loaded = False | 
| self.config = None | 
| def GetConfiguration(self, context): | 
| + """Retrieve a TestConfiguration subclass for this set of tests.""" | 
| if self.is_loaded: | 
| return self.config | 
| self.is_loaded = True | 
| - file = None | 
| + filename = None | 
| try: | 
| - (file, pathname, description) = imp.find_module('testcfg', [ self.path ]) | 
| - module = imp.load_module('testcfg', file, pathname, description) | 
| + (filename, pathname, description) = imp.find_module( | 
| + 'testcfg', [self.path]) | 
| + module = imp.load_module('testcfg', filename, pathname, description) | 
| self.config = module.GetConfiguration(context, self.path) | 
| finally: | 
| - if file: | 
| - file.close() | 
| + if filename: | 
| + filename.close() | 
| return self.config | 
| def ListTests(self, current_path, path, context, mode, arch): | 
| @@ -665,14 +541,15 @@ class TestRepository(TestSuite): | 
| class LiteralTestSuite(TestSuite): | 
| + """Represents one set of tests.""" | 
| def __init__(self, tests): | 
| super(LiteralTestSuite, self).__init__('root') | 
| self.tests = tests | 
| def ListTests(self, current_path, path, context, mode, arch): | 
| - name = path[0] | 
| - result = [ ] | 
| + name = path[0] | 
| + result = [] | 
| for test in self.tests: | 
| test_name = test.GetName() | 
| if name.match(test_name): | 
| @@ -684,23 +561,26 @@ class LiteralTestSuite(TestSuite): | 
| for test in self.tests: | 
| test.GetTestStatus(context, sections, defs) | 
| + | 
| class Context(object): | 
| + """A way to send global context for the test run to each test case.""" | 
| - def __init__(self, workspace, verbose, os, timeout, | 
| + 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
 | 
| processor, suppress_dialogs, executable, flags, | 
| keep_temporary_files, use_batch): | 
| self.workspace = workspace | 
| self.verbose = verbose | 
| - self.os = os | 
| + self.os = os_def | 
| self.timeout = timeout | 
| self.processor = processor | 
| self.suppress_dialogs = suppress_dialogs | 
| self.executable = executable | 
| self.flags = flags | 
| self.keep_temporary_files = keep_temporary_files | 
| - self.use_batch = use_batch == "true" | 
| + self.use_batch = use_batch == 'true' | 
| def GetBuildRoot(self, mode, arch): | 
| + """The top level directory containing compiler, runtime, tools...""" | 
| result = utils.GetBuildRoot(self.os, mode, arch) | 
| return result | 
| @@ -709,37 +589,43 @@ class Context(object): | 
| return result | 
| def GetExecutable(self, mode, arch, name): | 
| + """Returns the name of the executable used to run the test.""" | 
| if self.executable is not None: | 
| return self.executable | 
| - path = abspath(join(self.GetBuildRoot(mode, arch), name)) | 
| + path = os.path.abspath(os.path.join(self.GetBuildRoot(mode, arch), name)) | 
| if utils.IsWindows() and not path.endswith('.exe'): | 
| return path + '.exe' | 
| else: | 
| return path | 
| def GetDart(self, mode, arch): | 
| + """Returns the path to the Dart test runner (executes the .dart file).""" | 
| if arch == 'dartc': | 
| - command = [ abspath(join(self.GetBuildRoot(mode, arch), | 
| - 'compiler', 'bin', 'dartc_test')) ] | 
| + command = [os.path.abspath( | 
| + os.path.join(self.GetBuildRoot(mode, arch), | 
| + 'compiler', 'bin', 'dartc_test'))] | 
| else: | 
| - command = [ self.GetExecutable(mode, arch, 'dart_bin') ] | 
| + command = [self.GetExecutable(mode, arch, 'dart_bin')] | 
| return command | 
| def GetDartC(self, mode, arch): | 
| - dartc = abspath(os.path.join(self.GetBuildRoot(mode, arch), | 
| - 'compiler', 'bin', 'dartc')) | 
| + """Returns the path to the Dart --> JS compiler.""" | 
| + dartc = os.path.abspath(os.path.join( | 
| + self.GetBuildRoot(mode, arch), 'compiler', 'bin', 'dartc')) | 
| if utils.IsWindows(): dartc += '.exe' | 
| - command = [ dartc ] | 
| + command = [dartc] | 
| # Add the flags from the context to the command line. | 
| command += self.flags | 
| return command | 
| def GetRunTests(self, mode, arch): | 
| - return [ self.GetExecutable(mode, arch, 'run_vm_tests') ] | 
| + return [self.GetExecutable(mode, arch, 'run_vm_tests')] | 
| + | 
| def RunTestCases(cases_to_run, progress, tasks, context): | 
| + """Chooses a progress indicator and then starts the tests.""" | 
| progress = PROGRESS_INDICATORS[progress](cases_to_run, context) | 
| return progress.Run(tasks) | 
| @@ -748,6 +634,7 @@ def RunTestCases(cases_to_run, progress, tasks, context): | 
| # --- T e s t C o n f i g u r a t i o n --- | 
| # ------------------------------------------- | 
| + | 
| class Expression(object): | 
| pass | 
| @@ -755,25 +642,29 @@ class Expression(object): | 
| class Constant(Expression): | 
| def __init__(self, value): | 
| + super(Constant, self).__init__() | 
| self.value = value | 
| - def Evaluate(self, env, defs): | 
| + def Evaluate(self, unused_env, unused_defs): | 
| return self.value | 
| class Variable(Expression): | 
| def __init__(self, name): | 
| + super(Variable, self).__init__() | 
| self.name = name | 
| - def GetOutcomes(self, env, defs): | 
| - if self.name in env: return ListSet([env[self.name]]) | 
| + def GetOutcomes(self, env, unused_defs): | 
| + if self.name in env: | 
| + return ListSet([env[self.name]]) | 
| else: return Nothing() | 
| class Outcome(Expression): | 
| def __init__(self, name): | 
| + super(Outcome, self).__init__() | 
| self.name = name | 
| def GetOutcomes(self, env, defs): | 
| @@ -784,37 +675,42 @@ class Outcome(Expression): | 
| class Set(object): | 
| + """An abstract set class used to hold Rules.""" | 
| pass | 
| class ListSet(Set): | 
| + """A set that uses lists for storage.""" | 
| def __init__(self, elms): | 
| + super(ListSet, self).__init__() | 
| self.elms = elms | 
| def __str__(self): | 
| - return "ListSet%s" % str(self.elms) | 
| + return 'ListSet%s' % str(self.elms) | 
| def Intersect(self, that): | 
| if not isinstance(that, ListSet): | 
| return that.Intersect(self) | 
| - return ListSet([ x for x in self.elms if x in that.elms ]) | 
| + return ListSet([x for x in self.elms if x in that.elms]) | 
| def Union(self, that): | 
| if not isinstance(that, ListSet): | 
| return that.Union(self) | 
| - return ListSet(self.elms + [ x for x in that.elms if x not in self.elms ]) | 
| + return ListSet(self.elms + | 
| + [x for x in that.elms if x not in self.elms]) | 
| def IsEmpty(self): | 
| - return len(self.elms) == 0 | 
| + return not self.elms | 
| class Everything(Set): | 
| + """A set that represents all possible values.""" | 
| def Intersect(self, that): | 
| return that | 
| - def Union(self, that): | 
| + def Union(self, unused_that): | 
| return self | 
| def IsEmpty(self): | 
| @@ -823,7 +719,7 @@ class Everything(Set): | 
| class Nothing(Set): | 
| - def Intersect(self, that): | 
| + def Intersect(self, unused_that): | 
| return self | 
| def Union(self, that): | 
| @@ -834,19 +730,24 @@ class Nothing(Set): | 
| class Operation(Expression): | 
| + """A conditional expression. e.g. ($arch == ia32).""" | 
| def __init__(self, left, op, right): | 
| + super(Operation, self).__init__() | 
| self.left = left | 
| self.op = op | 
| self.right = right | 
| def Evaluate(self, env, defs): | 
| + """Evaluates expression in the .status file. e.g. ($arch == ia32).""" | 
| + | 
| if self.op == '||' or self.op == ',': | 
| return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs) | 
| elif self.op == 'if': | 
| return False | 
| elif self.op == '==': | 
| - inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs)) | 
| + outcomes = self.left.GetOutcomes(env, defs) | 
| + inter = outcomes.Intersect(self.right.GetOutcomes(env, defs)) | 
| return not inter.IsEmpty() | 
| else: | 
| assert self.op == '&&' | 
| @@ -854,25 +755,28 @@ class Operation(Expression): | 
| def GetOutcomes(self, env, defs): | 
| if self.op == '||' or self.op == ',': | 
| - return self.left.GetOutcomes(env, defs).Union(self.right.GetOutcomes(env, defs)) | 
| + outcomes = self.left.GetOutcomes(env, defs) | 
| + return outcomes.Union(self.right.GetOutcomes(env, defs)) | 
| elif self.op == 'if': | 
| - if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs) | 
| + if self.right.Evaluate(env, defs): | 
| + return self.left.GetOutcomes(env, defs) | 
| else: return Nothing() | 
| else: | 
| assert self.op == '&&' | 
| - return self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs)) | 
| + outcomes = self.left.GetOutcomes(env, defs) | 
| + return outcomes.Intersect(self.right.GetOutcomes(env, defs)) | 
| -def IsAlpha(str): | 
| - for char in str: | 
| +def IsAlpha(buf): | 
| + """Returns True if the entire string is alphanumeric.""" | 
| + for char in buf: | 
| if not (char.isalpha() or char.isdigit() or char == '_'): | 
| return False | 
| return True | 
| class Tokenizer(object): | 
| - """A simple string tokenizer that chops expressions into variables, | 
| - parens and operators""" | 
| + """Tokenizer that chops expressions into variables, parens and operators.""" | 
| def __init__(self, expr): | 
| self.index = 0 | 
| @@ -880,15 +784,15 @@ class Tokenizer(object): | 
| self.length = len(expr) | 
| self.tokens = None | 
| - def Current(self, length = 1): | 
| - if not self.HasMore(length): return "" | 
| + def Current(self, length=1): | 
| + if not self.HasMore(length): return '' | 
| return self.expr[self.index:self.index+length] | 
| - def HasMore(self, length = 1): | 
| + def HasMore(self, length=1): | 
| return self.index < self.length + (length - 1) | 
| - def Advance(self, count = 1): | 
| - self.index = self.index + count | 
| + def Advance(self, count=1): | 
| + self.index += count | 
| def AddToken(self, token): | 
| self.tokens.append(token) | 
| @@ -898,7 +802,19 @@ class Tokenizer(object): | 
| self.Advance() | 
| def Tokenize(self): | 
| - self.tokens = [ ] | 
| + """Lexical analysis of an expression in a .status file. | 
| + | 
| + Example: | 
| + [ $mode == debug && ($arch == chromium || $arch == dartc) ] | 
| + | 
| + Args: | 
| + None. | 
| + | 
| + Returns: | 
| + A list of tokens on success, None on failure. | 
| + """ | 
| + | 
| + self.tokens = [] | 
| while self.HasMore(): | 
| self.SkipSpaces() | 
| if not self.HasMore(): | 
| @@ -916,7 +832,7 @@ class Tokenizer(object): | 
| self.AddToken(',') | 
| self.Advance() | 
| elif IsAlpha(self.Current()): | 
| - buf = "" | 
| + buf = '' | 
| while self.HasMore() and IsAlpha(self.Current()): | 
| buf += self.Current() | 
| self.Advance() | 
| @@ -936,7 +852,7 @@ class Tokenizer(object): | 
| class Scanner(object): | 
| - """A simple scanner that can serve out tokens from a given list""" | 
| + """A simple scanner that can serve out tokens from a given list.""" | 
| def __init__(self, tokens): | 
| self.tokens = tokens | 
| @@ -950,14 +866,16 @@ class Scanner(object): | 
| return self.tokens[self.index] | 
| def Advance(self): | 
| - self.index = self.index + 1 | 
| + self.index += 1 | 
| def ParseAtomicExpression(scan): | 
| - if scan.Current() == "true": | 
| + """Parse an single (non recursive) expression in a .status file.""" | 
| + | 
| + if scan.Current() == 'true': | 
| scan.Advance() | 
| return Constant(True) | 
| - elif scan.Current() == "false": | 
| + elif scan.Current() == 'false': | 
| scan.Advance() | 
| return Constant(False) | 
| elif IsAlpha(scan.Current()): | 
| @@ -982,11 +900,11 @@ def ParseAtomicExpression(scan): | 
| return None | 
| -BINARIES = ['=='] | 
| def ParseOperatorExpression(scan): | 
| + """Parse an expression that has operators.""" | 
| left = ParseAtomicExpression(scan) | 
| if not left: return None | 
| - while scan.HasMore() and (scan.Current() in BINARIES): | 
| + while scan.HasMore() and (scan.Current() in ['==']): | 
| op = scan.Current() | 
| scan.Advance() | 
| right = ParseOperatorExpression(scan) | 
| @@ -1004,15 +922,15 @@ def ParseConditionalExpression(scan): | 
| right = ParseOperatorExpression(scan) | 
| if not right: | 
| return None | 
| - left= Operation(left, 'if', right) | 
| + left = Operation(left, 'if', right) | 
| return left | 
| -LOGICALS = ["&&", "||", ","] | 
| def ParseLogicalExpression(scan): | 
| + """Parse a binary expression separated by boolean operators.""" | 
| left = ParseConditionalExpression(scan) | 
| if not left: return None | 
| - while scan.HasMore() and (scan.Current() in LOGICALS): | 
| + while scan.HasMore() and (scan.Current() in ['&&', '||', ',']): | 
| op = scan.Current() | 
| scan.Advance() | 
| right = ParseConditionalExpression(scan) | 
| @@ -1023,18 +941,18 @@ def ParseLogicalExpression(scan): | 
| def ParseCondition(expr): | 
| - """Parses a logical expression into an Expression object""" | 
| + """Parses a boolean expression into an Expression object.""" | 
| tokens = Tokenizer(expr).Tokenize() | 
| if not tokens: | 
| - print "Malformed expression: '%s'" % expr | 
| + print 'Malformed expression: "%s"' % expr | 
| return None | 
| scan = Scanner(tokens) | 
| ast = ParseLogicalExpression(scan) | 
| if not ast: | 
| - print "Malformed expression: '%s'" % expr | 
| + print 'Malformed expression: "%s"' % expr | 
| return None | 
| if scan.HasMore(): | 
| - print "Malformed expression: '%s'" % expr | 
| + print 'Malformed expression: "%s"' % expr | 
| return None | 
| return ast | 
| @@ -1047,20 +965,34 @@ class ClassifiedTest(object): | 
| class Configuration(object): | 
| - """The parsed contents of a configuration file""" | 
| + """The parsed contents of a configuration file.""" | 
| def __init__(self, sections, defs): | 
| self.sections = sections | 
| self.defs = defs | 
| def ClassifyTests(self, cases, env): | 
| - sections = [s for s in self.sections if s.condition.Evaluate(env, self.defs)] | 
| + """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.
 | 
| + | 
| + This 'wraps' each TestCase object with some meta information | 
| + about the test. | 
| + | 
| + Args: | 
| + cases: list of TestCase objects to classify. | 
| + env: dictionary containing values for 'mode': mode, | 
| + 'system' and 'arch'. | 
| + | 
| + Returns: | 
| + A triplet of (result, rules, expected_outcomes). | 
| + """ | 
| + sections = [s for s in self.sections | 
| + if s.condition.Evaluate(env, self.defs)] | 
| all_rules = reduce(list.__add__, [s.rules for s in sections], []) | 
| unused_rules = set(all_rules) | 
| - result = [ ] | 
| + result = [] | 
| all_outcomes = set([]) | 
| for case in cases: | 
| - matches = [ r for r in all_rules if r.Contains(case.path) ] | 
| + matches = [r for r in all_rules if r.Contains(case.path)] | 
| outcomes = set([]) | 
| for rule in matches: | 
| outcomes = outcomes.union(rule.GetOutcomes(env, self.defs)) | 
| @@ -1074,20 +1006,22 @@ class Configuration(object): | 
| class Section(object): | 
| - """A section of the configuration file. Sections are enabled or | 
| - disabled prior to running the tests, based on their conditions""" | 
| + """A section of the configuration file. | 
| + | 
| + Sections are enabled or disabled prior to running the tests, | 
| + based on their conditions. | 
| + """ | 
| def __init__(self, condition): | 
| self.condition = condition | 
| - self.rules = [ ] | 
| + self.rules = [] | 
| def AddRule(self, rule): | 
| self.rules.append(rule) | 
| class Rule(object): | 
| - """A single rule that specifies the expected outcome for a single | 
| - test.""" | 
| + """A single rule that specifies the expected outcome for a single test.""" | 
| def __init__(self, raw_path, path, value): | 
| self.raw_path = raw_path | 
| @@ -1095,20 +1029,21 @@ class Rule(object): | 
| self.value = value | 
| def GetOutcomes(self, env, defs): | 
| - set = self.value.GetOutcomes(env, defs) | 
| - assert isinstance(set, ListSet) | 
| - return set.elms | 
| + outcomes = self.value.GetOutcomes(env, defs) | 
| + assert isinstance(outcomes, ListSet) | 
| + return outcomes.elms | 
| def Contains(self, path): | 
| + """Returns True if the specified path matches this rule (regexp).""" | 
| if len(self.path) > len(path): | 
| return False | 
| for i in xrange(len(self.path)): | 
| try: | 
| if not self.path[i].match(path[i]): | 
| return False | 
| - except: | 
| - print "Invalid reguar expression %s in .status file. " % '/'.join(path) | 
| - print "Try escaping special characters with \\" | 
| + except: | 
| + print 'Invalid regexp %s in .status file. ' % '/'.join(path) | 
| + print 'Try escaping special characters with \\' | 
| raise | 
| return True | 
| @@ -1120,6 +1055,7 @@ PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$') | 
| def ReadConfigurationInto(path, sections, defs): | 
| + """Parses a .status file into specified sections and defs arguments.""" | 
| current_section = Section(Constant(True)) | 
| sections.append(current_section) | 
| prefix = [] | 
| @@ -1134,7 +1070,7 @@ def ReadConfigurationInto(path, sections, defs): | 
| continue | 
| rule_match = RULE_PATTERN.match(line) | 
| if rule_match: | 
| - path = prefix + SplitPath(rule_match.group(1).strip()) | 
| + path = prefix + _SplitPath(rule_match.group(1).strip()) | 
| value_str = rule_match.group(2).strip() | 
| value = ParseCondition(value_str) | 
| if not value: | 
| @@ -1151,9 +1087,9 @@ def ReadConfigurationInto(path, sections, defs): | 
| continue | 
| prefix_match = PREFIX_PATTERN.match(line) | 
| if prefix_match: | 
| - prefix = SplitPath(prefix_match.group(1).strip()) | 
| + prefix = _SplitPath(prefix_match.group(1).strip()) | 
| continue | 
| - print "Malformed line: '%s'." % line | 
| + print 'Malformed line: "%s".' % line | 
| return False | 
| return True | 
| @@ -1164,86 +1100,103 @@ def ReadConfigurationInto(path, sections, defs): | 
| def BuildOptions(): | 
| + """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.
 | 
| result = optparse.OptionParser() | 
| - result.add_option("-m", "--mode", | 
| - help="The test modes in which to run (comma-separated)", | 
| + result.add_option( | 
| + '-m', '--mode', | 
| + help='The test modes in which to run (comma-separated)', | 
| metavar='[all,debug,release]', | 
| default='debug') | 
| - result.add_option("-v", "--verbose", | 
| - help="Verbose output", | 
| + result.add_option( | 
| + '-v', '--verbose', | 
| + help='Verbose output', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("-p", "--progress", | 
| - help="The style of progress indicator (verbose, line, color, mono)", | 
| + action='store_true') | 
| + result.add_option( | 
| + '-p', '--progress', | 
| + help='The style of progress indicator (verbose, line, color, mono)', | 
| choices=PROGRESS_INDICATORS.keys(), | 
| default=None) | 
| - result.add_option("--report", | 
| - help="Print a summary of the tests to be run", | 
| + result.add_option( | 
| + '--report', | 
| + help='Print a summary of the tests to be run', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("--list", | 
| - help="List all the tests, but don't run them", | 
| + action='store_true') | 
| + result.add_option( | 
| + '--list', | 
| + help='List all the tests, but don\'t run them', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("-s", "--suite", | 
| - help="A test suite", | 
| + action='store_true') | 
| + result.add_option( | 
| + '-s', '--suite', | 
| + help='A test suite', | 
| default=[], | 
| - action="append") | 
| - result.add_option("-t", "--timeout", | 
| - help="Timeout in seconds", | 
| + action='append') | 
| + result.add_option( | 
| + '-t', '--timeout', | 
| + help='Timeout in seconds', | 
| default=None, | 
| - type="int") | 
| - result.add_option("--checked", | 
| - help="Run tests in checked mode", | 
| + type='int') | 
| + result.add_option( | 
| + '--checked', | 
| + help='Run tests in checked mode', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("--flag", | 
| - help="Pass this additional flag to the VM", | 
| + action='store_true') | 
| + result.add_option( | 
| + '--flag', | 
| + help='Pass this additional flag to the VM', | 
| default=[], | 
| - action="append") | 
| - result.add_option("--arch", | 
| - help="The architecture to run tests for", | 
| - metavar="[all,ia32,x64,simarm,arm,dartc]", | 
| + action='append') | 
| + result.add_option( | 
| + '--arch', | 
| + help='The architecture to run tests for', | 
| + metavar='[all,ia32,x64,simarm,arm,dartc]', | 
| default=ARCH_GUESS) | 
| - result.add_option("--os", | 
| - help="The OS to run tests on", | 
| + result.add_option( | 
| + '--os', | 
| + help='The OS to run tests on', | 
| default=OS_GUESS) | 
| - result.add_option("--valgrind", | 
| - help="Run tests through valgrind", | 
| + result.add_option( | 
| + '--valgrind', | 
| + help='Run tests through valgrind', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("-j", "--tasks", | 
| - help="The number of parallel tasks to run", | 
| + action='store_true') | 
| + result.add_option( | 
| + '-j', '--tasks', | 
| + help='The number of parallel tasks to run', | 
| metavar=testing.HOST_CPUS, | 
| default=testing.USE_DEFAULT_CPUS, | 
| - type="int") | 
| - result.add_option("--time", | 
| - help="Print timing information after running", | 
| + type='int') | 
| + result.add_option( | 
| + '--time', | 
| + help='Print timing information after running', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("--executable", | 
| - help="The executable with which to run the tests", | 
| + action='store_true') | 
| + result.add_option( | 
| + '--executable', | 
| + help='The executable with which to run the tests', | 
| default=None) | 
| - result.add_option("--keep_temporary_files", | 
| - help="Do not delete temporary files after running the tests", | 
| + result.add_option( | 
| + '--keep_temporary_files', | 
| + help='Do not delete temporary files after running the tests', | 
| default=False, | 
| - action="store_true") | 
| - result.add_option("--batch", | 
| - help="Run multiple tests for dartc architecture in a single vm", | 
| - choices=["true","false"], | 
| - default="true", | 
| - type="choice"); | 
| - result.add_option("--optimize", | 
| - help="Invoke dart compiler with --optimize flag", | 
| + action='store_true') | 
| + result.add_option( | 
| + '--batch', | 
| + help='Run multiple tests for dartc architecture in a single vm', | 
| + choices=['true', 'false'], | 
| + default='true', | 
| + type='choice') | 
| + result.add_option( | 
| + '--optimize', | 
| + help='Invoke dart compiler with --optimize flag', | 
| default=False, | 
| - action="store_true") | 
| - | 
| + action='store_true') | 
| return result | 
| def ProcessOptions(options): | 
| - global VERBOSE | 
| - VERBOSE = options.verbose | 
| + """Process command line options.""" | 
| if options.arch == 'all': | 
| options.arch = 'ia32,x64,simarm' | 
| if options.mode == 'all': | 
| @@ -1252,22 +1205,24 @@ def ProcessOptions(options): | 
| # a simulated architecture and in debug mode. | 
| if not options.timeout: | 
| options.timeout = TIMEOUT_SECS | 
| - if 'dartc' in options.arch: options.timeout *= 4 | 
| - elif 'chromium' in options.arch: options.timeout *= 4 | 
| - elif 'dartium' in options.arch: options.timeout *= 4 | 
| - elif 'debug' in options.mode: options.timeout *= 2 | 
| - # TODO(zundel): is arch 'sim' out of date? | 
| - if 'sim' in options.arch: options.timeout *= 4 | 
| + if 'dartc' in options.arch: | 
| + options.timeout *= 4 | 
| + elif 'chromium' in options.arch: | 
| + options.timeout *= 4 | 
| + elif 'dartium' in options.arch: | 
| + options.timeout *= 4 | 
| + elif 'debug' in options.mode: | 
| + options.timeout *= 2 | 
| options.mode = options.mode.split(',') | 
| options.arch = options.arch.split(',') | 
| for mode in options.mode: | 
| if not mode in ['debug', 'release']: | 
| - print "Unknown mode %s" % mode | 
| + print 'Unknown mode %s' % mode | 
| return False | 
| for arch in options.arch: | 
| if not arch in ['ia32', 'x64', 'simarm', 'arm', 'dartc', 'dartium', | 
| 'chromium']: | 
| - print "Unknown arch %s" % arch | 
| + print 'Unknown arch %s' % arch | 
| return False | 
| options.flags = [] | 
| if (arch == 'dartc' or arch == 'chromium') and mode == 'release': | 
| @@ -1281,7 +1236,7 @@ def ProcessOptions(options): | 
| for flag in options.flag: | 
| options.flags.append(flag) | 
| if options.verbose: | 
| - print "Flags on the command line:" | 
| + print 'Flags on the command line:' | 
| for x in options.flags: | 
| print x | 
| # If the user hasn't specified the progress indicator, we pick | 
| @@ -1306,91 +1261,109 @@ Total: %(total)i tests | 
| * %(batched)4d tests are running in batch mode\ | 
| """ | 
| + | 
| def PrintReport(cases): | 
| - """Print a breakdown of which tests are marked pass/skip/fail """ | 
| + """Print a breakdown of which tests are marked pass/skip/fail.""" | 
| + | 
| def IsFlaky(o): | 
| return ((testing.PASS in o) and (testing.FAIL in o) | 
| and (not testing.CRASH in o) and (not testing.OKAY in o)) | 
| + | 
| def IsFailOk(o): | 
| 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.
 | 
| unskipped = [c for c in cases if not testing.SKIP in c.outcomes] | 
| print REPORT_TEMPLATE % { | 
| - 'total': len(cases), | 
| - 'skipped': len(cases) - len(unskipped), | 
| - 'nocrash': len([t for t in unskipped if IsFlaky(t.outcomes)]), | 
| - 'pass': len([t for t in unskipped if list(t.outcomes) == [testing.PASS]]), | 
| - 'fail_ok': len([t for t in unskipped if IsFailOk(t.outcomes)]), | 
| - 'fail': len([t for t in unskipped if list(t.outcomes) == [testing.FAIL]]), | 
| - 'crash': len([t for t in unskipped if list(t.outcomes) == [testing.CRASH]]), | 
| - 'batched' : len([t for t in unskipped if t.case.IsBatchable()]) | 
| + 'total': len(cases), | 
| + 'skipped': len(cases) - len(unskipped), | 
| + 'nocrash': len([t for t in unskipped if IsFlaky(t.outcomes)]), | 
| + 'pass': len([t for t in unskipped | 
| + if list(t.outcomes) == [testing.PASS]]), | 
| + 'fail_ok': len([t for t in unskipped | 
| + if IsFailOk(t.outcomes)]), | 
| + 'fail': len([t for t in unskipped | 
| + if list(t.outcomes) == [testing.FAIL]]), | 
| + 'crash': len([t for t in unskipped | 
| + if list(t.outcomes) == [testing.CRASH]]), | 
| + 'batched': len([t for t in unskipped if t.case.IsBatchable()]) | 
| } | 
| def PrintTests(cases): | 
| + """Print a table of the tests to be run (--list cmdline option).""" | 
| has_errors = False | 
| for case in cases: | 
| try: | 
| case.case.GetCommand() | 
| except: | 
| + # Python can throw an exception while parsing the .dart file. | 
| + # 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
 | 
| sys.stderr.write(case.case.filename + '\n') | 
| has_errors = True | 
| if has_errors: | 
| raise Exception('Errors in above files') | 
| for case in [c for c in cases if not testing.SKIP in c.outcomes]: | 
| - print "%s\t%s\t%s\t%s" %('/'.join(case.case.path), | 
| + print '%s\t%s\t%s\t%s' %('/'.join(case.case.path), | 
| ','.join(case.outcomes), | 
| case.case.IsNegative(), | 
| '\t'.join(case.case.GetCommand()[1:])) | 
| class Pattern(object): | 
| + """Convenience class to hold a compiled re pattern.""" | 
| def __init__(self, pattern): | 
| self.pattern = pattern | 
| self.compiled = None | 
| - def match(self, str): | 
| + def match(self, buf): | 
| if not self.compiled: | 
| - pattern = "^" + self.pattern.replace('*', '.*') + "$" | 
| + 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.
 | 
| self.compiled = re.compile(pattern) | 
| - return self.compiled.match(str) | 
| + return self.compiled.match(buf) | 
| def __str__(self): | 
| return self.pattern | 
| -def SplitPath(s): | 
| - stripped = [ c.strip() for c in s.split('/') ] | 
| - return [ Pattern(s) for s in stripped if len(s) > 0 ] | 
| +def _SplitPath(s): | 
| + """Split a path into directories - opposite of os.path.join()?""" | 
| + stripped = [c.strip() for c in s.split('/')] | 
| + return [Pattern(s) for s in stripped if s] | 
| def GetSpecialCommandProcessor(value): | 
| if (not value) or (value.find('@') == -1): | 
| + | 
| def ExpandCommand(args): | 
| return args | 
| + | 
| return ExpandCommand | 
| else: | 
| pos = value.find('@') | 
| - import urllib | 
| prefix = urllib.unquote(value[:pos]).split() | 
| suffix = urllib.unquote(value[pos+1:]).split() | 
| + | 
| def ExpandCommand(args): | 
| return prefix + args + suffix | 
| + | 
| return ExpandCommand | 
| def GetSuites(test_root): | 
| def IsSuite(path): | 
| - return isdir(path) and exists(join(path, 'testcfg.py')) | 
| - return [ f for f in os.listdir(test_root) if IsSuite(join(test_root, f)) ] | 
| + return os.path.isdir(path) and os.path.exists( | 
| + os.path.join(path, 'testcfg.py')) | 
| + return [f for f in os.listdir(test_root) if IsSuite( | 
| + os.path.join(test_root, f))] | 
| def FormatTime(d): | 
| millis = round(d * 1000) % 1000 | 
| - return time.strftime("%M:%S.", time.gmtime(d)) + ("%03i" % millis) | 
| + return time.strftime('%M:%S.', time.gmtime(d)) + ('%03i' % millis) | 
| def Main(): | 
| + """Main loop.""" | 
| utils.ConfigureJava() | 
| parser = BuildOptions() | 
| (options, args) = parser.parse_args() | 
| @@ -1398,32 +1371,33 @@ def Main(): | 
| parser.print_help() | 
| return 1 | 
| - client = abspath(join(dirname(sys.argv[0]), '..')) | 
| + client = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..')) | 
| repositories = [] | 
| for component in os.listdir(client) + ['.']: | 
| - test_path = join(client, component, 'tests') | 
| - if exists(test_path) and isdir(test_path): | 
| + test_path = os.path.join(client, component, 'tests') | 
| + if os.path.exists(test_path) and os.path.isdir(test_path): | 
| suites = GetSuites(test_path) | 
| - repositories += [TestRepository(join(test_path, name)) for name in suites] | 
| + repositories += [TestRepository(os.path.join(test_path, name)) | 
| + for name in suites] | 
| repositories += [TestRepository(a) for a in options.suite] | 
| root = LiteralTestSuite(repositories) | 
| - if len(args) == 0: | 
| - paths = [SplitPath(t) for t in BUILT_IN_TESTS] | 
| - else: | 
| - paths = [ ] | 
| + if args: | 
| + paths = [] | 
| for arg in args: | 
| - path = SplitPath(arg) | 
| + path = _SplitPath(arg) | 
| paths.append(path) | 
| + else: | 
| + paths = [_SplitPath(t) for t in BUILT_IN_TESTS] | 
| # Check for --valgrind option. If enabled, we overwrite the special | 
| # command flag with a command that uses the tools/valgrind.py script. | 
| if options.valgrind: | 
| - run_valgrind = join(client, 'runtime', 'tools', 'valgrind.py') | 
| - options.special_command = "python -u " + run_valgrind + " @" | 
| + run_valgrind = os.path.join(client, 'runtime', 'tools', 'valgrind.py') | 
| + options.special_command = 'python -u ' + run_valgrind + ' @' | 
| context = Context(client, | 
| - VERBOSE, | 
| + options.verbose, | 
| options.os, | 
| options.timeout, | 
| GetSpecialCommandProcessor(options.special_command), | 
| @@ -1434,31 +1408,31 @@ def Main(): | 
| options.batch) | 
| # Get status for tests | 
| - sections = [ ] | 
| - defs = { } | 
| + sections = [] | 
| + defs = {} | 
| root.GetTestStatus(context, sections, defs) | 
| config = Configuration(sections, defs) | 
| # List the tests | 
| - all_cases = [ ] | 
| - all_unused = [ ] | 
| - unclassified_tests = [ ] | 
| + all_cases = [] | 
| + all_unused = [] | 
| globally_unused_rules = None | 
| for path in paths: | 
| for mode in options.mode: | 
| for arch in options.arch: | 
| env = { | 
| - 'mode': mode, | 
| - 'system': utils.GuessOS(), | 
| - 'arch': arch, | 
| + 'mode': mode, | 
| + 'system': utils.GuessOS(), | 
| + 'arch': arch, | 
| } | 
| test_list = root.ListTests([], path, context, mode, arch) | 
| - unclassified_tests += test_list | 
| - (cases, unused_rules, all_outcomes) = config.ClassifyTests(test_list, env) | 
| + (cases, unused_rules, unused_outcomes) = config.ClassifyTests( | 
| + test_list, env) | 
| if globally_unused_rules is None: | 
| globally_unused_rules = set(unused_rules) | 
| else: | 
| - globally_unused_rules = globally_unused_rules.intersection(unused_rules) | 
| + globally_unused_rules = ( | 
| + globally_unused_rules.intersection(unused_rules)) | 
| all_cases += cases | 
| all_unused.append(unused_rules) | 
| @@ -1467,21 +1441,20 @@ def Main(): | 
| if options.list: | 
| PrintTests(all_cases) | 
| - return 0; | 
| + return 0 | 
| result = None | 
| + | 
| def DoSkip(case): | 
| return testing.SKIP in case.outcomes or testing.SLOW in case.outcomes | 
| - cases_to_run = [ c for c in all_cases if not DoSkip(c) ] | 
| + | 
| + cases_to_run = [c for c in all_cases if not DoSkip(c)] | 
| # Creating test cases may generate temporary files. Make sure | 
| # Skipped tests clean up these files. | 
| for c in all_cases: | 
| if DoSkip(c): c.case.Cleanup() | 
| - if len(cases_to_run) == 0: | 
| - print "No tests to run." | 
| - return 0 | 
| - else: | 
| + if cases_to_run: | 
| try: | 
| start = time.time() | 
| if RunTestCases(cases_to_run, options.progress, options.tasks, | 
| @@ -1491,18 +1464,21 @@ def Main(): | 
| result = 1 | 
| duration = time.time() - start | 
| except KeyboardInterrupt: | 
| - print "Exiting on KeyboardInterrupt" | 
| + print 'Exiting on KeyboardInterrupt' | 
| return 1 | 
| + else: | 
| + print 'No tests to run.' | 
| + return 0 | 
| if options.time: | 
| - print "--- Total time: %s ---" % FormatTime(duration) | 
| - timed_tests = [ t.case for t in cases_to_run if not t.case.duration is None ] | 
| + print '--- Total time: %s ---' % FormatTime(duration) | 
| + timed_tests = [t.case for t in cases_to_run if not t.case.duration is None] | 
| timed_tests.sort(lambda a, b: a.CompareTime(b)) | 
| index = 1 | 
| for entry in timed_tests[:20]: | 
| t = FormatTime(entry.duration) | 
| - print "%4i (%s) %s" % (index, t, entry.GetLabel()) | 
| + print '%4i (%s) %s' % (index, t, entry.GetLabel()) | 
| index += 1 | 
| return result |