#!/usr/bin/python # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. """ Dart2js buildbot steps Runs tests for the dart2js compiler. """ import os import platform import re import shutil import socket import subprocess import sys import bot DARTIUM_BUILDER = r'none-dartium-(linux|mac|windows)' DART2JS_BUILDER = ( r'dart2js-(linux|mac|windows)(-(jsshell))?-(debug|release)(-(checked|host-checked))?(-(host-checked))?(-(minified))?(-(x64))?-?(\d*)-?(\d*)') DART2JS_FULL_BUILDER = r'dart2js-full-(linux|mac|windows)(-checked)?(-minified)?-(\d+)-(\d+)' WEB_BUILDER = ( r'dart2js-(ie9|ie10|ff|safari|chrome|chromeOnAndroid|opera|drt)-(win7|win8|mac10\.8|mac10\.7|linux)(-(all|html))?(-(csp))?(-(\d+)-(\d+))?') DART2JS_FULL_CONFIGURATIONS = { 'linux' : [ ], 'mac' : [ ], 'windows' : [ {'runtime' : 'ie9'}, {'runtime' : 'ie9', 'additional_flags' : ['--checked']}, {'runtime' : 'ff'}, {'runtime' : 'chrome'}, ], } def GetBuildInfo(builder_name, is_buildbot): """Returns a BuildInfo object for the current buildbot based on the name of the builder. """ compiler = None runtime = None mode = None system = None checked = False host_checked = False minified = False shard_index = None total_shards = None test_set = None csp = None arch = None dart2js_full = False dart2js_pattern = re.match(DART2JS_BUILDER, builder_name) dart2js_full_pattern = re.match(DART2JS_FULL_BUILDER, builder_name) web_pattern = re.match(WEB_BUILDER, builder_name) dartium_pattern = re.match(DARTIUM_BUILDER, builder_name) if web_pattern: compiler = 'dart2js' runtime = web_pattern.group(1) system = web_pattern.group(2) mode = 'release' test_set = web_pattern.group(4) if web_pattern.group(6) == 'csp': csp = True shard_index = web_pattern.group(8) total_shards = web_pattern.group(9) elif dart2js_full_pattern: mode = 'release' compiler = 'dart2js' dart2js_full = True system = dart2js_full_pattern.group(1) if dart2js_full_pattern.group(2): checked = True if dart2js_full_pattern.group(3): minified = True shard_index = dart2js_full_pattern.group(4) total_shards = dart2js_full_pattern.group(5) elif dart2js_pattern: compiler = 'dart2js' system = dart2js_pattern.group(1) runtime = 'd8' arch = 'ia32' if dart2js_pattern.group(3) == 'jsshell': runtime = 'jsshell' mode = dart2js_pattern.group(4) # The valid naming parts for checked and host-checked are: # Empty: checked=False, host_checked=False # -checked: checked=True, host_checked=False # -host-checked: checked=False, host_checked=True # -checked-host-checked: checked=True, host_checked=True if dart2js_pattern.group(6) == 'checked': checked = True if dart2js_pattern.group(6) == 'host-checked': host_checked = True if dart2js_pattern.group(8) == 'host-checked': host_checked = True if dart2js_pattern.group(10) == 'minified': minified = True if dart2js_pattern.group(12) == 'x64': arch = 'x64' shard_index = dart2js_pattern.group(13) total_shards = dart2js_pattern.group(14) elif dartium_pattern: compiler = 'none' runtime = 'dartium' mode = 'release' system = dartium_pattern.group(1) else : return None # We have both win7 and win8 bots, functionality is the same. if system.startswith('win'): system = 'windows' # We have both 10.8 and 10.7 bots, functionality is the same. if system == 'mac10.8' or system == 'mac10.7': system = 'mac' if (system == 'windows' and platform.system() != 'Windows') or ( system == 'mac' and platform.system() != 'Darwin') or ( system == 'linux' and platform.system() != 'Linux'): print ('Error: You cannot emulate a buildbot with a platform different ' 'from your own.') return None return bot.BuildInfo(compiler, runtime, mode, system, checked, host_checked, minified, shard_index, total_shards, is_buildbot, test_set, csp, arch, dart2js_full) def NeedsXterm(compiler, runtime): return runtime in ['ie9', 'ie10', 'chrome', 'safari', 'opera', 'ff', 'drt', 'dartium'] def TestStepName(name, flags): # Filter out flags with '=' as this breaks the /stats feature of the # build bot. flags = [x for x in flags if not '=' in x] return ('%s tests %s' % (name, ' '.join(flags))).strip() IsFirstTestStepCall = True def TestStep(name, mode, system, compiler, runtime, targets, flags, arch): step_name = TestStepName(name, flags) with bot.BuildStep(step_name, swallow_error=True): sys.stdout.flush() if NeedsXterm(compiler, runtime) and system == 'linux': cmd = ['xvfb-run', '-a'] else: cmd = [] user_test = os.environ.get('USER_TEST', 'no') cmd.extend([sys.executable, os.path.join(os.curdir, 'tools', 'test.py'), '--step_name=' + step_name, '--mode=' + mode, '--compiler=' + compiler, '--runtime=' + runtime, '--arch=' + arch, '--time', '--use-sdk', '--report', '--write-debug-log', '--write-test-outcome-log']) if user_test == 'yes': cmd.append('--progress=color') else: cmd.extend(['--progress=buildbot', '-v']) if runtime == 'safari': cmd.append('--clear_safari_cache') global IsFirstTestStepCall if IsFirstTestStepCall: IsFirstTestStepCall = False else: cmd.append('--append_logs') if flags: cmd.extend(flags) cmd.extend(targets) print 'Running: %s' % (' '.join(map(lambda arg: '"%s"' % arg, cmd))) sys.stdout.flush() bot.RunProcess(cmd) def TestCompiler(runtime, mode, system, flags, is_buildbot, arch, compiler=None, dart2js_full=False): """ test the compiler. Args: - runtime: either 'd8', 'jsshell', or one of the browsers, see GetBuildInfo - mode: either 'debug' or 'release' - system: either 'linux', 'mac', 'windows' - flags: extra flags to pass to test.dart - is_buildbot: true if we are running on a real buildbot instead of emulating one. - arch: The architecture to run on. - compiler: The compiler to use for test.py (default is 'dart2js'). """ if not compiler: compiler = 'dart2js' def GetPath(runtime): """ Helper to get the path to the Chrome or Firefox executable for a particular platform on the buildbot. Throws a KeyError if runtime is not either 'chrome' or 'ff'.""" if system == 'mac': partDict = {'chrome': 'Google\\ Chrome', 'ff': 'Firefox'} mac_path = '/Applications/%s.app/Contents/MacOS/%s' path_dict = {'chrome': mac_path % (partDict[runtime], partDict[runtime]), 'ff': mac_path % (partDict[runtime], partDict[runtime].lower())} elif system == 'linux': path_dict = {'ff': 'firefox', 'chrome': 'google-chrome'} else: # Windows. path_dict = {'ff': os.path.join('C:/', 'Program Files (x86)', 'Mozilla Firefox', 'firefox.exe'), 'chrome': os.path.join('C:/', 'Users', 'chrome-bot', 'AppData', 'Local', 'Google', 'Chrome', 'Application', 'chrome.exe')} return path_dict[runtime] if (compiler == 'dart2js' and (runtime == 'ff' or runtime == 'chrome') and is_buildbot): # Print out browser version numbers if we're running on the buildbot (where # we know the paths to these browser installations). version_query_string = '"%s" --version' % GetPath(runtime) if runtime == 'ff' and system == 'windows': version_query_string += '| more' elif runtime == 'chrome' and system == 'windows': version_query_string = ('''reg query "HKCU\\Software\\Microsoft\\''' + '''Windows\\CurrentVersion\\Uninstall\\Google Chrome" /v Version''') p = subprocess.Popen(version_query_string, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) output, stderr = p.communicate() output = output.split() try: print 'Version of %s: %s' % (runtime, output[-1]) except IndexError: # Failed to obtain version information. Continue running tests. pass if runtime == 'd8': # The dart2js compiler isn't self-hosted (yet) so we run its # unit tests on the VM. We avoid doing this on the builders # that run the browser tests to cut down on the cycle time. unit_test_flags = [flag for flag in flags if flag.startswith('--shard')] # Run the unit tests in checked mode (the VM's checked mode). unit_test_flags.append('--checked') TestStep("dart2js_unit", mode, system, 'none', 'vm', ['dart2js'], unit_test_flags, arch) if compiler == 'dart2js' and system == 'windows' and runtime == 'ie10': TestStep("%s-%s" % (compiler, runtime), mode, system, compiler, runtime, ['html'], flags, arch) else: # Run the default set of test suites. TestStep("%s-%s" % (compiler, runtime), mode, system, compiler, runtime, [], flags, arch) if compiler == 'dart2js': # TODO(kasperl): Consider running peg and css tests too. extras = ['dart2js_extra', 'dart2js_native'] extras_flags = flags if (system == 'linux' and runtime == 'd8' and not '--host-checked' in extras_flags): # Run the extra tests in checked mode, but only on linux/d8. # Other systems have less resources and tend to time out. extras_flags = extras_flags + ['--host-checked'] TestStep("dart2js_extra", mode, system, 'dart2js', runtime, extras, extras_flags, arch) def GetHasHardCodedCheckedMode(build_info): # TODO(ricow): We currently run checked mode tests on chrome on linux and # on the slow (all) IE windows bots. This is a hack and we should use the # normal sharding and checked splitting functionality when we get more # vms for testing this. if (build_info.system == 'linux' and build_info.runtime == 'drt'): return True if build_info.runtime.startswith('ie') and build_info.test_set == 'all': return True return False def GetLocalIPAddress(): hostname = socket.gethostname() # '$ host chromeperf02' results for example in # 'chromeperf02.perf.chromium.org has address 172.22.28.55' output = subprocess.check_output(["host", hostname]) match = re.match(r'.*\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s+.*', output) if not match: raise Exception("Could not determine local ip address " "(hostname: '%s', host command output: '%s')." % (hostname, output)) return match.group(1) def AddAndroidToolsToPath(): par_dir = os.path.pardir join = os.path.join dart_dir = join(os.path.dirname(__file__), par_dir, par_dir) android_sdk = join(dart_dir, 'third_party', 'android_tools', 'sdk') tools_dir = os.path.abspath(join(android_sdk, 'tools')) platform_tools_dir = os.path.abspath(join(android_sdk, 'platform-tools')) os.environ['PATH'] = os.pathsep.join( [os.environ['PATH'], tools_dir, platform_tools_dir]) def RunCompilerTests(build_info): test_flags = [] if build_info.shard_index: test_flags = ['--shards=%s' % build_info.total_shards, '--shard=%s' % build_info.shard_index] if build_info.checked: test_flags += ['--checked'] if build_info.minified: test_flags += ['--minified'] if build_info.host_checked: test_flags += ['--host-checked'] if build_info.dart2js_full: compiler = build_info.compiler assert compiler == 'dart2js' system = build_info.system arch = build_info.arch mode = build_info.mode is_buildbot = build_info.is_buildbot for configuration in DART2JS_FULL_CONFIGURATIONS[system]: additional_flags = configuration.get('additional_flags', []) TestCompiler(configuration['runtime'], mode, system, test_flags + additional_flags, is_buildbot, arch, compiler=compiler, dart2js_full=True) else: if build_info.csp: test_flags += ['--csp'] if build_info.runtime == 'chromeOnAndroid': test_flags.append('--local_ip=%s' % GetLocalIPAddress()) # test.py expects the android tools directories to be in PATH # (they contain for example 'adb') AddAndroidToolsToPath() TestCompiler(build_info.runtime, build_info.mode, build_info.system, list(test_flags), build_info.is_buildbot, build_info.arch, compiler=build_info.compiler) # See comment in GetHasHardCodedCheckedMode, this is a hack. if (GetHasHardCodedCheckedMode(build_info)): TestCompiler(build_info.runtime, build_info.mode, build_info.system, test_flags + ['--checked'], build_info.is_buildbot, build_info.arch, compiler=build_info.compiler) def BuildCompiler(build_info): """ Builds the SDK. - build_info: the buildInfo object, containing information about what sort of build and test to be run. """ with bot.BuildStep('Build SDK'): args = [sys.executable, './tools/build.py', '--mode=' + build_info.mode, '--arch=' + build_info.arch, 'dart2js_bot'] print 'Build SDK and d8: %s' % (' '.join(args)) bot.RunProcess(args) if __name__ == '__main__': bot.RunBot(GetBuildInfo, RunCompilerTests, build_step=BuildCompiler)