Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 4 # for details. All rights reserved. Use of this source code is governed by a | 4 # for details. All rights reserved. Use of this source code is governed by a |
| 5 # BSD-style license that can be found in the LICENSE file. | 5 # BSD-style license that can be found in the LICENSE file. |
| 6 | 6 |
| 7 import datetime | 7 import datetime |
| 8 import getpass | 8 import getpass |
| 9 import math | 9 import math |
| 10 try: | 10 try: |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 import time | 24 import time |
| 25 import traceback | 25 import traceback |
| 26 | 26 |
| 27 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) | 27 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) |
| 28 sys.path.append(TOOLS_PATH) | 28 sys.path.append(TOOLS_PATH) |
| 29 import utils | 29 import utils |
| 30 | 30 |
| 31 """This script runs to track performance and size progress of | 31 """This script runs to track performance and size progress of |
| 32 different svn revisions. It tests to see if there a newer version of the code on | 32 different svn revisions. It tests to see if there a newer version of the code on |
| 33 the server, and will sync and run the performance tests if so.""" | 33 the server, and will sync and run the performance tests if so.""" |
| 34 | |
| 35 DART_INSTALL_LOCATION = abspath(os.path.join(dirname(abspath(__file__)), | |
| 36 '..', '..', '..')) | |
| 37 _suffix = '' | |
| 38 if platform.system() == 'Windows': | |
| 39 _suffix = '.exe' | |
| 40 DART_VM = os.path.join(DART_INSTALL_LOCATION, | |
| 41 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32'), | |
| 42 'dart-sdk', | |
| 43 'bin', | |
| 44 'dart' + _suffix) | |
| 45 DART_COMPILER = os.path.join(DART_INSTALL_LOCATION, | |
| 46 utils.GetBuildRoot(utils.GuessOS(), | |
| 47 'release', 'ia32'), | |
| 48 'dart-sdk', | |
| 49 'bin', | |
| 50 'frogc') | |
| 51 | |
| 52 GEO_MEAN = 'Geo-Mean' | |
| 53 COMMAND_LINE = 'commandline' | |
| 54 JS = 'js' | |
| 55 FROG = 'frog' | |
| 56 JS_AND_FROG = [JS, FROG] | |
| 57 COLORS = ['blue', 'green', 'red', 'cyan', 'magenta', 'black'] | |
| 58 GRAPH_OUT_DIR = 'graphs' | |
| 59 | |
| 60 BROWSER_PERF = 'browser-perf' | |
| 61 TIME_SIZE = 'time-size' | |
| 62 CL_PERF = 'cl-perf' | |
| 63 # TODO(vsm): Merge these? | |
| 64 DROMAEO = 'dromaeo' | |
| 65 DROMAEO_SIZE = 'dromaeo-size' | |
| 66 | |
| 67 SLEEP_TIME = 200 | |
| 68 VERBOSE = False | |
| 69 HAS_SHELL = False | |
| 70 if platform.system() == 'Windows': | |
| 71 # On Windows, shell must be true to get the correct environment variables. | |
| 72 HAS_SHELL = True | |
| 73 | |
| 74 """First, some utility methods.""" | |
| 75 | |
| 76 def run_cmd(cmd_list, outfile=None, append=False, std_in=''): | |
| 77 """Run the specified command and print out any output to stdout. | |
| 78 | |
| 79 Args: | |
| 80 cmd_list: a list of strings that make up the command to run | |
| 81 outfile: a string indicating the name of the file that we should write | |
| 82 stdout to | |
| 83 append: True if we want to append to the file instead of overwriting it""" | |
| 84 if VERBOSE: | |
| 85 print ' '.join(cmd_list) | |
| 86 out = subprocess.PIPE | |
| 87 if outfile: | |
| 88 mode = 'w' | |
| 89 if append: | |
| 90 mode = 'a' | |
| 91 out = open(outfile, mode) | |
| 92 if append: | |
| 93 # Annoying Windows "feature" -- append doesn't actually append unless you | |
| 94 # explicitly go to the end of the file. | |
| 95 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html | |
| 96 out.seek(0, os.SEEK_END) | |
| 97 p = subprocess.Popen(cmd_list, stdout = out, stderr=subprocess.PIPE, | |
| 98 stdin=subprocess.PIPE, shell=HAS_SHELL) | |
| 99 output, not_used = p.communicate(std_in); | |
| 100 if output: | |
| 101 print output | |
| 102 return output | |
| 103 | |
| 104 def time_cmd(cmd): | |
| 105 """Determine the amount of (real) time it takes to execute a given command.""" | |
| 106 start = time.time() | |
| 107 run_cmd(cmd) | |
| 108 return time.time() - start | |
| 109 | |
| 110 def sync_and_build(): | |
| 111 """Make sure we have the latest version of of the repo, and build it. We | |
| 112 begin and end standing in DART_INSTALL_LOCATION. | |
| 113 | |
| 114 Returns: | |
| 115 err_code = 1 if there was a problem building.""" | |
| 116 os.chdir(DART_INSTALL_LOCATION) | |
| 117 #Revert our newly built minfrog to prevent conflicts when we update | |
| 118 run_cmd(['svn', 'revert', os.path.join(os.getcwd(), 'frog', 'minfrog')]) | |
| 119 | |
| 120 run_cmd(['gclient', 'sync']) | |
| 121 | |
| 122 # On Windows, the output directory is marked as "Read Only," which causes an | |
| 123 # error to be thrown when we use shutil.rmtree. This helper function changes | |
| 124 # the permissions so we can still delete the directory. | |
| 125 def on_rm_error(func, path, exc_info): | |
| 126 if os.path.exists(path): | |
| 127 os.chmod(path, stat.S_IWRITE) | |
| 128 os.unlink(path) | |
| 129 # TODO(efortuna): building the sdk locally is a band-aid until all build | |
| 130 # platform SDKs are hosted in Google storage. Pull from https://sandbox. | |
| 131 # google.com/storage/?arg=dart-dump-render-tree#dart-dump-render-tree%2Fsdk | |
| 132 # eventually. | |
| 133 # TODO(efortuna): Currently always building ia32 architecture because we don't | |
| 134 # have test statistics for what's passing on x64. Eliminate arch specification | |
| 135 # when we have tests running on x64, too. | |
| 136 shutil.rmtree(os.path.join(os.getcwd(), | |
| 137 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), | |
| 138 onerror=on_rm_error) | |
| 139 lines = run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', 'release', | |
| 140 '--arch=ia32', 'create_sdk']) | |
| 141 lines = run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', 'release', | |
| 142 '--arch=ia32', 'dart2js']) #Built only for the v8 target for CL tests. | |
| 143 | |
| 144 for line in lines: | |
| 145 if 'BUILD FAILED' in lines: | |
| 146 # Someone checked in a broken build! Just stop trying to make it work | |
| 147 # and wait to try again. | |
| 148 print 'Broken Build' | |
| 149 return 1 | |
| 150 return 0 | |
| 151 | |
| 152 def ensure_output_directory(dir_name): | |
| 153 """Test that the listed directory name exists, and if not, create one for | |
| 154 our output to be placed. | |
| 155 | |
| 156 Args: | |
| 157 dir_name: the directory we will create if it does not exist.""" | |
| 158 dir_path = os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | |
| 159 'perf_testing', dir_name) | |
| 160 if not os.path.exists(dir_path): | |
| 161 os.mkdir(dir_path) | |
| 162 print 'Creating output directory ', dir_path | |
| 163 | |
| 164 def has_new_code(): | |
| 165 """Tests if there are any newer versions of files on the server.""" | |
| 166 os.chdir(DART_INSTALL_LOCATION) | |
| 167 # Pass 'p' in if we have a new certificate for the svn server, we want to | |
| 168 # (p)ermanently accept it. | |
| 169 results = run_cmd(['svn', 'st', '-u'], std_in='p') | |
| 170 for line in results: | |
| 171 if '*' in line: | |
| 172 return True | |
| 173 return False | |
| 174 | |
| 175 # TODO(vsm): Add Dartium. | |
| 176 def get_browsers(): | |
| 177 browsers = ['ff', 'chrome'] | |
| 178 if platform.system() == 'Darwin': | |
| 179 browsers += ['safari'] | |
| 180 if platform.system() == 'Windows': | |
| 181 browsers += ['ie'] | |
| 182 return browsers | |
| 183 | |
| 184 # TODO(vsm): Factor benchmark specific code to a better location. | |
| 185 def get_standalone_benchmarks(): | |
| 186 return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', | |
| 187 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', | |
| 188 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', | |
| 189 'TreeSort'] | |
| 190 | |
| 191 def get_os_directory(): | |
| 192 """Specifies the name of the directory for the testing build of dart, which | |
| 193 has yet a different naming convention from utils.getBuildRoot(...).""" | |
| 194 if platform.system() == 'Windows': | |
| 195 return 'windows' | |
| 196 elif platform.system() == 'Darwin': | |
| 197 return 'macos' | |
| 198 else: | |
| 199 return 'linux' | |
| 200 | |
| 201 def upload_to_app_engine(suite_names): | |
| 202 """Upload our results to our appengine server. | |
| 203 Arguments: | |
| 204 suite_names: Directories to upload data from (should match suite names) | |
| 205 """ | |
| 206 # TODO(efortuna): This is the most basic way to get the data up | |
| 207 # for others to view. Revisit this once we're serving nicer graphs (Google | |
| 208 # Chart Tools) and from multiple perfbots and once we're in a position to | |
| 209 # organize the data in a useful manner(!!). | |
| 210 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | |
| 211 'perf_testing')) | |
| 212 for data in suite_names: | |
| 213 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | |
| 214 shutil.rmtree(path, ignore_errors=True) | |
| 215 os.makedirs(path) | |
| 216 files = [] | |
| 217 # Copy the 1000 most recent trace files to be uploaded. | |
| 218 for f in os.listdir(data): | |
| 219 files += [(os.path.getmtime(os.path.join(data, f)), f)] | |
| 220 files.sort() | |
| 221 for f in files[-1000:]: | |
| 222 shutil.copyfile(os.path.join(data, f[1]), | |
| 223 os.path.join(path, f[1]+'.txt')) | |
| 224 # Generate directory listing. | |
| 225 for data in suite_names: | |
| 226 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | |
| 227 out = open(os.path.join('appengine', 'static', | |
| 228 '%s-%s.html' % (data, utils.GuessOS())), 'w') | |
| 229 out.write('<html>\n <body>\n <ul>\n') | |
| 230 for f in os.listdir(path): | |
| 231 if not f.startswith('.'): | |
| 232 out.write(' <li><a href=data' + \ | |
| 233 '''/%(data)s/%(os)s/%(file)s>%(file)s</a></li>\n''' % \ | |
| 234 {'data': data, 'os': utils.GuessOS(), 'file': f}) | |
| 235 out.write(' </ul>\n </body>\n</html>') | |
| 236 out.close() | |
| 237 | |
| 238 shutil.rmtree(os.path.join('appengine', 'static', 'graphs'), | |
| 239 ignore_errors=True) | |
| 240 shutil.copytree('graphs', os.path.join('appengine', 'static', 'graphs')) | |
| 241 shutil.copyfile('index.html', os.path.join('appengine', 'static', | |
| 242 'index.html')) | |
| 243 shutil.copyfile('dromaeo.html', os.path.join('appengine', 'static', | |
| 244 'dromaeo.html')) | |
| 245 shutil.copyfile('data.html', os.path.join('appengine', 'static', | |
| 246 'data.html')) | |
| 247 run_cmd([os.path.join('..', '..', '..', 'third_party', | |
| 248 'appengine-python', 'appcfg.py'), '--oauth2', 'update', | |
| 249 'appengine/']) | |
| 250 | |
| 251 | |
| 252 class TestRunner(object): | 34 class TestRunner(object): |
| 35 DART_INSTALL_LOCATION = abspath(os.path.join(dirname(abspath(__file__)), | |
|
vsm
2012/04/09 20:19:58
I'd hoist this up with TOOLS_PATH.
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 36 '..', '..', '..')) | |
| 37 def __init__(self): | |
| 38 self.verbose = False | |
| 39 self.has_shell = False | |
| 40 if platform.system() == 'Windows': | |
| 41 # On Windows, shell must be true to get the correct environment variables. | |
| 42 self.has_shell = True | |
| 43 | |
| 44 def run_cmd(self, cmd_list, outfile=None, append=False, std_in=''): | |
| 45 """Run the specified command and print out any output to stdout. | |
| 46 | |
| 47 Args: | |
| 48 cmd_list: a list of strings that make up the command to run | |
| 49 outfile: a string indicating the name of the file that we should write | |
| 50 stdout to | |
| 51 append: True if we want to append to the file instead of overwriting it | |
| 52 std_in: a string that should be written to the process executing to | |
| 53 interact with it (if needed)""" | |
| 54 if self.verbose: | |
| 55 print ' '.join(cmd_list) | |
| 56 out = subprocess.PIPE | |
| 57 if outfile: | |
| 58 mode = 'w' | |
| 59 if append: | |
| 60 mode = 'a' | |
| 61 out = open(outfile, mode) | |
| 62 if append: | |
| 63 # Annoying Windows "feature" -- append doesn't actually append unless | |
| 64 # you explicitly go to the end of the file. | |
| 65 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html | |
| 66 out.seek(0, os.SEEK_END) | |
| 67 p = subprocess.Popen(cmd_list, stdout = out, stderr=subprocess.PIPE, | |
| 68 stdin=subprocess.PIPE, shell=self.has_shell) | |
| 69 output, not_used = p.communicate(std_in); | |
|
vsm
2012/04/09 20:19:58
Not sure how standard this is, but I like:
outpu
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 70 if output: | |
| 71 print output | |
|
vsm
2012/04/09 20:19:58
Perhaps print only if self.verbose?
Emily Fortuna
2012/04/09 21:15:03
This is how we get all of our output in our trace
vsm
2012/04/09 21:50:18
Isn't trace file output going directly to the "out
Emily Fortuna
2012/04/09 22:21:45
Yes, you're right. This is one of those cases wher
| |
| 72 return output | |
| 73 | |
| 74 def time_cmd(self, cmd): | |
| 75 """Determine the amount of (real) time it takes to execute a given | |
| 76 command.""" | |
| 77 start = time.time() | |
| 78 self.run_cmd(cmd) | |
| 79 return time.time() - start | |
| 80 | |
| 81 @staticmethod | |
| 82 def get_build_targets(suites): | |
| 83 """Loop through a set of tests that we want to run and find the build | |
| 84 targets that are necessary. | |
| 85 | |
| 86 Args: | |
| 87 suites: The test suites that we wish to run.""" | |
| 88 build_targets = set() | |
| 89 for test in suites: | |
| 90 if test.build_targets is not None: | |
| 91 for target in test.build_targets: | |
| 92 build_targets.add(target) | |
| 93 return build_targets | |
| 94 | |
| 95 def sync_and_build(self, suites): | |
| 96 """Make sure we have the latest version of of the repo, and build it. We | |
| 97 begin and end standing in DART_INSTALL_LOCATION. | |
| 98 | |
| 99 Args: | |
| 100 suites: The set of suites that we wish to build. | |
| 101 | |
| 102 Returns: | |
| 103 err_code = 1 if there was a problem building.""" | |
| 104 os.chdir(TestRunner.DART_INSTALL_LOCATION) | |
| 105 | |
| 106 self.run_cmd(['gclient', 'sync']) | |
| 107 | |
| 108 # On Windows, the output directory is marked as "Read Only," which causes an | |
| 109 # error to be thrown when we use shutil.rmtree. This helper function changes | |
| 110 # the permissions so we can still delete the directory. | |
| 111 def on_rm_error(func, path, exc_info): | |
| 112 if os.path.exists(path): | |
| 113 os.chmod(path, stat.S_IWRITE) | |
| 114 os.unlink(path) | |
| 115 # TODO(efortuna): building the sdk locally is a band-aid until all build | |
| 116 # platform SDKs are hosted in Google storage. Pull from https://sandbox. | |
| 117 # google.com/storage/?arg=dart-dump-render-tree#dart-dump-render-tree%2Fsdk | |
| 118 # eventually. | |
| 119 # TODO(efortuna): Currently always building ia32 architecture because we | |
| 120 # don't have test statistics for what's passing on x64. Eliminate arch | |
| 121 # specification when we have tests running on x64, too. | |
| 122 shutil.rmtree(os.path.join(os.getcwd(), | |
| 123 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), | |
| 124 onerror=on_rm_error) | |
| 125 | |
| 126 for target in TestRunner.get_build_targets(suites): | |
| 127 lines = self.run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', | |
| 128 'release', '--arch=ia32', target]) | |
| 129 | |
| 130 for line in lines: | |
| 131 if 'BUILD FAILED' in lines: | |
| 132 # Someone checked in a broken build! Stop trying to make it work | |
| 133 # and wait to try again. | |
| 134 print 'Broken Build' | |
| 135 return 1 | |
| 136 return 0 | |
| 137 | |
| 138 def ensure_output_directory(self, dir_name): | |
| 139 """Test that the listed directory name exists, and if not, create one for | |
| 140 our output to be placed. | |
| 141 | |
| 142 Args: | |
| 143 dir_name: the directory we will create if it does not exist.""" | |
| 144 dir_path = os.path.join(TestRunner.DART_INSTALL_LOCATION, 'tools', | |
| 145 'testing', 'perf_testing', dir_name) | |
| 146 if not os.path.exists(dir_path): | |
| 147 os.mkdir(dir_path) | |
| 148 print 'Creating output directory ', dir_path | |
| 149 | |
| 150 def has_new_code(self): | |
| 151 """Tests if there are any newer versions of files on the server.""" | |
| 152 os.chdir(TestRunner.DART_INSTALL_LOCATION) | |
| 153 # Pass 'p' in if we have a new certificate for the svn server, we want to | |
| 154 # (p)ermanently accept it. | |
| 155 results = self.run_cmd(['svn', 'st', '-u'], std_in='p') | |
| 156 for line in results: | |
| 157 if '*' in line: | |
| 158 return True | |
| 159 return False | |
| 160 | |
| 161 def get_os_directory(self): | |
| 162 """Specifies the name of the directory for the testing build of dart, which | |
| 163 has yet a different naming convention from utils.getBuildRoot(...).""" | |
| 164 if platform.system() == 'Windows': | |
| 165 return 'windows' | |
| 166 elif platform.system() == 'Darwin': | |
| 167 return 'macos' | |
| 168 else: | |
| 169 return 'linux' | |
| 170 | |
| 171 def upload_to_app_engine(self, suite_names): | |
| 172 """Upload our results to our appengine server. | |
| 173 Arguments: | |
| 174 suite_names: Directories to upload data from (should match directory | |
| 175 names).""" | |
| 176 # TODO(efortuna): This is the most basic way to get the data up | |
| 177 # for others to view. Revisit this once we're serving nicer graphs (Google | |
| 178 # Chart Tools) and from multiple perfbots and once we're in a position to | |
| 179 # organize the data in a useful manner(!!). | |
| 180 os.chdir(os.path.join(TestRunner.DART_INSTALL_LOCATION, 'tools', 'testing', | |
| 181 'perf_testing')) | |
| 182 for data in suite_names: | |
| 183 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | |
| 184 shutil.rmtree(path, ignore_errors=True) | |
| 185 os.makedirs(path) | |
| 186 files = [] | |
| 187 # Copy the 1000 most recent trace files to be uploaded. | |
| 188 for f in os.listdir(data): | |
| 189 files += [(os.path.getmtime(os.path.join(data, f)), f)] | |
| 190 files.sort() | |
| 191 for f in files[-1000:]: | |
| 192 shutil.copyfile(os.path.join(data, f[1]), | |
| 193 os.path.join(path, f[1]+'.txt')) | |
| 194 # Generate directory listing. | |
| 195 for data in suite_names: | |
| 196 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | |
| 197 out = open(os.path.join('appengine', 'static', | |
| 198 '%s-%s.html' % (data, utils.GuessOS())), 'w') | |
| 199 out.write('<html>\n <body>\n <ul>\n') | |
| 200 for f in os.listdir(path): | |
| 201 if not f.startswith('.'): | |
| 202 out.write(' <li><a href=data' + \ | |
| 203 '''/%(data)s/%(os)s/%(file)s>%(file)s</a></li>\n''' % \ | |
| 204 {'data': data, 'os': utils.GuessOS(), 'file': f}) | |
| 205 out.write(' </ul>\n </body>\n</html>') | |
| 206 out.close() | |
| 207 | |
| 208 shutil.rmtree(os.path.join('appengine', 'static', 'graphs'), | |
| 209 ignore_errors=True) | |
| 210 shutil.copytree('graphs', os.path.join('appengine', 'static', 'graphs')) | |
| 211 shutil.copyfile('index.html', os.path.join('appengine', 'static', | |
| 212 'index.html')) | |
| 213 shutil.copyfile('dromaeo.html', os.path.join('appengine', 'static', | |
| 214 'dromaeo.html')) | |
| 215 shutil.copyfile('data.html', os.path.join('appengine', 'static', | |
| 216 'data.html')) | |
| 217 self.run_cmd([os.path.join('..', '..', '..', 'third_party', | |
| 218 'appengine-python', 'appcfg.py'), '--oauth2', | |
| 219 'update', 'appengine/']) | |
| 220 | |
| 221 def parse_args(self): | |
| 222 parser = optparse.OptionParser() | |
| 223 parser.add_option('--suites', '-s', dest='suites', help='Run the specified ' | |
| 224 'comma-separated test suites from set: %s' % \ | |
| 225 ','.join(TestBuilder.available_suite_names()), | |
| 226 action='store', default=None) | |
| 227 parser.add_option('--forever', '-f', dest='continuous', help='Run this scri' | |
| 228 'pt forever, always checking for the next svn checkin', | |
| 229 action='store_true', default=False) | |
| 230 parser.add_option('--graph-only', '-g', dest='graph_only', default=False, | |
| 231 help='Do not run tests, only regenerate graphs', | |
| 232 action='store_true') | |
| 233 parser.add_option('--nobuild', '-n', dest='no_build', action='store_true', | |
| 234 help='Do not sync with the repository and do not ' | |
| 235 'rebuild.', default=False) | |
| 236 parser.add_option('--upload', '-u', dest='upload', help='Upload data to ' | |
| 237 'app engine (will require authentication).', | |
| 238 action='store_true', default=False) | |
| 239 parser.add_option('--verbose', '-v', dest='verbose', help='Print extra ' | |
| 240 'debug output', action='store_true', default=False) | |
| 241 | |
| 242 args, ignored = parser.parse_args() | |
| 243 | |
| 244 if not args.suites: | |
| 245 suites = TestBuilder.available_suite_names() | |
| 246 else: | |
| 247 suites = [] | |
| 248 suitelist = args.suites.split(',') | |
| 249 for name in suitelist: | |
| 250 if name in TestBuilder.available_suite_names(): | |
| 251 suites.append(name) | |
| 252 else: | |
| 253 print ('Error: Invalid suite %s not in ' % name) + \ | |
| 254 '%s' % ','.join(TestBuilder.available_suite_names()) | |
| 255 sys.exit(1) | |
| 256 return (suites, args.continuous, args.verbose, args.no_build, | |
| 257 args.graph_only, args.upload) | |
| 258 | |
| 259 def run_test_sequence(self, suite_names, no_build, graph_only, upload): | |
| 260 """Run the set of commands to (possibly) build, run, and graph the results | |
| 261 of our tests. | |
| 262 | |
| 263 Args: | |
| 264 suite_names: The "display name" the user enters to specify which | |
| 265 benchmark(s) to run. | |
| 266 no_build: True if we should not check the repository and build the latest | |
| 267 version. | |
| 268 graph_only: True if we should not run the tests, just (re)generate graphs. | |
| 269 upload: True if we should upload our results to appengine.""" | |
| 270 suites = [] | |
| 271 for name in suite_names: | |
| 272 suites += [TestBuilder().make_test(name, self)] | |
| 273 | |
| 274 if not no_build and self.sync_and_build(suites) == 1: | |
| 275 return # The build is broken. | |
| 276 | |
| 277 for test in suites: | |
| 278 test.run(graph_only) | |
| 279 | |
| 280 if upload: | |
| 281 self.upload_to_app_engine(TestBuilder.available_site_names()) | |
| 282 | |
| 283 | |
| 284 class Test(object): | |
| 253 """The base class to provide shared code for different tests we will run and | 285 """The base class to provide shared code for different tests we will run and |
| 254 graph.""" | 286 graph. At a high level, each test has three visitors (the tester, the |
| 287 file_processor, and the grapher) that perform operations on the test | |
| 288 object.""" | |
| 255 | 289 |
| 256 def __init__(self, result_folder_name, platform_list, variants, | 290 def __init__(self, result_folder_name, platform_list, variants, |
| 257 values_list): | 291 values_list, test_runner, tester, file_processor, grapher, |
| 292 extra_metrics=['Geo-Mean'], build_targets=['create_sdk']): | |
| 258 """Args: | 293 """Args: |
| 259 result_folder_name the name of the folder where a tracefile of | 294 result_folder_name: The name of the folder where a tracefile of |
| 260 performance results will be stored. | 295 performance results will be stored. |
| 261 platform_list a list containing the platform(s) that our data has been | 296 platform_list: A list containing the platform(s) that our data has been |
| 262 run on. (command line, firefox, chrome, etc) | 297 run on. (command line, firefox, chrome, etc) |
| 263 variants a list specifying whether we hold data about Frog | 298 variants: A list specifying whether we hold data about Frog |
| 264 generated code, plain JS code (js), or a combination of both. | 299 generated code, plain JS code, or a combination of both, or |
| 265 values_list a list containing the type of data we will be graphing | 300 Dart depending on the test. |
| 266 (benchmarks, percentage passing, etc)""" | 301 values_list: A list containing the type of data we will be graphing |
| 302 (benchmarks, percentage passing, etc). | |
| 303 test_runner: Reference to the parent test runner object that notifies a | |
| 304 test when to run. | |
| 305 tester: The visitor that actually performs the test running mechanics. | |
| 306 file_processor: The visitor that processes files in the format | |
| 307 appropriate for this test. | |
| 308 grapher: The visitor that generates graphs given our test result data. | |
| 309 extra_metrics: A list of any additional measurements we wish to keep | |
| 310 track of (such as the geometric mean of a set, the sum, etc). | |
| 311 build_targets: The targets necessary to build to run these tests | |
| 312 (default target is create_sdk).""" | |
| 267 self.result_folder_name = result_folder_name | 313 self.result_folder_name = result_folder_name |
| 268 # cur_time is used as a timestamp of when this performance test was run. | 314 # cur_time is used as a timestamp of when this performance test was run. |
| 269 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) | 315 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) |
| 270 # TODO(vsm): Factor out. | |
| 271 self.browser_color = {'chrome': 'green', 'ie': 'blue', 'ff': 'red', | |
| 272 'safari':'black'} | |
| 273 self.values_list = values_list | 316 self.values_list = values_list |
| 274 self.platform_list = platform_list | 317 self.platform_list = platform_list |
| 275 self.revision_dict = dict() | 318 self.revision_dict = dict() |
| 276 self.values_dict = dict() | 319 self.values_dict = dict() |
| 277 self.color_index = 0 | 320 self.test_runner = test_runner |
| 321 self.tester = tester | |
| 322 self.file_processor = file_processor | |
| 323 self.grapher = grapher | |
| 324 self.extra_metrics = extra_metrics | |
| 325 self.build_targets = build_targets | |
| 326 # Initialize our values store. | |
| 278 for platform in platform_list: | 327 for platform in platform_list: |
| 279 self.revision_dict[platform] = dict() | 328 self.revision_dict[platform] = dict() |
| 280 self.values_dict[platform] = dict() | 329 self.values_dict[platform] = dict() |
| 281 for f in variants: | 330 for f in variants: |
| 282 self.revision_dict[platform][f] = dict() | 331 self.revision_dict[platform][f] = dict() |
| 283 self.values_dict[platform][f] = dict() | 332 self.values_dict[platform][f] = dict() |
| 284 for val in values_list: | 333 for val in values_list: |
| 285 self.revision_dict[platform][f][val] = [] | 334 self.revision_dict[platform][f][val] = [] |
| 286 self.values_dict[platform][f][val] = [] | 335 self.values_dict[platform][f][val] = [] |
| 287 self.revision_dict[platform][f][GEO_MEAN] = [] | 336 for extra_metric in extra_metrics: |
| 288 self.values_dict[platform][f][GEO_MEAN] = [] | 337 self.revision_dict[platform][f][extra_metric] = [] |
| 338 self.values_dict[platform][f][extra_metric] = [] | |
| 339 | |
| 340 def run(self, graph_only): | |
| 341 """Run the benchmarks/tests from the command line and plot the | |
| 342 results. | |
| 343 | |
| 344 Args: | |
| 345 graph_only: True if we should just graph the results instead of also | |
| 346 running tests.""" | |
| 347 for visitor in [self.tester, self.file_processor, self.grapher]: | |
| 348 visitor.prepare() | |
| 349 | |
| 350 os.chdir(TestRunner.DART_INSTALL_LOCATION) | |
| 351 self.test_runner.ensure_output_directory(self.result_folder_name) | |
| 352 if not graph_only: | |
| 353 self.tester.run_tests() | |
| 289 | 354 |
| 290 def get_color(self): | 355 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) |
| 291 color = COLORS[self.color_index] | 356 |
| 292 self.color_index = (self.color_index + 1) % len(COLORS) | 357 # TODO(efortuna): You will want to make this only use a subset of the files |
| 293 return color | 358 # eventually. |
| 359 files = os.listdir(self.result_folder_name) | |
| 360 | |
| 361 for afile in files: | |
| 362 if not afile.startswith('.'): | |
| 363 self.file_processor.process_file(afile) | |
| 364 | |
| 365 if 'plt' in globals(): | |
| 366 # Only run Matplotlib if it is installed. | |
| 367 self.grapher.plot_results('%s.png' % self.result_folder_name) | |
| 368 | |
| 369 | |
| 370 class Tester(object): | |
| 371 """The base level visitor class that runs tests. It contains convenience | |
| 372 methods that many Tester objects use. Any class that would like to be a | |
| 373 TesterVisitor must implement the run_tests() method.""" | |
| 374 | |
| 375 def __init__(self, test): | |
| 376 self.test = test | |
| 377 | |
| 378 def prepare(self): | |
| 379 """Perform any initial setup required before the test is run.""" | |
| 380 pass | |
| 381 | |
| 382 def add_svn_revision_to_trace(self, outfile): | |
| 383 """Add the svn version number to the provided tracefile.""" | |
| 384 def search_for_revision(svn_info_command): | |
| 385 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | |
| 386 stderr = subprocess.STDOUT, shell = | |
| 387 self.test.test_runner.has_shell) | |
| 388 output, _ = p.communicate() | |
| 389 for line in output.split('\n'): | |
| 390 if 'Revision' in line: | |
| 391 self.test.test_runner.run_cmd(['echo', line.strip()], outfile) | |
| 392 return True | |
| 393 return False | |
| 394 | |
| 395 if not search_for_revision(['svn', 'info']): | |
| 396 if not search_for_revision(['git', 'svn', 'info']): | |
| 397 self.test.test_runner.run_cmd(['echo', 'Revision: unknown'], outfile) | |
| 398 | |
| 399 | |
| 400 class Processor(object): | |
| 401 """The base level vistor class that processes tests. It contains convenience | |
| 402 methods that many File Processor objects use. Any class that would like to be | |
| 403 a ProcessorVisitor must implement the process_file() method.""" | |
| 404 | |
| 405 def __init__(self, test): | |
| 406 self.test = test | |
| 407 | |
| 408 def prepare(self): | |
| 409 """Perform any initial setup required before the test is run.""" | |
| 410 pass | |
| 411 | |
| 412 def calculate_geometric_mean(self, platform, variant, svn_revision): | |
| 413 """Calculate the aggregate geometric mean for JS and frog benchmark sets, | |
| 414 given two benchmark dictionaries.""" | |
| 415 geo_mean = 0 | |
| 416 for benchmark in self.test.values_list: | |
| 417 geo_mean += math.log(self.test.values_dict[platform][variant][benchmark][ | |
| 418 len(self.test.values_dict[platform][variant][benchmark]) - 1]) | |
| 419 | |
| 420 self.test.values_dict[platform][variant]['Geo-Mean'] += \ | |
| 421 [math.pow(math.e, geo_mean / len(self.test.values_list))] | |
| 422 self.test.revision_dict[platform][variant]['Geo-Mean'] += [svn_revision] | |
| 423 | |
| 424 | |
| 425 class Grapher(object): | |
| 426 """The base level visitor class that generates graphs for data. It contains | |
| 427 convenience methods that many Grapher objects use. Any class that would like | |
| 428 to be a GrapherVisitor must implement the plot_results() method.""" | |
| 429 | |
| 430 graph_out_dir = 'graphs' | |
| 431 | |
| 432 def __init__(self, test): | |
| 433 self.color_index = 0 | |
| 434 self.test = test | |
| 435 | |
| 436 def prepare(self): | |
| 437 """Perform any initial setup required before the test is run.""" | |
| 438 if 'plt' in globals(): | |
| 439 plt.cla() # cla = clear current axes | |
| 440 else: | |
| 441 print 'Unable to import Matplotlib and therefore unable to generate ' + \ | |
| 442 'graphs. Please install it for this version of Python.' | |
| 443 self.test.test_runner.ensure_output_directory(Grapher.graph_out_dir) | |
| 294 | 444 |
| 295 def style_and_save_perf_plot(self, chart_title, y_axis_label, size_x, size_y, | 445 def style_and_save_perf_plot(self, chart_title, y_axis_label, size_x, size_y, |
| 296 legend_loc, filename, platform_list, variants, values_list, | 446 legend_loc, filename, platform_list, variants, |
| 297 should_clear_axes=True): | 447 values_list, should_clear_axes=True): |
| 298 """Sets style preferences for chart boilerplate that is consistent across | 448 """Sets style preferences for chart boilerplate that is consistent across |
| 299 all charts, and saves the chart as a png. | 449 all charts, and saves the chart as a png. |
| 300 | 450 |
| 301 Args: | 451 Args: |
| 302 size_x: the size of the printed chart, in inches, in the horizontal | 452 size_x: the size of the printed chart, in inches, in the horizontal |
| 303 direction | 453 direction |
| 304 size_y: the size of the printed chart, in inches in the vertical direction | 454 size_y: the size of the printed chart, in inches in the vertical direction |
| 305 legend_loc: the location of the legend in on the chart. See suitable | 455 legend_loc: the location of the legend in on the chart. See suitable |
| 306 arguments for the loc argument in matplotlib | 456 arguments for the loc argument in matplotlib |
| 307 filename: the filename that we want to save the resulting chart as | 457 filename: the filename that we want to save the resulting chart as |
| 308 platform_list: a list containing the platform(s) that our data has been | 458 platform_list: a list containing the platform(s) that our data has been |
| 309 run on. (command line, firefox, chrome, etc) | 459 run on. (command line, firefox, chrome, etc) |
| 310 values_list: a list containing the type of data we will be graphing | 460 values_list: a list containing the type of data we will be graphing |
| 311 (performance, percentage passing, etc) | 461 (performance, percentage passing, etc) |
| 312 should_clear_axes: True if we want to create a fresh graph, instead of | 462 should_clear_axes: True if we want to create a fresh graph, instead of |
| 313 plotting additional lines on the current graph.""" | 463 plotting additional lines on the current graph.""" |
| 314 if should_clear_axes: | 464 if should_clear_axes: |
| 315 plt.cla() # cla = clear current axes | 465 plt.cla() # cla = clear current axes |
| 316 for platform in platform_list: | 466 for platform in platform_list: |
| 317 for f in variants: | 467 for f in variants: |
| 318 for val in values_list: | 468 for val in values_list: |
| 319 plt.plot(self.revision_dict[platform][f][val], | 469 plt.plot(self.test.revision_dict[platform][f][val], |
| 320 self.values_dict[platform][f][val], | 470 self.test.values_dict[platform][f][val], |
| 321 color=self.get_color(), label='%s-%s-%s' % (platform, f, val)) | 471 color=self.get_color(), label='%s-%s-%s' % (platform, f, val)) |
| 322 | 472 |
| 323 plt.xlabel('Revision Number') | 473 plt.xlabel('Revision Number') |
| 324 plt.ylabel(y_axis_label) | 474 plt.ylabel(y_axis_label) |
| 325 plt.title(chart_title) | 475 plt.title(chart_title) |
| 326 fontP = FontProperties() | 476 fontP = FontProperties() |
| 327 fontP.set_size('small') | 477 fontP.set_size('small') |
| 328 plt.legend(loc=legend_loc, prop = fontP) | 478 plt.legend(loc=legend_loc, prop = fontP) |
| 329 | 479 |
| 330 fig = plt.gcf() | 480 fig = plt.gcf() |
| 331 fig.set_size_inches(size_x, size_y) | 481 fig.set_size_inches(size_x, size_y) |
| 332 fig.savefig(os.path.join(GRAPH_OUT_DIR, filename)) | 482 fig.savefig(os.path.join(Grapher.graph_out_dir, filename)) |
| 333 | 483 |
| 334 def add_svn_revision_to_trace(self, outfile): | 484 def get_color(self): |
| 335 """Add the svn version number to the provided tracefile.""" | 485 # Just a bunch of distinct colors for a potentially large number of values |
| 336 def search_for_revision(svn_info_command): | 486 # we wish to graph. |
|
vsm
2012/04/09 20:19:58
:-)
| |
| 337 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | 487 colors = [ |
| 338 stderr = subprocess.STDOUT, shell = HAS_SHELL) | 488 '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#0099C6', |
| 339 output, _ = p.communicate() | 489 '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', |
| 340 for line in output.split('\n'): | 490 '#AAAA11', '#6633CC', '#E67300', '#8B0707', '#651067', '#329262', |
| 341 if 'Revision' in line: | 491 '#5574A6', '#3B3EAC', '#B77322', '#16D620', '#B91383', '#F4359E', |
| 342 run_cmd(['echo', line.strip()], outfile) | 492 '#9C5935', '#A9C413', '#2A778D', '#668D1C', '#BEA413', '#0C5922', |
| 343 return True | 493 '#743411', '#45AFE2', '#FF3300', '#FFCC00', '#14C21D', '#DF51FD', |
| 344 return False | 494 '#15CBFF', '#FF97D2', '#97FB00', '#DB6651', '#518BC6', '#BD6CBD', |
| 345 | 495 '#35D7C2', '#E9E91F', '#9877DD', '#FF8F20', '#D20B0B', '#B61DBA', |
| 346 if not search_for_revision(['svn', 'info']): | 496 '#40BD7E', '#6AA7C4', '#6D70CD', '#DA9136', '#2DEA36', '#E81EA6', |
| 347 if not search_for_revision(['git', 'svn', 'info']): | 497 '#F558AE', '#C07145', '#D7EE53', '#3EA7C6', '#97D129', '#E9CA1D', |
| 348 run_cmd(['echo', 'Revision: unknown'], outfile) | 498 '#149638', '#C5571D'] |
| 349 | 499 color = colors[self.color_index] |
| 350 def calculate_geometric_mean(self, platform, variant, svn_revision): | 500 self.color_index = (self.color_index + 1) % len(colors) |
| 351 """Calculate the aggregate geometric mean for JS and frog benchmark sets, | 501 return color |
| 352 given two benchmark dictionaries.""" | 502 |
| 353 geo_mean = 0 | 503 |
| 354 for benchmark in self.values_list: | 504 class RuntimePerformanceTest(Test): |
| 355 geo_mean += math.log(self.values_dict[platform][variant][benchmark][ | 505 """Super class for all runtime performance testing.""" |
| 356 len(self.values_dict[platform][variant][benchmark]) - 1]) | 506 |
| 357 | |
| 358 self.values_dict[platform][variant][GEO_MEAN] += \ | |
| 359 [math.pow(math.e, geo_mean / len(self.values_list))] | |
| 360 self.revision_dict[platform][variant][GEO_MEAN] += [svn_revision] | |
| 361 | |
| 362 def run(self, graph_only): | |
| 363 """Run the benchmarks/tests from the command line and plot the | |
| 364 results.""" | |
| 365 plt.cla() # cla = clear current axes | |
| 366 os.chdir(DART_INSTALL_LOCATION) | |
| 367 ensure_output_directory(self.result_folder_name) | |
| 368 ensure_output_directory(GRAPH_OUT_DIR) | |
| 369 if not graph_only: | |
| 370 self.run_tests() | |
| 371 | |
| 372 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) | |
| 373 | |
| 374 # TODO(efortuna): You will want to make this only use a subset of the files | |
| 375 # eventually. | |
| 376 files = os.listdir(self.result_folder_name) | |
| 377 | |
| 378 for afile in files: | |
| 379 if not afile.startswith('.'): | |
| 380 self.process_file(afile) | |
| 381 | |
| 382 if 'plt' in globals(): | |
| 383 # Only run Matplotlib if it is installed. | |
| 384 self.plot_results('%s.png' % self.result_folder_name) | |
| 385 else: | |
| 386 print 'Unable to import Matplotlib and therefore unable to generate ' + \ | |
| 387 'graphs. Please install it for this version of Python.' | |
| 388 | |
| 389 class PerformanceTest(TestRunner): | |
| 390 """Super class for all performance testing.""" | |
| 391 def __init__(self, result_folder_name, platform_list, platform_type, | 507 def __init__(self, result_folder_name, platform_list, platform_type, |
| 392 versions, benchmarks): | 508 versions, benchmarks, test_runner, tester, file_processor, |
| 393 super(PerformanceTest, self).__init__(result_folder_name, | 509 build_targets=['create_sdk']): |
| 394 platform_list, versions, benchmarks) | 510 """Args: |
| 511 result_folder_name: The name of the folder where a tracefile of | |
| 512 performance results will be stored. | |
| 513 platform_list: A list containing the platform(s) that our data has been | |
| 514 run on. (command line, firefox, chrome, etc) | |
| 515 variants: A list specifying whether we hold data about Frog | |
| 516 generated code, plain JS code, or a combination of both, or | |
| 517 Dart depending on the test. | |
| 518 values_list: A list containing the type of data we will be graphing | |
| 519 (benchmarks, percentage passing, etc). | |
| 520 test_runner: Reference to the parent test runner object that notifies a | |
| 521 test when to run. | |
| 522 tester: The visitor that actually performs the test running mechanics. | |
| 523 file_processor: The visitor that processes files in the format | |
| 524 appropriate for this test. | |
| 525 grapher: The visitor that generates graphs given our test result data. | |
| 526 extra_metrics: A list of any additional measurements we wish to keep | |
| 527 track of (such as the geometric mean of a set, the sum, etc). | |
| 528 build_targets: The targets necessary to build to run these tests | |
| 529 (default target is create_sdk).""" | |
| 530 super(RuntimePerformanceTest, self).__init__(result_folder_name, | |
| 531 platform_list, versions, benchmarks, test_runner, tester, | |
| 532 file_processor, RuntimePerformanceTest.RuntimePerfGrapher(self), | |
| 533 build_targets=build_targets) | |
| 395 self.platform_list = platform_list | 534 self.platform_list = platform_list |
| 396 self.platform_type = platform_type | 535 self.platform_type = platform_type |
| 397 self.versions = versions | 536 self.versions = versions |
| 398 self.benchmarks = benchmarks | 537 self.benchmarks = benchmarks |
| 399 | 538 |
| 400 def plot_all_perf(self, png_filename): | 539 class RuntimePerfGrapher(Grapher): |
| 401 """Create a plot that shows the performance changes of individual benchmarks | 540 def plot_all_perf(self, png_filename): |
| 402 run by JS and generated by frog, over svn history.""" | 541 """Create a plot that shows the performance changes of individual |
| 403 for benchmark in self.benchmarks: | 542 benchmarks run by JS and generated by frog, over svn history.""" |
| 404 self.style_and_save_perf_plot( | 543 for benchmark in self.test.benchmarks: |
| 405 'Performance of %s over time on the %s on %s' % (benchmark, | 544 self.style_and_save_perf_plot( |
| 406 self.platform_type, utils.GuessOS()), 'Speed (bigger = better)', 16, | 545 'Performance of %s over time on the %s on %s' % (benchmark, |
| 407 14, 'lower left', benchmark + png_filename, self.platform_list, | 546 self.test.platform_type, utils.GuessOS()), |
| 408 self.versions, [benchmark]) | 547 'Speed (bigger = better)', 16, 14, 'lower left', |
| 409 | 548 benchmark + png_filename, self.test.platform_list, |
| 410 def plot_avg_perf(self, png_filename): | 549 self.test.versions, [benchmark]) |
| 411 """Generate a plot that shows the performance changes of the geomentric mean | 550 |
| 412 of JS and frog benchmark performance over svn history.""" | 551 def plot_avg_perf(self, png_filename): |
| 413 (title, y_axis, size_x, size_y, loc, filename) = \ | 552 """Generate a plot that shows the performance changes of the geomentric |
| 414 ('Geometric Mean of benchmark %s performance on %s ' % | 553 mean of JS and frog benchmark performance over svn history.""" |
| 415 (self.platform_type, utils.GuessOS()), 'Speed (bigger = better)', 16, 5, | 554 (title, y_axis, size_x, size_y, loc, filename) = \ |
| 416 'lower left', 'avg'+png_filename) | 555 ('Geometric Mean of benchmark %s performance on %s ' % |
| 417 clear_axis = True | 556 (self.test.platform_type, utils.GuessOS()), 'Speed (bigger = better)', |
| 418 for platform in self.platform_list: | 557 16, 5, 'lower left', 'avg'+png_filename) |
| 419 for version in self.versions: | 558 clear_axis = True |
| 420 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, | 559 for platform in self.test.platform_list: |
| 421 filename, [platform], [version], | 560 for version in self.test.versions: |
| 422 [GEO_MEAN], clear_axis) | 561 for metric in self.test.extra_metrics: |
| 423 clear_axis = False | 562 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, |
| 424 | 563 filename, [platform], [version], |
| 425 def plot_results(self, png_filename): | 564 [metric], clear_axis) |
| 426 self.plot_all_perf(png_filename) | 565 |
| 427 self.plot_avg_perf('2' + png_filename) | 566 def plot_results(self, png_filename): |
| 428 | 567 self.plot_all_perf(png_filename) |
| 429 | 568 self.plot_avg_perf('2' + png_filename) |
| 430 class CommandLinePerformanceTest(PerformanceTest): | 569 |
| 570 | |
| 571 class BenchpressAndCoCommandLineTest(RuntimePerformanceTest): | |
|
vsm
2012/04/09 20:19:58
"BenchpressAndCo" is a mouthful (fingerpressfull?)
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 431 """Run performance tests from the command line.""" | 572 """Run performance tests from the command line.""" |
| 432 | 573 |
| 433 def __init__(self): | 574 def __init__(self, test_runner): |
| 434 super(CommandLinePerformanceTest, self).__init__( | 575 """Args: |
| 435 CL_PERF, [COMMAND_LINE], 'command line', | 576 test_runner: Reference to the object that notfies this test when to |
| 436 JS_AND_FROG, get_standalone_benchmarks()) | 577 run.""" |
| 437 | 578 super(BenchpressAndCoCommandLineTest, self).__init__( |
| 438 def process_file(self, afile): | 579 BenchpressAndCoCommandLineTest.name(), ['commandline'], |
|
vsm
2012/04/09 20:19:58
I could be wrong, but I think you could write self
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 439 """Pull all the relevant information out of a given tracefile. | 580 'command line', ['js', 'frog'], |
| 440 | 581 BenchpressAndCoCommandLineTest.get_standalone_benchmarks(), |
| 441 Args: | 582 test_runner, |
| 442 afile: The filename string we will be processing.""" | 583 BenchpressAndCoCommandLineTest.BenchpressAndCoCommandLineTester(self), |
| 443 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 584 BenchpressAndCoCommandLineTest |
| 444 'perf_testing')) | 585 .BenchpressAndCoCommandLineFileProcessor(self), |
| 445 f = open(os.path.join(self.result_folder_name, afile)) | 586 build_targets=['create_sdk', 'dart2js']) |
| 446 tabulate_data = False | 587 |
| 447 revision_num = 0 | 588 @staticmethod |
| 448 for line in f.readlines(): | 589 def name(): |
| 449 if 'Revision' in line: | 590 return 'cl-perf' |
| 450 revision_num = int(line.split()[1]) | 591 |
| 451 elif 'Benchmark' in line: | 592 @staticmethod |
| 452 tabulate_data = True | 593 def get_standalone_benchmarks(): |
| 453 elif tabulate_data: | 594 return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', |
| 454 tokens = line.split() | 595 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', |
| 455 if len(tokens) < 4 or tokens[0] not in self.benchmarks: | 596 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', |
| 456 #Done tabulating data. | 597 'TreeSort'] |
| 457 break | 598 |
| 458 js_value = float(tokens[1]) | 599 class BenchpressAndCoCommandLineTester(Tester): |
| 459 frog_value = float(tokens[3]) | 600 def run_tests(self): |
| 460 if js_value == 0 or frog_value == 0: | 601 """Run a performance test on our updated system.""" |
| 461 #Then there was an error when this performance test was run. Do not | 602 os.chdir('frog') |
| 462 #count it in our numbers. | 603 self.test.trace_file = os.path.join( |
| 463 return | 604 '..', 'tools', 'testing', 'perf_testing', |
| 464 benchmark = tokens[0] | 605 self.test.result_folder_name, 'result' + self.test.cur_time) |
| 465 self.revision_dict[COMMAND_LINE][JS][benchmark] += [revision_num] | 606 self.test.test_runner.run_cmd(['python', os.path.join('benchmarks', |
| 466 self.values_dict[COMMAND_LINE][JS][benchmark] += [js_value] | 607 'perf_tests.py')], self.test.trace_file) |
| 467 self.revision_dict[COMMAND_LINE][FROG][benchmark] += [revision_num] | 608 os.chdir('..') |
| 468 self.values_dict[COMMAND_LINE][FROG][benchmark] += [frog_value] | 609 |
| 469 f.close() | 610 class BenchpressAndCoCommandLineFileProcessor(Processor): |
| 470 | 611 def process_file(self, afile): |
| 471 self.calculate_geometric_mean(COMMAND_LINE, FROG, revision_num) | 612 """Pull all the relevant information out of a given tracefile. |
| 472 self.calculate_geometric_mean(COMMAND_LINE, JS, revision_num) | 613 |
| 473 | 614 Args: |
| 474 def run_tests(self): | 615 afile: The filename string we will be processing.""" |
| 475 """Run a performance test on our updated system.""" | 616 os.chdir(os.path.join(TestRunner.DART_INSTALL_LOCATION, 'tools', |
| 476 os.chdir('frog') | 617 'testing', 'perf_testing')) |
| 477 self.trace_file = os.path.join('..', 'tools', 'testing', 'perf_testing', | 618 f = open(os.path.join(self.test.result_folder_name, afile)) |
| 478 self.result_folder_name, 'result' + self.cur_time) | 619 tabulate_data = False |
| 479 run_cmd(['python', os.path.join('benchmarks', 'perf_tests.py')], | 620 revision_num = 0 |
| 480 self.trace_file) | 621 for line in f.readlines(): |
| 481 os.chdir('..') | 622 if 'Revision' in line: |
| 482 | 623 revision_num = int(line.split()[1]) |
| 483 | 624 elif 'Benchmark' in line: |
| 484 class BrowserStandalonePerformanceTest(PerformanceTest): | 625 tabulate_data = True |
| 626 elif tabulate_data: | |
| 627 tokens = line.split() | |
| 628 if len(tokens) < 4 or tokens[0] not in self.test.benchmarks: | |
| 629 #Done tabulating data. | |
| 630 break | |
| 631 js_value = float(tokens[1]) | |
| 632 frog_value = float(tokens[3]) | |
| 633 if js_value == 0 or frog_value == 0: | |
| 634 #Then there was an error when this performance test was run. Do not | |
| 635 #count it in our numbers. | |
| 636 return | |
| 637 benchmark = tokens[0] | |
| 638 self.test.revision_dict['commandline']['js'][benchmark] += \ | |
| 639 [revision_num] | |
| 640 self.test.values_dict['commandline']['js'][benchmark] += [js_value] | |
| 641 self.test.revision_dict['commandline']['frog'][benchmark] += \ | |
| 642 [revision_num] | |
| 643 self.test.values_dict['commandline']['frog'][benchmark] += \ | |
| 644 [frog_value] | |
| 645 f.close() | |
| 646 | |
| 647 self.calculate_geometric_mean('commandline', 'frog', revision_num) | |
| 648 self.calculate_geometric_mean('commandline', 'js', revision_num) | |
| 649 | |
| 650 | |
| 651 class BrowserTester(Tester): | |
| 652 # TODO(vsm): Add Dartium. | |
| 653 @staticmethod | |
| 654 def get_browsers(): | |
| 655 browsers = ['ff', 'chrome'] | |
| 656 if platform.system() == 'Darwin': | |
| 657 browsers += ['safari'] | |
| 658 if platform.system() == 'Windows': | |
| 659 browsers += ['ie'] | |
| 660 return browsers | |
| 661 | |
| 662 | |
| 663 class BenchpressAndCoBrowserTest(RuntimePerformanceTest): | |
| 485 """Runs standalone performance tests, in the browser.""" | 664 """Runs standalone performance tests, in the browser.""" |
| 486 | 665 |
| 487 def __init__(self): | 666 def __init__(self, test_runner): |
| 488 super(BrowserStandalonePerformanceTest, self).__init__( | 667 """Args: |
| 489 BROWSER_PERF, get_browsers(), 'browser', | 668 test_runner: Reference to the object that notifies us when to run.""" |
| 490 JS_AND_FROG, get_standalone_benchmarks()) | 669 super(BenchpressAndCoBrowserTest, self).__init__( |
| 491 | 670 BenchpressAndCoBrowserTest.name(), BrowserTester.get_browsers(), |
| 492 def run_tests(self): | 671 'browser', ['js', 'frog'], |
| 493 """Run a performance test in the browser.""" | 672 BenchpressAndCoBrowserTest.get_standalone_benchmarks(), test_runner, |
| 494 | 673 BenchpressAndCoBrowserTest.BenchpressAndCoBrowserTester(self), |
| 495 os.chdir('frog') | 674 BenchpressAndCoBrowserTest.BenchpressAndCoBrowserFileProcessor(self)) |
| 496 run_cmd(['python', os.path.join('benchmarks', 'make_web_benchmarks.py')]) | 675 |
| 497 os.chdir('..') | 676 @staticmethod |
| 498 | 677 def name(): |
| 499 for browser in get_browsers(): | 678 return 'browser-perf' |
| 500 for version in self.versions: | 679 |
| 501 self.trace_file = os.path.join('tools', 'testing', 'perf_testing', | 680 @staticmethod |
| 502 self.result_folder_name, | 681 def get_standalone_benchmarks(): |
| 503 'perf-%s-%s-%s' % (self.cur_time, browser, version)) | 682 return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', |
| 504 self.add_svn_revision_to_trace(self.trace_file) | 683 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', |
| 505 file_path = os.path.join(os.getcwd(), 'internal', 'browserBenchmarks', | 684 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', |
| 506 'benchmark_page_%s.html' % version) | 685 'TreeSort'] |
| 507 run_cmd(['python', os.path.join('tools', 'testing', 'run_selenium.py'), | 686 |
| 508 '--out', file_path, '--browser', browser, | 687 class BenchpressAndCoBrowserTester(BrowserTester): |
| 509 '--timeout', '600', '--mode', 'perf'], self.trace_file, append=True) | 688 def run_tests(self): |
| 510 | 689 """Run a performance test in the browser.""" |
| 511 def process_file(self, afile): | 690 os.chdir('frog') |
| 512 """Comb through the html to find the performance results.""" | 691 self.test.test_runner.run_cmd(['python', os.path.join('benchmarks', |
| 513 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 692 'make_web_benchmarks.py')]) |
| 514 'perf_testing')) | 693 os.chdir('..') |
| 515 parts = afile.split('-') | 694 |
| 516 browser = parts[2] | 695 for browser in BrowserTester.get_browsers(): |
| 517 version = parts[3] | 696 for version in self.test.versions: |
| 518 f = open(os.path.join(self.result_folder_name, afile)) | 697 self.test.trace_file = os.path.join( |
| 519 lines = f.readlines() | 698 'tools', 'testing', 'perf_testing', self.test.result_folder_name, |
| 520 line = '' | 699 'perf-%s-%s-%s' % (self.test.cur_time, browser, version)) |
| 521 i = 0 | 700 self.add_svn_revision_to_trace(self.test.trace_file) |
| 522 revision_num = 0 | 701 file_path = os.path.join( |
| 523 while '<div id="results">' not in line and i < len(lines): | 702 os.getcwd(), 'internal', 'browserBenchmarks', |
| 524 if 'Revision' in line: | 703 'benchmark_page_%s.html' % version) |
| 525 revision_num = int(line.split()[1].strip('"')) | 704 self.test.test_runner.run_cmd( |
| 705 ['python', os.path.join('tools', 'testing', 'run_selenium.py'), | |
| 706 '--out', file_path, '--browser', browser, | |
| 707 '--timeout', '600', '--mode', 'perf'], self.test.trace_file, | |
| 708 append=True) | |
| 709 | |
| 710 class BenchpressAndCoBrowserFileProcessor(Processor): | |
| 711 def process_file(self, afile): | |
| 712 """Comb through the html to find the performance results.""" | |
| 713 os.chdir(os.path.join(TestRunner.DART_INSTALL_LOCATION, 'tools', | |
| 714 'testing', 'perf_testing')) | |
| 715 parts = afile.split('-') | |
| 716 browser = parts[2] | |
| 717 version = parts[3] | |
| 718 f = open(os.path.join(self.test.result_folder_name, afile)) | |
| 719 lines = f.readlines() | |
| 720 line = '' | |
| 721 i = 0 | |
| 722 revision_num = 0 | |
| 723 while '<div id="results">' not in line and i < len(lines): | |
| 724 if 'Revision' in line: | |
| 725 revision_num = int(line.split()[1].strip('"')) | |
| 726 line = lines[i] | |
| 727 i += 1 | |
| 728 | |
| 729 if i >= len(lines) or revision_num == 0: | |
| 730 # Then this run did not complete. Ignore this tracefile. | |
| 731 return | |
| 732 | |
| 526 line = lines[i] | 733 line = lines[i] |
| 527 i += 1 | 734 i += 1 |
| 528 | 735 results = [] |
| 529 if i >= len(lines) or revision_num == 0: | 736 if line.find('<br>') > -1: |
| 530 # Then this run did not complete. Ignore this tracefile. | 737 results = line.split('<br>') |
| 531 return | |
| 532 | |
| 533 line = lines[i] | |
| 534 i += 1 | |
| 535 results = [] | |
| 536 if line.find('<br>') > -1: | |
| 537 results = line.split('<br>') | |
| 538 else: | |
| 539 results = line.split('<br />') | |
| 540 for result in results: | |
| 541 name_and_score = result.split(':') | |
| 542 if len(name_and_score) < 2: | |
| 543 break | |
| 544 name = name_and_score[0].strip() | |
| 545 score = name_and_score[1].strip() | |
| 546 if version == JS or version == 'v8': | |
| 547 version = JS | |
| 548 bench_dict = self.values_dict[browser][JS] | |
| 549 else: | 738 else: |
| 550 bench_dict = self.values_dict[browser][FROG] | 739 results = line.split('<br />') |
| 551 bench_dict[name] += [float(score)] | 740 for result in results: |
| 552 self.revision_dict[browser][version][name] += [revision_num] | 741 name_and_score = result.split(':') |
| 553 | 742 if len(name_and_score) < 2: |
| 554 f.close() | 743 break |
| 555 self.calculate_geometric_mean(browser, version, revision_num) | 744 name = name_and_score[0].strip() |
| 556 | 745 score = name_and_score[1].strip() |
| 557 | 746 if version == 'js' or version == 'v8': |
| 558 # TODO(vsm): This should not be hardcoded here if possible. | 747 version = 'js' |
| 559 DROMAEO_BENCHMARKS = { | 748 bench_dict = self.test.values_dict[browser]['js'] |
| 560 'attr': ('attributes', [ | 749 else: |
| 561 'getAttribute', | 750 bench_dict = self.test.values_dict[browser]['frog'] |
| 562 'element.property', | 751 bench_dict[name] += [float(score)] |
| 563 'setAttribute', | 752 self.test.revision_dict[browser][version][name] += [revision_num] |
| 564 'element.property = value']), | 753 |
| 565 'modify': ('modify', [ | 754 f.close() |
| 566 'createElement', | 755 self.calculate_geometric_mean(browser, version, revision_num) |
| 567 'createTextNode', | 756 |
| 568 'innerHTML', | 757 class DromaeoTester(Tester): |
| 569 'cloneNode', | 758 DROMAEO_BENCHMARKS = { |
| 570 'appendChild', | 759 'attr': ('attributes', [ |
| 571 'insertBefore']), | 760 'getAttribute', |
| 572 'query': ('query', [ | 761 'element.property', |
| 573 'getElementById', | 762 'setAttribute', |
| 574 'getElementById (not in document)', | 763 'element.property = value']), |
| 575 'getElementsByTagName(div)', | 764 'modify': ('modify', [ |
| 576 'getElementsByTagName(p)', | 765 'createElement', |
| 577 'getElementsByTagName(a)', | 766 'createTextNode', |
| 578 'getElementsByTagName(*)', | 767 'innerHTML', |
| 579 'getElementsByTagName (not in document)', | 768 'cloneNode', |
| 580 'getElementsByName', | 769 'appendChild', |
| 581 'getElementsByName (not in document)']), | 770 'insertBefore']), |
| 582 'traverse': ('traverse', [ | 771 'query': ('query', [ |
| 583 'firstChild', | 772 'getElementById', |
| 584 'lastChild', | 773 'getElementById (not in document)', |
| 585 'nextSibling', | 774 'getElementsByTagName(div)', |
| 586 'previousSibling', | 775 'getElementsByTagName(p)', |
| 587 'childNodes']) | 776 'getElementsByTagName(a)', |
| 588 } | 777 'getElementsByTagName(*)', |
| 589 | 778 'getElementsByTagName (not in document)', |
| 590 # Use legal appengine filenames for benchmark names. | 779 'getElementsByName', |
| 591 def legalize_filename(str): | 780 'getElementsByName (not in document)']), |
| 592 remap = { | 781 'traverse': ('traverse', [ |
| 593 ' ': '_', | 782 'firstChild', |
| 594 '(': '_', | 783 'lastChild', |
| 595 ')': '_', | 784 'nextSibling', |
| 596 '*': 'ALL', | 785 'previousSibling', |
| 597 '=': 'ASSIGN', | 786 'childNodes']) |
| 598 } | 787 } |
| 599 for (old, new) in remap.iteritems(): | 788 |
| 600 str = str.replace(old, new) | 789 # Use legal appengine filenames for benchmark names. |
| 601 return str | 790 @staticmethod |
| 602 | 791 def legalize_filename(str): |
| 603 # TODO(vsm): This is a hack to skip breaking tests. Triage this | 792 remap = { |
| 604 # failure properly. The modify suite fails on 32-bit chrome on | 793 ' ': '_', |
| 605 # the mac. | 794 '(': '_', |
| 606 def get_valid_dromaeo_tags(): | 795 ')': '_', |
| 607 tags = [tag for (tag, _) in DROMAEO_BENCHMARKS.values()] | 796 '*': 'ALL', |
| 608 if platform.system() == 'Darwin': | 797 '=': 'ASSIGN', |
| 609 tags.remove('modify') | 798 } |
| 610 return tags | 799 for (old, new) in remap.iteritems(): |
| 611 | 800 str = str.replace(old, new) |
| 612 def get_dromaeo_benchmarks(): | 801 return str |
| 613 valid = get_valid_dromaeo_tags() | 802 |
| 614 benchmarks = reduce(lambda l1,l2: l1+l2, | 803 # TODO(vsm): This is a hack to skip breaking tests. Triage this |
| 615 [tests for (tag, tests) in | 804 # failure properly. The modify suite fails on 32-bit chrome on |
| 616 DROMAEO_BENCHMARKS.values() if tag in valid]) | 805 # the mac. |
| 617 return map(legalize_filename, benchmarks) | 806 @staticmethod |
| 618 | 807 def get_valid_dromaeo_tags(): |
| 619 def get_dromaeo_versions(): | 808 tags = [tag for (tag, _) in DromaeoTester.DROMAEO_BENCHMARKS.values()] |
| 620 return ['js', 'frog_dom', 'frog_html'] | 809 if platform.system() == 'Darwin': |
| 621 | 810 tags.remove('modify') |
| 622 def get_dromaeo_url_query(version): | 811 return tags |
| 623 version = version.replace('_','&') | 812 |
| 624 tags = get_valid_dromaeo_tags() | 813 @staticmethod |
| 625 return '|'.join([ '%s&%s' % (version, tag) for tag in tags]) | 814 def get_dromaeo_benchmarks(): |
| 626 | 815 valid = DromaeoTester.get_valid_dromaeo_tags() |
| 627 class DromaeoTest(PerformanceTest): | 816 benchmarks = reduce(lambda l1,l2: l1+l2, |
| 817 [tests for (tag, tests) in | |
| 818 DromaeoTester.DROMAEO_BENCHMARKS.values() | |
| 819 if tag in valid]) | |
| 820 return map(DromaeoTester.legalize_filename, benchmarks) | |
| 821 | |
| 822 @staticmethod | |
| 823 def get_dromaeo_versions(): | |
| 824 return ['js', 'frog_dom', 'frog_html'] | |
| 825 | |
| 826 | |
| 827 class DromaeoTest(RuntimePerformanceTest): | |
| 628 """Runs Dromaeo tests, in the browser.""" | 828 """Runs Dromaeo tests, in the browser.""" |
| 629 def __init__(self): | 829 def __init__(self, test_runner): |
| 630 super(DromaeoTest, self).__init__( | 830 super(DromaeoTest, self).__init__( |
| 631 DROMAEO, get_browsers(), 'browser', | 831 DromaeoTest.name(), BrowserTester.get_browsers(), 'browser', |
| 632 get_dromaeo_versions(), get_dromaeo_benchmarks()) | 832 DromaeoTester.get_dromaeo_versions(), |
| 633 | 833 DromaeoTester.get_dromaeo_benchmarks(), test_runner, |
| 634 def run_tests(self): | 834 DromaeoTest.DromaeoPerfTester(self), |
| 635 """Run dromaeo in the browser.""" | 835 DromaeoTest.DromaeoFileProcessor(self)) |
| 636 | 836 |
| 637 # Build tests. | 837 @staticmethod |
| 638 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | 838 def name(): |
| 639 current_path = os.getcwd() | 839 return 'dromaeo' |
| 640 os.chdir(dromaeo_path) | 840 |
| 641 run_cmd(['python', 'generate_frog_tests.py']) | 841 class DromaeoPerfTester(DromaeoTester): |
| 642 os.chdir(current_path) | 842 def run_tests(self): |
| 643 | 843 """Run dromaeo in the browser.""" |
| 644 versions = get_dromaeo_versions() | 844 |
| 645 | 845 # Build tests. |
| 646 for browser in get_browsers(): | 846 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') |
| 647 for version_name in versions: | 847 current_path = os.getcwd() |
| 648 version = get_dromaeo_url_query(version_name) | 848 os.chdir(dromaeo_path) |
| 649 self.trace_file = os.path.join('tools', 'testing', 'perf_testing', | 849 self.test.test_runner.run_cmd(['python', 'generate_frog_tests.py']) |
| 650 self.result_folder_name, | 850 os.chdir(current_path) |
| 651 'dromaeo-%s-%s-%s' % (self.cur_time, browser, version_name)) | 851 |
| 652 self.add_svn_revision_to_trace(self.trace_file) | 852 versions = DromaeoTester.get_dromaeo_versions() |
| 653 file_path = os.path.join(os.getcwd(), dromaeo_path, | 853 |
| 654 'index-js.html?%s' % version) | 854 for browser in BrowserTester.get_browsers(): |
| 655 run_cmd(['python', os.path.join('tools', 'testing', 'run_selenium.py'), | 855 for version_name in versions: |
| 656 '--out', file_path, '--browser', browser, | 856 version = DromaeoTest.DromaeoPerfTester.get_dromaeo_url_query( |
| 657 '--timeout', '200', '--mode', 'dromaeo'], self.trace_file, | 857 version_name) |
| 658 append=True) | 858 self.test.trace_file = os.path.join( |
| 659 | 859 'tools', 'testing', 'perf_testing', self.test.result_folder_name, |
| 660 def process_file(self, afile): | 860 'dromaeo-%s-%s-%s' % (self.test.cur_time, browser, version_name)) |
| 661 """Comb through the html to find the performance results.""" | 861 self.add_svn_revision_to_trace(self.test.trace_file) |
| 662 parts = afile.split('-') | 862 file_path = os.path.join(os.getcwd(), dromaeo_path, |
| 663 browser = parts[2] | 863 'index-js.html?%s' % version) |
| 664 version = parts[3] | 864 self.test.test_runner.run_cmd( |
| 665 | 865 ['python', os.path.join('tools', 'testing', 'run_selenium.py'), |
| 666 bench_dict = self.values_dict[browser][version] | 866 '--out', file_path, '--browser', browser, |
| 667 | 867 '--timeout', '200', '--mode', 'dromaeo'], self.test.trace_file, |
| 668 f = open(os.path.join(self.result_folder_name, afile)) | 868 append=True) |
| 669 lines = f.readlines() | 869 |
| 670 i = 0 | 870 @staticmethod |
| 671 revision_num = 0 | 871 def get_dromaeo_url_query(version): |
| 672 revision_pattern = r'Revision: (\d+)' | 872 version = version.replace('_','&') |
| 673 suite_pattern = r'<div class="result-item done">(.+?)</ol></div>' | 873 tags = DromaeoTester.get_valid_dromaeo_tags() |
| 674 result_pattern = r'<b>(.+?)</b>(.+?)<small> runs/s(.+)' | 874 return '|'.join([ '%s&%s' % (version, tag) for tag in tags]) |
| 675 | 875 |
| 676 for line in lines: | 876 |
| 677 rev = re.match(revision_pattern, line.strip()) | 877 class DromaeoFileProcessor(Processor): |
| 678 if rev: | 878 def process_file(self, afile): |
| 679 revision_num = int(rev.group(1)) | 879 """Comb through the html to find the performance results.""" |
| 680 continue | 880 parts = afile.split('-') |
| 681 | 881 browser = parts[2] |
| 682 suite_results = re.findall(suite_pattern, line) | 882 version = parts[3] |
| 683 if suite_results: | 883 |
| 684 for suite_result in suite_results: | 884 bench_dict = self.test.values_dict[browser][version] |
| 685 results = re.findall(r'<li>(.*?)</li>', suite_result) | 885 |
| 686 if results: | 886 f = open(os.path.join(self.test.result_folder_name, afile)) |
| 687 for result in results: | 887 lines = f.readlines() |
| 688 r = re.match(result_pattern, result) | 888 i = 0 |
| 689 name = legalize_filename(r.group(1).strip(':')) | 889 revision_num = 0 |
| 690 score = float(r.group(2)) | 890 revision_pattern = r'Revision: (\d+)' |
| 691 bench_dict[name] += [float(score)] | 891 suite_pattern = r'<div class="result-item done">(.+?)</ol></div>' |
| 692 self.revision_dict[browser][version][name] += [revision_num] | 892 result_pattern = r'<b>(.+?)</b>(.+?)<small> runs/s(.+)' |
| 693 | 893 |
| 694 f.close() | 894 for line in lines: |
| 695 self.calculate_geometric_mean(browser, version, revision_num) | 895 rev = re.match(revision_pattern, line.strip()) |
| 696 | 896 if rev: |
| 697 | 897 revision_num = int(rev.group(1)) |
| 698 class DromaeoSizeTest(TestRunner): | 898 continue |
| 899 | |
| 900 suite_results = re.findall(suite_pattern, line) | |
| 901 if suite_results: | |
| 902 for suite_result in suite_results: | |
| 903 results = re.findall(r'<li>(.*?)</li>', suite_result) | |
| 904 if results: | |
| 905 for result in results: | |
| 906 r = re.match(result_pattern, result) | |
| 907 name = DromaeoTester.legalize_filename( | |
| 908 r.group(1).strip(':')) | |
| 909 score = float(r.group(2)) | |
| 910 bench_dict[name] += [float(score)] | |
| 911 self.test.revision_dict[browser][version][name] += \ | |
| 912 [revision_num] | |
| 913 | |
| 914 f.close() | |
| 915 self.calculate_geometric_mean(browser, version, revision_num) | |
| 916 | |
| 917 | |
| 918 class DromaeoSizeTest(Test): | |
| 699 """Run tests to determine the compiled file output size of Dromaeo.""" | 919 """Run tests to determine the compiled file output size of Dromaeo.""" |
| 700 def __init__(self): | 920 def __init__(self, test_runner): |
| 701 super(DromaeoSizeTest, self).__init__( | 921 super(DromaeoSizeTest, self).__init__( |
| 702 DROMAEO_SIZE, | 922 DromaeoSizeTest.name(), |
| 703 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | 923 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], |
| 704 DROMAEO_BENCHMARKS.keys()) | 924 DromaeoTester.DROMAEO_BENCHMARKS.keys(), test_runner, |
| 705 | 925 DromaeoSizeTest.DromaeoSizeTester(self), |
| 706 def run_tests(self): | 926 DromaeoSizeTest.DromaeoSizeProcessor(self), |
| 707 # Build tests. | 927 DromaeoSizeTest.DromaeoSizeGrapher(self), extra_metrics=['sum']) |
| 708 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | 928 |
| 709 current_path = os.getcwd() | 929 @staticmethod |
| 710 os.chdir(dromaeo_path) | 930 def name(): |
| 711 run_cmd(['python', os.path.join('generate_frog_tests.py')]) | 931 return 'dromaeo-size' |
| 712 os.chdir(current_path) | 932 |
| 713 | 933 |
| 714 self.trace_file = os.path.join('tools', 'testing', 'perf_testing', | 934 class DromaeoSizeTester(DromaeoTester): |
| 715 self.result_folder_name, self.result_folder_name + self.cur_time) | 935 def run_tests(self): |
| 716 self.add_svn_revision_to_trace(self.trace_file) | 936 # Build tests. |
| 717 | 937 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') |
| 718 variants = [ | 938 current_path = os.getcwd() |
| 719 ('frog_dom', ''), | 939 os.chdir(dromaeo_path) |
| 720 ('frog_html', '-html'), | 940 self.test.test_runner.run_cmd( |
| 721 ('frog_htmlidiomatic', '-htmlidiomatic')] | 941 ['python', os.path.join('generate_frog_tests.py')]) |
| 722 | 942 os.chdir(current_path) |
| 723 test_path = os.path.join(dromaeo_path, 'tests') | 943 |
| 724 frog_path = os.path.join(test_path, 'frog') | 944 self.test.trace_file = os.path.join( |
| 725 total_size = {} | 945 'tools', 'testing', 'perf_testing', self.test.result_folder_name, |
| 726 for (variant, _) in variants: | 946 self.test.result_folder_name + self.test.cur_time) |
| 727 total_size[variant] = 0 | 947 self.add_svn_revision_to_trace(self.test.trace_file) |
| 728 total_dart_size = 0 | 948 |
| 729 for suite in DROMAEO_BENCHMARKS.keys(): | 949 variants = [ |
| 730 dart_size = 0 | 950 ('frog_dom', ''), |
| 951 ('frog_html', '-html'), | |
| 952 ('frog_htmlidiomatic', '-htmlidiomatic')] | |
| 953 | |
| 954 test_path = os.path.join(dromaeo_path, 'tests') | |
| 955 frog_path = os.path.join(test_path, 'frog') | |
| 956 total_size = {} | |
| 957 for (variant, _) in variants: | |
| 958 total_size[variant] = 0 | |
| 959 total_dart_size = 0 | |
| 960 for suite in DromaeoTester.DROMAEO_BENCHMARKS.keys(): | |
| 961 dart_size = 0 | |
| 962 try: | |
| 963 dart_size = os.path.getsize(os.path.join(test_path, | |
| 964 'dom-%s.dart' % suite)) | |
| 965 except OSError: | |
| 966 pass #If compilation failed, continue on running other tests. | |
| 967 | |
| 968 total_dart_size += dart_size | |
| 969 self.test.test_runner.run_cmd( | |
| 970 ['echo', 'Size (dart, %s): %s' % (suite, str(dart_size))], | |
| 971 self.test.trace_file, append=True) | |
| 972 | |
| 973 for (variant, suffix) in variants: | |
| 974 name = 'dom-%s%s.dart.js' % (suite, suffix) | |
| 975 js_size = 0 | |
| 976 try: | |
| 977 # TODO(vsm): Strip comments at least. Consider compression. | |
| 978 js_size = os.path.getsize(os.path.join(frog_path, name)) | |
| 979 except OSError: | |
| 980 pass #If compilation failed, continue on running other tests. | |
| 981 | |
| 982 total_size[variant] += js_size | |
| 983 self.test.test_runner.run_cmd( | |
| 984 ['echo', 'Size (%s, %s): %s' % (variant, suite, str(js_size))], | |
| 985 self.test.trace_file, append=True) | |
| 986 | |
| 987 self.test.test_runner.run_cmd( | |
| 988 ['echo', 'Size (dart, %s): %s' % (total_dart_size, | |
| 989 self.test.extra_metrics[0])], | |
| 990 self.test.trace_file, append=True) | |
| 991 for (variant, _) in variants: | |
| 992 self.test.test_runner.run_cmd( | |
| 993 ['echo', 'Size (%s, %s): %s' % (variant, self.test.extra_metrics[0], | |
| 994 total_size[variant])], | |
| 995 self.test.trace_file, append=True) | |
| 996 | |
| 997 class DromaeoSizeProcessor(Processor): | |
| 998 def process_file(self, afile): | |
| 999 """Pull all the relevant information out of a given tracefile. | |
| 1000 | |
| 1001 Args: | |
| 1002 afile: is the filename string we will be processing.""" | |
| 1003 os.chdir(os.path.join(TestRunner.DART_INSTALL_LOCATION, 'tools', | |
| 1004 'testing', 'perf_testing')) | |
| 1005 f = open(os.path.join(self.test.result_folder_name, afile)) | |
| 1006 tabulate_data = False | |
| 1007 revision_num = 0 | |
| 1008 revision_pattern = r'Revision: (\d+)' | |
| 1009 result_pattern = r'Size \((\w+), ([a-zA-Z0-9-]+)\): (\d+)' | |
| 1010 | |
| 1011 for line in f.readlines(): | |
| 1012 rev = re.match(revision_pattern, line.strip()) | |
| 1013 if rev: | |
| 1014 revision_num = int(rev.group(1)) | |
| 1015 continue | |
| 1016 | |
| 1017 result = re.match(result_pattern, line.strip()) | |
| 1018 if result: | |
| 1019 variant = result.group(1) | |
| 1020 metric = result.group(2) | |
| 1021 num = result.group(3) | |
| 1022 if num.find('.') == -1: | |
| 1023 num = int(num) | |
| 1024 else: | |
| 1025 num = float(num) | |
| 1026 self.test.values_dict['browser'][variant][metric] += [num] | |
| 1027 self.test.revision_dict['browser'][variant][metric] += [revision_num] | |
| 1028 | |
| 1029 f.close() | |
| 1030 class DromaeoSizeGrapher(Grapher): | |
| 1031 def plot_results(self, png_filename): | |
| 1032 self.style_and_save_perf_plot( | |
| 1033 'Compiled Dromaeo Sizes', | |
| 1034 'Size (in bytes)', 10, 10, 'lower left', png_filename, | |
| 1035 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | |
| 1036 DromaeoTester.DROMAEO_BENCHMARKS.keys()) | |
| 1037 | |
| 1038 self.style_and_save_perf_plot( | |
| 1039 'Compiled Dromaeo Sizes', | |
| 1040 'Size (in bytes)', 10, 10, 'lower left', '2' + png_filename, | |
| 1041 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | |
| 1042 [self.test.extra_metrics[0]]) | |
| 1043 | |
| 1044 | |
| 1045 class CompileTimeAndSizeTest(Test): | |
| 1046 """Run tests to determine how long minfrog takes to compile, and the compiled | |
| 1047 file output size of some benchmarking files.""" | |
| 1048 def __init__(self, test_runner): | |
| 1049 """Reference to the test_runner object that notifies us when to begin | |
| 1050 testing.""" | |
| 1051 super(CompileTimeAndSizeTest, self).__init__( | |
| 1052 CompileTimeAndSizeTest.name(), ['commandline'], ['frog'], | |
| 1053 ['Compiling on Dart VM', 'Bootstrapping', 'minfrog', 'swarm', 'total'], | |
| 1054 test_runner, CompileTimeAndSizeTest.CompileTester(self), | |
| 1055 CompileTimeAndSizeTest.CompileProcessor(self), | |
| 1056 CompileTimeAndSizeTest.CompileGrapher(self)) | |
| 1057 self.dart_compiler = os.path.join( | |
| 1058 TestRunner.DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), | |
| 1059 'release', 'ia32'), 'dart-sdk', 'bin', 'frogc') | |
| 1060 _suffix = '' | |
| 1061 if platform.system() == 'Windows': | |
| 1062 _suffix = '.exe' | |
| 1063 self.dart_vm = os.path.join( | |
| 1064 TestRunner.DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), | |
| 1065 'release', 'ia32'), 'dart-sdk', 'bin','dart' + _suffix) | |
| 1066 self.failure_threshold = { | |
| 1067 'Compiling on Dart VM' : 1, 'Bootstrapping' : .5, 'minfrog' : 100, | |
| 1068 'swarm' : 100, 'total' : 100} | |
| 1069 | |
| 1070 @staticmethod | |
| 1071 def name(): | |
| 1072 return 'time-size' | |
| 1073 | |
| 1074 class CompileTester(Tester): | |
| 1075 def run_tests(self): | |
| 1076 os.chdir('frog') | |
| 1077 self.test.trace_file = os.path.join( | |
| 1078 '..', 'tools', 'testing', 'perf_testing', | |
| 1079 self.test.result_folder_name, | |
| 1080 self.test.result_folder_name + self.test.cur_time) | |
| 1081 | |
| 1082 self.add_svn_revision_to_trace(self.test.trace_file) | |
| 1083 | |
| 1084 elapsed = self.test.test_runner.time_cmd( | |
| 1085 [self.test.dart_vm, os.path.join('.', 'minfrogc.dart'), | |
| 1086 '--out=minfrog', 'minfrog.dart']) | |
| 1087 self.test.test_runner.run_cmd( | |
| 1088 ['echo', '%f Compiling on Dart VM in production mode in seconds' | |
| 1089 % elapsed], self.test.trace_file, append=True) | |
| 1090 elapsed = self.test.test_runner.time_cmd( | |
| 1091 [os.path.join('.', 'minfrog'), '--out=minfrog', 'minfrog.dart', | |
| 1092 os.path.join('tests', 'hello.dart')]) | |
| 1093 if elapsed < self.test.failure_threshold['Bootstrapping']: | |
| 1094 #minfrog didn't compile correctly. Stop testing now, because subsequent | |
| 1095 #numbers will be meaningless. | |
| 1096 return | |
| 1097 size = os.path.getsize('minfrog') | |
| 1098 self.test.test_runner.run_cmd( | |
| 1099 ['echo', '%f Bootstrapping time in seconds in production mode' % | |
| 1100 elapsed], self.test.trace_file, append=True) | |
| 1101 self.test.test_runner.run_cmd( | |
| 1102 ['echo', '%d Generated checked minfrog size' % size], | |
| 1103 self.test.trace_file, append=True) | |
| 1104 | |
| 1105 self.test.test_runner.run_cmd( | |
| 1106 [self.test.dart_compiler, '--out=swarm-result', | |
| 1107 os.path.join('..', 'samples', 'swarm', | |
| 1108 'swarm.dart')]) | |
| 1109 | |
| 1110 swarm_size = 0 | |
| 731 try: | 1111 try: |
| 732 dart_size = os.path.getsize(os.path.join(test_path, | 1112 swarm_size = os.path.getsize('swarm-result') |
| 733 'dom-%s.dart' % suite)) | |
| 734 except OSError: | 1113 except OSError: |
| 735 pass #If compilation failed, continue on running other tests. | 1114 pass #If compilation failed, continue on running other tests. |
| 736 | 1115 |
| 737 total_dart_size += dart_size | 1116 self.test.test_runner.run_cmd( |
| 738 run_cmd(['echo', 'Size (dart, %s): %s' % (suite, str(dart_size))], | 1117 [self.test.dart_compiler, '--out=total-result', |
| 739 self.trace_file, append=True) | 1118 os.path.join('..', 'samples', 'total', |
| 740 | 1119 'client', 'Total.dart')]) |
| 741 for (variant, suffix) in variants: | 1120 total_size = 0 |
| 742 name = 'dom-%s%s.dart.js' % (suite, suffix) | 1121 try: |
| 743 js_size = 0 | 1122 total_size = os.path.getsize('total-result') |
| 744 try: | 1123 except OSError: |
| 745 # TODO(vsm): Strip comments at least. Consider compression. | 1124 pass #If compilation failed, continue on running other tests. |
| 746 js_size = os.path.getsize(os.path.join(frog_path, name)) | 1125 |
| 747 except OSError: | 1126 self.test.test_runner.run_cmd( |
| 748 pass #If compilation failed, continue on running other tests. | 1127 ['echo', '%d Generated checked swarm size' % swarm_size], |
| 749 | 1128 self.test.trace_file, append=True) |
| 750 total_size[variant] += js_size | 1129 |
| 751 run_cmd(['echo', 'Size (%s, %s): %s' % (variant, suite, | 1130 self.test.test_runner.run_cmd( |
| 752 str(js_size))], | 1131 ['echo', '%d Generated checked total size' % total_size], |
| 753 self.trace_file, append=True) | 1132 self.test.trace_file, append=True) |
| 754 | 1133 |
| 755 # TODO(vsm): Change GEO_MEAN to sum. The base class assumes | 1134 #Revert our newly built minfrog to prevent conflicts when we update |
| 756 # GEO_MEAN right now. | 1135 self.test.test_runner.run_cmd( |
| 757 run_cmd(['echo', 'Size (dart, %s): %s' % (total_dart_size, GEO_MEAN)], | 1136 ['svn', 'revert', os.path.join(os.getcwd(), 'frog', 'minfrog')]) |
| 758 self.trace_file, append=True) | 1137 |
| 759 for (variant, _) in variants: | 1138 os.chdir('..') |
| 760 run_cmd(['echo', 'Size (%s, %s): %s' % (variant, GEO_MEAN, | 1139 |
| 761 total_size[variant])], | 1140 class CompileProcessor(Processor): |
| 762 self.trace_file, append=True) | 1141 def process_file(self, afile): |
| 763 | 1142 """Pull all the relevant information out of a given tracefile. |
| 764 | 1143 |
| 765 def process_file(self, afile): | 1144 Args: |
| 766 """Pull all the relevant information out of a given tracefile. | 1145 afile: is the filename string we will be processing.""" |
| 767 | 1146 os.chdir(os.path.join(TestRunner.DART_INSTALL_LOCATION, 'tools', |
| 768 Args: | 1147 'testing', 'perf_testing')) |
| 769 afile: is the filename string we will be processing.""" | 1148 f = open(os.path.join(self.test.result_folder_name, afile)) |
| 770 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 1149 tabulate_data = False |
| 771 'perf_testing')) | 1150 revision_num = 0 |
| 772 f = open(os.path.join(self.result_folder_name, afile)) | 1151 for line in f.readlines(): |
| 773 tabulate_data = False | 1152 tokens = line.split() |
| 774 revision_num = 0 | 1153 if 'Revision' in line: |
| 775 revision_pattern = r'Revision: (\d+)' | 1154 revision_num = int(line.split()[1]) |
| 776 result_pattern = r'Size \((\w+), ([a-zA-Z0-9-]+)\): (\d+)' | |
| 777 | |
| 778 for line in f.readlines(): | |
| 779 rev = re.match(revision_pattern, line.strip()) | |
| 780 if rev: | |
| 781 revision_num = int(rev.group(1)) | |
| 782 continue | |
| 783 | |
| 784 result = re.match(result_pattern, line.strip()) | |
| 785 if result: | |
| 786 variant = result.group(1) | |
| 787 metric = result.group(2) | |
| 788 num = result.group(3) | |
| 789 if num.find('.') == -1: | |
| 790 num = int(num) | |
| 791 else: | 1155 else: |
| 792 num = float(num) | 1156 for metric in self.test.values_list: |
| 793 self.values_dict['browser'][variant][metric] += [num] | 1157 if metric in line: |
| 794 self.revision_dict['browser'][variant][metric] += [revision_num] | 1158 num = tokens[0] |
| 795 | 1159 if num.find('.') == -1: |
| 796 f.close() | 1160 num = int(num) |
| 797 | 1161 else: |
| 798 def plot_results(self, png_filename): | 1162 num = float(num) |
| 799 self.style_and_save_perf_plot( | 1163 self.test.values_dict['commandline']['frog'][metric] += [num] |
| 800 'Compiled Dromaeo Sizes', | 1164 self.test.revision_dict['commandline']['frog'][metric] += \ |
| 801 'Size (in bytes)', 10, 10, 'lower left', png_filename, | 1165 [revision_num] |
| 802 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | 1166 |
| 803 DROMAEO_BENCHMARKS.keys()) | 1167 if revision_num != 0: |
| 804 | 1168 for metric in self.test.values_list: |
| 805 self.style_and_save_perf_plot( | 1169 self.test.revision_dict['commandline']['frog'][metric].pop() |
| 806 'Compiled Dromaeo Sizes', | 1170 self.test.revision_dict['commandline']['frog'][metric] += \ |
| 807 'Size (in bytes)', 10, 10, 'lower left', '2' + png_filename, | 1171 [revision_num] |
| 808 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | 1172 # Fill in 0 if compilation failed. |
| 809 [GEO_MEAN]) | 1173 if self.test.values_dict['commandline']['frog'][metric][-1] < \ |
| 810 | 1174 self.test.failure_threshold[metric]: |
| 811 | 1175 self.test.values_dict['commandline']['frog'][metric] += [0] |
| 812 | 1176 self.test.revision_dict['commandline']['frog'][metric] += \ |
| 813 class CompileTimeAndSizeTest(TestRunner): | 1177 [revision_num] |
| 814 """Run tests to determine how long minfrog takes to compile, and the compiled | 1178 |
| 815 file output size of some benchmarking files.""" | 1179 f.close() |
|
vsm
2012/04/09 20:19:58
Extra newline for new class.
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 816 def __init__(self): | 1180 class CompileGrapher(Grapher): |
| 817 super(CompileTimeAndSizeTest, self).__init__(TIME_SIZE, | 1181 |
| 818 [COMMAND_LINE], [FROG], ['Compiling on Dart VM', 'Bootstrapping', | 1182 def plot_results(self, png_filename): |
| 819 'minfrog', 'swarm', 'total']) | 1183 self.style_and_save_perf_plot( |
| 820 self.failure_threshold = {'Compiling on Dart VM' : 1, 'Bootstrapping' : .5, | 1184 'Compiled minfrog Sizes', 'Size (in bytes)', 10, 10, 'lower left', |
| 821 'minfrog' : 100, 'swarm' : 100, 'total' : 100} | 1185 png_filename, ['commandline'], ['frog'], |
| 822 | 1186 ['swarm', 'total', 'minfrog']) |
| 823 def run_tests(self): | 1187 |
| 824 os.chdir('frog') | 1188 self.style_and_save_perf_plot( |
| 825 self.trace_file = os.path.join('..', 'tools', 'testing', 'perf_testing', | 1189 'Time to compile and bootstrap', |
| 826 self.result_folder_name, self.result_folder_name + self.cur_time) | 1190 'Seconds', 10, 10, 'lower left', '2' + png_filename, ['commandline'], |
| 827 | 1191 ['frog'], ['Bootstrapping', 'Compiling on Dart VM']) |
| 828 self.add_svn_revision_to_trace(self.trace_file) | 1192 |
| 829 | 1193 |
| 830 elapsed = time_cmd([DART_VM, os.path.join('.', 'minfrogc.dart'), | 1194 class TestBuilder(object): |
| 831 '--out=minfrog', 'minfrog.dart']) | 1195 """Construct the desired test object.""" |
| 832 run_cmd(['echo', '%f Compiling on Dart VM in production mode in seconds' | 1196 available_suites = { |
| 833 % elapsed], self.trace_file, append=True) | 1197 BenchpressAndCoCommandLineTest.name(): BenchpressAndCoCommandLineTest, |
| 834 elapsed = time_cmd([os.path.join('.', 'minfrog'), '--out=minfrog', | 1198 CompileTimeAndSizeTest.name(): CompileTimeAndSizeTest, |
| 835 'minfrog.dart', os.path.join('tests', 'hello.dart')]) | 1199 BenchpressAndCoBrowserTest.name(): BenchpressAndCoBrowserTest, |
| 836 if elapsed < self.failure_threshold['Bootstrapping']: | 1200 DromaeoTest.name(): DromaeoTest, |
| 837 #minfrog didn't compile correctly. Stop testing now, because subsequent | 1201 DromaeoSizeTest.name(): DromaeoSizeTest, |
|
vsm
2012/04/09 20:19:58
Can you get rid of the redundancy here? E.g., som
Emily Fortuna
2012/04/09 21:15:03
Ah, much nicer. done.
| |
| 838 #numbers will be meaningless. | 1202 } |
| 839 return | 1203 |
| 840 size = os.path.getsize('minfrog') | 1204 def make_test(self, test_name, test_runner): |
|
vsm
2012/04/09 20:19:58
Why is this an instance method but not the below?
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 841 run_cmd(['echo', '%f Bootstrapping time in seconds in production mode' % | 1205 return TestBuilder.available_suites[test_name](test_runner) |
| 842 elapsed], self.trace_file, append=True) | 1206 |
| 843 run_cmd(['echo', '%d Generated checked minfrog size' % size], | 1207 @staticmethod |
| 844 self.trace_file, append=True) | 1208 def available_suite_names(): |
| 845 | 1209 return TestBuilder.available_suites.keys() |
| 846 run_cmd([DART_COMPILER, '--out=swarm-result', | 1210 |
| 847 os.path.join('..', 'samples', 'swarm', | |
| 848 'swarm.dart')]) | |
| 849 swarm_size = 0 | |
| 850 try: | |
| 851 swarm_size = os.path.getsize('swarm-result') | |
| 852 except OSError: | |
| 853 pass #If compilation failed, continue on running other tests. | |
| 854 | |
| 855 run_cmd([DART_COMPILER, '--out=total-result', | |
| 856 os.path.join('..', 'samples', 'total', | |
| 857 'client', 'Total.dart')]) | |
| 858 total_size = 0 | |
| 859 try: | |
| 860 total_size = os.path.getsize('total-result') | |
| 861 except OSError: | |
| 862 pass #If compilation failed, continue on running other tests. | |
| 863 | |
| 864 run_cmd(['echo', '%d Generated checked swarm size' % swarm_size], | |
| 865 self.trace_file, append=True) | |
| 866 | |
| 867 run_cmd(['echo', '%d Generated checked total size' % total_size], | |
| 868 self.trace_file, append=True) | |
| 869 os.chdir('..') | |
| 870 | |
| 871 def process_file(self, afile): | |
| 872 """Pull all the relevant information out of a given tracefile. | |
| 873 | |
| 874 Args: | |
| 875 afile: is the filename string we will be processing.""" | |
| 876 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | |
| 877 'perf_testing')) | |
| 878 f = open(os.path.join(self.result_folder_name, afile)) | |
| 879 tabulate_data = False | |
| 880 revision_num = 0 | |
| 881 for line in f.readlines(): | |
| 882 tokens = line.split() | |
| 883 if 'Revision' in line: | |
| 884 revision_num = int(line.split()[1]) | |
| 885 else: | |
| 886 for metric in self.values_list: | |
| 887 if metric in line: | |
| 888 num = tokens[0] | |
| 889 if num.find('.') == -1: | |
| 890 num = int(num) | |
| 891 else: | |
| 892 num = float(num) | |
| 893 self.values_dict[COMMAND_LINE][FROG][metric] += [num] | |
| 894 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] | |
| 895 | |
| 896 if revision_num != 0: | |
| 897 for metric in self.values_list: | |
| 898 self.revision_dict[COMMAND_LINE][FROG][metric].pop() | |
| 899 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] | |
| 900 # Fill in 0 if compilation failed. | |
| 901 if self.values_dict[COMMAND_LINE][FROG][metric][-1] < \ | |
| 902 self.failure_threshold[metric]: | |
| 903 self.values_dict[COMMAND_LINE][FROG][metric] += [0] | |
| 904 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] | |
| 905 | |
| 906 f.close() | |
| 907 | |
| 908 def plot_results(self, png_filename): | |
| 909 self.style_and_save_perf_plot('Compiled minfrog Sizes', | |
| 910 'Size (in bytes)', 10, 10, 'lower left', png_filename, [COMMAND_LINE], | |
| 911 [FROG], ['swarm', 'total', 'minfrog']) | |
| 912 | |
| 913 self.style_and_save_perf_plot('Time to compile and bootstrap', | |
| 914 'Seconds', 10, 10, 'lower left', '2' + png_filename, [COMMAND_LINE], | |
| 915 [FROG], ['Bootstrapping', 'Compiling on Dart VM']) | |
| 916 | |
| 917 # TODO(vsm): Make these names consistent with BROWSER_PERF, CL_PERF, | |
| 918 # etc. above. | |
| 919 SUITES = { | |
| 920 CL_PERF: CommandLinePerformanceTest, | |
| 921 TIME_SIZE: CompileTimeAndSizeTest, | |
| 922 BROWSER_PERF: BrowserStandalonePerformanceTest, | |
| 923 DROMAEO: DromaeoTest, | |
| 924 DROMAEO_SIZE: DromaeoSizeTest, | |
| 925 } | |
| 926 | |
| 927 def parse_args(): | |
| 928 parser = optparse.OptionParser() | |
| 929 # TODO(vsm): Change to a list to scale. | |
| 930 parser.add_option('--suites', '-s', dest='suites', | |
| 931 help='Run the specified comma-separated test suites from set: %s' % \ | |
| 932 ','.join(SUITES.keys()), | |
| 933 action='store', default=None) | |
| 934 parser.add_option('--forever', '-f', dest='continuous', | |
| 935 help='Run this script forever, always checking for the next svn ' | |
| 936 'checkin', action='store_true', default=False) | |
| 937 parser.add_option('--graph-only', '-g', dest='graph_only', default=False, | |
| 938 help='Do not run tests, only regenerate graphs', action='store_true') | |
| 939 parser.add_option('--nobuild', '-n', dest='no_build', action='store_true', | |
| 940 help='Do not sync with the repository and do not rebuild.', default=False) | |
| 941 parser.add_option('--upload', '-u', dest='upload', | |
| 942 help='Upload data to app engine (will require authentication).', | |
| 943 action='store_true', default=False) | |
| 944 parser.add_option('--verbose', '-v', dest='verbose', | |
| 945 help='Print extra debug output', action='store_true', default=False) | |
| 946 | |
| 947 args, ignored = parser.parse_args() | |
| 948 | |
| 949 if not args.suites: | |
| 950 suites = SUITES.values() | |
| 951 else: | |
| 952 suites = [] | |
| 953 suitelist = args.suites.split(',') | |
| 954 for name in suitelist: | |
| 955 if name in SUITES: | |
| 956 suites.append(SUITES[name]) | |
| 957 else: | |
| 958 print 'Error: Invalid suite %s not in %s' % (name, | |
| 959 ','.join(SUITES.keys())) | |
| 960 sys.exit(1) | |
| 961 return (suites, args.continuous, args.verbose, args.no_build, | |
| 962 args.graph_only, args.upload) | |
| 963 | |
| 964 def run_test_sequence(suites, no_build, graph_only, upload): | |
| 965 # The buildbot already builds and syncs to a specific revision. Don't fight | |
| 966 # with it or replicate work. | |
| 967 if not no_build and sync_and_build() == 1: | |
| 968 return # The build is broken. | |
| 969 | |
| 970 for test in suites: | |
| 971 test().run(graph_only) | |
| 972 | |
| 973 if upload: | |
| 974 upload_to_app_engine(SUITES.keys()) | |
| 975 | 1211 |
| 976 def main(): | 1212 def main(): |
| 977 global VERBOSE | 1213 runner = TestRunner() |
| 978 (suites, continuous, verbose, no_build, graph_only, upload) = parse_args() | 1214 (suites, continuous, verbose, no_build, graph_only, upload) = \ |
| 979 VERBOSE = verbose | 1215 runner.parse_args() |
|
vsm
2012/04/09 20:19:58
I suggest changing this method to only return cont
Emily Fortuna
2012/04/09 21:15:03
Done.
| |
| 1216 runner.verbose = verbose | |
| 980 if continuous: | 1217 if continuous: |
| 981 while True: | 1218 while True: |
| 982 if has_new_code(): | 1219 if runner.has_new_code(): |
| 983 run_test_sequence(suites, no_build, graph_only, upload) | 1220 runner.run_test_sequence(suites, no_build, graph_only, upload) |
| 984 else: | 1221 else: |
| 985 time.sleep(SLEEP_TIME) | 1222 time.sleep(200) |
| 986 else: | 1223 else: |
| 987 run_test_sequence(suites, no_build, graph_only, upload) | 1224 runner.run_test_sequence(suites, no_build, graph_only, upload) |
| 988 | 1225 |
| 989 if __name__ == '__main__': | 1226 if __name__ == '__main__': |
| 990 main() | 1227 main() |
| OLD | NEW |