| Index: tools/testing/perf_testing/run_perf_tests.py | 
| =================================================================== | 
| --- tools/testing/perf_testing/run_perf_tests.py	(revision 11181) | 
| +++ tools/testing/perf_testing/run_perf_tests.py	(working copy) | 
| @@ -45,8 +45,9 @@ | 
| if platform.system() == 'Windows': | 
| # On Windows, shell must be true to get the correct environment variables. | 
| self.has_shell = True | 
| +    self.current_revision_num = None | 
|  | 
| -  def run_cmd(self, cmd_list, outfile=None, append=False, std_in=''): | 
| +  def RunCmd(self, cmd_list, outfile=None, append=False, std_in=''): | 
| """Run the specified command and print out any output to stdout. | 
|  | 
| Args: | 
| @@ -78,18 +79,18 @@ | 
| print stderr | 
| return output, stderr | 
|  | 
| -  def time_cmd(self, cmd): | 
| +  def TimeCmd(self, cmd): | 
| """Determine the amount of (real) time it takes to execute a given | 
| command.""" | 
| start = time.time() | 
| -    self.run_cmd(cmd) | 
| +    self.RunCmd(cmd) | 
| return time.time() - start | 
|  | 
| -  def clear_out_unversioned_files(self): | 
| +  def ClearOutUnversionedFiles(self): | 
| """Remove all files that are unversioned by svn.""" | 
| if os.path.exists(DART_REPO_LOC): | 
| os.chdir(DART_REPO_LOC) | 
| -      results, _ = self.run_cmd(['svn', 'st']) | 
| +      results, _ = self.RunCmd(['svn', 'st']) | 
| for line in results.split('\n'): | 
| if line.startswith('?'): | 
| to_remove = line.split()[1] | 
| @@ -98,32 +99,30 @@ | 
| else: | 
| os.remove(to_remove) | 
|  | 
| -  def get_archive(self, archive_name): | 
| +  def GetArchive(self, archive_name): | 
| """Wrapper around the pulling down a specific archive from Google Storage. | 
| Adds a specific revision argument as needed. | 
| -    Returns: The stderr from running this command.""" | 
| -    cmd = ['python', os.path.join(DART_REPO_LOC, 'tools', 'get_archive.py'), | 
| -           archive_name] | 
| -    if self.current_revision_num != -1: | 
| -      cmd += ['-r', self.current_revision_num] | 
| -    _, stderr = self.run_cmd(cmd) | 
| -    return stderr | 
| +    Returns: The stdout and stderr from running this command.""" | 
| +    while True: | 
| +      cmd = ['python', os.path.join(DART_REPO_LOC, 'tools', 'get_archive.py'), | 
| +             archive_name] | 
| +      if int(self.current_revision_num) != -1: | 
| +        cmd += ['-r', str(self.current_revision_num)] | 
| +      stdout, stderr = self.RunCmd(cmd) | 
| +      if 'Please try again later' in stdout: | 
| +        time.sleep(100) | 
| +      else: | 
| +        break | 
| +    return (stdout, stderr) | 
|  | 
| -  def sync_and_build(self, suites, revision_num=''): | 
| -    """Make sure we have the latest version of of the repo, and build it. We | 
| -    begin and end standing in DART_REPO_LOC. | 
| - | 
| -    Args: | 
| -      suites: The set of suites that we wish to build. | 
| - | 
| -    Returns: | 
| -      err_code = 1 if there was a problem building.""" | 
| +  def _Sync(self, revision_num=None): | 
| +    """Update the repository to the latest or specified revision.""" | 
| os.chdir(dirname(DART_REPO_LOC)) | 
| -    self.clear_out_unversioned_files() | 
| -    if revision_num == '': | 
| -      self.run_cmd(['gclient', 'sync']) | 
| +    self.ClearOutUnversionedFiles() | 
| +    if not revision_num: | 
| +      self.RunCmd(['gclient', 'sync']) | 
| else: | 
| -      self.run_cmd(['gclient', 'sync', '-r', revision_num, '-t']) | 
| +      self.RunCmd(['gclient', 'sync', '-r', str(revision_num), '-t']) | 
|  | 
| shutil.copytree(os.path.join(TOP_LEVEL_DIR, 'internal'), | 
| os.path.join(DART_REPO_LOC, 'internal')) | 
| @@ -133,14 +132,24 @@ | 
| os.path.join(TOP_LEVEL_DIR, 'tools', 'testing', 'run_selenium.py'), | 
| os.path.join(DART_REPO_LOC, 'tools', 'testing', 'run_selenium.py')) | 
|  | 
| -    if revision_num == '': | 
| -      revision_num = search_for_revision() | 
| +  def SyncAndBuild(self, suites, revision_num=None): | 
| +    """Make sure we have the latest version of of the repo, and build it. We | 
| +    begin and end standing in DART_REPO_LOC. | 
|  | 
| +    Args: | 
| +      suites: The set of suites that we wish to build. | 
| + | 
| +    Returns: | 
| +      err_code = 1 if there was a problem building.""" | 
| +    self._Sync(revision_num) | 
| +    if not revision_num: | 
| +      revision_num = SearchForRevision() | 
| + | 
| self.current_revision_num = revision_num | 
| -    stderr = self.get_archive('sdk') | 
| -    if not os.path.exists(os.path.join( | 
| -        DART_REPO_LOC, 'tools', 'get_archive.py')) \ | 
| -        or 'InvalidUriError' in stderr: | 
| +    stdout, stderr = self.GetArchive('sdk') | 
| +    if (not os.path.exists(os.path.join( | 
| +        DART_REPO_LOC, 'tools', 'get_archive.py')) | 
| +        or 'InvalidUriError' in stderr or "Couldn't download" in stdout): | 
| # Couldn't find the SDK on Google Storage. Build it locally. | 
|  | 
| # On Windows, the output directory is marked as "Read Only," which causes | 
| @@ -156,7 +165,7 @@ | 
| shutil.rmtree(os.path.join(os.getcwd(), | 
| utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), | 
| onerror=on_rm_error) | 
| -      lines = self.run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', | 
| +      lines = self.RunCmd([os.path.join('.', 'tools', 'build.py'), '-m', | 
| 'release', '--arch=ia32', 'create_sdk']) | 
|  | 
| for line in lines: | 
| @@ -167,7 +176,7 @@ | 
| return 1 | 
| return 0 | 
|  | 
| -  def ensure_output_directory(self, dir_name): | 
| +  def EnsureOutputDirectory(self, dir_name): | 
| """Test that the listed directory name exists, and if not, create one for | 
| our output to be placed. | 
|  | 
| @@ -179,49 +188,95 @@ | 
| os.makedirs(dir_path) | 
| print 'Creating output directory ', dir_path | 
|  | 
| -  def has_interesting_code(self, past_revision_num=None): | 
| +  def HasInterestingCode(self, revision_num=None): | 
| """Tests if there are any versions of files that might change performance | 
| -    results on the server.""" | 
| +    results on the server. | 
| + | 
| +    Returns: | 
| +       (False, None): There is no interesting code to run. | 
| +       (True, revisionNumber): There is interesting code to run at revision | 
| +                              revisionNumber. | 
| +       (True, None): There is interesting code to run by syncing to the | 
| +                     tip-of-tree.""" | 
| if not os.path.exists(DART_REPO_LOC): | 
| -      return True | 
| +      self._Sync() | 
| os.chdir(DART_REPO_LOC) | 
| -    no_effect = ['client', 'compiler', 'editor', 'pkg', 'samples', 'tests', | 
| -                 'third_party', 'tools', 'utils'] | 
| -    # Pass 'p' in if we have a new certificate for the svn server, we want to | 
| -    # (p)ermanently accept it. | 
| -    if past_revision_num: | 
| +    no_effect = ['dart/client', 'dart/compiler', 'dart/editor', | 
| +                 'dart/lib/html/doc', 'dart/pkg', 'dart/tests', 'dart/samples', | 
| +                 'dart/lib/dartdoc', 'dart/lib/i18n', 'dart/lib/unittest', | 
| +                 'dart/tools/dartc', 'dart/tools/get_archive.py', | 
| +                 'dart/tools/test.py', 'dart/tools/testing', | 
| +                 'dart/tools/utils', 'dart/third_party', 'dart/utils'] | 
| +    definitely_yes = ['dart/samples/third_party/dromaeo', | 
| +                      'dart/lib/html/dart2js', 'dart/lib/html/dartium', | 
| +                      'dart/lib/scripts', 'dart/lib/src', | 
| +                      'dart/third_party/WebCore'] | 
| +    def GetFileList(revision): | 
| +      """Determine the set of files that were changed for a particular | 
| +      revision.""" | 
| # TODO(efortuna): This assumes you're using svn. Have a git fallback as | 
| -      # well. | 
| -      results, _ = self.run_cmd(['svn', 'log', '-v', '-r', | 
| -                                 str(past_revision_num)], std_in='p\r\n') | 
| +      # well. Pass 'p' in if we have a new certificate for the svn server, we | 
| +      # want to (p)ermanently accept it. | 
| +      results, _ = self.RunCmd([ | 
| +          'svn', 'log', 'http://dart.googlecode.com/svn/branches/bleeding_edge', | 
| +          '-v', '-r', str(revision)], std_in='p\r\n') | 
| results = results.split('\n') | 
| if len(results) <= 3: | 
| -        results = [] | 
| +        return [] | 
| else: | 
| # Trim off the details about revision number and commit message. We're | 
| # only interested in the files that are changed. | 
| results = results[3:] | 
| changed_files = [] | 
| for result in results: | 
| -          if result == '': | 
| +          if len(result) <= 1: | 
| break | 
| -          changed_files += [result.replace('/branches/bleeding_edge/dart/', '')] | 
| -        results = changed_files | 
| +          tokens = result.split() | 
| +          if len(tokens) > 1: | 
| +            changed_files += [tokens[1].replace('/branches/bleeding_edge/', '')] | 
| +        return changed_files | 
| + | 
| +    def HasPerfAffectingResults(files_list): | 
| +      """Determine if this set of changed files might effect performance | 
| +      tests.""" | 
| +      def IsSafeFile(f): | 
| +        if not any(f.startswith(prefix) for prefix in definitely_yes): | 
| +          return any(f.startswith(prefix) for prefix in no_effect) | 
| +        return False | 
| +      return not all(IsSafeFile(f) for f in files_list) | 
| + | 
| +    if revision_num: | 
| +      return (HasPerfAffectingResults(GetFileList( | 
| +          revision_num)), revision_num) | 
| else: | 
| -      results, _ = self.run_cmd(['svn', 'st', '-u'], std_in='p\r\n') | 
| -      results = results.split('\n') | 
| -    for line in results: | 
| -      tokens = line.split() | 
| -      if past_revision_num or len(tokens) >= 3 and '*' in tokens[-3]: | 
| -        # Loop through the changed files to see if it contains any files that | 
| -        # are NOT listed in the no_effect list (directories not listed in | 
| -        # the "no_effect" list are assumed to potentially affect performance. | 
| -        if not reduce(lambda x, y: x or y, | 
| -            [tokens[-1].startswith(item) for item in no_effect], False): | 
| -          return True | 
| -    return False | 
| +      results, _ = self.RunCmd(['svn', 'st', '-u'], std_in='p\r\n') | 
| +      latest_interesting_server_rev = int(results.split('\n')[-2].split()[-1]) | 
| +      if self.backfill: | 
| +        done_cls = list(UpdateSetOfDoneCls()) | 
| +        done_cls.sort() | 
| +        if done_cls: | 
| +          last_done_cl = int(done_cls[-1]) | 
| +        else: | 
| +          last_done_cl = EARLIEST_REVISION | 
| +        while latest_interesting_server_rev >= last_done_cl: | 
| +          file_list = GetFileList(latest_interesting_server_rev) | 
| +          if HasPerfAffectingResults(file_list): | 
| +            return (True, latest_interesting_server_rev) | 
| +          else: | 
| +            UpdateSetOfDoneCls(latest_interesting_server_rev) | 
| +          latest_interesting_server_rev -= 1 | 
| +      else: | 
| +        last_done_cl = int(SearchForRevision(DART_REPO_LOC)) + 1 | 
| +        while last_done_cl <= latest_interesting_server_rev: | 
| +          file_list = GetFileList(last_done_cl) | 
| +          if HasPerfAffectingResults(file_list): | 
| +            return (True, last_done_cl) | 
| +          else: | 
| +            UpdateSetOfDoneCls(last_done_cl) | 
| +          last_done_cl += 1 | 
| +      return (False, None) | 
|  | 
| -  def get_os_directory(self): | 
| +  def GetOsDirectory(self): | 
| """Specifies the name of the directory for the testing build of dart, which | 
| has yet a different naming convention from utils.getBuildRoot(...).""" | 
| if platform.system() == 'Windows': | 
| @@ -231,11 +286,11 @@ | 
| else: | 
| return 'linux' | 
|  | 
| -  def parse_args(self): | 
| +  def ParseArgs(self): | 
| parser = optparse.OptionParser() | 
| parser.add_option('--suites', '-s', dest='suites', help='Run the specified ' | 
| 'comma-separated test suites from set: %s' % \ | 
| -                      ','.join(TestBuilder.available_suite_names()), | 
| +                      ','.join(TestBuilder.AvailableSuiteNames()), | 
| action='store', default=None) | 
| parser.add_option('--forever', '-f', dest='continuous', help='Run this scri' | 
| 'pt forever, always checking for the next svn checkin', | 
| @@ -247,46 +302,55 @@ | 
| help='Do not post the results of the run.', default=False) | 
| parser.add_option('--notest', '-t', dest='no_test', action='store_true', | 
| help='Do not run the tests.', default=False) | 
| -    parser.add_option('--verbose', '-v', dest='verbose', help='Print extra ' | 
| -                      'debug output', action='store_true', default=False) | 
| +    parser.add_option('--verbose', '-v', dest='verbose', | 
| +                      help='Print extra debug output', action='store_true', | 
| +                      default=False) | 
| +    parser.add_option('--backfill', '-b', dest='backfill', | 
| +                      help='Backfill earlier CLs with additional results when ' | 
| +                      'there is idle time.', action='store_true', | 
| +                      default=False) | 
|  | 
| args, ignored = parser.parse_args() | 
|  | 
| if not args.suites: | 
| -      suites = TestBuilder.available_suite_names() | 
| +      suites = TestBuilder.AvailableSuiteNames() | 
| else: | 
| suites = [] | 
| suitelist = args.suites.split(',') | 
| for name in suitelist: | 
| -        if name in TestBuilder.available_suite_names(): | 
| +        if name in TestBuilder.AvailableSuiteNames(): | 
| suites.append(name) | 
| else: | 
| print ('Error: Invalid suite %s not in ' % name) + \ | 
| -              '%s' % ','.join(TestBuilder.available_suite_names()) | 
| +              '%s' % ','.join(TestBuilder.AvailableSuiteNames()) | 
| sys.exit(1) | 
| self.suite_names = suites | 
| self.no_build = args.no_build | 
| self.no_upload = args.no_upload | 
| self.no_test = args.no_test | 
| self.verbose = args.verbose | 
| +    self.backfill = args.backfill | 
| return args.continuous | 
|  | 
| -  def run_test_sequence(self, revision_num='', num_reruns=1): | 
| +  def RunTestSequence(self, revision_num=None, num_reruns=1): | 
| """Run the set of commands to (possibly) build, run, and post the results | 
| of our tests. Returns 0 on a successful run, 1 if we fail to post results or | 
| the run failed, -1 if the build is broken. | 
| """ | 
| suites = [] | 
| success = True | 
| -    if not self.no_build and self.sync_and_build(suites, revision_num) == 1: | 
| +    if not self.no_build and self.SyncAndBuild(suites, revision_num) == 1: | 
| return -1 # The build is broken. | 
|  | 
| +    if not self.current_revision_num: | 
| +      self.current_revision_num = SearchForRevision(DART_REPO_LOC) | 
| + | 
| for name in self.suite_names: | 
| for run in range(num_reruns): | 
| -        suites += [TestBuilder.make_test(name, self)] | 
| +        suites += [TestBuilder.MakeTest(name, self)] | 
|  | 
| for test in suites: | 
| -      success = success and test.run() | 
| +      success = success and test.Run() | 
| if success: | 
| return 0 | 
| else: | 
| @@ -343,7 +407,7 @@ | 
| self.revision_dict[platform][f][extra_metric] = [] | 
| self.values_dict[platform][f][extra_metric] = [] | 
|  | 
| -  def is_valid_combination(self, platform, variant): | 
| +  def IsValidCombination(self, platform, variant): | 
| """Check whether data should be captured for this platform/variant | 
| combination. | 
| """ | 
| @@ -360,20 +424,20 @@ | 
| return False | 
| return True | 
|  | 
| -  def run(self): | 
| +  def Run(self): | 
| """Run the benchmarks/tests from the command line and plot the | 
| results. | 
| """ | 
| for visitor in [self.tester, self.file_processor]: | 
| -      visitor.prepare() | 
| +      visitor.Prepare() | 
|  | 
| os.chdir(TOP_LEVEL_DIR) | 
| -    self.test_runner.ensure_output_directory(self.result_folder_name) | 
| -    self.test_runner.ensure_output_directory(os.path.join( | 
| +    self.test_runner.EnsureOutputDirectory(self.result_folder_name) | 
| +    self.test_runner.EnsureOutputDirectory(os.path.join( | 
| 'old', self.result_folder_name)) | 
| os.chdir(DART_REPO_LOC) | 
| if not self.test_runner.no_test: | 
| -      self.tester.run_tests() | 
| +      self.tester.RunTests() | 
|  | 
| os.chdir(os.path.join(TOP_LEVEL_DIR, 'tools', 'testing', 'perf_testing')) | 
|  | 
| @@ -381,7 +445,7 @@ | 
| post_success = True | 
| for afile in files: | 
| if not afile.startswith('.'): | 
| -        should_move_file = self.file_processor.process_file(afile, True) | 
| +        should_move_file = self.file_processor.ProcessFile(afile, True) | 
| if should_move_file: | 
| shutil.move(os.path.join(self.result_folder_name, afile), | 
| os.path.join('old', self.result_folder_name, afile)) | 
| @@ -394,16 +458,16 @@ | 
| class Tester(object): | 
| """The base level visitor class that runs tests. It contains convenience | 
| methods that many Tester objects use. Any class that would like to be a | 
| -  TesterVisitor must implement the run_tests() method.""" | 
| +  TesterVisitor must implement the RunTests() method.""" | 
|  | 
| def __init__(self, test): | 
| self.test = test | 
|  | 
| -  def prepare(self): | 
| +  def Prepare(self): | 
| """Perform any initial setup required before the test is run.""" | 
| pass | 
|  | 
| -  def add_svn_revision_to_trace(self, outfile, browser = None): | 
| +  def AddSvnRevisionToTrace(self, outfile, browser = None): | 
| """Add the svn version number to the provided tracefile.""" | 
| def get_dartium_revision(): | 
| version_file_name = os.path.join(DART_REPO_LOC, 'client', 'tests', | 
| @@ -415,16 +479,16 @@ | 
|  | 
| if browser and browser == 'dartium': | 
| revision = get_dartium_revision() | 
| -      self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) | 
| +      self.test.test_runner.RunCmd(['echo', 'Revision: ' + revision], outfile) | 
| else: | 
| -      revision = search_for_revision() | 
| -      self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) | 
| +      revision = SearchForRevision() | 
| +      self.test.test_runner.RunCmd(['echo', 'Revision: ' + revision], outfile) | 
|  | 
|  | 
| class Processor(object): | 
| """The base level vistor class that processes tests. It contains convenience | 
| methods that many File Processor objects use. Any class that would like to be | 
| -  a ProcessorVisitor must implement the process_file() method.""" | 
| +  a ProcessorVisitor must implement the ProcessFile() method.""" | 
|  | 
| SCORE = 'Score' | 
| COMPILE_TIME = 'CompileTime' | 
| @@ -433,11 +497,11 @@ | 
| def __init__(self, test): | 
| self.test = test | 
|  | 
| -  def prepare(self): | 
| +  def Prepare(self): | 
| """Perform any initial setup required before the test is run.""" | 
| pass | 
|  | 
| -  def open_trace_file(self, afile, not_yet_uploaded): | 
| +  def OpenTraceFile(self, afile, not_yet_uploaded): | 
| """Find the correct location for the trace file, and open it. | 
| Args: | 
| afile: The tracefile name. | 
| @@ -449,7 +513,7 @@ | 
| file_path = os.path.join('old', file_path) | 
| return open(file_path) | 
|  | 
| -  def report_results(self, benchmark_name, score, platform, variant, | 
| +  def ReportResults(self, benchmark_name, score, platform, variant, | 
| revision_number, metric): | 
| """Store the results of the benchmark run. | 
| Args: | 
| @@ -465,21 +529,24 @@ | 
| return post_results.report_results(benchmark_name, score, platform, variant, | 
| revision_number, metric) | 
|  | 
| -  def calculate_geometric_mean(self, platform, variant, svn_revision): | 
| +  def CalculateGeometricMean(self, platform, variant, svn_revision): | 
| """Calculate the aggregate geometric mean for JS and dart2js benchmark sets, | 
| given two benchmark dictionaries.""" | 
| geo_mean = 0 | 
| -    if self.test.is_valid_combination(platform, variant): | 
| +    if self.test.IsValidCombination(platform, variant): | 
| for benchmark in self.test.values_list: | 
| +        if not self.test.values_dict[platform][variant][benchmark]: | 
| +          print 'Error determining mean for %s %s %s' % (platform, variant, | 
| +                                                         benchmark) | 
| +          continue | 
| geo_mean += math.log( | 
| -            self.test.values_dict[platform][variant][benchmark][ | 
| -                len(self.test.values_dict[platform][variant][benchmark]) - 1]) | 
| +            self.test.values_dict[platform][variant][benchmark][-1]) | 
|  | 
| self.test.values_dict[platform][variant]['Geo-Mean'] += \ | 
| [math.pow(math.e, geo_mean / len(self.test.values_list))] | 
| self.test.revision_dict[platform][variant]['Geo-Mean'] += [svn_revision] | 
|  | 
| -  def get_score_type(self, benchmark_name): | 
| +  def GetScoreType(self, benchmark_name): | 
| """Determine the type of score for posting -- default is 'Score' (aka | 
| Runtime), other options are CompileTime and CodeSize.""" | 
| return self.SCORE | 
| @@ -518,8 +585,8 @@ | 
|  | 
| class BrowserTester(Tester): | 
| @staticmethod | 
| -  def get_browsers(add_dartium=True): | 
| -    browsers = []#['ff', 'chrome'] | 
| +  def GetBrowsers(add_dartium=True): | 
| +    browsers = ['ff', 'chrome'] | 
| if add_dartium: | 
| browsers += ['dartium'] | 
| has_shell = False | 
| @@ -539,43 +606,43 @@ | 
| """Args: | 
| test_runner: Reference to the object that notifies us when to run.""" | 
| super(CommonBrowserTest, self).__init__( | 
| -        self.name(), BrowserTester.get_browsers(False), | 
| +        self.Name(), BrowserTester.GetBrowsers(False), | 
| 'browser', ['js', 'dart2js'], | 
| -        self.get_standalone_benchmarks(), test_runner, | 
| +        self.GetStandaloneBenchmarks(), test_runner, | 
| self.CommonBrowserTester(self), | 
| self.CommonBrowserFileProcessor(self)) | 
|  | 
| @staticmethod | 
| -  def name(): | 
| +  def Name(): | 
| return 'browser-perf' | 
|  | 
| @staticmethod | 
| -  def get_standalone_benchmarks(): | 
| +  def GetStandaloneBenchmarks(): | 
| return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', | 
| 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', | 
| 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', | 
| 'TreeSort'] | 
|  | 
| class CommonBrowserTester(BrowserTester): | 
| -    def run_tests(self): | 
| +    def RunTests(self): | 
| """Run a performance test in the browser.""" | 
| os.chdir(DART_REPO_LOC) | 
| -      self.test.test_runner.run_cmd([ | 
| +      self.test.test_runner.RunCmd([ | 
| 'python', os.path.join('internal', 'browserBenchmarks', | 
| 'make_web_benchmarks.py')]) | 
|  | 
| for browser in self.test.platform_list: | 
| for version in self.test.versions: | 
| -          if not self.test.is_valid_combination(browser, version): | 
| +          if not self.test.IsValidCombination(browser, version): | 
| continue | 
| self.test.trace_file = os.path.join(TOP_LEVEL_DIR, | 
| 'tools', 'testing', 'perf_testing', self.test.result_folder_name, | 
| 'perf-%s-%s-%s' % (self.test.cur_time, browser, version)) | 
| -          self.add_svn_revision_to_trace(self.test.trace_file, browser) | 
| +          self.AddSvnRevisionToTrace(self.test.trace_file, browser) | 
| file_path = os.path.join( | 
| os.getcwd(), 'internal', 'browserBenchmarks', | 
| 'benchmark_page_%s.html' % version) | 
| -          self.test.test_runner.run_cmd( | 
| +          self.test.test_runner.RunCmd( | 
| ['python', os.path.join('tools', 'testing', 'run_selenium.py'), | 
| '--out', file_path, '--browser', browser, | 
| '--timeout', '600', '--mode', 'perf'], self.test.trace_file, | 
| @@ -583,7 +650,7 @@ | 
|  | 
| class CommonBrowserFileProcessor(Processor): | 
|  | 
| -    def process_file(self, afile, should_post_file): | 
| +    def ProcessFile(self, afile, should_post_file): | 
| """Comb through the html to find the performance results. | 
| Returns: True if we successfully posted our data to storage and/or we can | 
| delete the trace file.""" | 
| @@ -592,7 +659,7 @@ | 
| parts = afile.split('-') | 
| browser = parts[2] | 
| version = parts[3] | 
| -      f = self.open_trace_file(afile, should_post_file) | 
| +      f = self.OpenTraceFile(afile, should_post_file) | 
| lines = f.readlines() | 
| line = '' | 
| i = 0 | 
| @@ -629,14 +696,14 @@ | 
| bench_dict[name] += [float(score)] | 
| self.test.revision_dict[browser][version][name] += [revision_num] | 
| if not self.test.test_runner.no_upload and should_post_file: | 
| -          upload_success = upload_success and self.report_results( | 
| +          upload_success = upload_success and self.ReportResults( | 
| name, score, browser, version, revision_num, | 
| -              self.get_score_type(name)) | 
| +              self.GetScoreType(name)) | 
| else: | 
| upload_success = False | 
|  | 
| f.close() | 
| -      self.calculate_geometric_mean(browser, version, revision_num) | 
| +      self.CalculateGeometricMean(browser, version, revision_num) | 
| return upload_success | 
|  | 
|  | 
| @@ -674,7 +741,7 @@ | 
|  | 
| # Use filenames that don't have unusual characters for benchmark names. | 
| @staticmethod | 
| -  def legalize_filename(str): | 
| +  def LegalizeFilename(str): | 
| remap = { | 
| ' ': '_', | 
| '(': '_', | 
| @@ -690,23 +757,23 @@ | 
| # failure properly.  The modify suite fails on 32-bit chrome, which | 
| # is the default on mac and win. | 
| @staticmethod | 
| -  def get_valid_dromaeo_tags(): | 
| +  def GetValidDromaeoTags(): | 
| tags = [tag for (tag, _) in DromaeoTester.DROMAEO_BENCHMARKS.values()] | 
| if platform.system() == 'Darwin' or platform.system() == 'Windows': | 
| tags.remove('modify') | 
| return tags | 
|  | 
| @staticmethod | 
| -  def get_dromaeo_benchmarks(): | 
| -    valid = DromaeoTester.get_valid_dromaeo_tags() | 
| +  def GetDromaeoBenchmarks(): | 
| +    valid = DromaeoTester.GetValidDromaeoTags() | 
| benchmarks = reduce(lambda l1,l2: l1+l2, | 
| [tests for (tag, tests) in | 
| DromaeoTester.DROMAEO_BENCHMARKS.values() | 
| if tag in valid]) | 
| -    return map(DromaeoTester.legalize_filename, benchmarks) | 
| +    return map(DromaeoTester.LegalizeFilename, benchmarks) | 
|  | 
| @staticmethod | 
| -  def get_dromaeo_versions(): | 
| +  def GetDromaeoVersions(): | 
| return ['js', 'dart2js_html'] | 
|  | 
|  | 
| @@ -714,20 +781,20 @@ | 
| """Runs Dromaeo tests, in the browser.""" | 
| def __init__(self, test_runner): | 
| super(DromaeoTest, self).__init__( | 
| -        self.name(), | 
| -        BrowserTester.get_browsers(True), | 
| +        self.Name(), | 
| +        BrowserTester.GetBrowsers(True), | 
| 'browser', | 
| -        DromaeoTester.get_dromaeo_versions(), | 
| -        DromaeoTester.get_dromaeo_benchmarks(), test_runner, | 
| +        DromaeoTester.GetDromaeoVersions(), | 
| +        DromaeoTester.GetDromaeoBenchmarks(), test_runner, | 
| self.DromaeoPerfTester(self), | 
| self.DromaeoFileProcessor(self)) | 
|  | 
| @staticmethod | 
| -  def name(): | 
| +  def Name(): | 
| return 'dromaeo' | 
|  | 
| class DromaeoPerfTester(DromaeoTester): | 
| -    def move_chrome_driver_if_needed(self, browser): | 
| +    def MoveChromeDriverIfNeeded(self, browser): | 
| """Move the appropriate version of ChromeDriver onto the path. | 
| TODO(efortuna): This is a total hack because the latest version of Chrome | 
| (Dartium builds) requires a different version of ChromeDriver, that is | 
| @@ -737,7 +804,7 @@ | 
| in the default location (inside depot_tools). | 
| """ | 
| current_dir = os.getcwd() | 
| -      self.test.test_runner.get_archive('chromedriver') | 
| +      self.test.test_runner.GetArchive('chromedriver') | 
| path = os.environ['PATH'].split(os.pathsep) | 
| orig_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', 'testing', | 
| 'orig-chromedriver') | 
| @@ -748,7 +815,7 @@ | 
| if platform.system() == 'Windows': | 
| extension = '.exe' | 
|  | 
| -      def move_chromedriver(depot_tools, copy_to_depot_tools_dir=True, | 
| +      def MoveChromedriver(depot_tools, copy_to_depot_tools_dir=True, | 
| from_path=None): | 
| if from_path: | 
| from_dir = from_path + extension | 
| @@ -770,75 +837,78 @@ | 
| if 'depot_tools' in loc: | 
| if browser == 'chrome': | 
| if os.path.exists(orig_chromedriver_path): | 
| -              move_chromedriver(loc) | 
| +              MoveChromedriver(loc) | 
| elif browser == 'dartium': | 
| -            if self.test.test_runner.current_revision_num < FIRST_CHROMEDRIVER: | 
| +            if (int(self.test.test_runner.current_revision_num) < | 
| +                FIRST_CHROMEDRIVER): | 
| # If we don't have a stashed a different chromedriver just use | 
| # the regular chromedriver. | 
| -              self.test.test_runner.run_cmd(os.path.join( | 
| +              self.test.test_runner.RunCmd(os.path.join( | 
| TOP_LEVEL_DIR, 'tools', 'testing', 'webdriver_test_setup.py'), | 
| '-f', '-s', '-p') | 
| elif not os.path.exists(dartium_chromedriver_path): | 
| -              self.test.test_runner.get_archive('chromedriver') | 
| +              stdout, _ = self.test.test_runner.GetArchive('chromedriver') | 
| # Move original chromedriver for storage. | 
| if not os.path.exists(orig_chromedriver_path): | 
| -              move_chromedriver(loc, copy_to_depot_tools_dir=False) | 
| +              MoveChromedriver(loc, copy_to_depot_tools_dir=False) | 
| if self.test.test_runner.current_revision_num >= FIRST_CHROMEDRIVER: | 
| # Copy Dartium chromedriver into depot_tools | 
| -              move_chromedriver(loc, from_path=os.path.join( | 
| +              MoveChromedriver(loc, from_path=os.path.join( | 
| dartium_chromedriver_path, 'chromedriver')) | 
| os.chdir(current_dir) | 
|  | 
| -    def run_tests(self): | 
| +    def RunTests(self): | 
| """Run dromaeo in the browser.""" | 
|  | 
| -      self.test.test_runner.get_archive('dartium') | 
| +      self.test.test_runner.GetArchive('dartium') | 
|  | 
| # Build tests. | 
| dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | 
| current_path = os.getcwd() | 
| os.chdir(dromaeo_path) | 
| -      self.test.test_runner.run_cmd(['python', 'generate_dart2js_tests.py']) | 
| +      if os.path.exists('generate_dart2js_tests.py'): | 
| +        stdout, _ = self.test.test_runner.RunCmd( | 
| +            ['python', 'generate_dart2js_tests.py']) | 
| +      else: | 
| +        stdout, _ = self.test.test_runner.RunCmd( | 
| +            ['python', 'generate_frog_tests.py']) | 
| os.chdir(current_path) | 
| +      if 'Error: Compilation failed' in stdout: | 
| +        return | 
| +      versions = DromaeoTester.GetDromaeoVersions() | 
|  | 
| -      versions = DromaeoTester.get_dromaeo_versions() | 
| - | 
| -      for browser in BrowserTester.get_browsers(): | 
| -        self.move_chrome_driver_if_needed(browser) | 
| +      for browser in BrowserTester.GetBrowsers(): | 
| +        self.MoveChromeDriverIfNeeded(browser) | 
| for version_name in versions: | 
| -          if not self.test.is_valid_combination(browser, version_name): | 
| +          if not self.test.IsValidCombination(browser, version_name): | 
| continue | 
| -          version = DromaeoTest.DromaeoPerfTester.get_dromaeo_url_query( | 
| +          version = DromaeoTest.DromaeoPerfTester.GetDromaeoUrlQuery( | 
| browser, version_name) | 
| self.test.trace_file = os.path.join(TOP_LEVEL_DIR, | 
| 'tools', 'testing', 'perf_testing', self.test.result_folder_name, | 
| 'dromaeo-%s-%s-%s' % (self.test.cur_time, browser, version_name)) | 
| -          self.add_svn_revision_to_trace(self.test.trace_file, browser) | 
| +          self.AddSvnRevisionToTrace(self.test.trace_file, browser) | 
| file_path = '"%s"' % os.path.join(os.getcwd(), dromaeo_path, | 
| 'index-js.html?%s' % version) | 
| -          if platform.system() == 'Windows': | 
| -            file_path = file_path.replace('&', '^&') | 
| -            file_path = file_path.replace('?', '^?') | 
| -            file_path = file_path.replace('|', '^|') | 
| -          self.test.test_runner.run_cmd( | 
| +          self.test.test_runner.RunCmd( | 
| ['python', os.path.join('tools', 'testing', 'run_selenium.py'), | 
| '--out', file_path, '--browser', browser, | 
| '--timeout', '900', '--mode', 'dromaeo'], self.test.trace_file, | 
| append=True) | 
| # Put default Chromedriver back in. | 
| -      self.move_chrome_driver_if_needed('chrome') | 
| +      self.MoveChromeDriverIfNeeded('chrome') | 
|  | 
| @staticmethod | 
| -    def get_dromaeo_url_query(browser, version): | 
| +    def GetDromaeoUrlQuery(browser, version): | 
| if browser == 'dartium': | 
| version = version.replace('frog', 'dart') | 
| -      version = version.replace('_','&') | 
| -      tags = DromaeoTester.get_valid_dromaeo_tags() | 
| -      return '|'.join([ '%s&%s' % (version, tag) for tag in tags]) | 
| +      version = version.replace('_','AND') | 
| +      tags = DromaeoTester.GetValidDromaeoTags() | 
| +      return 'OR'.join([ '%sAND%s' % (version, tag) for tag in tags]) | 
|  | 
|  | 
| class DromaeoFileProcessor(Processor): | 
| -    def process_file(self, afile, should_post_file): | 
| +    def ProcessFile(self, afile, should_post_file): | 
| """Comb through the html to find the performance results. | 
| Returns: True if we successfully posted our data to storage.""" | 
| parts = afile.split('-') | 
| @@ -847,7 +917,7 @@ | 
|  | 
| bench_dict = self.test.values_dict[browser][version] | 
|  | 
| -      f = self.open_trace_file(afile, should_post_file) | 
| +      f = self.OpenTraceFile(afile, should_post_file) | 
| lines = f.readlines() | 
| i = 0 | 
| revision_num = 0 | 
| @@ -869,39 +939,40 @@ | 
| if results: | 
| for result in results: | 
| r = re.match(result_pattern, result) | 
| -                name = DromaeoTester.legalize_filename(r.group(1).strip(':')) | 
| +                name = DromaeoTester.LegalizeFilename(r.group(1).strip(':')) | 
| score = float(r.group(2)) | 
| bench_dict[name] += [float(score)] | 
| self.test.revision_dict[browser][version][name] += \ | 
| [revision_num] | 
| if not self.test.test_runner.no_upload and should_post_file: | 
| -                  upload_success = upload_success and self.report_results( | 
| +                  upload_success = upload_success and self.ReportResults( | 
| name, score, browser, version, revision_num, | 
| -                      self.get_score_type(name)) | 
| +                      self.GetScoreType(name)) | 
| else: | 
| upload_success = False | 
|  | 
| f.close() | 
| -      self.calculate_geometric_mean(browser, version, revision_num) | 
| +      self.CalculateGeometricMean(browser, version, revision_num) | 
| return upload_success | 
|  | 
| class TestBuilder(object): | 
| """Construct the desired test object.""" | 
| -  available_suites = dict((suite.name(), suite) for suite in [ | 
| +  available_suites = dict((suite.Name(), suite) for suite in [ | 
| CommonBrowserTest, DromaeoTest]) | 
|  | 
| @staticmethod | 
| -  def make_test(test_name, test_runner): | 
| +  def MakeTest(test_name, test_runner): | 
| return TestBuilder.available_suites[test_name](test_runner) | 
|  | 
| @staticmethod | 
| -  def available_suite_names(): | 
| +  def AvailableSuiteNames(): | 
| return TestBuilder.available_suites.keys() | 
|  | 
| -def search_for_revision(directory = None): | 
| + | 
| +def SearchForRevision(directory = None): | 
| """Find the current revision number in the desired directory. If directory is | 
| None, find the revision number in the current directory.""" | 
| -  def find_revision(svn_info_command): | 
| +  def FindRevision(svn_info_command): | 
| p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | 
| stderr = subprocess.STDOUT, | 
| shell = (platform.system() == 'Windows')) | 
| @@ -915,13 +986,14 @@ | 
| if not directory: | 
| directory = cwd | 
| os.chdir(directory) | 
| -  revision_num = int(find_revision(['svn', 'info'])) | 
| +  revision_num = int(FindRevision(['svn', 'info'])) | 
| if revision_num == -1: | 
| -    revision_num = int(find_revision(['git', 'svn', 'info'])) | 
| +    revision_num = int(FindRevision(['git', 'svn', 'info'])) | 
| os.chdir(cwd) | 
| return str(revision_num) | 
|  | 
| -def update_set_of_done_cls(revision_num=None): | 
| + | 
| +def UpdateSetOfDoneCls(revision_num=None): | 
| """Update the set of CLs that do not need additional performance runs. | 
| Args: | 
| revision_num: an additional number to be added to the 'done set' | 
| @@ -941,40 +1013,41 @@ | 
| f.close() | 
| return result_set | 
|  | 
| -def fill_in_back_history(results_set, runner): | 
| + | 
| +def FillInBackHistory(results_set, runner): | 
| """Fill in back history performance data. This is done one of two ways, with | 
| equal probability of trying each way (falling back on the sequential version | 
| as our data becomes more densely populated).""" | 
| has_run_extra = False | 
| -  revision_num = int(search_for_revision(DART_REPO_LOC)) | 
| +  revision_num = int(SearchForRevision(DART_REPO_LOC)) | 
|  | 
| -  def try_to_run_additional(revision_number): | 
| +  def TryToRunAdditional(revision_number): | 
| """Determine the number of results we have stored for a particular revision | 
| number, and if it is less than 10, run some extra tests. | 
| Args: | 
| - revision_number: the revision whose performance we want to potentially | 
| test. | 
| Returns: True if we successfully ran some additional tests.""" | 
| -    if not runner.has_interesting_code(revision_number): | 
| -      results_set = update_set_of_done_cls(revision_number) | 
| +    if not runner.HasInterestingCode(revision_number)[0]: | 
| +      results_set = UpdateSetOfDoneCls(revision_number) | 
| return False | 
| -    a_test = TestBuilder.make_test(runner.suite_names[0], runner) | 
| +    a_test = TestBuilder.MakeTest(runner.suite_names[0], runner) | 
| benchmark_name = a_test.values_list[0] | 
| platform_name = a_test.platform_list[0] | 
| variant = a_test.values_dict[platform_name].keys()[0] | 
| num_results = post_results.get_num_results(benchmark_name, | 
| platform_name, variant, revision_number, | 
| -        a_test.file_processor.get_score_type(benchmark_name)) | 
| +        a_test.file_processor.GetScoreType(benchmark_name)) | 
| if num_results < 10: | 
| # Run at most two more times. | 
| if num_results > 8: | 
| reruns = 10 - num_results | 
| else: | 
| reruns = 2 | 
| -      run = runner.run_test_sequence(revision_num=str(revision_number), | 
| +      run = runner.RunTestSequence(revision_num=str(revision_number), | 
| num_reruns=reruns) | 
| if num_results >= 10 or run == 0 and num_results + reruns >= 10: | 
| -      results_set = update_set_of_done_cls(revision_number) | 
| +      results_set = UpdateSetOfDoneCls(revision_number) | 
| else: | 
| return False | 
| return True | 
| @@ -993,8 +1066,8 @@ | 
| weighted_total = sum(thousands_list) | 
| generated_random_number = random.randint(0, weighted_total - 1) | 
| for i in list(reversed(thousands_list)): | 
| -      thousands = thousands_list[i - 1] | 
| -      weighted_total -= thousands_list[i - 1] | 
| +      thousands = i | 
| +      weighted_total -= i | 
| if weighted_total <= generated_random_number: | 
| break | 
| while tries < 15 and not has_run_extra: | 
| @@ -1005,7 +1078,7 @@ | 
| max_range = bucket_size | 
| rev = thousands * bucket_size + random.randrange(0, max_range) | 
| if rev not in results_set: | 
| -        has_run_extra = try_to_run_additional(rev) | 
| +        has_run_extra = TryToRunAdditional(rev) | 
| tries += 1 | 
|  | 
| if not has_run_extra: | 
| @@ -1015,16 +1088,17 @@ | 
| # checked in. | 
| while revision_num > EARLIEST_REVISION and not has_run_extra: | 
| if revision_num not in results_set: | 
| -        has_run_extra = try_to_run_additional(revision_num) | 
| +        has_run_extra = TryToRunAdditional(revision_num) | 
| revision_num -= 1 | 
| if not has_run_extra: | 
| # No more extra back-runs to do (for now). Wait for new code. | 
| time.sleep(200) | 
| return results_set | 
|  | 
| + | 
| def main(): | 
| runner = TestRunner() | 
| -  continuous = runner.parse_args() | 
| +  continuous = runner.ParseArgs() | 
|  | 
| if not os.path.exists(DART_REPO_LOC): | 
| os.mkdir(dirname(DART_REPO_LOC)) | 
| @@ -1036,13 +1110,17 @@ | 
| p.communicate() | 
| if continuous: | 
| while True: | 
| -      results_set = update_set_of_done_cls() | 
| -      if runner.has_interesting_code(): | 
| -        runner.run_test_sequence() | 
| +      results_set = UpdateSetOfDoneCls() | 
| +      (is_interesting, interesting_rev_num) = runner.HasInterestingCode() | 
| +      if is_interesting: | 
| +        runner.RunTestSequence(interesting_rev_num) | 
| else: | 
| -        results_set = fill_in_back_history(results_set, runner) | 
| +        if runner.backfill: | 
| +          results_set = FillInBackHistory(results_set, runner) | 
| +        else: | 
| +          time.sleep(200) | 
| else: | 
| -    runner.run_test_sequence() | 
| +    runner.RunTestSequence() | 
|  | 
| if __name__ == '__main__': | 
| main() | 
|  |