| # -*- python -*- |
| # ex: set syntax=python: |
| |
| from buildbot.buildslave import BuildSlave |
| from buildbot.changes.pb import PBChangeSource |
| from buildbot.scheduler import AnyBranchScheduler, Triggerable |
| from buildbot.schedulers.forcesched import FixedParameter, ForceScheduler, StringParameter |
| from buildbot.schedulers.filter import ChangeFilter |
| from buildbot.status import html |
| from buildbot.status.web.authz import Authz |
| from buildbot.process import buildstep, factory, properties |
| from buildbot.steps import master, shell, source, transfer, trigger |
| from buildbot.status.builder import SUCCESS, FAILURE, WARNINGS, SKIPPED |
| |
| from twisted.internet import defer |
| |
| import os |
| import re |
| import json |
| import operator |
| import urllib |
| |
| from committer_auth import CommitterAuth |
| from webkitpy.common.config import build as wkbuild |
| |
| |
| c = BuildmasterConfig = {} |
| |
| c['change_source'] = PBChangeSource() |
| |
| # permissions for WebStatus |
| authz = Authz( |
| auth=CommitterAuth('auth.json'), |
| forceBuild='auth', |
| forceAllBuilds='auth', |
| pingBuilder=True, |
| gracefulShutdown=False, |
| stopBuild='auth', |
| stopAllBuilds='auth', |
| cancelPendingBuild='auth', |
| stopChange=True, |
| cleanShutdown=False) |
| |
| c['status'] = [] |
| c['status'].append(html.WebStatus(http_port=8710, |
| revlink="http://trac.webkit.org/changeset/%s", |
| authz=authz)) |
| |
| c['slavePortnum'] = 17000 |
| c['projectName'] = "WebKit" |
| c['projectURL'] = "http://webkit.org" |
| c['buildbotURL'] = "http://build.webkit.org/" |
| |
| c['buildHorizon'] = 1000 |
| c['logHorizon'] = 500 |
| c['eventHorizon'] = 200 |
| c['buildCacheSize'] = 60 |
| |
| WithProperties = properties.WithProperties |
| |
| |
| class TestWithFailureCount(shell.Test): |
| failedTestsFormatString = "%d tests failed" |
| |
| def countFailures(self, cmd): |
| return 0 |
| |
| def commandComplete(self, cmd): |
| shell.Test.commandComplete(self, cmd) |
| self.failedTestCount = self.countFailures(cmd) |
| |
| def evaluateCommand(self, cmd): |
| if self.failedTestCount: |
| return FAILURE |
| |
| if cmd.rc != 0: |
| return FAILURE |
| |
| return SUCCESS |
| |
| def getText(self, cmd, results): |
| return self.getText2(cmd, results) |
| |
| def getText2(self, cmd, results): |
| if results != SUCCESS and self.failedTestCount: |
| return [self.failedTestsFormatString % self.failedTestCount] |
| |
| return [self.name] |
| |
| |
| class ConfigureBuild(buildstep.BuildStep): |
| name = "configure build" |
| description = ["configuring build"] |
| descriptionDone = ["configured build"] |
| def __init__(self, platform, configuration, architecture, buildOnly, SVNMirror, *args, **kwargs): |
| buildstep.BuildStep.__init__(self, *args, **kwargs) |
| self.platform = platform.split('-', 1)[0] |
| self.fullPlatform = platform |
| self.configuration = configuration |
| self.architecture = architecture |
| self.buildOnly = buildOnly |
| self.SVNMirror = SVNMirror |
| self.addFactoryArguments(platform=platform, configuration=configuration, architecture=architecture, buildOnly=buildOnly, SVNMirror=SVNMirror) |
| |
| def start(self): |
| self.setProperty("platform", self.platform) |
| self.setProperty("fullPlatform", self.fullPlatform) |
| self.setProperty("configuration", self.configuration) |
| self.setProperty("architecture", self.architecture) |
| self.setProperty("buildOnly", self.buildOnly) |
| self.setProperty("shouldAbortEarly", True) |
| self.setProperty("SVNMirror", self.SVNMirror) |
| self.finished(SUCCESS) |
| return defer.succeed(None) |
| |
| |
| class CheckOutSource(source.SVN): |
| mode = "update" |
| def __init__(self, SVNMirror, **kwargs): |
| kwargs['baseURL'] = SVNMirror or "http://svn.webkit.org/repository/webkit/" |
| kwargs['defaultBranch'] = "trunk" |
| kwargs['mode'] = self.mode |
| source.SVN.__init__(self, **kwargs) |
| self.addFactoryArguments(SVNMirror=SVNMirror) |
| |
| class WaitForSVNServer(shell.ShellCommand): |
| name = "wait-for-svn-server" |
| command = ["python", "./Tools/BuildSlaveSupport/wait-for-SVN-server.py", "-r", WithProperties("%(revision)s"), "-s", WithProperties("%(SVNMirror)s")] |
| description = ["waiting for SVN server"] |
| descriptionDone = ["SVN server is ready"] |
| haltOnFailure = True |
| |
| class InstallWin32Dependencies(shell.Compile): |
| description = ["installing dependencies"] |
| descriptionDone = ["installed dependencies"] |
| command = ["perl", "./Tools/Scripts/update-webkit-auxiliary-libs"] |
| |
| class KillOldProcesses(shell.Compile): |
| name = "kill old processes" |
| description = ["killing old processes"] |
| descriptionDone = ["killed old processes"] |
| command = ["python", "./Tools/BuildSlaveSupport/kill-old-processes"] |
| |
| class InstallEflDependencies(shell.ShellCommand): |
| name = "jhbuild" |
| description = ["updating efl dependencies"] |
| descriptionDone = ["updated efl dependencies"] |
| command = ["perl", "./Tools/Scripts/update-webkitefl-libs"] |
| haltOnFailure = True |
| |
| class InstallGtkDependencies(shell.ShellCommand): |
| name = "jhbuild" |
| description = ["updating gtk dependencies"] |
| descriptionDone = ["updated gtk dependencies"] |
| command = ["perl", "./Tools/Scripts/update-webkitgtk-libs"] |
| haltOnFailure = True |
| |
| class InstallChromiumDependencies(shell.ShellCommand): |
| name = "gclient" |
| description = ["updating chromium dependencies"] |
| descriptionDone = ["updated chromium dependencies"] |
| command = ["perl", "./Tools/Scripts/update-webkit-chromium", "--force"] |
| haltOnFailure = True |
| def start(self): |
| if self.getProperty('fullPlatform') == "chromium-android": |
| self.setCommand(self.command + ['--chromium-android']) |
| |
| return shell.ShellCommand.start(self) |
| |
| class CleanupChromiumCrashLogs(shell.ShellCommand): |
| name = "cleanup crash logs" |
| description = ["removing crash logs"] |
| descriptionDone = ["removed crash logs"] |
| command = ["python", "./Tools/BuildSlaveSupport/chromium/remove-crash-logs"] |
| haltOnFailure = False |
| |
| |
| def appendCustomBuildFlags(step, platform, fullPlatform=""): |
| if fullPlatform == "chromium-android": |
| step.setCommand(step.command + ['--chromium-android']) |
| elif platform in ('chromium', 'efl', 'gtk', 'qt', 'wincairo', 'wince', 'wx'): |
| step.setCommand(step.command + ['--' + platform]) |
| |
| |
| class CompileWebKit(shell.Compile): |
| command = ["perl", "./Tools/Scripts/build-webkit", WithProperties("--%(configuration)s")] |
| env = {'MFLAGS':''} |
| name = "compile-webkit" |
| description = ["compiling"] |
| descriptionDone = ["compiled"] |
| warningPattern = ".*arning: .*" |
| |
| def start(self): |
| platform = self.getProperty('platform') |
| buildOnly = self.getProperty('buildOnly') |
| if platform == 'mac' and buildOnly: |
| self.setCommand(self.command + ['DEBUG_INFORMATION_FORMAT=dwarf-with-dsym']) |
| |
| appendCustomBuildFlags(self, platform, self.getProperty('fullPlatform')) |
| |
| return shell.Compile.start(self) |
| |
| |
| class ArchiveBuiltProduct(shell.ShellCommand): |
| command = ["python", "./Tools/BuildSlaveSupport/built-product-archive", |
| WithProperties("--platform=%(fullPlatform)s"), WithProperties("--%(configuration)s"), "archive"] |
| name = "archive-built-product" |
| description = ["archiving built product"] |
| descriptionDone = ["archived built product"] |
| haltOnFailure = True |
| |
| |
| class ExtractBuiltProduct(shell.ShellCommand): |
| command = ["python", "./Tools/BuildSlaveSupport/built-product-archive", |
| WithProperties("--platform=%(fullPlatform)s"), WithProperties("--%(configuration)s"), "extract"] |
| name = "extract-built-product" |
| description = ["extracting built product"] |
| descriptionDone = ["extracted built product"] |
| haltOnFailure = True |
| |
| |
| class UploadBuiltProduct(transfer.FileUpload): |
| slavesrc = WithProperties("WebKitBuild/%(configuration)s.zip") |
| masterdest = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(configuration)s/%(got_revision)s.zip") |
| haltOnFailure = True |
| |
| def __init__(self, **kwargs): |
| kwargs['slavesrc'] = self.slavesrc |
| kwargs['masterdest'] = self.masterdest |
| kwargs['mode'] = 0644 |
| transfer.FileUpload.__init__(self, **kwargs) |
| |
| |
| class DownloadBuiltProduct(shell.ShellCommand): |
| command = ["python", "./Tools/BuildSlaveSupport/download-built-product", |
| WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), |
| WithProperties(c["buildbotURL"] + "archives/%(fullPlatform)s-%(architecture)s-%(configuration)s/%(got_revision)s.zip")] |
| name = "download-built-product" |
| description = ["downloading built product"] |
| descriptionDone = ["downloaded built product"] |
| haltOnFailure = True |
| flunkOnFailure = True |
| |
| |
| class RunJavaScriptCoreTests(shell.Test): |
| name = "jscore-test" |
| description = ["jscore-tests running"] |
| descriptionDone = ["jscore-tests"] |
| command = ["perl", "./Tools/Scripts/run-javascriptcore-tests", WithProperties("--%(configuration)s")] |
| logfiles = {'actual.html (source)': 'Source/JavaScriptCore/tests/mozilla/actual.html'} |
| |
| def __init__(self, buildJSCTool=True, *args, **kwargs): |
| self.buildJSCTool = buildJSCTool |
| shell.Test.__init__(self, *args, **kwargs) |
| self.addFactoryArguments(buildJSCTool=buildJSCTool) |
| |
| def start(self): |
| appendCustomBuildFlags(self, self.getProperty('platform')) |
| if not self.buildJSCTool: |
| self.setCommand(self.command + ['--no-build']) |
| return shell.Test.start(self) |
| |
| def commandComplete(self, cmd): |
| shell.Test.commandComplete(self, cmd) |
| |
| logText = cmd.logs['stdio'].getText() |
| statusLines = [line for line in logText.splitlines() if line.find('regression') >= 0 and line.find(' found.') >= 0] |
| if statusLines and statusLines[0].split()[0] != '0': |
| self.regressionLine = statusLines[0] |
| else: |
| self.regressionLine = None |
| |
| if 'actual.html (source)' in cmd.logs: |
| self.addHTMLLog('actual.html', cmd.logs['actual.html (source)'].getText()) |
| |
| def evaluateCommand(self, cmd): |
| if self.regressionLine: |
| return FAILURE |
| |
| if cmd.rc != 0: |
| return FAILURE |
| |
| return SUCCESS |
| |
| def getText(self, cmd, results): |
| return self.getText2(cmd, results) |
| |
| def getText2(self, cmd, results): |
| if results != SUCCESS and self.regressionLine: |
| return [self.name, self.regressionLine] |
| |
| return [self.name] |
| |
| |
| class RunWebKitTests(shell.Test): |
| name = "layout-test" |
| description = ["layout-tests running"] |
| descriptionDone = ["layout-tests"] |
| command = ["perl", "./Tools/Scripts/run-webkit-tests", |
| "--no-launch-safari", |
| "--no-new-test-results", |
| "--no-sample-on-timeout", |
| "--results-directory", "layout-test-results", |
| "--use-remote-links-to-tests", |
| "--builder-name", WithProperties("%(buildername)s"), |
| "--build-number", WithProperties("%(buildnumber)s"), |
| "--master-name", "webkit.org", |
| "--test-results-server", "test-results.appspot.com", |
| WithProperties("--%(configuration)s")] |
| |
| def __init__(self, buildJSCTool=True, *args, **kwargs): |
| self.buildJSCTool = buildJSCTool |
| shell.Test.__init__(self, *args, **kwargs) |
| self.addFactoryArguments(buildJSCTool=buildJSCTool) |
| |
| def start(self): |
| platform = self.getProperty('platform') |
| shouldAbortEarly = self.getProperty('shouldAbortEarly') |
| appendCustomBuildFlags(self, platform, self.getProperty('fullPlatform')) |
| if platform.startswith('mac'): |
| self.setCommand(self.command + ['--no-build']) |
| if shouldAbortEarly: |
| self.setCommand(self.command + ["--exit-after-n-crashes-or-timeouts", "20", "--exit-after-n-failures", "500"]) |
| |
| if platform == "win": |
| rootArgument = ['--root=' + os.path.join("WebKitBuild", self.getProperty('configuration'), "bin")] |
| self.setCommand(self.command + ['--no-build']) |
| else: |
| rootArgument = ['--root=WebKitBuild/bin'] |
| if not self.buildJSCTool: |
| self.setCommand(self.command + rootArgument) |
| return shell.Test.start(self) |
| |
| def _parseOldRunWebKitTestsOutput(self, logText): |
| incorrectLayoutLines = [] |
| for line in logText.splitlines(): |
| if line.find('had incorrect layout') >= 0 or line.find('were new') >= 0 or line.find('was new') >= 0: |
| incorrectLayoutLines.append(line) |
| elif line.find('test case') >= 0 and (line.find(' crashed') >= 0 or line.find(' timed out') >= 0): |
| incorrectLayoutLines.append(line) |
| elif line.startswith("WARNING:") and line.find(' leak') >= 0: |
| incorrectLayoutLines.append(line.replace('WARNING: ', '')) |
| elif line.find('Exiting early') >= 0: |
| incorrectLayoutLines.append(line) |
| |
| # FIXME: Detect and summarize leaks of RefCounted objects |
| |
| self.incorrectLayoutLines = incorrectLayoutLines |
| |
| # FIXME: This will break if new-run-webkit-tests changes its default log formatter. |
| nrwt_log_message_regexp = re.compile(r'(?P<log_prefix>.*) (?P<log_level>DEBUG|INFO) (?P<message>.*)') |
| |
| def _strip_python_logging_prefix(self, line): |
| match_object = self.nrwt_log_message_regexp.match(line) |
| if match_object: |
| return match_object.group('message') |
| return line |
| |
| def _parseNewRunWebKitTestsOutput(self, logText): |
| incorrectLayoutLines = [] |
| expressions = [ |
| ('flakes', re.compile(r'[Uu]nexpected flakiness.+:?\s*\((\d+)\)')), |
| ('new passes', re.compile(r'Expected to .+, but passed:\s+\((\d+)\)')), |
| ('missing results', re.compile(r'(?:no expected results found|missing results)\s*:\s+\((\d+)\)')), |
| ('failures', re.compile(r'Regressions: [Uu]nexpected.+:?\s*\((\d+)\)')), |
| ] |
| testFailures = {} |
| |
| for line in logText.splitlines(): |
| if line.find('Exiting early') >= 0 or line.find('leaks found') >= 0: |
| incorrectLayoutLines.append(self._strip_python_logging_prefix(line)) |
| continue |
| for name, expression in expressions: |
| match = expression.search(line) |
| |
| if match: |
| testFailures[name] = testFailures.get(name, 0) + int(match.group(1)) |
| break |
| |
| # FIXME: Parse file names and put them in results |
| |
| for name in testFailures: |
| incorrectLayoutLines.append(str(testFailures[name]) + ' ' + name) |
| |
| self.incorrectLayoutLines = incorrectLayoutLines |
| |
| def commandComplete(self, cmd): |
| shell.Test.commandComplete(self, cmd) |
| |
| logText = cmd.logs['stdio'].getText() |
| if logText.find("Collecting tests ...") >= 0: |
| self._parseNewRunWebKitTestsOutput(logText) |
| else: |
| self._parseOldRunWebKitTestsOutput(logText) |
| |
| def evaluateCommand(self, cmd): |
| result = SUCCESS |
| |
| if self.incorrectLayoutLines: |
| if len(self.incorrectLayoutLines) == 1: |
| line = self.incorrectLayoutLines[0] |
| if line.find('were new') >= 0 or line.find('was new') >= 0 or line.find(' leak') >= 0: |
| return WARNINGS |
| |
| for line in self.incorrectLayoutLines: |
| if line.find('flakes') >= 0 or line.find('new passes') >= 0 or line.find('missing results') >= 0: |
| result = WARNINGS |
| else: |
| return FAILURE |
| |
| if cmd.rc != 0: |
| return FAILURE |
| |
| return result |
| |
| def getText(self, cmd, results): |
| return self.getText2(cmd, results) |
| |
| def getText2(self, cmd, results): |
| if results != SUCCESS and self.incorrectLayoutLines: |
| return self.incorrectLayoutLines |
| |
| return [self.name] |
| |
| |
| class RunUnitTests(TestWithFailureCount): |
| name = "run-api-tests" |
| description = ["unit tests running"] |
| descriptionDone = ["unit-tests"] |
| command = ["perl", "./Tools/Scripts/run-api-tests", WithProperties("--%(configuration)s"), "--verbose"] |
| failedTestsFormatString = "%d unit tests failed or timed out" |
| |
| def start(self): |
| platform = self.getProperty('fullPlatform') |
| if platform.startswith('win'): |
| self.setCommand(self.command + ['--no-build']) |
| if platform.startswith('mac'): |
| self.setCommand(self.command + ['--no-build']) |
| if platform.startswith('chromium'): |
| self.setCommand(self.command + ['--chromium']) |
| if platform == 'chromium-android': |
| self.setCommand(self.command + ['--chromium-android']) |
| return shell.Test.start(self) |
| |
| def countFailures(self, cmd): |
| log_text = cmd.logs['stdio'].getText() |
| count = 0 |
| |
| split = re.split(r'\sTests that timed out:\s', log_text) |
| if len(split) > 1: |
| count += len(re.findall(r'^\s+\S+$', split[1], flags=re.MULTILINE)) |
| |
| split = re.split(r'\sTests that failed:\s', split[0]) |
| if len(split) > 1: |
| count += len(re.findall(r'^\s+\S+$', split[1], flags=re.MULTILINE)) |
| |
| return count |
| |
| |
| class RunPythonTests(TestWithFailureCount): |
| name = "webkitpy-test" |
| description = ["python-tests running"] |
| descriptionDone = ["python-tests"] |
| command = ["python", "./Tools/Scripts/test-webkitpy"] |
| failedTestsFormatString = "%d python tests failed" |
| |
| def start(self): |
| platform = self.getProperty('platform') |
| # Python tests are flaky on the GTK builders, running them serially |
| # helps and does not significantly prolong the cycle time. |
| if platform == 'gtk': |
| self.setCommand(self.command + ['--child-processes', '1']) |
| # Python tests fail on windows bots when running more than one child process |
| # https://bugs.webkit.org/show_bug.cgi?id=97465 |
| if platform == 'win': |
| self.setCommand(self.command + ['--child-processes', '1']) |
| return shell.Test.start(self) |
| |
| def countFailures(self, cmd): |
| logText = cmd.logs['stdio'].getText() |
| # We're looking for the line that looks like this: FAILED (failures=2, errors=1) |
| regex = re.compile(r'^FAILED \((?P<counts>[^)]+)\)') |
| for line in logText.splitlines(): |
| match = regex.match(line) |
| if not match: |
| continue |
| return sum(int(component.split('=')[1]) for component in match.group('counts').split(', ')) |
| return 0 |
| |
| |
| class RunPerlTests(TestWithFailureCount): |
| name = "webkitperl-test" |
| description = ["perl-tests running"] |
| descriptionDone = ["perl-tests"] |
| command = ["perl", "./Tools/Scripts/test-webkitperl"] |
| failedTestsFormatString = "%d perl tests failed" |
| |
| def countFailures(self, cmd): |
| logText = cmd.logs['stdio'].getText() |
| # We're looking for the line that looks like this: Failed 2/19 test programs. 5/363 subtests failed. |
| regex = re.compile(r'^Failed \d+/\d+ test programs\. (?P<count>\d+)/\d+ subtests failed\.') |
| for line in logText.splitlines(): |
| match = regex.match(line) |
| if not match: |
| continue |
| return int(match.group('count')) |
| return 0 |
| |
| |
| class RunBindingsTests(shell.Test): |
| name = "bindings-generation-tests" |
| description = ["bindings-tests running"] |
| descriptionDone = ["bindings-tests"] |
| command = ["python", "./Tools/Scripts/run-bindings-tests"] |
| |
| |
| class RunEflAPITests(shell.Test): |
| name = "API tests" |
| description = ["API tests running"] |
| descriptionDone = ["API tests"] |
| command = ["perl", "./Tools/Scripts/run-efl-tests", WithProperties("--%(configuration)s")] |
| |
| |
| class RunGtkAPITests(shell.Test): |
| name = "API tests" |
| description = ["API tests running"] |
| descriptionDone = ["API tests"] |
| command = ["python", "./Tools/Scripts/run-gtk-tests", "--verbose", WithProperties("--%(configuration)s")] |
| |
| def commandComplete(self, cmd): |
| shell.Test.commandComplete(self, cmd) |
| |
| logText = cmd.logs['stdio'].getText() |
| incorrectLines = [] |
| for line in logText.splitlines(): |
| if line.startswith('ERROR'): |
| incorrectLines.append(line) |
| |
| self.incorrectLines = incorrectLines |
| |
| def evaluateCommand(self, cmd): |
| if self.incorrectLines: |
| return FAILURE |
| |
| if cmd.rc != 0: |
| return FAILURE |
| |
| return SUCCESS |
| |
| def getText(self, cmd, results): |
| return self.getText2(cmd, results) |
| |
| def getText2(self, cmd, results): |
| if results != SUCCESS and self.incorrectLines: |
| return ["%d API tests failed" % len(self.incorrectLines)] |
| |
| return [self.name] |
| |
| class RunQtAPITests(shell.Test): |
| name = "API tests" |
| description = ["API tests running"] |
| descriptionDone = ["API tests"] |
| command = ["python", "./Tools/Scripts/run-qtwebkit-tests", |
| "--output-file=qt-unit-tests.html", "--do-not-open-results", "--timeout=120", |
| WithProperties("WebKitBuild/%(configuration_pretty)s/Source/WebKit/qt/tests/", configuration_pretty=lambda build: build.getProperty("configuration").title()) |
| ] |
| |
| def commandComplete(self, cmd): |
| shell.Test.commandComplete(self, cmd) |
| |
| logText = cmd.logs['stdio'].getText() |
| foundItems = re.findall("TOTALS: (?P<passed>\d+) passed, (?P<failed>\d+) failed, (?P<skipped>\d+) skipped, (?P<crashed>\d+) crashed", logText) |
| |
| self.incorrectTests = 0 |
| self.crashedTests = 0 |
| self.statusLine = [] |
| |
| if foundItems: |
| self.incorrectTests = int(foundItems[0][1]) |
| self.crashedTests = int(foundItems[0][3]) |
| |
| if self.incorrectTests > 0 or self.crashedTests > 0: |
| self.statusLine = [ |
| "%s passed, %s failed, %s skipped, %s crashed" % (foundItems[0][0], foundItems[0][1], foundItems[0][2], foundItems[0][3]) |
| ] |
| |
| def evaluateCommand(self, cmd): |
| if self.crashedTests: |
| return FAILURE |
| |
| if re.findall("Timeout, process", cmd.logs['stdio'].getText()): |
| self.statusLine = ["Failure: timeout occured during testing"] |
| return FAILURE |
| |
| if self.incorrectTests: |
| return WARNINGS |
| |
| if cmd.rc != 0: |
| return FAILURE |
| |
| return SUCCESS |
| |
| def getText(self, cmd, results): |
| return self.getText2(cmd, results) |
| |
| def getText2(self, cmd, results): |
| if results != SUCCESS and self.incorrectTests: |
| return self.statusLine |
| |
| return [self.name] |
| |
| class RunWebKitLeakTests(RunWebKitTests): |
| warnOnWarnings = True |
| def start(self): |
| self.setCommand(self.command + ["--leaks"]) |
| return RunWebKitTests.start(self) |
| |
| |
| class RunWebKit2Tests(RunWebKitTests): |
| def start(self): |
| self.setProperty("shouldAbortEarly", False) |
| self.setCommand(self.command + ["--webkit-test-runner"]) |
| return RunWebKitTests.start(self) |
| |
| |
| class RunChromiumWebKitUnitTests(shell.Test): |
| name = "webkit-unit-tests" |
| description = ["webkit-unit-tests running"] |
| descriptionDone = ["webkit-unit-tests"] |
| command = ["perl", "./Tools/Scripts/run-chromium-webkit-unit-tests", |
| WithProperties("--%(configuration)s"), WithProperties("--platform=%(fullPlatform)s")] |
| |
| |
| class RunAndUploadPerfTests(shell.Test): |
| name = "perf-test" |
| description = ["perf-tests running"] |
| descriptionDone = ["perf-tests"] |
| command = ["python", "./Tools/Scripts/run-perf-tests", |
| "--output-json-path", "perf-test-results.json", |
| "--slave-config-json-path", "../../perf-test-config.json", |
| "--no-show-results", |
| "--reset-results", |
| "--test-results-server", "webkit-perf.appspot.com", |
| "--builder-name", WithProperties("%(buildername)s"), |
| "--build-number", WithProperties("%(buildnumber)s"), |
| "--platform", WithProperties("%(fullPlatform)s"), |
| WithProperties("--%(configuration)s")] |
| |
| def start(self): |
| self.setCommand(self.command) |
| return shell.Test.start(self) |
| |
| |
| class RunAndUploadPerfTestsWebKit2(RunAndUploadPerfTests): |
| def start(self): |
| self.setCommand(self.command + ["--webkit-test-runner"]) |
| return RunAndUploadPerfTests.start(self) |
| |
| |
| class ArchiveTestResults(shell.ShellCommand): |
| command = ["python", "./Tools/BuildSlaveSupport/test-result-archive", |
| WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "archive"] |
| name = "archive-test-results" |
| description = ["archiving test results"] |
| descriptionDone = ["archived test results"] |
| haltOnFailure = True |
| |
| |
| class UploadTestResults(transfer.FileUpload): |
| slavesrc = "layout-test-results.zip" |
| masterdest = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s).zip") |
| |
| def __init__(self, **kwargs): |
| kwargs['slavesrc'] = self.slavesrc |
| kwargs['masterdest'] = self.masterdest |
| kwargs['mode'] = 0644 |
| transfer.FileUpload.__init__(self, **kwargs) |
| |
| |
| class ExtractTestResults(master.MasterShellCommand): |
| zipFile = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s).zip") |
| resultDirectory = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s)") |
| descriptionDone = ["uploaded results"] |
| |
| def __init__(self, **kwargs): |
| kwargs['command'] = "" |
| master.MasterShellCommand.__init__(self, **kwargs) |
| |
| def resultDirectoryURL(self): |
| return self.build.getProperties().render(self.resultDirectory).replace("public_html/", "/") + "/" |
| |
| def start(self): |
| self.command = ["unzip", self.build.getProperties().render(self.zipFile), "-d", self.build.getProperties().render(self.resultDirectory)] |
| return master.MasterShellCommand.start(self) |
| |
| def addCustomURLs(self): |
| url = self.resultDirectoryURL() + "results.html" |
| self.addURL("view results", url) |
| |
| def finished(self, result): |
| self.addCustomURLs() |
| return master.MasterShellCommand.finished(self, result) |
| |
| |
| class ExtractTestResultsAndLeaks(ExtractTestResults): |
| def addCustomURLs(self): |
| ExtractTestResults.addCustomURLs(self) |
| url = "/LeaksViewer/?url=" + urllib.quote(self.resultDirectoryURL(), safe="") |
| self.addURL("view leaks", url) |
| |
| |
| class Factory(factory.BuildFactory): |
| def __init__(self, platform, configuration, architectures, buildOnly, SVNMirror): |
| factory.BuildFactory.__init__(self) |
| self.addStep(ConfigureBuild(platform=platform, configuration=configuration, architecture=" ".join(architectures), buildOnly=buildOnly, SVNMirror=SVNMirror)) |
| if SVNMirror: |
| self.addStep(WaitForSVNServer()) |
| self.addStep(CheckOutSource(SVNMirror=SVNMirror)) |
| # There are multiple Qt slaves running on same machines, so buildslaves shouldn't kill the processes of other slaves. |
| if not platform.startswith("qt"): |
| self.addStep(KillOldProcesses()) |
| if platform == "win": |
| self.addStep(InstallWin32Dependencies()) |
| if platform.startswith("chromium"): |
| self.addStep(InstallChromiumDependencies()) |
| if platform == "gtk": |
| self.addStep(InstallGtkDependencies()) |
| if platform == "efl": |
| self.addStep(InstallEflDependencies()) |
| |
| |
| class BuildFactory(Factory): |
| def __init__(self, platform, configuration, architectures, triggers=None, SVNMirror=None): |
| Factory.__init__(self, platform, configuration, architectures, True, SVNMirror) |
| self.addStep(CompileWebKit()) |
| if triggers: |
| self.addStep(ArchiveBuiltProduct()) |
| self.addStep(UploadBuiltProduct()) |
| self.addStep(trigger.Trigger(schedulerNames=triggers)) |
| |
| def unitTestsSupported(configuration, platform): |
| if platform.startswith('mac') and configuration == "release": |
| return False; # https://bugs.webkit.org/show_bug.cgi?id=82652 |
| return (platform == 'win' or platform.startswith('mac') or platform.startswith('chromium')) |
| |
| def pickLatestBuild(builder, requests): |
| return max(requests, key=operator.attrgetter("submittedAt")) |
| |
| class TestFactory(Factory): |
| TestClass = RunWebKitTests |
| ExtractTestResultsClass = ExtractTestResults |
| def __init__(self, platform, configuration, architectures, SVNMirror=None): |
| Factory.__init__(self, platform, configuration, architectures, False, SVNMirror) |
| if platform.startswith("chromium"): |
| self.addStep(CleanupChromiumCrashLogs()) |
| self.addStep(DownloadBuiltProduct()) |
| self.addStep(ExtractBuiltProduct()) |
| if not platform.startswith("chromium"): |
| self.addStep(RunJavaScriptCoreTests(buildJSCTool=False)) |
| if platform.startswith("chromium"): |
| self.addStep(RunChromiumWebKitUnitTests()) |
| self.addStep(self.TestClass(buildJSCTool=(platform != 'win'))) |
| |
| if unitTestsSupported(configuration, platform): |
| self.addStep(RunUnitTests()) |
| self.addStep(RunPythonTests()) |
| # Chromium Win runs in non-Cygwin environment, which is not yet fit |
| # for running tests. This can be removed once bug 48166 is fixed. |
| if platform != "chromium-win": |
| self.addStep(RunPerlTests()) |
| self.addStep(RunBindingsTests()) |
| self.addStep(ArchiveTestResults()) |
| self.addStep(UploadTestResults()) |
| self.addStep(self.ExtractTestResultsClass()) |
| if platform == "efl": |
| self.addStep(RunEflAPITests) |
| if platform == "gtk": |
| self.addStep(RunGtkAPITests()) |
| if platform.startswith("qt"): |
| self.addStep(RunQtAPITests) |
| |
| class BuildAndTestFactory(Factory): |
| TestClass = RunWebKitTests |
| ExtractTestResultsClass = ExtractTestResults |
| def __init__(self, platform, configuration, architectures, triggers=None, SVNMirror=None, **kwargs): |
| Factory.__init__(self, platform, configuration, architectures, False, SVNMirror, **kwargs) |
| if platform.startswith("chromium"): |
| self.addStep(CleanupChromiumCrashLogs) |
| self.addStep(CompileWebKit()) |
| if not platform.startswith("chromium"): |
| self.addStep(RunJavaScriptCoreTests()) |
| if platform.startswith("chromium"): |
| self.addStep(RunChromiumWebKitUnitTests) |
| self.addStep(self.TestClass()) |
| if unitTestsSupported(configuration, platform): |
| self.addStep(RunUnitTests()) |
| self.addStep(RunPythonTests()) |
| # Chromium Win runs in non-Cygwin environment, which is not yet fit |
| # for running tests. This can be removed once bug 48166 is fixed. |
| if platform != "chromium-win": |
| self.addStep(RunPerlTests()) |
| self.addStep(RunBindingsTests()) |
| self.addStep(ArchiveTestResults()) |
| self.addStep(UploadTestResults()) |
| self.addStep(self.ExtractTestResultsClass()) |
| if platform == "efl": |
| self.addStep(RunEflAPITests) |
| if platform == "gtk": |
| self.addStep(RunGtkAPITests()) |
| if platform.startswith("qt"): |
| self.addStep(RunQtAPITests()) |
| if triggers: |
| self.addStep(ArchiveBuiltProduct()) |
| self.addStep(UploadBuiltProduct()) |
| self.addStep(trigger.Trigger(schedulerNames=triggers)) |
| |
| class BuildAndTestWebKit2Factory(BuildAndTestFactory): |
| TestClass = RunWebKit2Tests |
| |
| class BuildAndTestLeaksFactory(BuildAndTestFactory): |
| TestClass = RunWebKitLeakTests |
| ExtractTestResultsClass = ExtractTestResultsAndLeaks |
| |
| |
| class TestWebKit2Factory(TestFactory): |
| TestClass = RunWebKit2Tests |
| |
| class BuildAndPerfTestFactory(Factory): |
| def __init__(self, platform, configuration, architectures, SVNMirror=None, **kwargs): |
| Factory.__init__(self, platform, configuration, architectures, False, SVNMirror, **kwargs) |
| if platform.startswith("chromium"): |
| self.addStep(CleanupChromiumCrashLogs) |
| self.addStep(CompileWebKit()) |
| self.addStep(RunAndUploadPerfTests()) |
| |
| class BuildAndPerfTestWebKit2Factory(Factory): |
| def __init__(self, platform, configuration, architectures, SVNMirror=None, **kwargs): |
| Factory.__init__(self, platform, configuration, architectures, False, SVNMirror, **kwargs) |
| if platform.startswith("chromium"): |
| self.addStep(CleanupChromiumCrashLogs) |
| self.addStep(CompileWebKit()) |
| self.addStep(RunAndUploadPerfTestsWebKit2()) |
| |
| class DownloadAndPerfTestFactory(Factory): |
| def __init__(self, platform, configuration, architectures, SVNMirror=None, **kwargs): |
| Factory.__init__(self, platform, configuration, architectures, False, SVNMirror, **kwargs) |
| self.addStep(DownloadBuiltProduct()) |
| self.addStep(ExtractBuiltProduct()) |
| self.addStep(RunAndUploadPerfTests()) |
| |
| class DownloadAndPerfTestWebKit2Factory(Factory): |
| def __init__(self, platform, configuration, architectures, SVNMirror=None, **kwargs): |
| Factory.__init__(self, platform, configuration, architectures, False, SVNMirror, **kwargs) |
| self.addStep(DownloadBuiltProduct) |
| self.addStep(ExtractBuiltProduct) |
| self.addStep(RunAndUploadPerfTestsWebKit2) |
| |
| class PlatformSpecificScheduler(AnyBranchScheduler): |
| def __init__(self, platform, branch, **kwargs): |
| self.platform = platform |
| filter = ChangeFilter(branch=[branch, None], filter_fn=self.filter) |
| AnyBranchScheduler.__init__(self, name=platform, change_filter=filter, **kwargs) |
| |
| def filter(self, change): |
| return wkbuild.should_build(self.platform, change.files) |
| |
| trunk_filter = ChangeFilter(branch=["trunk", None]) |
| |
| def loadBuilderConfig(c): |
| # FIXME: These file handles are leaked. |
| passwords = json.load(open('passwords.json')) |
| config = json.load(open('config.json')) |
| |
| c['slaves'] = [BuildSlave(slave['name'], passwords[slave['name']], max_builds=1) for slave in config['slaves']] |
| |
| c['schedulers'] = [] |
| for scheduler in config['schedulers']: |
| if "change_filter" in scheduler: |
| scheduler["change_filter"] = globals()[scheduler["change_filter"]] |
| kls = globals()[scheduler.pop('type')] |
| # Python 2.6 can't handle unicode keys as keyword arguments: |
| # http://bugs.python.org/issue2646. Modern versions of json return |
| # unicode strings from json.load, so we map all keys to str objects. |
| scheduler = dict(map(lambda key_value_pair: (str(key_value_pair[0]), key_value_pair[1]), scheduler.items())) |
| |
| c['schedulers'].append(kls(**scheduler)) |
| |
| forceScheduler = ForceScheduler( |
| name="force", |
| builderNames=[builder['name'] for builder in config['builders']], |
| reason=StringParameter(name="reason", default="", size=40), |
| |
| # Validate SVN revision: number or emtpy string |
| revision=StringParameter(name="revision", default="", regex=re.compile(r'^(\d*)$')), |
| |
| # Disable default enabled input fields: branch, repository, project, additional properties |
| branch=FixedParameter(name="branch"), |
| repository=FixedParameter(name="repository"), |
| project=FixedParameter(name="project"), |
| properties=[] |
| ) |
| c['schedulers'].append(forceScheduler) |
| |
| c['builders'] = [] |
| for builder in config['builders']: |
| for slaveName in builder['slavenames']: |
| for slave in config['slaves']: |
| if slave['name'] != slaveName or slave['platform'] == '*': |
| continue |
| |
| if slave['platform'] != builder['platform']: |
| raise Exception, "Builder %r is for platform %r but has slave %r for platform %r!" % (builder['name'], builder['platform'], slave['name'], slave['platform']) |
| |
| break |
| |
| platform = builder['platform'] |
| |
| builderType = builder.pop('type') |
| factory = globals()["%sFactory" % builderType] |
| factorykwargs = {} |
| for key in "platform", "configuration", "architectures", "triggers", "SVNMirror": |
| value = builder.pop(key, None) |
| if value: |
| factorykwargs[key] = value |
| |
| builder["factory"] = factory(**factorykwargs) |
| |
| if platform.startswith('chromium'): |
| builder["category"] = 'Chromium' |
| elif platform.startswith('mac'): |
| builder["category"] = 'AppleMac' |
| elif platform == 'win': |
| builder["category"] = 'AppleWin' |
| elif platform.startswith('gtk'): |
| builder["category"] = 'GTK' |
| elif platform.startswith('qt'): |
| builder["category"] = 'Qt' |
| elif platform.startswith('efl'): |
| builder["category"] = "EFL" |
| else: |
| builder["category"] = 'misc' |
| |
| if (builder['category'] == 'AppleMac' or builder['category'] == 'AppleWin') and builderType != 'Build': |
| builder['nextBuild'] = pickLatestBuild |
| |
| c['builders'].append(builder) |
| |
| loadBuilderConfig(c) |