diff options
author | Ben Murdoch <benm@google.com> | 2011-05-05 14:36:32 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-05-10 15:38:30 +0100 |
commit | f05b935882198ccf7d81675736e3aeb089c5113a (patch) | |
tree | 4ea0ca838d9ef1b15cf17ddb3928efb427c7e5a1 /WebKitTools/Scripts/webkitpy/tool/commands | |
parent | 60fbdcc62bced8db2cb1fd233cc4d1e4ea17db1b (diff) | |
download | external_webkit-f05b935882198ccf7d81675736e3aeb089c5113a.zip external_webkit-f05b935882198ccf7d81675736e3aeb089c5113a.tar.gz external_webkit-f05b935882198ccf7d81675736e3aeb089c5113a.tar.bz2 |
Merge WebKit at r74534: Initial merge by git.
Change-Id: I6ccd1154fa1b19c2ec2a66878eb675738735f1eb
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/tool/commands')
30 files changed, 0 insertions, 5245 deletions
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py b/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py deleted file mode 100644 index d2aa503..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Required for Python to search this directory for module files - -from webkitpy.tool.commands.download import * -from webkitpy.tool.commands.earlywarningsystem import * -from webkitpy.tool.commands.openbugs import OpenBugs -from webkitpy.tool.commands.prettydiff import PrettyDiff -from webkitpy.tool.commands.queries import * -from webkitpy.tool.commands.queues import * -from webkitpy.tool.commands.rebaseline import Rebaseline -from webkitpy.tool.commands.rebaselineserver import RebaselineServer -from webkitpy.tool.commands.sheriffbot import * -from webkitpy.tool.commands.upload import * diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py b/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py deleted file mode 100644 index fc5a794..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from webkitpy.tool.commands.stepsequence import StepSequence -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand - - -class AbstractSequencedCommand(AbstractDeclarativeCommand): - steps = None - def __init__(self): - self._sequence = StepSequence(self.steps) - AbstractDeclarativeCommand.__init__(self, self._sequence.options()) - - def _prepare_state(self, options, args, tool): - return None - - def execute(self, options, args, tool): - self._sequence.run_and_handle_errors(tool, options, self._prepare_state(options, args, tool)) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py b/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py deleted file mode 100644 index adc6d81..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import unittest - -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.tool.mocktool import MockOptions, MockTool - -class CommandsTest(unittest.TestCase): - def assert_execute_outputs(self, command, args, expected_stdout="", expected_stderr="", options=MockOptions(), tool=MockTool()): - options.blocks = True - options.cc = 'MOCK cc' - options.component = 'MOCK component' - options.confirm = True - options.email = 'MOCK email' - options.git_commit = 'MOCK git commit' - options.obsolete_patches = True - options.open_bug = True - options.port = 'MOCK port' - options.quiet = True - options.reviewer = 'MOCK reviewer' - command.bind_to_tool(tool) - OutputCapture().assert_outputs(self, command.execute, [options, args, tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html deleted file mode 100644 index 5b58301..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html +++ /dev/null @@ -1,180 +0,0 @@ -<!DOCTYPE html> -<!-- - Copyright (c) 2010 Google Inc. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---> -<html> -<head> - <title>Layout Test Rebaseline Server</title> - <link rel="stylesheet" href="/main.css" type="text/css"> - <script src="/util.js"></script> - <script src="/loupe.js"></script> - <script src="/main.js"></script> - <script src="/queue.js"></script> -</head> -<body class="loading"> - -<pre id="log" style="display: none"></pre> -<div id="queue" style="display: none"> - Queue: - <select id="queue-select" size="10"></select> - <button id="remove-queue-selection">Remove selection</button> - <button id="rebaseline-queue">Rebaseline queue</button> -</div> - -<div id="header"> - <div id="controls"> - <!-- Add a dummy <select> node so that this lines up with the text on the left --> - <select style="visibility: hidden"></select> - <span id="toggle-log" class="link">Log</span> - <span class="divider">|</span> - <a href="/quitquitquit">Exit</a> - </div> - - <span id="selectors"> - <label> - Failure type: - <select id="failure-type-selector"></select> - </label> - - <label> - Directory: - <select id="directory-selector"></select> - </label> - - <label> - Test: - <select id="test-selector"></select> - </label> - </span> - - <a id="test-link" target="_blank">View test</a> - - <span id="nav-buttons"> - <button id="previous-test">«</button> - <span id="test-index"></span> of <span id="test-count"></span> - <button id="next-test">»</button> - </span> -</div> - -<table id="test-output"> - <thead id="labels"> - <tr> - <th>Expected</th> - <th>Actual</th> - <th>Diff</th> - </tr> - </thead> - <tbody id="image-outputs" style="display: none"> - <tr> - <td colspan="3"><h2>Image</h2></td> - </tr> - <tr> - <td><img id="expected-image"></td> - <td><img id="actual-image"></td> - <td> - <canvas id="diff-canvas" width="800" height="600"></canvas> - <div id="diff-checksum" style="display: none"> - <h3>Checksum mismatch</h3> - Expected: <span id="expected-checksum"></span><br> - Actual: <span id="actual-checksum"></span> - </div> - </td> - </tr> - </tbody> - <tbody id="text-outputs" style="display: none"> - <tr> - <td colspan="3"><h2>Text</h2></td> - </tr> - <tr> - <td><pre id="expected-text"></pre></td> - <td><pre id="actual-text"></pre></td> - <td><pre id="diff-text"><pre></td> - </tr> - </tbody> -</table> - -<div id="footer"> - <label>State: <span id="state"></span></label> - <label>Existing baselines: <span id="current-baselines"></span></label> - <label> - Baseline target: - <select id="baseline-target"></select> - </label> - <label> - Move current baselines to: - <select id="baseline-move-to"> - <option value="none">Nowhere (replace)</option> - </select> - </label> - - <!-- Add a dummy <button> node so that this lines up with the text on the right --> - <button style="visibility: hidden; padding-left: 0; padding-right: 0;"></button> - - <div id="action-buttons"> - <span id="toggle-queue" class="link">Queue</span> - <button id="add-to-rebaseline-queue">Add to rebaseline queue</button> - </div> -</div> - -<table id="loupe" style="display: none"> - <tr> - <td colspan="3" id="loupe-info"> - <span id="loupe-close" class="link">Close</span> - <label>Coordinate: <span id="loupe-coordinate"></span></label> - </td> - </tr> - <tr> - <td> - <div class="loupe-container"> - <canvas id="expected-loupe" width="210" height="210"></canvas> - <div class="center-highlight"></div> - </div> - </td> - <td> - <div class="loupe-container"> - <canvas id="actual-loupe" width="210" height="210"></canvas> - <div class="center-highlight"></div> - </div> - </td> - <td> - <div class="loupe-container"> - <canvas id="diff-loupe" width="210" height="210"></canvas> - <div class="center-highlight"></div> - </div> - </td> - </tr> - <tr id="loupe-colors"> - <td><label>Exp. color: <span id="expected-loupe-color"></span></label></td> - <td><label>Actual color: <span id="actual-loupe-color"></span></label></td> - <td><label>Diff color: <span id="diff-loupe-color"></span></label></td> - </tr> -</table> - -</body> -</html> diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js deleted file mode 100644 index 41f977a..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -var LOUPE_MAGNIFICATION_FACTOR = 10; - -function Loupe() -{ - this._node = $('loupe'); - this._currentCornerX = -1; - this._currentCornerY = -1; - - var self = this; - - function handleOutputClick(event) { self._handleOutputClick(event); } - $('expected-image').addEventListener('click', handleOutputClick); - $('actual-image').addEventListener('click', handleOutputClick); - $('diff-canvas').addEventListener('click', handleOutputClick); - - function handleLoupeClick(event) { self._handleLoupeClick(event); } - $('expected-loupe').addEventListener('click', handleLoupeClick); - $('actual-loupe').addEventListener('click', handleLoupeClick); - $('diff-loupe').addEventListener('click', handleLoupeClick); - - function hide(event) { self.hide(); } - $('loupe-close').addEventListener('click', hide); -} - -Loupe.prototype._handleOutputClick = function(event) -{ - // The -1 compensates for the border around the image/canvas. - this._showFor(event.offsetX - 1, event.offsetY - 1); -}; - -Loupe.prototype._handleLoupeClick = function(event) -{ - var deltaX = Math.floor(event.offsetX/LOUPE_MAGNIFICATION_FACTOR); - var deltaY = Math.floor(event.offsetY/LOUPE_MAGNIFICATION_FACTOR); - - this._showFor( - this._currentCornerX + deltaX, this._currentCornerY + deltaY); -} - -Loupe.prototype.hide = function() -{ - this._node.style.display = 'none'; -}; - -Loupe.prototype._showFor = function(x, y) -{ - this._fillFromImage(x, y, 'expected', $('expected-image')); - this._fillFromImage(x, y, 'actual', $('actual-image')); - this._fillFromCanvas(x, y, 'diff', $('diff-canvas')); - - this._node.style.display = ''; -}; - -Loupe.prototype._fillFromImage = function(x, y, type, sourceImage) -{ - var tempCanvas = document.createElement('canvas'); - tempCanvas.width = sourceImage.width; - tempCanvas.height = sourceImage.height; - var tempContext = tempCanvas.getContext('2d'); - - tempContext.drawImage(sourceImage, 0, 0); - - this._fillFromCanvas(x, y, type, tempCanvas); -}; - -Loupe.prototype._fillFromCanvas = function(x, y, type, canvas) -{ - var context = canvas.getContext('2d'); - var sourceImageData = - context.getImageData(0, 0, canvas.width, canvas.height); - - var targetCanvas = $(type + '-loupe'); - var targetContext = targetCanvas.getContext('2d'); - targetContext.fillStyle = 'rgba(255, 255, 255, 1)'; - targetContext.fillRect(0, 0, targetCanvas.width, targetCanvas.height); - - var sourceXOffset = (targetCanvas.width/LOUPE_MAGNIFICATION_FACTOR - 1)/2; - var sourceYOffset = (targetCanvas.height/LOUPE_MAGNIFICATION_FACTOR - 1)/2; - - function readPixelComponent(x, y, component) { - var offset = (y * sourceImageData.width + x) * 4 + component; - return sourceImageData.data[offset]; - } - - for (var i = -sourceXOffset; i <= sourceXOffset; i++) { - for (var j = -sourceYOffset; j <= sourceYOffset; j++) { - var sourceX = x + i; - var sourceY = y + j; - - var sourceR = readPixelComponent(sourceX, sourceY, 0); - var sourceG = readPixelComponent(sourceX, sourceY, 1); - var sourceB = readPixelComponent(sourceX, sourceY, 2); - var sourceA = readPixelComponent(sourceX, sourceY, 3)/255; - sourceA = Math.round(sourceA * 10)/10; - - var targetX = (i + sourceXOffset) * LOUPE_MAGNIFICATION_FACTOR; - var targetY = (j + sourceYOffset) * LOUPE_MAGNIFICATION_FACTOR; - var colorString = - sourceR + ', ' + sourceG + ', ' + sourceB + ', ' + sourceA; - targetContext.fillStyle = 'rgba(' + colorString + ')'; - targetContext.fillRect( - targetX, targetY, - LOUPE_MAGNIFICATION_FACTOR, LOUPE_MAGNIFICATION_FACTOR); - - if (i == 0 && j == 0) { - $('loupe-coordinate').textContent = sourceX + ', ' + sourceY; - $(type + '-loupe-color').textContent = colorString; - } - } - } - - this._currentCornerX = x - sourceXOffset; - this._currentCornerY = y - sourceYOffset; -}; diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css deleted file mode 100644 index 62afda6..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -body { - font-size: 12px; - font-family: Helvetica, Arial, sans-serif; - padding: 0; - margin: 0; -} - -.loading { - opacity: 0.5; -} - -div { - margin: 0; -} - -a, .link { - color: #aaf; - text-decoration: underline; - cursor: pointer; -} - -.link.selected { - color: #fff; - font-weight: bold; - text-decoration: none; -} - -#log, -#queue { - padding: .25em 0 0 .25em; - position: absolute; - right: 0; - height: 200px; - overflow: auto; - background: #fff; - -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .5); -} - -#log { - top: 2em; - width: 500px; -} - -#queue { - bottom: 3em; - width: 400px; -} - -#queue-select { - display: block; - width: 390px; -} - -#header, -#footer { - padding: .5em 1em; - background: #333; - color: #fff; - -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5); -} - -#header { - margin-bottom: 1em; -} - -#header .divider, -#footer .divider { - opacity: .3; - padding: 0 .5em; -} - -#header label, -#footer label { - padding-right: 1em; - color: #ccc; -} - -#test-link { - margin-right: 1em; -} - -#header label span, -#footer label span { - color: #fff; - font-weight: bold; -} - -#nav-buttons { - white-space: nowrap; -} - -#nav-buttons button { - background: #fff; - border: 0; - border-radius: 10px; -} - -#nav-buttons button:active { - -webkit-box-shadow: 0 0 5px #33f inset; - background: #aaa; -} - -#nav-buttons button[disabled] { - opacity: .5; -} - -#controls { - float: right; -} - -#test-output { - border-spacing: 0; - border-collapse: collapse; - margin: 0 auto; - width: 100%; -} - -#test-output td, -#test-output th { - padding: 0; - vertical-align: top; -} - -#image-outputs img, -#image-outputs canvas, -#image-outputs #diff-checksum { - width: 800px; - height: 600px; - border: solid 1px #ddd; - -webkit-user-select: none; - -webkit-user-drag: none; -} - -#image-outputs img, -#image-outputs canvas { - cursor: crosshair; -} - -#image-outputs img.loading, -#image-outputs canvas.loading { - opacity: .5; -} - -#image-outputs #actual-image { - margin: 0 1em; -} - -#test-output #labels th { - text-align: center; - color: #666; -} - -#text-outputs pre { - height: 600px; - width: 800px; - overflow: auto; -} - -#test-output h2 { - border-bottom: solid 1px #ccc; - font-weight: bold; - margin: 0; - background: #eee; -} - -#footer { - position: absolute; - bottom: 0; - left: 0; - right: 0; - margin-top: 1em; -} - -#state.needs_rebaseline { - color: yellow; -} - -#state.rebaseline_failed { - color: red; -} - -#state.rebaseline_succeeded { - color: green; -} - -#state.in_queue { - color: gray; -} - -#action-buttons { - float: right; -} - -#action-buttons .link { - margin-right: 1em; -} - -#footer button { - padding: 1em; -} - -#loupe { - -webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, .5); - position: absolute; - width: 634px; - top: 50%; - left: 50%; - margin-left: -151px; - margin-top: -50px; - background: #fff; - border-spacing: 0; - border-collapse: collapse; -} - -#loupe td { - padding: 0; - border: solid 1px #ccc; -} - -#loupe label { - color: #999; - padding-right: 1em; -} - -#loupe span { - color: #000; - font-weight: bold; -} - -#loupe canvas { - cursor: crosshair; -} - -#loupe #loupe-close { - float: right; -} - -#loupe #loupe-info { - background: #eee; - padding: .3em .5em; -} - -#loupe #loupe-colors td { - text-align: center; -} - -#loupe .loupe-container { - position: relative; - width: 210px; - height: 210px; -} - -#loupe .center-highlight { - position: absolute; - width: 10px; - height: 10px; - top: 50%; - left: 50%; - margin-left: -5px; - margin-top: -5px; - outline: solid 1px #999; -} diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js deleted file mode 100644 index 25f9146..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -var ALL_DIRECTORY_PATH = '[all]'; - -var STATE_NEEDS_REBASELINE = 'needs_rebaseline'; -var STATE_REBASELINE_FAILED = 'rebaseline_failed'; -var STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded'; -var STATE_IN_QUEUE = 'in_queue'; -var STATE_TO_DISPLAY_STATE = {}; -STATE_TO_DISPLAY_STATE[STATE_NEEDS_REBASELINE] = 'Needs rebaseline'; -STATE_TO_DISPLAY_STATE[STATE_REBASELINE_FAILED] = 'Rebaseline failed'; -STATE_TO_DISPLAY_STATE[STATE_REBASELINE_SUCCEEDED] = 'Rebaseline succeeded'; -STATE_TO_DISPLAY_STATE[STATE_IN_QUEUE] = 'In queue'; - -var results; -var testsByFailureType = {}; -var testsByDirectory = {}; -var selectedTests = []; -var loupe; -var queue; - -function main() -{ - $('failure-type-selector').addEventListener('change', selectFailureType); - $('directory-selector').addEventListener('change', selectDirectory); - $('test-selector').addEventListener('change', selectTest); - $('next-test').addEventListener('click', nextTest); - $('previous-test').addEventListener('click', previousTest); - - $('toggle-log').addEventListener('click', function() { toggle('log'); }); - - loupe = new Loupe(); - queue = new RebaselineQueue(); - - document.addEventListener('keydown', function(event) { - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { - return; - } - - switch (event.keyIdentifier) { - case 'Left': - event.preventDefault(); - previousTest(); - break; - case 'Right': - event.preventDefault(); - nextTest(); - break; - case 'U+0051': // q - queue.addCurrentTest(); - break; - case 'U+0058': // x - queue.removeCurrentTest(); - break; - case 'U+0052': // r - queue.rebaseline(); - break; - } - }); - - loadText('/platforms.json', function(text) { - var platforms = JSON.parse(text); - platforms.platforms.forEach(function(platform) { - var platformOption = document.createElement('option'); - platformOption.value = platform; - platformOption.textContent = platform; - - var targetOption = platformOption.cloneNode(true); - targetOption.selected = platform == platforms.defaultPlatform; - $('baseline-target').appendChild(targetOption); - $('baseline-move-to').appendChild(platformOption.cloneNode(true)); - }); - }); - - loadText('/results.json', function(text) { - results = JSON.parse(text); - displayResults(); - }); -} - -/** - * Groups test results by failure type. - */ -function displayResults() -{ - var failureTypeSelector = $('failure-type-selector'); - var failureTypes = []; - - for (var testName in results.tests) { - var test = results.tests[testName]; - if (test.actual == 'PASS') { - continue; - } - var failureType = test.actual + ' (expected ' + test.expected + ')'; - if (!(failureType in testsByFailureType)) { - testsByFailureType[failureType] = []; - failureTypes.push(failureType); - } - testsByFailureType[failureType].push(testName); - } - - // Sort by number of failures - failureTypes.sort(function(a, b) { - return testsByFailureType[b].length - testsByFailureType[a].length; - }); - - for (var i = 0, failureType; failureType = failureTypes[i]; i++) { - var failureTypeOption = document.createElement('option'); - failureTypeOption.value = failureType; - failureTypeOption.textContent = failureType + ' - ' + testsByFailureType[failureType].length + ' tests'; - failureTypeSelector.appendChild(failureTypeOption); - } - - selectFailureType(); - - document.body.classList.remove('loading'); -} - -/** - * For a given failure type, gets all the tests and groups them by directory - * (populating the directory selector with them). - */ -function selectFailureType() -{ - var selectedFailureType = getSelectValue('failure-type-selector'); - var tests = testsByFailureType[selectedFailureType]; - - testsByDirectory = {} - var displayDirectoryNamesByDirectory = {}; - var directories = []; - - // Include a special option for all tests - testsByDirectory[ALL_DIRECTORY_PATH] = tests; - displayDirectoryNamesByDirectory[ALL_DIRECTORY_PATH] = 'all'; - directories.push(ALL_DIRECTORY_PATH); - - // Roll up tests by ancestor directories - tests.forEach(function(test) { - var pathPieces = test.split('/'); - var pathDirectories = pathPieces.slice(0, pathPieces.length -1); - var ancestorDirectory = ''; - - pathDirectories.forEach(function(pathDirectory, index) { - ancestorDirectory += pathDirectory + '/'; - if (!(ancestorDirectory in testsByDirectory)) { - testsByDirectory[ancestorDirectory] = []; - var displayDirectoryName = new Array(index * 6).join(' ') + pathDirectory; - displayDirectoryNamesByDirectory[ancestorDirectory] = displayDirectoryName; - directories.push(ancestorDirectory); - } - - testsByDirectory[ancestorDirectory].push(test); - }); - }); - - directories.sort(); - - var directorySelector = $('directory-selector'); - directorySelector.innerHTML = ''; - - directories.forEach(function(directory) { - var directoryOption = document.createElement('option'); - directoryOption.value = directory; - directoryOption.innerHTML = - displayDirectoryNamesByDirectory[directory] + ' - ' + - testsByDirectory[directory].length + ' tests'; - directorySelector.appendChild(directoryOption); - }); - - selectDirectory(); -} - -/** - * For a given failure type and directory and failure type, gets all the tests - * in that directory and populatest the test selector with them. - */ -function selectDirectory() -{ - var selectedDirectory = getSelectValue('directory-selector'); - selectedTests = testsByDirectory[selectedDirectory]; - - selectedTests.sort(); - - var testSelector = $('test-selector'); - testSelector.innerHTML = ''; - - selectedTests.forEach(function(testName) { - var testOption = document.createElement('option'); - testOption.value = testName; - var testDisplayName = testName; - if (testName.lastIndexOf(selectedDirectory) == 0) { - testDisplayName = testName.substring(selectedDirectory.length); - } - testOption.innerHTML = ' ' + testDisplayName; - testSelector.appendChild(testOption); - }); - - selectTest(); -} - -function getSelectedTest() -{ - return getSelectValue('test-selector'); -} - -function selectTest() -{ - var selectedTest = getSelectedTest(); - - if (results.tests[selectedTest].actual.indexOf('IMAGE') != -1) { - $('image-outputs').style.display = ''; - displayImageResults(selectedTest); - } else { - $('image-outputs').style.display = 'none'; - } - - if (results.tests[selectedTest].actual.indexOf('TEXT') != -1) { - $('text-outputs').style.display = ''; - displayTextResults(selectedTest); - } else { - $('text-outputs').style.display = 'none'; - } - - var currentBaselines = $('current-baselines'); - currentBaselines.textContent = ''; - var baselines = results.tests[selectedTest].baselines; - var testName = selectedTest.split('.').slice(0, -1).join('.'); - for (var platform in baselines) { - if (currentBaselines.firstChild) { - currentBaselines.appendChild(document.createTextNode('; ')); - } - currentBaselines.appendChild(document.createTextNode(platform + ' (')); - for (var i = 0, extension; extension = baselines[platform][i]; i++) { - if (i != 0) { - currentBaselines.appendChild(document.createTextNode(', ')); - } - var link = document.createElement('a'); - var baselinePath = ''; - if (platform != 'base') { - baselinePath += 'platform/' + platform + '/'; - } - baselinePath += testName + '-expected' + extension; - link.href = getTracUrl(baselinePath); - if (extension == '.checksum') { - link.textContent = 'chk'; - } else { - link.textContent = extension.substring(1); - } - link.target = '_blank'; - currentBaselines.appendChild(link); - } - currentBaselines.appendChild(document.createTextNode(')')); - } - - updateState(); - loupe.hide(); - - prefetchNextImageTest(); -} - -function prefetchNextImageTest() -{ - var testSelector = $('test-selector'); - if (testSelector.selectedIndex == testSelector.options.length - 1) { - return; - } - var nextTest = testSelector.options[testSelector.selectedIndex + 1].value; - if (results.tests[nextTest].actual.indexOf('IMAGE') != -1) { - new Image().src = getTestResultUrl(nextTest, 'expected-image'); - new Image().src = getTestResultUrl(nextTest, 'actual-image'); - } -} - -function updateState() -{ - var testName = getSelectedTest(); - var testIndex = selectedTests.indexOf(testName); - var testCount = selectedTests.length - $('test-index').textContent = testIndex + 1; - $('test-count').textContent = testCount; - - $('next-test').disabled = testIndex == testCount - 1; - $('previous-test').disabled = testIndex == 0; - - $('test-link').href = getTracUrl(testName); - - var state = results.tests[testName].state; - $('state').className = state; - $('state').innerHTML = STATE_TO_DISPLAY_STATE[state]; - - queue.updateState(); -} - -function getTestResultUrl(testName, mode) -{ - return '/test_result?test=' + testName + '&mode=' + mode; -} - -var currentExpectedImageTest; -var currentActualImageTest; - -function displayImageResults(testName) -{ - if (currentExpectedImageTest == currentActualImageTest - && currentExpectedImageTest == testName) { - return; - } - - function displayImageResult(mode, callback) { - var image = $(mode); - image.className = 'loading'; - image.src = getTestResultUrl(testName, mode); - image.onload = function() { - image.className = ''; - callback(); - updateImageDiff(); - }; - } - - displayImageResult( - 'expected-image', - function() { currentExpectedImageTest = testName; }); - displayImageResult( - 'actual-image', - function() { currentActualImageTest = testName; }); - - $('diff-canvas').className = 'loading'; - $('diff-canvas').style.display = ''; - $('diff-checksum').style.display = 'none'; -} - -/** - * Computes a graphical a diff between the expected and actual images by - * rendering each to a canvas, getting the image data, and comparing the RGBA - * components of each pixel. The output is put into the diff canvas, with - * identical pixels appearing at 12.5% opacity and different pixels being - * highlighted in red. - */ -function updateImageDiff() { - if (currentExpectedImageTest != currentActualImageTest) - return; - - var expectedImage = $('expected-image'); - var actualImage = $('actual-image'); - - function getImageData(image) { - var imageCanvas = document.createElement('canvas'); - imageCanvas.width = image.width; - imageCanvas.height = image.height; - imageCanvasContext = imageCanvas.getContext('2d'); - - imageCanvasContext.fillStyle = 'rgba(255, 255, 255, 1)'; - imageCanvasContext.fillRect( - 0, 0, image.width, image.height); - - imageCanvasContext.drawImage(image, 0, 0); - return imageCanvasContext.getImageData( - 0, 0, image.width, image.height); - } - - var expectedImageData = getImageData(expectedImage); - var actualImageData = getImageData(actualImage); - - var diffCanvas = $('diff-canvas'); - var diffCanvasContext = diffCanvas.getContext('2d'); - var diffImageData = - diffCanvasContext.createImageData(diffCanvas.width, diffCanvas.height); - - // Avoiding property lookups for all these during the per-pixel loop below - // provides a significant performance benefit. - var expectedWidth = expectedImage.width; - var expectedHeight = expectedImage.height; - var expected = expectedImageData.data; - - var actualWidth = actualImage.width; - var actual = actualImageData.data; - - var diffWidth = diffImageData.width; - var diff = diffImageData.data; - - var hadDiff = false; - for (var x = 0; x < expectedWidth; x++) { - for (var y = 0; y < expectedHeight; y++) { - var expectedOffset = (y * expectedWidth + x) * 4; - var actualOffset = (y * actualWidth + x) * 4; - var diffOffset = (y * diffWidth + x) * 4; - if (expected[expectedOffset] != actual[actualOffset] || - expected[expectedOffset + 1] != actual[actualOffset + 1] || - expected[expectedOffset + 2] != actual[actualOffset + 2] || - expected[expectedOffset + 3] != actual[actualOffset + 3]) { - hadDiff = true; - diff[diffOffset] = 255; - diff[diffOffset + 1] = 0; - diff[diffOffset + 2] = 0; - diff[diffOffset + 3] = 255; - } else { - diff[diffOffset] = expected[expectedOffset]; - diff[diffOffset + 1] = expected[expectedOffset + 1]; - diff[diffOffset + 2] = expected[expectedOffset + 2]; - diff[diffOffset + 3] = 32; - } - } - } - - diffCanvasContext.putImageData( - diffImageData, - 0, 0, - 0, 0, - diffImageData.width, diffImageData.height); - diffCanvas.className = ''; - - if (!hadDiff) { - diffCanvas.style.display = 'none'; - $('diff-checksum').style.display = ''; - loadTextResult(currentExpectedImageTest, 'expected-checksum'); - loadTextResult(currentExpectedImageTest, 'actual-checksum'); - } -} - -function loadTextResult(testName, mode) -{ - loadText(getTestResultUrl(testName, mode), function(text) { - $(mode).textContent = text; - }); -} - -function displayTextResults(testName) -{ - loadTextResult(testName, 'expected-text'); - loadTextResult(testName, 'actual-text'); - loadTextResult(testName, 'diff-text'); -} - -function nextTest() -{ - var testSelector = $('test-selector'); - var nextTestIndex = testSelector.selectedIndex + 1; - while (true) { - if (nextTestIndex == testSelector.options.length) { - return; - } - if (testSelector.options[nextTestIndex].disabled) { - nextTestIndex++; - } else { - testSelector.selectedIndex = nextTestIndex; - selectTest(); - return; - } - } -} - -function previousTest() -{ - var testSelector = $('test-selector'); - var previousTestIndex = testSelector.selectedIndex - 1; - while (true) { - if (previousTestIndex == -1) { - return; - } - if (testSelector.options[previousTestIndex].disabled) { - previousTestIndex--; - } else { - testSelector.selectedIndex = previousTestIndex; - selectTest(); - return - } - } -} - -window.addEventListener('DOMContentLoaded', main); diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js deleted file mode 100644 index f57c919..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -function RebaselineQueue() -{ - this._selectNode = $('queue-select'); - this._rebaselineButtonNode = $('rebaseline-queue'); - this._toggleNode = $('toggle-queue'); - this._removeSelectionButtonNode = $('remove-queue-selection'); - - this._inProgressRebaselineCount = 0; - - var self = this; - $('add-to-rebaseline-queue').addEventListener( - 'click', function() { self.addCurentTest(); }); - this._selectNode.addEventListener('change', updateState); - this._removeSelectionButtonNode.addEventListener( - 'click', function() { self._removeSelection(); }); - this._rebaselineButtonNode.addEventListener( - 'click', function() { self.rebaseline(); }); - this._toggleNode.addEventListener( - 'click', function() { toggle('queue'); }); -} - -RebaselineQueue.prototype.updateState = function() -{ - var testName = getSelectedTest(); - - var state = results.tests[testName].state; - $('add-to-rebaseline-queue').disabled = state != STATE_NEEDS_REBASELINE; - - var queueLength = this._selectNode.options.length; - if (this._inProgressRebaselineCount > 0) { - this._rebaselineButtonNode.disabled = true; - this._rebaselineButtonNode.textContent = - 'Rebaseline in progress (' + this._inProgressRebaselineCount + - ' tests left)'; - } else if (queueLength == 0) { - this._rebaselineButtonNode.disabled = true; - this._rebaselineButtonNode.textContent = 'Rebaseline queue'; - this._toggleNode.textContent = 'Queue'; - } else { - this._rebaselineButtonNode.disabled = false; - this._rebaselineButtonNode.textContent = - 'Rebaseline queue (' + queueLength + ' tests)'; - this._toggleNode.textContent = 'Queue (' + queueLength + ' tests)'; - } - this._removeSelectionButtonNode.disabled = - this._selectNode.selectedIndex == -1; -}; - -RebaselineQueue.prototype.addCurrentTest = function() -{ - var testName = getSelectedTest(); - var test = results.tests[testName]; - - if (test.state != STATE_NEEDS_REBASELINE) { - log('Cannot add test with state "' + test.state + '" to queue.', - log.WARNING); - return; - } - - var queueOption = document.createElement('option'); - queueOption.value = testName; - queueOption.textContent = testName; - this._selectNode.appendChild(queueOption); - test.state = STATE_IN_QUEUE; - updateState(); -}; - -RebaselineQueue.prototype.removeCurrentTest = function() -{ - this._removeTest(getSelectedTest()); -}; - -RebaselineQueue.prototype._removeSelection = function() -{ - if (this._selectNode.selectedIndex == -1) - return; - - this._removeTest( - this._selectNode.options[this._selectNode.selectedIndex].value); -}; - -RebaselineQueue.prototype._removeTest = function(testName) -{ - var queueOption = this._selectNode.firstChild; - - while (queueOption && queueOption.value != testName) { - queueOption = queueOption.nextSibling; - } - - if (!queueOption) - return; - - this._selectNode.removeChild(queueOption); - var test = results.tests[testName]; - test.state = STATE_NEEDS_REBASELINE; - updateState(); -}; - -RebaselineQueue.prototype.rebaseline = function() -{ - var testNames = []; - for (var queueOption = this._selectNode.firstChild; - queueOption; - queueOption = queueOption.nextSibling) { - testNames.push(queueOption.value); - } - - this._inProgressRebaselineCount = testNames.length; - updateState(); - - testNames.forEach(this._rebaselineTest, this); -}; - -RebaselineQueue.prototype._rebaselineTest = function(testName) -{ - var baselineTarget = getSelectValue('baseline-target'); - var baselineMoveTo = getSelectValue('baseline-move-to'); - - // FIXME: actually rebaseline - log('Rebaselining ' + testName + ' for platform ' + baselineTarget + '...'); - var test = results.tests[testName]; - this._removeTest(testName); - this._inProgressRebaselineCount--; - test.state = STATE_REBASELINE_SUCCEEDED; - updateState(); - log('Rebaselined add test with state ' + test.state + ' to queue', - log.SUCCESS); -}; diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js deleted file mode 100644 index b348462..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -var results; -var testsByFailureType = {}; -var testsByDirectory = {}; -var selectedTests = []; - -function $(id) -{ - return document.getElementById(id); -} - -function getSelectValue(id) -{ - var select = $(id); - if (select.selectedIndex == -1) { - return null; - } else { - return select.options[select.selectedIndex].value; - } -} - -function loadText(url, callback) -{ - var xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.addEventListener('load', function() { callback(xhr.responseText); }); - xhr.send(); -} - -function log(text, type) -{ - var node = $('log'); - - if (type) { - var typeNode = document.createElement('span'); - typeNode.textContent = type.text; - typeNode.style.color = type.color; - node.appendChild(typeNode); - } - - node.appendChild(document.createTextNode(text + '\n')); - node.scrollTop = node.scrollHeight; -} - -log.WARNING = {text: 'Warning: ', color: '#aa3'}; -log.SUCCESS = {text: 'Success: ', color: 'green'}; -log.ERROR = {text: 'Error: ', color: 'red'}; - -function toggle(id) -{ - var element = $(id); - var toggler = $('toggle-' + id); - if (element.style.display == 'none') { - element.style.display = ''; - toggler.className = 'link selected'; - } else { - element.style.display = 'none'; - toggler.className = 'link'; - } -} - -function getTracUrl(layoutTestPath) -{ - return 'http://trac.webkit.org/browser/trunk/LayoutTests/' + layoutTestPath; -}
\ No newline at end of file diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download.py b/WebKitTools/Scripts/webkitpy/tool/commands/download.py deleted file mode 100644 index 457c050..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/download.py +++ /dev/null @@ -1,394 +0,0 @@ -# Copyright (c) 2009 Google Inc. All rights reserved. -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os - -import webkitpy.tool.steps as steps - -from webkitpy.common.checkout.changelog import ChangeLog, view_source_url -from webkitpy.common.system.executive import ScriptError -from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand -from webkitpy.tool.commands.stepsequence import StepSequence -from webkitpy.tool.comments import bug_comment_from_commit_text -from webkitpy.tool.grammar import pluralize -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand -from webkitpy.common.system.deprecated_logging import error, log - - -class Clean(AbstractSequencedCommand): - name = "clean" - help_text = "Clean the working copy" - steps = [ - steps.CleanWorkingDirectory, - ] - - def _prepare_state(self, options, args, tool): - options.force_clean = True - - -class Update(AbstractSequencedCommand): - name = "update" - help_text = "Update working copy (used internally)" - steps = [ - steps.CleanWorkingDirectory, - steps.Update, - ] - - -class Build(AbstractSequencedCommand): - name = "build" - help_text = "Update working copy and build" - steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.Build, - ] - - def _prepare_state(self, options, args, tool): - options.build = True - - -class BuildAndTest(AbstractSequencedCommand): - name = "build-and-test" - help_text = "Update working copy, build, and run the tests" - steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.Build, - steps.RunTests, - ] - - -class Land(AbstractSequencedCommand): - name = "land" - help_text = "Land the current working directory diff and updates the associated bug if any" - argument_names = "[BUGID]" - show_in_main_help = True - steps = [ - steps.EnsureBuildersAreGreen, - steps.UpdateChangeLogsWithReviewer, - steps.ValidateReviewer, - steps.Build, - steps.RunTests, - steps.Commit, - steps.CloseBugForLandDiff, - ] - long_help = """land commits the current working copy diff (just as svn or git commit would). -land will NOT build and run the tests before committing, but you can use the --build option for that. -If a bug id is provided, or one can be found in the ChangeLog land will update the bug after committing.""" - - def _prepare_state(self, options, args, tool): - changed_files = self._tool.scm().changed_files(options.git_commit) - return { - "changed_files": changed_files, - "bug_id": (args and args[0]) or tool.checkout().bug_id_for_this_commit(options.git_commit, changed_files), - } - - -class LandCowboy(AbstractSequencedCommand): - name = "land-cowboy" - help_text = "Prepares a ChangeLog and lands the current working directory diff." - steps = [ - steps.PrepareChangeLog, - steps.EditChangeLog, - steps.ConfirmDiff, - steps.Build, - steps.RunTests, - steps.Commit, - ] - - -class AbstractPatchProcessingCommand(AbstractDeclarativeCommand): - # Subclasses must implement the methods below. We don't declare them here - # because we want to be able to implement them with mix-ins. - # - # def _fetch_list_of_patches_to_process(self, options, args, tool): - # def _prepare_to_process(self, options, args, tool): - - @staticmethod - def _collect_patches_by_bug(patches): - bugs_to_patches = {} - for patch in patches: - bugs_to_patches[patch.bug_id()] = bugs_to_patches.get(patch.bug_id(), []) + [patch] - return bugs_to_patches - - def execute(self, options, args, tool): - self._prepare_to_process(options, args, tool) - patches = self._fetch_list_of_patches_to_process(options, args, tool) - - # It's nice to print out total statistics. - bugs_to_patches = self._collect_patches_by_bug(patches) - log("Processing %s from %s." % (pluralize("patch", len(patches)), pluralize("bug", len(bugs_to_patches)))) - - for patch in patches: - self._process_patch(patch, options, args, tool) - - -class AbstractPatchSequencingCommand(AbstractPatchProcessingCommand): - prepare_steps = None - main_steps = None - - def __init__(self): - options = [] - self._prepare_sequence = StepSequence(self.prepare_steps) - self._main_sequence = StepSequence(self.main_steps) - options = sorted(set(self._prepare_sequence.options() + self._main_sequence.options())) - AbstractPatchProcessingCommand.__init__(self, options) - - def _prepare_to_process(self, options, args, tool): - self._prepare_sequence.run_and_handle_errors(tool, options) - - def _process_patch(self, patch, options, args, tool): - state = { "patch" : patch } - self._main_sequence.run_and_handle_errors(tool, options, state) - - -class ProcessAttachmentsMixin(object): - def _fetch_list_of_patches_to_process(self, options, args, tool): - return map(lambda patch_id: tool.bugs.fetch_attachment(patch_id), args) - - -class ProcessBugsMixin(object): - def _fetch_list_of_patches_to_process(self, options, args, tool): - all_patches = [] - for bug_id in args: - patches = tool.bugs.fetch_bug(bug_id).reviewed_patches() - log("%s found on bug %s." % (pluralize("reviewed patch", len(patches)), bug_id)) - all_patches += patches - return all_patches - - -class CheckStyle(AbstractPatchSequencingCommand, ProcessAttachmentsMixin): - name = "check-style" - help_text = "Run check-webkit-style on the specified attachments" - argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]" - main_steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.ApplyPatch, - steps.CheckStyle, - ] - - -class BuildAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsMixin): - name = "build-attachment" - help_text = "Apply and build patches from bugzilla" - argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]" - main_steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.ApplyPatch, - steps.Build, - ] - - -class BuildAndTestAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsMixin): - name = "build-and-test-attachment" - help_text = "Apply, build, and test patches from bugzilla" - argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]" - main_steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.ApplyPatch, - steps.Build, - steps.RunTests, - ] - - -class AbstractPatchApplyingCommand(AbstractPatchSequencingCommand): - prepare_steps = [ - steps.EnsureLocalCommitIfNeeded, - steps.CleanWorkingDirectoryWithLocalCommits, - steps.Update, - ] - main_steps = [ - steps.ApplyPatchWithLocalCommit, - ] - long_help = """Updates the working copy. -Downloads and applies the patches, creating local commits if necessary.""" - - -class ApplyAttachment(AbstractPatchApplyingCommand, ProcessAttachmentsMixin): - name = "apply-attachment" - help_text = "Apply an attachment to the local working directory" - argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]" - show_in_main_help = True - - -class ApplyFromBug(AbstractPatchApplyingCommand, ProcessBugsMixin): - name = "apply-from-bug" - help_text = "Apply reviewed patches from provided bugs to the local working directory" - argument_names = "BUGID [BUGIDS]" - show_in_main_help = True - - -class AbstractPatchLandingCommand(AbstractPatchSequencingCommand): - prepare_steps = [ - steps.EnsureBuildersAreGreen, - ] - main_steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.ApplyPatch, - steps.ValidateReviewer, - steps.Build, - steps.RunTests, - steps.Commit, - steps.ClosePatch, - steps.CloseBug, - ] - long_help = """Checks to make sure builders are green. -Updates the working copy. -Applies the patch. -Builds. -Runs the layout tests. -Commits the patch. -Clears the flags on the patch. -Closes the bug if no patches are marked for review.""" - - -class LandAttachment(AbstractPatchLandingCommand, ProcessAttachmentsMixin): - name = "land-attachment" - help_text = "Land patches from bugzilla, optionally building and testing them first" - argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]" - show_in_main_help = True - - -class LandFromBug(AbstractPatchLandingCommand, ProcessBugsMixin): - name = "land-from-bug" - help_text = "Land all patches on the given bugs, optionally building and testing them first" - argument_names = "BUGID [BUGIDS]" - show_in_main_help = True - - -class AbstractRolloutPrepCommand(AbstractSequencedCommand): - argument_names = "REVISION REASON" - - def _commit_info(self, revision): - commit_info = self._tool.checkout().commit_info_for_revision(revision) - if commit_info and commit_info.bug_id(): - # Note: Don't print a bug URL here because it will confuse the - # SheriffBot because the SheriffBot just greps the output - # of create-rollout for bug URLs. It should do better - # parsing instead. - log("Preparing rollout for bug %s." % commit_info.bug_id()) - else: - log("Unable to parse bug number from diff.") - return commit_info - - def _prepare_state(self, options, args, tool): - revision = args[0] - commit_info = self._commit_info(revision) - cc_list = sorted([party.bugzilla_email() - for party in commit_info.responsible_parties() - if party.bugzilla_email()]) - return { - "revision": revision, - "bug_id": commit_info.bug_id(), - # FIXME: We should used the list as the canonical representation. - "bug_cc": ",".join(cc_list), - "reason": args[1], - } - - -class PrepareRollout(AbstractRolloutPrepCommand): - name = "prepare-rollout" - help_text = "Revert the given revision in the working copy and prepare ChangeLogs with revert reason" - long_help = """Updates the working copy. -Applies the inverse diff for the provided revision. -Creates an appropriate rollout ChangeLog, including a trac link and bug link. -""" - steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.RevertRevision, - steps.PrepareChangeLogForRevert, - ] - - -class CreateRollout(AbstractRolloutPrepCommand): - name = "create-rollout" - help_text = "Creates a bug to track a broken SVN revision and uploads a rollout patch." - steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.RevertRevision, - steps.CreateBug, - steps.PrepareChangeLogForRevert, - steps.PostDiffForRevert, - ] - - def _prepare_state(self, options, args, tool): - state = AbstractRolloutPrepCommand._prepare_state(self, options, args, tool) - # Currently, state["bug_id"] points to the bug that caused the - # regression. We want to create a new bug that blocks the old bug - # so we move state["bug_id"] to state["bug_blocked"] and delete the - # old state["bug_id"] so that steps.CreateBug will actually create - # the new bug that we want (and subsequently store its bug id into - # state["bug_id"]) - state["bug_blocked"] = state["bug_id"] - del state["bug_id"] - state["bug_title"] = "REGRESSION(r%s): %s" % (state["revision"], state["reason"]) - state["bug_description"] = "%s broke the build:\n%s" % (view_source_url(state["revision"]), state["reason"]) - # FIXME: If we had more context here, we could link to other open bugs - # that mention the test that regressed. - if options.parent_command == "sheriff-bot": - state["bug_description"] += """ - -This is an automatic bug report generated by the sheriff-bot. If this bug -report was created because of a flaky test, please file a bug for the flaky -test (if we don't already have one on file) and dup this bug against that bug -so that we can track how often these flaky tests case pain. - -"Only you can prevent forest fires." -- Smokey the Bear -""" - return state - - -class Rollout(AbstractRolloutPrepCommand): - name = "rollout" - show_in_main_help = True - help_text = "Revert the given revision in the working copy and optionally commit the revert and re-open the original bug" - long_help = """Updates the working copy. -Applies the inverse diff for the provided revision. -Creates an appropriate rollout ChangeLog, including a trac link and bug link. -Opens the generated ChangeLogs in $EDITOR. -Shows the prepared diff for confirmation. -Commits the revert and updates the bug (including re-opening the bug if necessary).""" - steps = [ - steps.CleanWorkingDirectory, - steps.Update, - steps.RevertRevision, - steps.PrepareChangeLogForRevert, - steps.EditChangeLog, - steps.ConfirmDiff, - steps.Build, - steps.Commit, - steps.ReopenBugAfterRollout, - ] diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py deleted file mode 100644 index 9ca343b..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import unittest - -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.commands.commandtest import CommandsTest -from webkitpy.tool.commands.download import * -from webkitpy.tool.mocktool import MockOptions, MockTool - - -class AbstractRolloutPrepCommandTest(unittest.TestCase): - def test_commit_info(self): - command = AbstractRolloutPrepCommand() - tool = MockTool() - command.bind_to_tool(tool) - output = OutputCapture() - - expected_stderr = "Preparing rollout for bug 42.\n" - commit_info = output.assert_outputs(self, command._commit_info, [1234], expected_stderr=expected_stderr) - self.assertTrue(commit_info) - - mock_commit_info = Mock() - mock_commit_info.bug_id = lambda: None - tool._checkout.commit_info_for_revision = lambda revision: mock_commit_info - expected_stderr = "Unable to parse bug number from diff.\n" - commit_info = output.assert_outputs(self, command._commit_info, [1234], expected_stderr=expected_stderr) - self.assertEqual(commit_info, mock_commit_info) - - -class DownloadCommandsTest(CommandsTest): - def _default_options(self): - options = MockOptions() - options.build = True - options.build_style = True - options.check_builders = True - options.check_style = True - options.clean = True - options.close_bug = True - options.force_clean = False - options.force_patch = True - options.non_interactive = False - options.parent_command = 'MOCK parent command' - options.quiet = False - options.test = True - options.update = True - return options - - def test_build(self): - expected_stderr = "Updating working directory\nBuilding WebKit\n" - self.assert_execute_outputs(Build(), [], options=self._default_options(), expected_stderr=expected_stderr) - - def test_build_and_test(self): - expected_stderr = "Updating working directory\nBuilding WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\n" - self.assert_execute_outputs(BuildAndTest(), [], options=self._default_options(), expected_stderr=expected_stderr) - - def test_apply_attachment(self): - options = self._default_options() - options.update = True - options.local_commit = True - expected_stderr = "Updating working directory\nProcessing 1 patch from 1 bug.\nProcessing patch 197 from bug 42.\n" - self.assert_execute_outputs(ApplyAttachment(), [197], options=options, expected_stderr=expected_stderr) - - def test_apply_patches(self): - options = self._default_options() - options.update = True - options.local_commit = True - expected_stderr = "Updating working directory\n2 reviewed patches found on bug 42.\nProcessing 2 patches from 1 bug.\nProcessing patch 197 from bug 42.\nProcessing patch 128 from bug 42.\n" - self.assert_execute_outputs(ApplyFromBug(), [42], options=options, expected_stderr=expected_stderr) - - def test_land_diff(self): - expected_stderr = "Building WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\nUpdating bug 42\n" - mock_tool = MockTool() - mock_tool.scm().create_patch = Mock() - mock_tool.checkout().modified_changelogs = Mock(return_value=[]) - self.assert_execute_outputs(Land(), [42], options=self._default_options(), expected_stderr=expected_stderr, tool=mock_tool) - # Make sure we're not calling expensive calls too often. - self.assertEqual(mock_tool.scm().create_patch.call_count, 0) - self.assertEqual(mock_tool.checkout().modified_changelogs.call_count, 1) - - def test_land_red_builders(self): - expected_stderr = '\nWARNING: Builders ["Builder2"] are red, please watch your commit carefully.\nSee http://dummy_buildbot_host/console?category=core\n\nBuilding WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\nUpdating bug 42\n' - mock_tool = MockTool() - mock_tool.buildbot.light_tree_on_fire() - self.assert_execute_outputs(Land(), [42], options=self._default_options(), expected_stderr=expected_stderr, tool=mock_tool) - - def test_check_style(self): - expected_stderr = "Processing 1 patch from 1 bug.\nUpdating working directory\nProcessing patch 197 from bug 42.\nRunning check-webkit-style\n" - self.assert_execute_outputs(CheckStyle(), [197], options=self._default_options(), expected_stderr=expected_stderr) - - def test_build_attachment(self): - expected_stderr = "Processing 1 patch from 1 bug.\nUpdating working directory\nProcessing patch 197 from bug 42.\nBuilding WebKit\n" - self.assert_execute_outputs(BuildAttachment(), [197], options=self._default_options(), expected_stderr=expected_stderr) - - def test_land_attachment(self): - # FIXME: This expected result is imperfect, notice how it's seeing the same patch as still there after it thought it would have cleared the flags. - expected_stderr = """Processing 1 patch from 1 bug. -Updating working directory -Processing patch 197 from bug 42. -Building WebKit -Running Python unit tests -Running Perl unit tests -Running JavaScriptCore tests -Running run-webkit-tests -Committed r49824: <http://trac.webkit.org/changeset/49824> -Not closing bug 42 as attachment 197 has review=+. Assuming there are more patches to land from this bug. -""" - self.assert_execute_outputs(LandAttachment(), [197], options=self._default_options(), expected_stderr=expected_stderr) - - def test_land_patches(self): - # FIXME: This expected result is imperfect, notice how it's seeing the same patch as still there after it thought it would have cleared the flags. - expected_stderr = """2 reviewed patches found on bug 42. -Processing 2 patches from 1 bug. -Updating working directory -Processing patch 197 from bug 42. -Building WebKit -Running Python unit tests -Running Perl unit tests -Running JavaScriptCore tests -Running run-webkit-tests -Committed r49824: <http://trac.webkit.org/changeset/49824> -Not closing bug 42 as attachment 197 has review=+. Assuming there are more patches to land from this bug. -Updating working directory -Processing patch 128 from bug 42. -Building WebKit -Running Python unit tests -Running Perl unit tests -Running JavaScriptCore tests -Running run-webkit-tests -Committed r49824: <http://trac.webkit.org/changeset/49824> -Not closing bug 42 as attachment 197 has review=+. Assuming there are more patches to land from this bug. -""" - self.assert_execute_outputs(LandFromBug(), [42], options=self._default_options(), expected_stderr=expected_stderr) - - def test_prepare_rollout(self): - expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\n" - self.assert_execute_outputs(PrepareRollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr) - - def test_create_rollout(self): - expected_stderr = """Preparing rollout for bug 42. -Updating working directory -MOCK create_bug -bug_title: REGRESSION(r852): Reason -bug_description: http://trac.webkit.org/changeset/852 broke the build: -Reason -Running prepare-ChangeLog -MOCK add_patch_to_bug: bug_id=None, description=ROLLOUT of r852, mark_for_review=False, mark_for_commit_queue=True, mark_for_landing=False --- Begin comment -- -Any committer can land this patch automatically by marking it commit-queue+. The commit-queue will build and test the patch before landing to ensure that the rollout will be successful. This process takes approximately 15 minutes. - -If you would like to land the rollout faster, you can use the following command: - - webkit-patch land-attachment ATTACHMENT_ID --ignore-builders - -where ATTACHMENT_ID is the ID of this attachment. --- End comment -- -""" - self.assert_execute_outputs(CreateRollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr) - - def test_rollout(self): - expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\nMOCK: user.open_url: file://...\nBuilding WebKit\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\n" - expected_stdout = "Was that diff correct?\n" - self.assert_execute_outputs(Rollout(), [852, "Reason"], options=self._default_options(), expected_stdout=expected_stdout, expected_stderr=expected_stderr) - diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py deleted file mode 100644 index 3b53d1a..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from webkitpy.tool.commands.queues import AbstractReviewQueue -from webkitpy.common.config.committers import CommitterList -from webkitpy.common.config.ports import WebKitPort -from webkitpy.common.system.executive import ScriptError -from webkitpy.tool.bot.queueengine import QueueEngine - - -class AbstractEarlyWarningSystem(AbstractReviewQueue): - _build_style = "release" - - def __init__(self): - AbstractReviewQueue.__init__(self) - self.port = WebKitPort.port(self.port_name) - - def should_proceed_with_work_item(self, patch): - return True - - def _can_build(self): - try: - self.run_webkit_patch([ - "build", - self.port.flag(), - "--build-style=%s" % self._build_style, - "--force-clean", - "--no-update"]) - return True - except ScriptError, e: - failure_log = self._log_from_script_error_for_upload(e) - self._update_status("Unable to perform a build", results_file=failure_log) - return False - - def _build(self, patch, first_run=False): - try: - args = [ - "build-attachment", - self.port.flag(), - "--build", - "--build-style=%s" % self._build_style, - "--force-clean", - "--quiet", - "--non-interactive", - patch.id()] - if not first_run: - # See commit-queue for an explanation of what we're doing here. - args.append("--no-update") - args.append("--parent-command=%s" % self.name) - self.run_webkit_patch(args) - return True - except ScriptError, e: - if first_run: - return False - raise - - def review_patch(self, patch): - if patch.is_obsolete(): - self._did_error(patch, "%s does not process obsolete patches." % self.name) - return False - - if patch.bug().is_closed(): - self._did_error(patch, "%s does not process patches on closed bugs." % self.name) - return False - - if not self._build(patch, first_run=True): - if not self._can_build(): - return False - self._build(patch) - return True - - @classmethod - def handle_script_error(cls, tool, state, script_error): - is_svn_apply = script_error.command_name() == "svn-apply" - status_id = cls._update_status_for_script_error(tool, state, script_error, is_error=is_svn_apply) - if is_svn_apply: - QueueEngine.exit_after_handled_error(script_error) - results_link = tool.status_server.results_url_for_status(status_id) - message = "Attachment %s did not build on %s:\nBuild output: %s" % (state["patch"].id(), cls.port_name, results_link) - tool.bugs.post_comment_to_bug(state["patch"].bug_id(), message, cc=cls.watchers) - exit(1) - - -class GtkEWS(AbstractEarlyWarningSystem): - name = "gtk-ews" - port_name = "gtk" - watchers = AbstractEarlyWarningSystem.watchers + [ - "gns@gnome.org", - "xan.lopez@gmail.com", - ] - - -class EflEWS(AbstractEarlyWarningSystem): - name = "efl-ews" - port_name = "efl" - watchers = AbstractEarlyWarningSystem.watchers + [ - "leandro@profusion.mobi", - "antognolli@profusion.mobi", - "lucas.demarchi@profusion.mobi", - ] - - -class QtEWS(AbstractEarlyWarningSystem): - name = "qt-ews" - port_name = "qt" - - -class WinEWS(AbstractEarlyWarningSystem): - name = "win-ews" - port_name = "win" - # Use debug, the Apple Win port fails to link Release on 32-bit Windows. - # https://bugs.webkit.org/show_bug.cgi?id=39197 - _build_style = "debug" - - -class AbstractChromiumEWS(AbstractEarlyWarningSystem): - port_name = "chromium" - watchers = AbstractEarlyWarningSystem.watchers + [ - "dglazkov@chromium.org", - ] - - -class ChromiumLinuxEWS(AbstractChromiumEWS): - # FIXME: We should rename this command to cr-linux-ews, but that requires - # a database migration. :( - name = "chromium-ews" - - -class ChromiumWindowsEWS(AbstractChromiumEWS): - name = "cr-win-ews" - - -# For platforms that we can't run inside a VM (like Mac OS X), we require -# patches to be uploaded by committers, who are generally trustworthy folk. :) -class AbstractCommitterOnlyEWS(AbstractEarlyWarningSystem): - def __init__(self, committers=CommitterList()): - AbstractEarlyWarningSystem.__init__(self) - self._committers = committers - - def process_work_item(self, patch): - if not self._committers.committer_by_email(patch.attacher_email()): - self._did_error(patch, "%s cannot process patches from non-committers :(" % self.name) - return False - return AbstractEarlyWarningSystem.process_work_item(self, patch) - - -# FIXME: Inheriting from AbstractCommitterOnlyEWS is kinda a hack, but it -# happens to work because AbstractChromiumEWS and AbstractCommitterOnlyEWS -# provide disjoint sets of functionality, and Python is otherwise smart -# enough to handle the diamond inheritance. -class ChromiumMacEWS(AbstractChromiumEWS, AbstractCommitterOnlyEWS): - name = "cr-mac-ews" - - -class MacEWS(AbstractCommitterOnlyEWS): - name = "mac-ews" - port_name = "mac" diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py deleted file mode 100644 index 830e11c..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os - -from webkitpy.thirdparty.mock import Mock -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.tool.bot.queueengine import QueueEngine -from webkitpy.tool.commands.earlywarningsystem import * -from webkitpy.tool.commands.queuestest import QueuesTest -from webkitpy.tool.mocktool import MockTool, MockOptions - - -class AbstractEarlyWarningSystemTest(QueuesTest): - def test_can_build(self): - # Needed to define port_name, used in AbstractEarlyWarningSystem.__init__ - class TestEWS(AbstractEarlyWarningSystem): - port_name = "win" # Needs to be a port which port/factory understands. - - queue = TestEWS() - queue.bind_to_tool(MockTool(log_executive=True)) - queue._options = MockOptions(port=None) - expected_stderr = "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--port=win', '--build-style=release', '--force-clean', '--no-update']\n" - OutputCapture().assert_outputs(self, queue._can_build, [], expected_stderr=expected_stderr) - - def mock_run_webkit_patch(args): - raise ScriptError("MOCK script error") - - queue.run_webkit_patch = mock_run_webkit_patch - expected_stderr = "MOCK: update_status: None Unable to perform a build\n" - OutputCapture().assert_outputs(self, queue._can_build, [], expected_stderr=expected_stderr) - - # FIXME: This belongs on an AbstractReviewQueueTest object in queues_unittest.py - def test_subprocess_handled_error(self): - queue = AbstractReviewQueue() - queue.bind_to_tool(MockTool()) - - def mock_review_patch(patch): - raise ScriptError('MOCK script error', exit_code=QueueEngine.handled_error_code) - - queue.review_patch = mock_review_patch - mock_patch = queue._tool.bugs.fetch_attachment(197) - expected_stderr = "MOCK: release_work_item: None 197\n" - OutputCapture().assert_outputs(self, queue.process_work_item, [mock_patch], expected_stderr=expected_stderr, expected_exception=ScriptError) - - -class EarlyWarningSytemTest(QueuesTest): - def test_failed_builds(self): - ews = ChromiumLinuxEWS() - ews.bind_to_tool(MockTool()) - ews._build = lambda patch, first_run=False: False - ews._can_build = lambda: True - mock_patch = ews._tool.bugs.fetch_attachment(197) - ews.review_patch(mock_patch) - - def _default_expected_stderr(self, ews): - string_replacemnts = { - "name": ews.name, - "port": ews.port_name, - "watchers": ews.watchers, - } - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr(ews.name, ews._tool.scm().checkout_root), - "handle_unexpected_error": "Mock error message\n", - "next_work_item": "", - "process_work_item": "MOCK: update_status: %(name)s Pass\nMOCK: release_work_item: %(name)s 197\n" % string_replacemnts, - "handle_script_error": "MOCK: update_status: %(name)s ScriptError error message\nMOCK bug comment: bug_id=42, cc=%(watchers)s\n--- Begin comment ---\nAttachment 197 did not build on %(port)s:\nBuild output: http://dummy_url\n--- End comment ---\n\n" % string_replacemnts, - } - return expected_stderr - - def _test_ews(self, ews): - ews.bind_to_tool(MockTool()) - expected_exceptions = { - "handle_script_error": SystemExit, - } - self.assert_queue_outputs(ews, expected_stderr=self._default_expected_stderr(ews), expected_exceptions=expected_exceptions) - - def _test_committer_only_ews(self, ews): - ews.bind_to_tool(MockTool()) - expected_stderr = self._default_expected_stderr(ews) - string_replacemnts = {"name": ews.name} - expected_stderr["process_work_item"] = "MOCK: update_status: %(name)s Error: %(name)s cannot process patches from non-committers :(\nMOCK: release_work_item: %(name)s 197\n" % string_replacemnts - expected_exceptions = {"handle_script_error": SystemExit} - self.assert_queue_outputs(ews, expected_stderr=expected_stderr, expected_exceptions=expected_exceptions) - - # FIXME: If all EWSes are going to output the same text, we - # could test them all in one method with a for loop over an array. - def test_chromium_linux_ews(self): - self._test_ews(ChromiumLinuxEWS()) - - def test_chromium_windows_ews(self): - self._test_ews(ChromiumWindowsEWS()) - - def test_qt_ews(self): - self._test_ews(QtEWS()) - - def test_gtk_ews(self): - self._test_ews(GtkEWS()) - - def test_efl_ews(self): - self._test_ews(EflEWS()) - - def test_mac_ews(self): - self._test_committer_only_ews(MacEWS()) - - def test_chromium_mac_ews(self): - self._test_committer_only_ews(ChromiumMacEWS()) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py deleted file mode 100644 index 1b51c9f..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import re -import sys - -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand -from webkitpy.common.system.deprecated_logging import log - - -class OpenBugs(AbstractDeclarativeCommand): - name = "open-bugs" - help_text = "Finds all bug numbers passed in arguments (or stdin if no args provided) and opens them in a web browser" - - bug_number_regexp = re.compile(r"\b\d{4,6}\b") - - def _open_bugs(self, bug_ids): - for bug_id in bug_ids: - bug_url = self._tool.bugs.bug_url_for_bug_id(bug_id) - self._tool.user.open_url(bug_url) - - # _find_bugs_in_string mostly exists for easy unit testing. - def _find_bugs_in_string(self, string): - return self.bug_number_regexp.findall(string) - - def _find_bugs_in_iterable(self, iterable): - return sum([self._find_bugs_in_string(string) for string in iterable], []) - - def execute(self, options, args, tool): - if args: - bug_ids = self._find_bugs_in_iterable(args) - else: - # This won't open bugs until stdin is closed but could be made to easily. That would just make unit testing slightly harder. - bug_ids = self._find_bugs_in_iterable(sys.stdin) - - log("%s bugs found in input." % len(bug_ids)) - - self._open_bugs(bug_ids) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py deleted file mode 100644 index 40a6e1b..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from webkitpy.tool.commands.commandtest import CommandsTest -from webkitpy.tool.commands.openbugs import OpenBugs - -class OpenBugsTest(CommandsTest): - - find_bugs_in_string_expectations = [ - ["123", []], - ["1234", ["1234"]], - ["12345", ["12345"]], - ["123456", ["123456"]], - ["1234567", []], - [" 123456 234567", ["123456", "234567"]], - ] - - def test_find_bugs_in_string(self): - openbugs = OpenBugs() - for expectation in self.find_bugs_in_string_expectations: - self.assertEquals(openbugs._find_bugs_in_string(expectation[0]), expectation[1]) - - def test_args_parsing(self): - expected_stderr = "2 bugs found in input.\nMOCK: user.open_url: http://example.com/12345\nMOCK: user.open_url: http://example.com/23456\n" - self.assert_execute_outputs(OpenBugs(), ["12345\n23456"], expected_stderr=expected_stderr) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py b/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py deleted file mode 100644 index e3fc00c..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand -import webkitpy.tool.steps as steps - - -class PrettyDiff(AbstractSequencedCommand): - name = "pretty-diff" - help_text = "Shows the pretty diff in the default browser" - steps = [ - steps.ConfirmDiff, - ] diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries.py deleted file mode 100644 index 16ddc2c..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py +++ /dev/null @@ -1,393 +0,0 @@ -# Copyright (c) 2009 Google Inc. All rights reserved. -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -from optparse import make_option - -import webkitpy.tool.steps as steps - -from webkitpy.common.checkout.commitinfo import CommitInfo -from webkitpy.common.config.committers import CommitterList -from webkitpy.common.net.buildbot import BuildBot -from webkitpy.common.net.regressionwindow import RegressionWindow -from webkitpy.common.system.user import User -from webkitpy.tool.grammar import pluralize -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand -from webkitpy.common.system.deprecated_logging import log -from webkitpy.layout_tests import port - - -class SuggestReviewers(AbstractDeclarativeCommand): - name = "suggest-reviewers" - help_text = "Suggest reviewers for a patch based on recent changes to the modified files." - - def __init__(self): - options = [ - steps.Options.git_commit, - ] - AbstractDeclarativeCommand.__init__(self, options=options) - - def execute(self, options, args, tool): - reviewers = tool.checkout().suggested_reviewers(options.git_commit) - print "\n".join([reviewer.full_name for reviewer in reviewers]) - - -class BugsToCommit(AbstractDeclarativeCommand): - name = "bugs-to-commit" - help_text = "List bugs in the commit-queue" - - def execute(self, options, args, tool): - # FIXME: This command is poorly named. It's fetching the commit-queue list here. The name implies it's fetching pending-commit (all r+'d patches). - bug_ids = tool.bugs.queries.fetch_bug_ids_from_commit_queue() - for bug_id in bug_ids: - print "%s" % bug_id - - -class PatchesInCommitQueue(AbstractDeclarativeCommand): - name = "patches-in-commit-queue" - help_text = "List patches in the commit-queue" - - def execute(self, options, args, tool): - patches = tool.bugs.queries.fetch_patches_from_commit_queue() - log("Patches in commit queue:") - for patch in patches: - print patch.url() - - -class PatchesToCommitQueue(AbstractDeclarativeCommand): - name = "patches-to-commit-queue" - help_text = "Patches which should be added to the commit queue" - def __init__(self): - options = [ - make_option("--bugs", action="store_true", dest="bugs", help="Output bug links instead of patch links"), - ] - AbstractDeclarativeCommand.__init__(self, options=options) - - @staticmethod - def _needs_commit_queue(patch): - if patch.commit_queue() == "+": # If it's already cq+, ignore the patch. - log("%s already has cq=%s" % (patch.id(), patch.commit_queue())) - return False - - # We only need to worry about patches from contributers who are not yet committers. - committer_record = CommitterList().committer_by_email(patch.attacher_email()) - if committer_record: - log("%s committer = %s" % (patch.id(), committer_record)) - return not committer_record - - def execute(self, options, args, tool): - patches = tool.bugs.queries.fetch_patches_from_pending_commit_list() - patches_needing_cq = filter(self._needs_commit_queue, patches) - if options.bugs: - bugs_needing_cq = map(lambda patch: patch.bug_id(), patches_needing_cq) - bugs_needing_cq = sorted(set(bugs_needing_cq)) - for bug_id in bugs_needing_cq: - print "%s" % tool.bugs.bug_url_for_bug_id(bug_id) - else: - for patch in patches_needing_cq: - print "%s" % tool.bugs.attachment_url_for_id(patch.id(), action="edit") - - -class PatchesToReview(AbstractDeclarativeCommand): - name = "patches-to-review" - help_text = "List patches that are pending review" - - def execute(self, options, args, tool): - patch_ids = tool.bugs.queries.fetch_attachment_ids_from_review_queue() - log("Patches pending review:") - for patch_id in patch_ids: - print patch_id - - -class LastGreenRevision(AbstractDeclarativeCommand): - name = "last-green-revision" - help_text = "Prints the last known good revision" - - def execute(self, options, args, tool): - print self._tool.buildbot.last_green_revision() - - -class WhatBroke(AbstractDeclarativeCommand): - name = "what-broke" - help_text = "Print failing buildbots (%s) and what revisions broke them" % BuildBot.default_host - - def _print_builder_line(self, builder_name, max_name_width, status_message): - print "%s : %s" % (builder_name.ljust(max_name_width), status_message) - - def _print_blame_information_for_builder(self, builder_status, name_width, avoid_flakey_tests=True): - builder = self._tool.buildbot.builder_with_name(builder_status["name"]) - red_build = builder.build(builder_status["build_number"]) - regression_window = builder.find_regression_window(red_build) - if not regression_window.failing_build(): - self._print_builder_line(builder.name(), name_width, "FAIL (error loading build information)") - return - if not regression_window.build_before_failure(): - self._print_builder_line(builder.name(), name_width, "FAIL (blame-list: sometime before %s?)" % regression_window.failing_build().revision()) - return - - revisions = regression_window.revisions() - first_failure_message = "" - if (regression_window.failing_build() == builder.build(builder_status["build_number"])): - first_failure_message = " FIRST FAILURE, possibly a flaky test" - self._print_builder_line(builder.name(), name_width, "FAIL (blame-list: %s%s)" % (revisions, first_failure_message)) - for revision in revisions: - commit_info = self._tool.checkout().commit_info_for_revision(revision) - if commit_info: - print commit_info.blame_string(self._tool.bugs) - else: - print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision - - def execute(self, options, args, tool): - builder_statuses = tool.buildbot.builder_statuses() - longest_builder_name = max(map(len, map(lambda builder: builder["name"], builder_statuses))) - failing_builders = 0 - for builder_status in builder_statuses: - # If the builder is green, print OK, exit. - if builder_status["is_green"]: - continue - self._print_blame_information_for_builder(builder_status, name_width=longest_builder_name) - failing_builders += 1 - if failing_builders: - print "%s of %s are failing" % (failing_builders, pluralize("builder", len(builder_statuses))) - else: - print "All builders are passing!" - - -class ResultsFor(AbstractDeclarativeCommand): - name = "results-for" - help_text = "Print a list of failures for the passed revision from bots on %s" % BuildBot.default_host - argument_names = "REVISION" - - def _print_layout_test_results(self, results): - if not results: - print " No results." - return - for title, files in results.parsed_results().items(): - print " %s" % title - for filename in files: - print " %s" % filename - - def execute(self, options, args, tool): - builders = self._tool.buildbot.builders() - for builder in builders: - print "%s:" % builder.name() - build = builder.build_for_revision(args[0], allow_failed_lookups=True) - self._print_layout_test_results(build.layout_test_results()) - - -class FailureReason(AbstractDeclarativeCommand): - name = "failure-reason" - help_text = "Lists revisions where individual test failures started at %s" % BuildBot.default_host - - def _blame_line_for_revision(self, revision): - try: - commit_info = self._tool.checkout().commit_info_for_revision(revision) - except Exception, e: - return "FAILED to fetch CommitInfo for r%s, exception: %s" % (revision, e) - if not commit_info: - return "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision - return commit_info.blame_string(self._tool.bugs) - - def _print_blame_information_for_transition(self, regression_window, failing_tests): - red_build = regression_window.failing_build() - print "SUCCESS: Build %s (r%s) was the first to show failures: %s" % (red_build._number, red_build.revision(), failing_tests) - print "Suspect revisions:" - for revision in regression_window.revisions(): - print self._blame_line_for_revision(revision) - - def _explain_failures_for_builder(self, builder, start_revision): - print "Examining failures for \"%s\", starting at r%s" % (builder.name(), start_revision) - revision_to_test = start_revision - build = builder.build_for_revision(revision_to_test, allow_failed_lookups=True) - layout_test_results = build.layout_test_results() - if not layout_test_results: - # FIXME: This could be made more user friendly. - print "Failed to load layout test results; can't continue. (start revision = r%s)" % start_revision - return 1 - - results_to_explain = set(layout_test_results.failing_tests()) - last_build_with_results = build - print "Starting at %s" % revision_to_test - while results_to_explain: - revision_to_test -= 1 - new_build = builder.build_for_revision(revision_to_test, allow_failed_lookups=True) - if not new_build: - print "No build for %s" % revision_to_test - continue - build = new_build - latest_results = build.layout_test_results() - if not latest_results: - print "No results build %s (r%s)" % (build._number, build.revision()) - continue - failures = set(latest_results.failing_tests()) - if len(failures) >= 20: - # FIXME: We may need to move this logic into the LayoutTestResults class. - # The buildbot stops runs after 20 failures so we don't have full results to work with here. - print "Too many failures in build %s (r%s), ignoring." % (build._number, build.revision()) - continue - fixed_results = results_to_explain - failures - if not fixed_results: - print "No change in build %s (r%s), %s unexplained failures (%s in this build)" % (build._number, build.revision(), len(results_to_explain), len(failures)) - last_build_with_results = build - continue - regression_window = RegressionWindow(build, last_build_with_results) - self._print_blame_information_for_transition(regression_window, fixed_results) - last_build_with_results = build - results_to_explain -= fixed_results - if results_to_explain: - print "Failed to explain failures: %s" % results_to_explain - return 1 - print "Explained all results for %s" % builder.name() - return 0 - - def _builder_to_explain(self): - builder_statuses = self._tool.buildbot.builder_statuses() - red_statuses = [status for status in builder_statuses if not status["is_green"]] - print "%s failing" % (pluralize("builder", len(red_statuses))) - builder_choices = [status["name"] for status in red_statuses] - # We could offer an "All" choice here. - chosen_name = User.prompt_with_list("Which builder to diagnose:", builder_choices) - # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object. - for status in red_statuses: - if status["name"] == chosen_name: - return (self._tool.buildbot.builder_with_name(chosen_name), status["built_revision"]) - - def execute(self, options, args, tool): - (builder, latest_revision) = self._builder_to_explain() - start_revision = self._tool.user.prompt("Revision to walk backwards from? [%s] " % latest_revision) or latest_revision - if not start_revision: - print "Revision required." - return 1 - return self._explain_failures_for_builder(builder, start_revision=int(start_revision)) - - -class FindFlakyTests(AbstractDeclarativeCommand): - name = "find-flaky-tests" - help_text = "Lists tests that often fail for a single build at %s" % BuildBot.default_host - - def _find_failures(self, builder, revision): - build = builder.build_for_revision(revision, allow_failed_lookups=True) - if not build: - print "No build for %s" % revision - return (None, None) - results = build.layout_test_results() - if not results: - print "No results build %s (r%s)" % (build._number, build.revision()) - return (None, None) - failures = set(results.failing_tests()) - if len(failures) >= 20: - # FIXME: We may need to move this logic into the LayoutTestResults class. - # The buildbot stops runs after 20 failures so we don't have full results to work with here. - print "Too many failures in build %s (r%s), ignoring." % (build._number, build.revision()) - return (None, None) - return (build, failures) - - def _increment_statistics(self, flaky_tests, flaky_test_statistics): - for test in flaky_tests: - count = flaky_test_statistics.get(test, 0) - flaky_test_statistics[test] = count + 1 - - def _print_statistics(self, statistics): - print "=== Results ===" - print "Occurances Test name" - for value, key in sorted([(value, key) for key, value in statistics.items()]): - print "%10d %s" % (value, key) - - def _walk_backwards_from(self, builder, start_revision, limit): - flaky_test_statistics = {} - all_previous_failures = set([]) - one_time_previous_failures = set([]) - previous_build = None - for i in range(limit): - revision = start_revision - i - print "Analyzing %s ... " % revision, - (build, failures) = self._find_failures(builder, revision) - if failures == None: - # Notice that we don't loop on the empty set! - continue - print "has %s failures" % len(failures) - flaky_tests = one_time_previous_failures - failures - if flaky_tests: - print "Flaky tests: %s %s" % (sorted(flaky_tests), - previous_build.results_url()) - self._increment_statistics(flaky_tests, flaky_test_statistics) - one_time_previous_failures = failures - all_previous_failures - all_previous_failures = failures - previous_build = build - self._print_statistics(flaky_test_statistics) - - def _builder_to_analyze(self): - statuses = self._tool.buildbot.builder_statuses() - choices = [status["name"] for status in statuses] - chosen_name = User.prompt_with_list("Which builder to analyze:", choices) - for status in statuses: - if status["name"] == chosen_name: - return (self._tool.buildbot.builder_with_name(chosen_name), status["built_revision"]) - - def execute(self, options, args, tool): - (builder, latest_revision) = self._builder_to_analyze() - limit = self._tool.user.prompt("How many revisions to look through? [10000] ") or 10000 - return self._walk_backwards_from(builder, latest_revision, limit=int(limit)) - - -class TreeStatus(AbstractDeclarativeCommand): - name = "tree-status" - help_text = "Print the status of the %s buildbots" % BuildBot.default_host - long_help = """Fetches build status from http://build.webkit.org/one_box_per_builder -and displayes the status of each builder.""" - - def execute(self, options, args, tool): - for builder in tool.buildbot.builder_statuses(): - status_string = "ok" if builder["is_green"] else "FAIL" - print "%s : %s" % (status_string.ljust(4), builder["name"]) - - -class SkippedPorts(AbstractDeclarativeCommand): - name = "skipped-ports" - help_text = "Print the list of ports skipping the given layout test(s)" - long_help = """Scans the the Skipped file of each port and figure -out what ports are skipping the test(s). Categories are taken in account too.""" - argument_names = "TEST_NAME" - - def execute(self, options, args, tool): - class Options: - # Required for chromium port. - use_drt = True - - results = dict([(test_name, []) for test_name in args]) - for port_name, port_object in tool.port_factory.get_all(options=Options).iteritems(): - for test_name in args: - if port_object.skips_layout_test(test_name): - results[test_name].append(port_name) - - for test_name, ports in results.iteritems(): - if ports: - print "Ports skipping test %r: %s" % (test_name, ', '.join(ports)) - else: - print "Test %r is not skipped by any port." % test_name diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py deleted file mode 100644 index 05a4a5c..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import unittest - -from webkitpy.common.net.bugzilla import Bugzilla -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.commands.commandtest import CommandsTest -from webkitpy.tool.commands.queries import * -from webkitpy.tool.mocktool import MockTool - - -class QueryCommandsTest(CommandsTest): - def test_bugs_to_commit(self): - expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\n" - self.assert_execute_outputs(BugsToCommit(), None, "42\n77\n", expected_stderr) - - def test_patches_in_commit_queue(self): - expected_stdout = "http://example.com/197\nhttp://example.com/103\n" - expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\nPatches in commit queue:\n" - self.assert_execute_outputs(PatchesInCommitQueue(), None, expected_stdout, expected_stderr) - - def test_patches_to_commit_queue(self): - expected_stdout = "http://example.com/104&action=edit\n" - expected_stderr = "197 already has cq=+\n128 already has cq=+\n105 committer = \"Eric Seidel\" <eric@webkit.org>\n" - options = Mock() - options.bugs = False - self.assert_execute_outputs(PatchesToCommitQueue(), None, expected_stdout, expected_stderr, options=options) - - expected_stdout = "http://example.com/77\n" - options.bugs = True - self.assert_execute_outputs(PatchesToCommitQueue(), None, expected_stdout, expected_stderr, options=options) - - def test_patches_to_review(self): - expected_stdout = "103\n" - expected_stderr = "Patches pending review:\n" - self.assert_execute_outputs(PatchesToReview(), None, expected_stdout, expected_stderr) - - def test_tree_status(self): - expected_stdout = "ok : Builder1\nok : Builder2\n" - self.assert_execute_outputs(TreeStatus(), None, expected_stdout) - - def test_skipped_ports(self): - expected_stdout = "Ports skipping test 'media/foo/bar.html': test_port1, test_port2\n" - self.assert_execute_outputs(SkippedPorts(), ("media/foo/bar.html",), expected_stdout) - - expected_stdout = "Ports skipping test 'foo': test_port1\n" - self.assert_execute_outputs(SkippedPorts(), ("foo",), expected_stdout) - - expected_stdout = "Test 'media' is not skipped by any port.\n" - self.assert_execute_outputs(SkippedPorts(), ("media",), expected_stdout) - - -class FailureReasonTest(unittest.TestCase): - def test_blame_line_for_revision(self): - tool = MockTool() - command = FailureReason() - command.bind_to_tool(tool) - # This is an artificial example, mostly to test the CommitInfo lookup failure case. - self.assertEquals(command._blame_line_for_revision(None), "FAILED to fetch CommitInfo for rNone, likely missing ChangeLog") - - def raising_mock(self): - raise Exception("MESSAGE") - tool.checkout().commit_info_for_revision = raising_mock - self.assertEquals(command._blame_line_for_revision(None), "FAILED to fetch CommitInfo for rNone, exception: MESSAGE") diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py deleted file mode 100644 index bfaeb08..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py +++ /dev/null @@ -1,417 +0,0 @@ -# Copyright (c) 2009 Google Inc. All rights reserved. -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from __future__ import with_statement - -import codecs -import time -import traceback -import os - -from datetime import datetime -from optparse import make_option -from StringIO import StringIO - -from webkitpy.common.config.committervalidator import CommitterValidator -from webkitpy.common.net.bugzilla import Attachment -from webkitpy.common.net.layouttestresults import path_for_layout_test, LayoutTestResults -from webkitpy.common.net.statusserver import StatusServer -from webkitpy.common.system.deprecated_logging import error, log -from webkitpy.common.system.executive import ScriptError -from webkitpy.tool.bot.commitqueuetask import CommitQueueTask, CommitQueueTaskDelegate -from webkitpy.tool.bot.feeders import CommitQueueFeeder, EWSFeeder -from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate -from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler -from webkitpy.tool.grammar import pluralize, join_with_separators -from webkitpy.tool.multicommandtool import Command, TryAgain - - -class AbstractQueue(Command, QueueEngineDelegate): - watchers = [ - ] - - _pass_status = "Pass" - _fail_status = "Fail" - _retry_status = "Retry" - _error_status = "Error" - - def __init__(self, options=None): # Default values should never be collections (like []) as default values are shared between invocations - options_list = (options or []) + [ - make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Do not ask the user for confirmation before running the queue. Dangerous!"), - make_option("--exit-after-iteration", action="store", type="int", dest="iterations", default=None, help="Stop running the queue after iterating this number of times."), - ] - Command.__init__(self, "Run the %s" % self.name, options=options_list) - self._iteration_count = 0 - - def _cc_watchers(self, bug_id): - try: - self._tool.bugs.add_cc_to_bug(bug_id, self.watchers) - except Exception, e: - traceback.print_exc() - log("Failed to CC watchers.") - - def run_webkit_patch(self, args): - webkit_patch_args = [self._tool.path()] - # FIXME: This is a hack, we should have a more general way to pass global options. - # FIXME: We must always pass global options and their value in one argument - # because our global option code looks for the first argument which does - # not begin with "-" and assumes that is the command name. - webkit_patch_args += ["--status-host=%s" % self._tool.status_server.host] - if self._tool.status_server.bot_id: - webkit_patch_args += ["--bot-id=%s" % self._tool.status_server.bot_id] - if self._options.port: - webkit_patch_args += ["--port=%s" % self._options.port] - webkit_patch_args.extend(args) - # FIXME: There is probably no reason to use run_and_throw_if_fail anymore. - # run_and_throw_if_fail was invented to support tee'd output - # (where we write both to a log file and to the console at once), - # but the queues don't need live-progress, a dump-of-output at the - # end should be sufficient. - return self._tool.executive.run_and_throw_if_fail(webkit_patch_args) - - def _log_directory(self): - return "%s-logs" % self.name - - # QueueEngineDelegate methods - - def queue_log_path(self): - return os.path.join(self._log_directory(), "%s.log" % self.name) - - def work_item_log_path(self, work_item): - raise NotImplementedError, "subclasses must implement" - - def begin_work_queue(self): - log("CAUTION: %s will discard all local changes in \"%s\"" % (self.name, self._tool.scm().checkout_root)) - if self._options.confirm: - response = self._tool.user.prompt("Are you sure? Type \"yes\" to continue: ") - if (response != "yes"): - error("User declined.") - log("Running WebKit %s." % self.name) - self._tool.status_server.update_status(self.name, "Starting Queue") - - def stop_work_queue(self, reason): - self._tool.status_server.update_status(self.name, "Stopping Queue, reason: %s" % reason) - - def should_continue_work_queue(self): - self._iteration_count += 1 - return not self._options.iterations or self._iteration_count <= self._options.iterations - - def next_work_item(self): - raise NotImplementedError, "subclasses must implement" - - def should_proceed_with_work_item(self, work_item): - raise NotImplementedError, "subclasses must implement" - - def process_work_item(self, work_item): - raise NotImplementedError, "subclasses must implement" - - def handle_unexpected_error(self, work_item, message): - raise NotImplementedError, "subclasses must implement" - - # Command methods - - def execute(self, options, args, tool, engine=QueueEngine): - self._options = options # FIXME: This code is wrong. Command.options is a list, this assumes an Options element! - self._tool = tool # FIXME: This code is wrong too! Command.bind_to_tool handles this! - return engine(self.name, self, self._tool.wakeup_event).run() - - @classmethod - def _log_from_script_error_for_upload(cls, script_error, output_limit=None): - # We have seen request timeouts with app engine due to large - # log uploads. Trying only the last 512k. - if not output_limit: - output_limit = 512 * 1024 # 512k - output = script_error.message_with_output(output_limit=output_limit) - # We pre-encode the string to a byte array before passing it - # to status_server, because ClientForm (part of mechanize) - # wants a file-like object with pre-encoded data. - return StringIO(output.encode("utf-8")) - - @classmethod - def _update_status_for_script_error(cls, tool, state, script_error, is_error=False): - message = str(script_error) - if is_error: - message = "Error: %s" % message - failure_log = cls._log_from_script_error_for_upload(script_error) - return tool.status_server.update_status(cls.name, message, state["patch"], failure_log) - - -class FeederQueue(AbstractQueue): - name = "feeder-queue" - - _sleep_duration = 30 # seconds - - # AbstractPatchQueue methods - - def begin_work_queue(self): - AbstractQueue.begin_work_queue(self) - self.feeders = [ - CommitQueueFeeder(self._tool), - EWSFeeder(self._tool), - ] - - def next_work_item(self): - # This really show inherit from some more basic class that doesn't - # understand work items, but the base class in the heirarchy currently - # understands work items. - return "synthetic-work-item" - - def should_proceed_with_work_item(self, work_item): - return True - - def process_work_item(self, work_item): - for feeder in self.feeders: - feeder.feed() - time.sleep(self._sleep_duration) - return True - - def work_item_log_path(self, work_item): - return None - - def handle_unexpected_error(self, work_item, message): - log(message) - - -class AbstractPatchQueue(AbstractQueue): - def _update_status(self, message, patch=None, results_file=None): - return self._tool.status_server.update_status(self.name, message, patch, results_file) - - def _next_patch(self): - patch_id = self._tool.status_server.next_work_item(self.name) - if not patch_id: - return None - patch = self._tool.bugs.fetch_attachment(patch_id) - if not patch: - # FIXME: Using a fake patch because release_work_item has the wrong API. - # We also don't really need to release the lock (although that's fine), - # mostly we just need to remove this bogus patch from our queue. - # If for some reason bugzilla is just down, then it will be re-fed later. - patch = Attachment({'id': patch_id}, None) - self._release_work_item(patch) - return None - return patch - - def _release_work_item(self, patch): - self._tool.status_server.release_work_item(self.name, patch) - - def _did_pass(self, patch): - self._update_status(self._pass_status, patch) - self._release_work_item(patch) - - def _did_fail(self, patch): - self._update_status(self._fail_status, patch) - self._release_work_item(patch) - - def _did_retry(self, patch): - self._update_status(self._retry_status, patch) - self._release_work_item(patch) - - def _did_error(self, patch, reason): - message = "%s: %s" % (self._error_status, reason) - self._update_status(message, patch) - self._release_work_item(patch) - - def work_item_log_path(self, patch): - return os.path.join(self._log_directory(), "%s.log" % patch.bug_id()) - - -class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskDelegate): - name = "commit-queue" - - # AbstractPatchQueue methods - - def begin_work_queue(self): - AbstractPatchQueue.begin_work_queue(self) - self.committer_validator = CommitterValidator(self._tool.bugs) - - def next_work_item(self): - return self._next_patch() - - def should_proceed_with_work_item(self, patch): - patch_text = "rollout patch" if patch.is_rollout() else "patch" - self._update_status("Processing %s" % patch_text, patch) - return True - - def process_work_item(self, patch): - self._cc_watchers(patch.bug_id()) - task = CommitQueueTask(self, patch) - try: - if task.run(): - self._did_pass(patch) - return True - self._did_retry(patch) - except ScriptError, e: - validator = CommitterValidator(self._tool.bugs) - validator.reject_patch_from_commit_queue(patch.id(), self._error_message_for_bug(task.failure_status_id, e)) - self._did_fail(patch) - - def _error_message_for_bug(self, status_id, script_error): - if not script_error.output: - return script_error.message_with_output() - results_link = self._tool.status_server.results_url_for_status(status_id) - return "%s\nFull output: %s" % (script_error.message_with_output(), results_link) - - def handle_unexpected_error(self, patch, message): - self.committer_validator.reject_patch_from_commit_queue(patch.id(), message) - - # CommitQueueTaskDelegate methods - - def run_command(self, command): - self.run_webkit_patch(command) - - def command_passed(self, message, patch): - self._update_status(message, patch=patch) - - def command_failed(self, message, script_error, patch): - failure_log = self._log_from_script_error_for_upload(script_error) - return self._update_status(message, patch=patch, results_file=failure_log) - - # FIXME: This exists for mocking, but should instead be mocked via - # some sort of tool.filesystem() object. - def _read_file_contents(self, path): - try: - with codecs.open(path, "r", "utf-8") as open_file: - return open_file.read() - except OSError, e: # File does not exist or can't be read. - return None - - # FIXME: This may belong on the Port object. - def layout_test_results(self): - results_path = self._tool.port().layout_tests_results_path() - results_html = self._read_file_contents(results_path) - if not results_html: - return None - return LayoutTestResults.results_from_string(results_html) - - def refetch_patch(self, patch): - return self._tool.bugs.fetch_attachment(patch.id()) - - def _author_emails_for_tests(self, flaky_tests): - test_paths = map(path_for_layout_test, flaky_tests) - commit_infos = self._tool.checkout().recent_commit_infos_for_files(test_paths) - return set([commit_info.author().bugzilla_email() for commit_info in commit_infos if commit_info.author()]) - - def report_flaky_tests(self, patch, flaky_tests): - message = "The %s encountered the following flaky tests while processing attachment %s:" % (self.name, patch.id()) - message += "\n\n%s\n\n" % ("\n".join(flaky_tests)) - message += "Please file bugs against the tests. " - author_emails = self._author_emails_for_tests(flaky_tests) - if author_emails: - message += "These tests were authored by %s. " % (join_with_separators(sorted(author_emails))) - message += "The commit-queue is continuing to process your patch." - self._tool.bugs.post_comment_to_bug(patch.bug_id(), message) - - # StepSequenceErrorHandler methods - - def handle_script_error(cls, tool, state, script_error): - # Hitting this error handler should be pretty rare. It does occur, - # however, when a patch no longer applies to top-of-tree in the final - # land step. - log(script_error.message_with_output()) - - @classmethod - def handle_checkout_needs_update(cls, tool, state, options, error): - message = "Tests passed, but commit failed (checkout out of date). Updating, then landing without building or re-running tests." - tool.status_server.update_status(cls.name, message, state["patch"]) - # The only time when we find out that out checkout needs update is - # when we were ready to actually pull the trigger and land the patch. - # Rather than spinning in the master process, we retry without - # building or testing, which is much faster. - options.build = False - options.test = False - options.update = True - raise TryAgain() - - -class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler): - """This is the base-class for the EWS queues and the style-queue.""" - def __init__(self, options=None): - AbstractPatchQueue.__init__(self, options) - - def review_patch(self, patch): - raise NotImplementedError("subclasses must implement") - - # AbstractPatchQueue methods - - def begin_work_queue(self): - AbstractPatchQueue.begin_work_queue(self) - - def next_work_item(self): - return self._next_patch() - - def should_proceed_with_work_item(self, patch): - raise NotImplementedError("subclasses must implement") - - def process_work_item(self, patch): - try: - if not self.review_patch(patch): - return False - self._did_pass(patch) - return True - except ScriptError, e: - if e.exit_code != QueueEngine.handled_error_code: - self._did_fail(patch) - else: - # The subprocess handled the error, but won't have released the patch, so we do. - # FIXME: We need to simplify the rules by which _release_work_item is called. - self._release_work_item(patch) - raise e - - def handle_unexpected_error(self, patch, message): - log(message) - - # StepSequenceErrorHandler methods - - @classmethod - def handle_script_error(cls, tool, state, script_error): - log(script_error.message_with_output()) - - -class StyleQueue(AbstractReviewQueue): - name = "style-queue" - def __init__(self): - AbstractReviewQueue.__init__(self) - - def should_proceed_with_work_item(self, patch): - self._update_status("Checking style", patch) - return True - - def review_patch(self, patch): - self.run_webkit_patch(["check-style", "--force-clean", "--non-interactive", "--parent-command=style-queue", patch.id()]) - return True - - @classmethod - def handle_script_error(cls, tool, state, script_error): - is_svn_apply = script_error.command_name() == "svn-apply" - status_id = cls._update_status_for_script_error(tool, state, script_error, is_error=is_svn_apply) - if is_svn_apply: - QueueEngine.exit_after_handled_error(script_error) - message = "Attachment %s did not pass %s:\n\n%s\n\nIf any of these errors are false positives, please file a bug against check-webkit-style." % (state["patch"].id(), cls.name, script_error.message_with_output(output_limit=3*1024)) - tool.bugs.post_comment_to_bug(state["patch"].bug_id(), message, cc=cls.watchers) - exit(1) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py deleted file mode 100644 index b45db7b..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py +++ /dev/null @@ -1,378 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os - -from webkitpy.common.checkout.scm import CheckoutNeedsUpdate -from webkitpy.common.config.committers import Committer -from webkitpy.common.net.bugzilla import Attachment -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.commands.commandtest import CommandsTest -from webkitpy.tool.commands.queues import * -from webkitpy.tool.commands.queuestest import QueuesTest -from webkitpy.tool.commands.stepsequence import StepSequence -from webkitpy.tool.mocktool import MockTool, MockSCM, MockStatusServer - - -class TestQueue(AbstractPatchQueue): - name = "test-queue" - - -class TestReviewQueue(AbstractReviewQueue): - name = "test-review-queue" - - -class TestFeederQueue(FeederQueue): - _sleep_duration = 0 - - -class AbstractQueueTest(CommandsTest): - def test_log_directory(self): - self.assertEquals(TestQueue()._log_directory(), "test-queue-logs") - - def _assert_run_webkit_patch(self, run_args, port=None): - queue = TestQueue() - tool = MockTool() - tool.status_server.bot_id = "gort" - tool.executive = Mock() - queue.bind_to_tool(tool) - queue._options = Mock() - queue._options.port = port - - queue.run_webkit_patch(run_args) - expected_run_args = ["echo", "--status-host=example.com", "--bot-id=gort"] - if port: - expected_run_args.append("--port=%s" % port) - expected_run_args.extend(run_args) - tool.executive.run_and_throw_if_fail.assert_called_with(expected_run_args) - - def test_run_webkit_patch(self): - self._assert_run_webkit_patch([1]) - self._assert_run_webkit_patch(["one", 2]) - self._assert_run_webkit_patch([1], port="mockport") - - def test_iteration_count(self): - queue = TestQueue() - queue._options = Mock() - queue._options.iterations = 3 - self.assertTrue(queue.should_continue_work_queue()) - self.assertTrue(queue.should_continue_work_queue()) - self.assertTrue(queue.should_continue_work_queue()) - self.assertFalse(queue.should_continue_work_queue()) - - def test_no_iteration_count(self): - queue = TestQueue() - queue._options = Mock() - self.assertTrue(queue.should_continue_work_queue()) - self.assertTrue(queue.should_continue_work_queue()) - self.assertTrue(queue.should_continue_work_queue()) - self.assertTrue(queue.should_continue_work_queue()) - - def _assert_log_message(self, script_error, log_message): - failure_log = AbstractQueue._log_from_script_error_for_upload(script_error, output_limit=10) - self.assertTrue(failure_log.read(), log_message) - - def test_log_from_script_error_for_upload(self): - self._assert_log_message(ScriptError("test"), "test") - # In python 2.5 unicode(Exception) is busted. See: - # http://bugs.python.org/issue2517 - # With no good workaround, we just ignore these tests. - if not hasattr(Exception, "__unicode__"): - return - - unicode_tor = u"WebKit \u2661 Tor Arne Vestb\u00F8!" - utf8_tor = unicode_tor.encode("utf-8") - self._assert_log_message(ScriptError(unicode_tor), utf8_tor) - script_error = ScriptError(unicode_tor, output=unicode_tor) - expected_output = "%s\nLast %s characters of output:\n%s" % (utf8_tor, 10, utf8_tor[-10:]) - self._assert_log_message(script_error, expected_output) - - -class FeederQueueTest(QueuesTest): - def test_feeder_queue(self): - queue = TestFeederQueue() - tool = MockTool(log_executive=True) - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("feeder-queue", MockSCM.fake_checkout_root), - "should_proceed_with_work_item": "", - "next_work_item": "", - "process_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) -Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) -MOCK setting flag 'commit-queue' to '-' on attachment '128' with comment 'Rejecting patch 128 from commit-queue.' and additional comment 'non-committer@example.com does not have committer permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py. - -- If you do not have committer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags. - -- If you have committer rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). The commit-queue restarts itself every 2 hours. After restart the commit-queue will correctly respect your committer rights.' -MOCK: update_work_items: commit-queue [106, 197] -Feeding commit-queue items [106, 197] -Feeding EWS (1 r? patch, 1 new) -MOCK: submit_to_ews: 103 -""", - "handle_unexpected_error": "Mock error message\n", - } - self.assert_queue_outputs(queue, tool=tool, expected_stderr=expected_stderr) - - -class AbstractPatchQueueTest(CommandsTest): - def test_next_patch(self): - queue = AbstractPatchQueue() - tool = MockTool() - queue.bind_to_tool(tool) - queue._options = Mock() - queue._options.port = None - self.assertEquals(queue._next_patch(), None) - tool.status_server = MockStatusServer(work_items=[2, 197]) - expected_stdout = "MOCK: fetch_attachment: 2 is not a known attachment id\n" # A mock-only message to prevent us from making mistakes. - expected_stderr = "MOCK: release_work_item: None 2\n" - patch_id = OutputCapture().assert_outputs(self, queue._next_patch, [], expected_stdout=expected_stdout, expected_stderr=expected_stderr) - self.assertEquals(patch_id, None) # 2 is an invalid patch id - self.assertEquals(queue._next_patch().id(), 197) - - -class NeedsUpdateSequence(StepSequence): - def _run(self, tool, options, state): - raise CheckoutNeedsUpdate([], 1, "", None) - - -class AlwaysCommitQueueTool(object): - def __init__(self): - self.status_server = MockStatusServer() - - def command_by_name(self, name): - return CommitQueue - - -class SecondThoughtsCommitQueue(CommitQueue): - def __init__(self): - self._reject_patch = False - CommitQueue.__init__(self) - - def run_command(self, command): - # We want to reject the patch after the first validation, - # so wait to reject it until after some other command has run. - self._reject_patch = True - return CommitQueue.run_command(self, command) - - def refetch_patch(self, patch): - if not self._reject_patch: - return self._tool.bugs.fetch_attachment(patch.id()) - - attachment_dictionary = { - "id": patch.id(), - "bug_id": patch.bug_id(), - "name": "Rejected", - "is_obsolete": True, - "is_patch": False, - "review": "-", - "reviewer_email": "foo@bar.com", - "commit-queue": "-", - "committer_email": "foo@bar.com", - "attacher_email": "Contributer1", - } - return Attachment(attachment_dictionary, None) - - -# Creating fake CommitInfos is a pain, so we use a mock one here. -class MockCommitInfo(object): - def __init__(self, author_email): - self._author_email = author_email - - def author(self): - # It's definitely possible to have commits with authors who - # are not in our committers.py list. - if not self._author_email: - return None - return Committer("Mock Committer", self._author_email) - - -class CommitQueueTest(QueuesTest): - def test_commit_queue(self): - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root), - "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n", - "next_work_item": "", - "process_work_item": """MOCK: update_status: commit-queue Applied patch -MOCK: update_status: commit-queue Built patch -MOCK: update_status: commit-queue Passed tests -MOCK: update_status: commit-queue Landed patch -MOCK: update_status: commit-queue Pass -MOCK: release_work_item: commit-queue 197 -""", - "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n", - "handle_script_error": "ScriptError error message\n", - } - self.assert_queue_outputs(CommitQueue(), expected_stderr=expected_stderr) - - def test_commit_queue_failure(self): - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root), - "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n", - "next_work_item": "", - "process_work_item": """MOCK: update_status: commit-queue Patch does not apply -MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'MOCK script error' -MOCK: update_status: commit-queue Fail -MOCK: release_work_item: commit-queue 197 -""", - "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n", - "handle_script_error": "ScriptError error message\n", - } - queue = CommitQueue() - - def mock_run_webkit_patch(command): - raise ScriptError('MOCK script error') - - queue.run_webkit_patch = mock_run_webkit_patch - self.assert_queue_outputs(queue, expected_stderr=expected_stderr) - - def test_rollout(self): - tool = MockTool(log_executive=True) - tool.buildbot.light_tree_on_fire() - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root), - "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n", - "next_work_item": "", - "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', 197] -MOCK: update_status: commit-queue Applied patch -MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both'] -MOCK: update_status: commit-queue Built patch -MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive'] -MOCK: update_status: commit-queue Passed tests -MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197] -MOCK: update_status: commit-queue Landed patch -MOCK: update_status: commit-queue Pass -MOCK: release_work_item: commit-queue 197 -""", - "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n", - "handle_script_error": "ScriptError error message\n", - } - self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr) - - def test_rollout_lands(self): - tool = MockTool(log_executive=True) - tool.buildbot.light_tree_on_fire() - rollout_patch = tool.bugs.fetch_attachment(106) # _patch6, a rollout patch. - assert(rollout_patch.is_rollout()) - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root), - "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing rollout patch\n", - "next_work_item": "", - "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', 106] -MOCK: update_status: commit-queue Applied patch -MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both'] -MOCK: update_status: commit-queue Built patch -MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 106] -MOCK: update_status: commit-queue Landed patch -MOCK: update_status: commit-queue Pass -MOCK: release_work_item: commit-queue 106 -""", - "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '106' with comment 'Rejecting patch 106 from commit-queue.' and additional comment 'Mock error message'\n", - "handle_script_error": "ScriptError error message\n", - } - self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr) - - def test_auto_retry(self): - queue = CommitQueue() - options = Mock() - options.parent_command = "commit-queue" - tool = AlwaysCommitQueueTool() - sequence = NeedsUpdateSequence(None) - - expected_stderr = "Commit failed because the checkout is out of date. Please update and try again.\nMOCK: update_status: commit-queue Tests passed, but commit failed (checkout out of date). Updating, then landing without building or re-running tests.\n" - state = {'patch': None} - OutputCapture().assert_outputs(self, sequence.run_and_handle_errors, [tool, options, state], expected_exception=TryAgain, expected_stderr=expected_stderr) - - self.assertEquals(options.update, True) - self.assertEquals(options.build, False) - self.assertEquals(options.test, False) - - def test_manual_reject_during_processing(self): - queue = SecondThoughtsCommitQueue() - queue.bind_to_tool(MockTool()) - queue._options = Mock() - queue._options.port = None - expected_stderr = """MOCK: update_status: commit-queue Applied patch -MOCK: update_status: commit-queue Built patch -MOCK: update_status: commit-queue Passed tests -MOCK: update_status: commit-queue Retry -MOCK: release_work_item: commit-queue 197 -""" - OutputCapture().assert_outputs(self, queue.process_work_item, [QueuesTest.mock_work_item], expected_stderr=expected_stderr) - - def _assert_emails_for_tests(self, emails): - queue = CommitQueue() - tool = MockTool() - queue.bind_to_tool(tool) - commit_infos = [MockCommitInfo(email) for email in emails] - tool.checkout().recent_commit_infos_for_files = lambda paths: set(commit_infos) - self.assertEqual(queue._author_emails_for_tests([]), set(emails)) - - def test_author_emails_for_tests(self): - self._assert_emails_for_tests([]) - self._assert_emails_for_tests(["test1@test.com", "test1@test.com"]) - self._assert_emails_for_tests(["test1@test.com", "test2@test.com"]) - - def test_report_flaky_tests(self): - queue = CommitQueue() - queue.bind_to_tool(MockTool()) - expected_stderr = """MOCK bug comment: bug_id=42, cc=None ---- Begin comment --- -The commit-queue encountered the following flaky tests while processing attachment 197: - -foo/bar.html -bar/baz.html - -Please file bugs against the tests. These tests were authored by abarth@webkit.org. The commit-queue is continuing to process your patch. ---- End comment --- - -""" - OutputCapture().assert_outputs(self, queue.report_flaky_tests, [QueuesTest.mock_work_item, ["foo/bar.html", "bar/baz.html"]], expected_stderr=expected_stderr) - - def test_layout_test_results(self): - queue = CommitQueue() - queue.bind_to_tool(MockTool()) - queue._read_file_contents = lambda path: None - self.assertEquals(queue.layout_test_results(), None) - queue._read_file_contents = lambda path: "" - self.assertEquals(queue.layout_test_results(), None) - - -class StyleQueueTest(QueuesTest): - def test_style_queue(self): - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("style-queue", MockSCM.fake_checkout_root), - "next_work_item": "", - "should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n", - "process_work_item": "MOCK: update_status: style-queue Pass\nMOCK: release_work_item: style-queue 197\n", - "handle_unexpected_error": "Mock error message\n", - "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=42, cc=[]\n--- Begin comment ---\nAttachment 197 did not pass style-queue:\n\nScriptError error message\n\nIf any of these errors are false positives, please file a bug against check-webkit-style.\n--- End comment ---\n\n", - } - expected_exceptions = { - "handle_script_error": SystemExit, - } - self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, expected_exceptions=expected_exceptions) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py deleted file mode 100644 index 6455617..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import unittest - -from webkitpy.common.net.bugzilla import Attachment -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.common.system.executive import ScriptError -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler -from webkitpy.tool.mocktool import MockTool - - -class MockQueueEngine(object): - def __init__(self, name, queue, wakeup_event): - pass - - def run(self): - pass - - -class QueuesTest(unittest.TestCase): - # This is _patch1 in mocktool.py - mock_work_item = MockTool().bugs.fetch_attachment(197) - - def assert_outputs(self, func, func_name, args, expected_stdout, expected_stderr, expected_exceptions): - exception = None - if expected_exceptions and func_name in expected_exceptions: - exception = expected_exceptions[func_name] - - OutputCapture().assert_outputs(self, - func, - args=args, - expected_stdout=expected_stdout.get(func_name, ""), - expected_stderr=expected_stderr.get(func_name, ""), - expected_exception=exception) - - def _default_begin_work_queue_stderr(self, name, checkout_dir): - string_replacements = {"name": name, 'checkout_dir': checkout_dir} - return "CAUTION: %(name)s will discard all local changes in \"%(checkout_dir)s\"\nRunning WebKit %(name)s.\nMOCK: update_status: %(name)s Starting Queue\n" % string_replacements - - def assert_queue_outputs(self, queue, args=None, work_item=None, expected_stdout=None, expected_stderr=None, expected_exceptions=None, options=None, tool=None): - if not tool: - tool = MockTool() - if not expected_stdout: - expected_stdout = {} - if not expected_stderr: - expected_stderr = {} - if not args: - args = [] - if not options: - options = Mock() - options.port = None - if not work_item: - work_item = self.mock_work_item - tool.user.prompt = lambda message: "yes" - - queue.execute(options, args, tool, engine=MockQueueEngine) - - self.assert_outputs(queue.queue_log_path, "queue_log_path", [], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.work_item_log_path, "work_item_log_path", [work_item], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.begin_work_queue, "begin_work_queue", [], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.should_continue_work_queue, "should_continue_work_queue", [], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.next_work_item, "next_work_item", [], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.should_proceed_with_work_item, "should_proceed_with_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.process_work_item, "process_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions) - self.assert_outputs(queue.handle_unexpected_error, "handle_unexpected_error", [work_item, "Mock error message"], expected_stdout, expected_stderr, expected_exceptions) - # Should we have a different function for testing StepSequenceErrorHandlers? - if isinstance(queue, StepSequenceErrorHandler): - self.assert_outputs(queue.handle_script_error, "handle_script_error", [tool, {"patch": self.mock_work_item}, ScriptError(message="ScriptError error message", script_args="MockErrorCommand")], expected_stdout, expected_stderr, expected_exceptions) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py deleted file mode 100644 index abfa850..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os.path -import re -import shutil -import urllib - -from webkitpy.common.net.buildbot import BuildBot, LayoutTestResults -from webkitpy.common.system.user import User -from webkitpy.layout_tests.port import factory -from webkitpy.tool.grammar import pluralize -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand - - -# FIXME: I'm not sure where this logic should go in the end. -# For now it's here, until we have a second need for it. -class BuilderToPort(object): - _builder_name_to_port_name = { - r"SnowLeopard": "mac-snowleopard", - r"Leopard": "mac-leopard", - r"Tiger": "mac-tiger", - r"Windows": "win", - r"GTK": "gtk", - r"Qt": "qt", - r"Chromium Mac": "chromium-mac", - r"Chromium Linux": "chromium-linux", - r"Chromium Win": "chromium-win", - } - - def _port_name_for_builder_name(self, builder_name): - for regexp, port_name in self._builder_name_to_port_name.items(): - if re.match(regexp, builder_name): - return port_name - - def port_for_builder(self, builder_name): - port_name = self._port_name_for_builder_name(builder_name) - assert(port_name) # Need to update _builder_name_to_port_name - port = factory.get(port_name) - assert(port) # Need to update _builder_name_to_port_name - return port - - -class Rebaseline(AbstractDeclarativeCommand): - name = "rebaseline" - help_text = "Replaces local expected.txt files with new results from build bots" - - # FIXME: This should share more code with FailureReason._builder_to_explain - def _builder_to_pull_from(self): - builder_statuses = self._tool.buildbot.builder_statuses() - red_statuses = [status for status in builder_statuses if not status["is_green"]] - print "%s failing" % (pluralize("builder", len(red_statuses))) - builder_choices = [status["name"] for status in red_statuses] - chosen_name = self._tool.user.prompt_with_list("Which builder to pull results from:", builder_choices) - # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object. - for status in red_statuses: - if status["name"] == chosen_name: - return (self._tool.buildbot.builder_with_name(chosen_name), status["build_number"]) - - def _replace_expectation_with_remote_result(self, local_file, remote_file): - (downloaded_file, headers) = urllib.urlretrieve(remote_file) - shutil.move(downloaded_file, local_file) - - def _tests_to_update(self, build): - parsed_results = build.layout_test_results().parsed_results() - # FIXME: This probably belongs as API on LayoutTestResults - # but .failing_tests() already means something else. - failing_tests = parsed_results[LayoutTestResults.fail_key] - return self._tool.user.prompt_with_list("Which test(s) to rebaseline:", failing_tests, can_choose_multiple=True) - - def _results_url_for_test(self, build, test): - test_base = os.path.splitext(test)[0] - actual_path = test_base + "-actual.txt" - return build.results_url() + "/" + actual_path - - def execute(self, options, args, tool): - builder, build_number = self._builder_to_pull_from() - build = builder.build(build_number) - port = BuilderToPort().port_for_builder(builder.name()) - - for test in self._tests_to_update(build): - results_url = self._results_url_for_test(build, test) - # Port operates with absolute paths. - absolute_path = os.path.join(port.layout_tests_dir(), test) - expected_file = port.expected_filename(absolute_path, ".txt") - print test - self._replace_expectation_with_remote_result(expected_file, results_url) - - # FIXME: We should handle new results too. diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py deleted file mode 100644 index d6582a7..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import unittest - -from webkitpy.tool.commands.rebaseline import BuilderToPort - - -class BuilderToPortTest(unittest.TestCase): - def test_port_for_builder(self): - converter = BuilderToPort() - port = converter.port_for_builder("Leopard Intel Debug (Tests)") - self.assertEqual(port.name(), "mac-leopard") diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py deleted file mode 100644 index 2924a6f..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py +++ /dev/null @@ -1,268 +0,0 @@ -# Copyright (c) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Starts a local HTTP server which displays layout test failures (given a test -results directory), provides comparisons of expected and actual results (both -images and text) and allows one-click rebaselining of tests.""" -from __future__ import with_statement - -import codecs -import datetime -import mimetypes -import os -import os.path -import shutil -import threading -import time -import urlparse -import BaseHTTPServer - -from optparse import make_option -from wsgiref.handlers import format_date_time - -from webkitpy.common import system -from webkitpy.layout_tests.port import factory -from webkitpy.layout_tests.port.webkit import WebKitPort -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand -from webkitpy.thirdparty import simplejson - -STATE_NEEDS_REBASELINE = 'needs_rebaseline' -STATE_REBASELINE_FAILED = 'rebaseline_failed' -STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded' - -class RebaselineHTTPServer(BaseHTTPServer.HTTPServer): - def __init__(self, httpd_port, results_directory, results_json, platforms_json): - BaseHTTPServer.HTTPServer.__init__(self, ("", httpd_port), RebaselineHTTPRequestHandler) - self.results_directory = results_directory - self.results_json = results_json - self.platforms_json = platforms_json - - -class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): - STATIC_FILE_NAMES = frozenset([ - "index.html", - "loupe.js", - "main.js", - "main.css", - "queue.js", - "util.js", - ]) - - STATIC_FILE_DIRECTORY = os.path.join( - os.path.dirname(__file__), "data", "rebaselineserver") - - def do_GET(self): - self._handle_request() - - def do_POST(self): - self._handle_request() - - def _handle_request(self): - # Parse input. - if "?" in self.path: - path, query_string = self.path.split("?", 1) - self.query = urlparse.parse_qs(query_string) - else: - path = self.path - self.query = {} - function_or_file_name = path[1:] or "index.html" - - # See if a static file matches. - if function_or_file_name in RebaselineHTTPRequestHandler.STATIC_FILE_NAMES: - self._serve_static_file(function_or_file_name) - return - - # See if a class method matches. - function_name = function_or_file_name.replace(".", "_") - if not hasattr(self, function_name): - self.send_error(404, "Unknown function %s" % function_name) - return - if function_name[0] == "_": - self.send_error( - 401, "Not allowed to invoke private or protected methods") - return - function = getattr(self, function_name) - function() - - def _serve_static_file(self, static_path): - self._serve_file(os.path.join( - RebaselineHTTPRequestHandler.STATIC_FILE_DIRECTORY, static_path)) - - def quitquitquit(self): - self.send_response(200) - self.send_header("Content-type", "text/plain") - self.end_headers() - self.wfile.write("Quit.\n") - - # Shutdown has to happen on another thread from the server's thread, - # otherwise there's a deadlock - threading.Thread(target=lambda: self.server.shutdown()).start() - - def test_result(self): - test_name, _ = os.path.splitext(self.query['test'][0]) - mode = self.query['mode'][0] - if mode == 'expected-image': - file_name = test_name + '-expected.png' - elif mode == 'actual-image': - file_name = test_name + '-actual.png' - if mode == 'expected-checksum': - file_name = test_name + '-expected.checksum' - elif mode == 'actual-checksum': - file_name = test_name + '-actual.checksum' - elif mode == 'diff-image': - file_name = test_name + '-diff.png' - if mode == 'expected-text': - file_name = test_name + '-expected.txt' - elif mode == 'actual-text': - file_name = test_name + '-actual.txt' - elif mode == 'diff-text': - file_name = test_name + '-diff.txt' - - file_path = os.path.join(self.server.results_directory, file_name) - - # Let results be cached for 60 seconds, so that they can be pre-fetched - # by the UI - self._serve_file(file_path, cacheable_seconds=60) - - def results_json(self): - self._serve_json(self.server.results_json) - - def platforms_json(self): - self._serve_json(self.server.platforms_json) - - def _serve_json(self, json): - self.send_response(200) - self.send_header('Content-type', 'application/json') - self.end_headers() - simplejson.dump(json, self.wfile) - - def _serve_file(self, file_path, cacheable_seconds=0): - if not os.path.exists(file_path): - self.send_error(404, "File not found") - return - with codecs.open(file_path, "rb") as static_file: - self.send_response(200) - self.send_header("Content-Length", os.path.getsize(file_path)) - mime_type, encoding = mimetypes.guess_type(file_path) - if mime_type: - self.send_header("Content-type", mime_type) - - if cacheable_seconds: - expires_time = (datetime.datetime.now() + - datetime.timedelta(0, cacheable_seconds)) - expires_formatted = format_date_time( - time.mktime(expires_time.timetuple())) - self.send_header("Expires", expires_formatted) - self.end_headers() - - shutil.copyfileobj(static_file, self.wfile) - - -def _get_test_baselines(test_file, layout_tests_directory, platforms, filesystem): - class AllPlatformsPort(WebKitPort): - def __init__(self): - WebKitPort.__init__(self, filesystem=filesystem) - self._platforms_by_directory = dict( - [(self._webkit_baseline_path(p), p) for p in platforms]) - - def baseline_search_path(self): - return self._platforms_by_directory.keys() - - def platform_from_directory(self, directory): - return self._platforms_by_directory[directory] - - all_platforms_port = AllPlatformsPort() - - test_baselines = {} - for baseline_extension in ('.txt', '.checksum', '.png'): - test_path = filesystem.join(layout_tests_directory, test_file) - baselines = all_platforms_port.expected_baselines( - test_path, baseline_extension, all_baselines=True) - for platform_directory, expected_filename in baselines: - if not platform_directory: - continue - if platform_directory == layout_tests_directory: - platform = 'base' - else: - platform = all_platforms_port.platform_from_directory( - platform_directory) - if platform not in test_baselines: - test_baselines[platform] = [] - test_baselines[platform].append(baseline_extension) - - for platform, extensions in test_baselines.items(): - test_baselines[platform] = tuple(extensions) - - return test_baselines - -class RebaselineServer(AbstractDeclarativeCommand): - name = "rebaseline-server" - help_text = __doc__ - argument_names = "/path/to/results/directory" - - def __init__(self): - options = [ - make_option("--httpd-port", action="store", type="int", default=8127, help="Port to use for the the rebaseline HTTP server"), - ] - AbstractDeclarativeCommand.__init__(self, options=options) - - def execute(self, options, args, tool): - results_directory = args[0] - filesystem = system.filesystem.FileSystem() - - print 'Parsing unexpected_results.json...' - results_json_path = filesystem.join( - results_directory, 'unexpected_results.json') - with codecs.open(results_json_path, "r") as results_json_file: - results_json_file = file(results_json_path) - results_json = simplejson.load(results_json_file) - - port = factory.get() - layout_tests_directory = port.layout_tests_dir() - platforms = filesystem.listdir( - filesystem.join(layout_tests_directory, 'platform')) - - print 'Gathering current baselines...' - for test_file, test_json in results_json['tests'].items(): - test_json['state'] = STATE_NEEDS_REBASELINE - test_json['baselines'] = _get_test_baselines( - test_file, layout_tests_directory, platforms, filesystem) - - print "Starting server at http://localhost:%d/" % options.httpd_port - print ("Use the 'Exit' link in the UI, http://localhost:%d/" - "quitquitquit or Ctrl-C to stop") % options.httpd_port - - httpd = RebaselineHTTPServer( - httpd_port=options.httpd_port, - results_directory=results_directory, - results_json=results_json, - platforms_json={ - 'platforms': platforms, - 'defaultPlatform': port.name(), - }) - httpd.serve_forever() diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py deleted file mode 100644 index ea52902..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (C) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import unittest - -from webkitpy.common.system import filesystem_mock -from webkitpy.layout_tests.port import base -from webkitpy.tool.commands import rebaselineserver - - -class GetBaselinesTest(unittest.TestCase): - def test_no_baselines(self): - self._assertBaselines( - test_files=(), - test='fast/missing.html', - expected_baselines={}) - - def test_text_baselines(self): - self._assertBaselines( - test_files=( - 'fast/text-expected.txt', - 'platform/mac/fast/text-expected.txt', - ), - test='fast/text.html', - expected_baselines={'mac': ('.txt',), 'base': ('.txt',)}) - - def test_image_and_text_baselines(self): - self._assertBaselines( - test_files=( - 'fast/image-expected.txt', - 'platform/mac/fast/image-expected.png', - 'platform/mac/fast/image-expected.checksum', - 'platform/win/fast/image-expected.png', - 'platform/win/fast/image-expected.checksum', - ), - test='fast/image.html', - expected_baselines={ - 'base': ('.txt',), - 'mac': ('.checksum', '.png'), - 'win': ('.checksum', '.png'), - }) - - def test_extra_baselines(self): - self._assertBaselines( - test_files=( - 'fast/text-expected.txt', - 'platform/nosuchplatform/fast/text-expected.txt', - ), - test='fast/text.html', - expected_baselines={'base': ('.txt',)}) - - def _assertBaselines(self, test_files, test, expected_baselines): - layout_tests_directory = base.Port().layout_tests_dir() - mock_filesystem = filesystem_mock.MockFileSystem() - for file in test_files + (test,): - file_path = mock_filesystem.join(layout_tests_directory, file) - mock_filesystem.files[file_path] = '' - actual_baselines = rebaselineserver._get_test_baselines( - test, - layout_tests_directory, - ('mac', 'win', 'linux'), - mock_filesystem) - self.assertEqual(actual_baselines, expected_baselines) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py deleted file mode 100644 index 145f485..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os - -from webkitpy.common.system.deprecated_logging import log -from webkitpy.common.config.ports import WebKitPort -from webkitpy.tool.bot.sheriff import Sheriff -from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot -from webkitpy.tool.commands.queues import AbstractQueue -from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler - - -class SheriffBot(AbstractQueue, StepSequenceErrorHandler): - name = "sheriff-bot" - watchers = AbstractQueue.watchers + [ - "abarth@webkit.org", - "eric@webkit.org", - ] - - def _update(self): - self.run_webkit_patch(["update", "--force-clean", "--quiet"]) - - # AbstractQueue methods - - def begin_work_queue(self): - AbstractQueue.begin_work_queue(self) - self._sheriff = Sheriff(self._tool, self) - self._irc_bot = SheriffIRCBot(self._tool, self._sheriff) - self._tool.ensure_irc_connected(self._irc_bot.irc_delegate()) - - def work_item_log_path(self, failure_map): - return None - - def _is_old_failure(self, revision): - return self._tool.status_server.svn_revision(revision) - - def next_work_item(self): - self._irc_bot.process_pending_messages() - self._update() - - # FIXME: We need to figure out how to provoke_flaky_builders. - - failure_map = self._tool.buildbot.failure_map() - failure_map.filter_out_old_failures(self._is_old_failure) - if failure_map.is_empty(): - return None - return failure_map - - def should_proceed_with_work_item(self, failure_map): - # Currently, we don't have any reasons not to proceed with work items. - return True - - def process_work_item(self, failure_map): - failing_revisions = failure_map.failing_revisions() - for revision in failing_revisions: - builders = failure_map.builders_failing_for(revision) - tests = failure_map.tests_failing_for(revision) - try: - commit_info = self._tool.checkout().commit_info_for_revision(revision) - if not commit_info: - print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision - continue - self._sheriff.post_irc_warning(commit_info, builders) - self._sheriff.post_blame_comment_on_bug(commit_info, builders, tests) - - finally: - for builder in builders: - self._tool.status_server.update_svn_revision(revision, builder.name()) - return True - - def handle_unexpected_error(self, failure_map, message): - log(message) - - # StepSequenceErrorHandler methods - - @classmethod - def handle_script_error(cls, tool, state, script_error): - # Ideally we would post some information to IRC about what went wrong - # here, but we don't have the IRC password in the child process. - pass diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py deleted file mode 100644 index 4db463e..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) 2010 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os - -from webkitpy.tool.commands.queuestest import QueuesTest -from webkitpy.tool.commands.sheriffbot import SheriffBot -from webkitpy.tool.mocktool import * - - -class SheriffBotTest(QueuesTest): - builder1 = MockBuilder("Builder1") - builder2 = MockBuilder("Builder2") - - def test_sheriff_bot(self): - tool = MockTool() - mock_work_item = MockFailureMap(tool.buildbot) - expected_stderr = { - "begin_work_queue": self._default_begin_work_queue_stderr("sheriff-bot", tool.scm().checkout_root), - "next_work_item": "", - "process_work_item": """MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Builder1 -MOCK bug comment: bug_id=42, cc=['abarth@webkit.org', 'eric@webkit.org'] ---- Begin comment --- -http://trac.webkit.org/changeset/29837 might have broken Builder1 -The following tests are not passing: -mock-test-1 ---- End comment --- - -""", - "handle_unexpected_error": "Mock error message\n" - } - self.assert_queue_outputs(SheriffBot(), work_item=mock_work_item, expected_stderr=expected_stderr) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py b/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py deleted file mode 100644 index be2ed4c..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import webkitpy.tool.steps as steps - -from webkitpy.common.system.executive import ScriptError -from webkitpy.common.checkout.scm import CheckoutNeedsUpdate -from webkitpy.tool.bot.queueengine import QueueEngine -from webkitpy.common.system.deprecated_logging import log - - -class StepSequenceErrorHandler(): - @classmethod - def handle_script_error(cls, tool, patch, script_error): - raise NotImplementedError, "subclasses must implement" - - @classmethod - def handle_checkout_needs_update(cls, tool, state, options, error): - raise NotImplementedError, "subclasses must implement" - - -class StepSequence(object): - def __init__(self, steps): - self._steps = steps or [] - - def options(self): - collected_options = [ - steps.Options.parent_command, - steps.Options.quiet, - ] - for step in self._steps: - collected_options = collected_options + step.options() - # Remove duplicates. - collected_options = sorted(set(collected_options)) - return collected_options - - def _run(self, tool, options, state): - for step in self._steps: - step(tool, options).run(state) - - def run_and_handle_errors(self, tool, options, state=None): - if not state: - state = {} - try: - self._run(tool, options, state) - except CheckoutNeedsUpdate, e: - log("Commit failed because the checkout is out of date. Please update and try again.") - if options.parent_command: - command = tool.command_by_name(options.parent_command) - command.handle_checkout_needs_update(tool, state, options, e) - QueueEngine.exit_after_handled_error(e) - except ScriptError, e: - if not options.quiet: - log(e.message_with_output()) - if options.parent_command: - command = tool.command_by_name(options.parent_command) - command.handle_script_error(tool, state, e) - QueueEngine.exit_after_handled_error(e) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py deleted file mode 100644 index e12c8e2..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py +++ /dev/null @@ -1,483 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2009, 2010 Google Inc. All rights reserved. -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os -import re -import sys - -from optparse import make_option - -import webkitpy.tool.steps as steps - -from webkitpy.common.config.committers import CommitterList -from webkitpy.common.net.bugzilla import parse_bug_id -from webkitpy.common.system.user import User -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand -from webkitpy.tool.grammar import pluralize, join_with_separators -from webkitpy.tool.comments import bug_comment_from_svn_revision -from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand -from webkitpy.common.system.deprecated_logging import error, log - - -class CommitMessageForCurrentDiff(AbstractDeclarativeCommand): - name = "commit-message" - help_text = "Print a commit message suitable for the uncommitted changes" - - def __init__(self): - options = [ - steps.Options.git_commit, - ] - AbstractDeclarativeCommand.__init__(self, options=options) - - def execute(self, options, args, tool): - # This command is a useful test to make sure commit_message_for_this_commit - # always returns the right value regardless of the current working directory. - print "%s" % tool.checkout().commit_message_for_this_commit(options.git_commit).message() - - -class CleanPendingCommit(AbstractDeclarativeCommand): - name = "clean-pending-commit" - help_text = "Clear r+ on obsolete patches so they do not appear in the pending-commit list." - - # NOTE: This was designed to be generic, but right now we're only processing patches from the pending-commit list, so only r+ matters. - def _flags_to_clear_on_patch(self, patch): - if not patch.is_obsolete(): - return None - what_was_cleared = [] - if patch.review() == "+": - if patch.reviewer(): - what_was_cleared.append("%s's review+" % patch.reviewer().full_name) - else: - what_was_cleared.append("review+") - return join_with_separators(what_was_cleared) - - def execute(self, options, args, tool): - committers = CommitterList() - for bug_id in tool.bugs.queries.fetch_bug_ids_from_pending_commit_list(): - bug = self._tool.bugs.fetch_bug(bug_id) - patches = bug.patches(include_obsolete=True) - for patch in patches: - flags_to_clear = self._flags_to_clear_on_patch(patch) - if not flags_to_clear: - continue - message = "Cleared %s from obsolete attachment %s so that this bug does not appear in http://webkit.org/pending-commit." % (flags_to_clear, patch.id()) - self._tool.bugs.obsolete_attachment(patch.id(), message) - - -# FIXME: This should be share more logic with AssignToCommitter and CleanPendingCommit -class CleanReviewQueue(AbstractDeclarativeCommand): - name = "clean-review-queue" - help_text = "Clear r? on obsolete patches so they do not appear in the pending-commit list." - - def execute(self, options, args, tool): - queue_url = "http://webkit.org/pending-review" - # We do this inefficient dance to be more like webkit.org/pending-review - # bugs.queries.fetch_bug_ids_from_review_queue() doesn't return - # closed bugs, but folks using /pending-review will see them. :( - for patch_id in tool.bugs.queries.fetch_attachment_ids_from_review_queue(): - patch = self._tool.bugs.fetch_attachment(patch_id) - if not patch.review() == "?": - continue - attachment_obsolete_modifier = "" - if patch.is_obsolete(): - attachment_obsolete_modifier = "obsolete " - elif patch.bug().is_closed(): - bug_closed_explanation = " If you would like this patch reviewed, please attach it to a new bug (or re-open this bug before marking it for review again)." - else: - # Neither the patch was obsolete or the bug was closed, next patch... - continue - message = "Cleared review? from %sattachment %s so that this bug does not appear in %s.%s" % (attachment_obsolete_modifier, patch.id(), queue_url, bug_closed_explanation) - self._tool.bugs.obsolete_attachment(patch.id(), message) - - -class AssignToCommitter(AbstractDeclarativeCommand): - name = "assign-to-committer" - help_text = "Assign bug to whoever attached the most recent r+'d patch" - - def _patches_have_commiters(self, reviewed_patches): - for patch in reviewed_patches: - if not patch.committer(): - return False - return True - - def _assign_bug_to_last_patch_attacher(self, bug_id): - committers = CommitterList() - bug = self._tool.bugs.fetch_bug(bug_id) - if not bug.is_unassigned(): - assigned_to_email = bug.assigned_to_email() - log("Bug %s is already assigned to %s (%s)." % (bug_id, assigned_to_email, committers.committer_by_email(assigned_to_email))) - return - - reviewed_patches = bug.reviewed_patches() - if not reviewed_patches: - log("Bug %s has no non-obsolete patches, ignoring." % bug_id) - return - - # We only need to do anything with this bug if one of the r+'d patches does not have a valid committer (cq+ set). - if self._patches_have_commiters(reviewed_patches): - log("All reviewed patches on bug %s already have commit-queue+, ignoring." % bug_id) - return - - latest_patch = reviewed_patches[-1] - attacher_email = latest_patch.attacher_email() - committer = committers.committer_by_email(attacher_email) - if not committer: - log("Attacher %s is not a committer. Bug %s likely needs commit-queue+." % (attacher_email, bug_id)) - return - - reassign_message = "Attachment %s was posted by a committer and has review+, assigning to %s for commit." % (latest_patch.id(), committer.full_name) - self._tool.bugs.reassign_bug(bug_id, committer.bugzilla_email(), reassign_message) - - def execute(self, options, args, tool): - for bug_id in tool.bugs.queries.fetch_bug_ids_from_pending_commit_list(): - self._assign_bug_to_last_patch_attacher(bug_id) - - -class ObsoleteAttachments(AbstractSequencedCommand): - name = "obsolete-attachments" - help_text = "Mark all attachments on a bug as obsolete" - argument_names = "BUGID" - steps = [ - steps.ObsoletePatches, - ] - - def _prepare_state(self, options, args, tool): - return { "bug_id" : args[0] } - - -class AbstractPatchUploadingCommand(AbstractSequencedCommand): - def _bug_id(self, options, args, tool, state): - # Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs). - bug_id = args and args[0] - if not bug_id: - changed_files = self._tool.scm().changed_files(options.git_commit) - state["changed_files"] = changed_files - bug_id = tool.checkout().bug_id_for_this_commit(options.git_commit, changed_files) - return bug_id - - def _prepare_state(self, options, args, tool): - state = {} - state["bug_id"] = self._bug_id(options, args, tool, state) - if not state["bug_id"]: - error("No bug id passed and no bug url found in ChangeLogs.") - return state - - -class Post(AbstractPatchUploadingCommand): - name = "post" - help_text = "Attach the current working directory diff to a bug as a patch file" - argument_names = "[BUGID]" - steps = [ - steps.CheckStyle, - steps.ConfirmDiff, - steps.ObsoletePatches, - steps.SuggestReviewers, - steps.PostDiff, - ] - - -class LandSafely(AbstractPatchUploadingCommand): - name = "land-safely" - help_text = "Land the current diff via the commit-queue" - argument_names = "[BUGID]" - long_help = """land-safely updates the ChangeLog with the reviewer listed - in bugs.webkit.org for BUGID (or the bug ID detected from the ChangeLog). - The command then uploads the current diff to the bug and marks it for - commit by the commit-queue.""" - show_in_main_help = True - steps = [ - steps.UpdateChangeLogsWithReviewer, - steps.ObsoletePatches, - steps.PostDiffForCommit, - ] - - -class Prepare(AbstractSequencedCommand): - name = "prepare" - help_text = "Creates a bug (or prompts for an existing bug) and prepares the ChangeLogs" - argument_names = "[BUGID]" - steps = [ - steps.PromptForBugOrTitle, - steps.CreateBug, - steps.PrepareChangeLog, - ] - - def _prepare_state(self, options, args, tool): - bug_id = args and args[0] - return { "bug_id" : bug_id } - - -class Upload(AbstractPatchUploadingCommand): - name = "upload" - help_text = "Automates the process of uploading a patch for review" - argument_names = "[BUGID]" - show_in_main_help = True - steps = [ - steps.CheckStyle, - steps.PromptForBugOrTitle, - steps.CreateBug, - steps.PrepareChangeLog, - steps.EditChangeLog, - steps.ConfirmDiff, - steps.ObsoletePatches, - steps.SuggestReviewers, - steps.PostDiff, - ] - long_help = """upload uploads the current diff to bugs.webkit.org. - If no bug id is provided, upload will create a bug. - If the current diff does not have a ChangeLog, upload - will prepare a ChangeLog. Once a patch is read, upload - will open the ChangeLogs for editing using the command in the - EDITOR environment variable and will display the diff using the - command in the PAGER environment variable.""" - - def _prepare_state(self, options, args, tool): - state = {} - state["bug_id"] = self._bug_id(options, args, tool, state) - return state - - -class EditChangeLogs(AbstractSequencedCommand): - name = "edit-changelogs" - help_text = "Opens modified ChangeLogs in $EDITOR" - show_in_main_help = True - steps = [ - steps.EditChangeLog, - ] - - -class PostCommits(AbstractDeclarativeCommand): - name = "post-commits" - help_text = "Attach a range of local commits to bugs as patch files" - argument_names = "COMMITISH" - - def __init__(self): - options = [ - make_option("-b", "--bug-id", action="store", type="string", dest="bug_id", help="Specify bug id if no URL is provided in the commit log."), - make_option("--add-log-as-comment", action="store_true", dest="add_log_as_comment", default=False, help="Add commit log message as a comment when uploading the patch."), - make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: description from commit message)"), - steps.Options.obsolete_patches, - steps.Options.review, - steps.Options.request_commit, - ] - AbstractDeclarativeCommand.__init__(self, options=options, requires_local_commits=True) - - def _comment_text_for_commit(self, options, commit_message, tool, commit_id): - comment_text = None - if (options.add_log_as_comment): - comment_text = commit_message.body(lstrip=True) - comment_text += "---\n" - comment_text += tool.scm().files_changed_summary_for_commit(commit_id) - return comment_text - - def execute(self, options, args, tool): - commit_ids = tool.scm().commit_ids_from_commitish_arguments(args) - if len(commit_ids) > 10: # We could lower this limit, 10 is too many for one bug as-is. - error("webkit-patch does not support attaching %s at once. Are you sure you passed the right commit range?" % (pluralize("patch", len(commit_ids)))) - - have_obsoleted_patches = set() - for commit_id in commit_ids: - commit_message = tool.scm().commit_message_for_local_commit(commit_id) - - # Prefer --bug-id=, then a bug url in the commit message, then a bug url in the entire commit diff (i.e. ChangeLogs). - bug_id = options.bug_id or parse_bug_id(commit_message.message()) or parse_bug_id(tool.scm().create_patch(git_commit=commit_id)) - if not bug_id: - log("Skipping %s: No bug id found in commit or specified with --bug-id." % commit_id) - continue - - if options.obsolete_patches and bug_id not in have_obsoleted_patches: - state = { "bug_id": bug_id } - steps.ObsoletePatches(tool, options).run(state) - have_obsoleted_patches.add(bug_id) - - diff = tool.scm().create_patch(git_commit=commit_id) - description = options.description or commit_message.description(lstrip=True, strip_url=True) - comment_text = self._comment_text_for_commit(options, commit_message, tool, commit_id) - tool.bugs.add_patch_to_bug(bug_id, diff, description, comment_text, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - -# FIXME: This command needs to be brought into the modern age with steps and CommitInfo. -class MarkBugFixed(AbstractDeclarativeCommand): - name = "mark-bug-fixed" - help_text = "Mark the specified bug as fixed" - argument_names = "[SVN_REVISION]" - def __init__(self): - options = [ - make_option("--bug-id", action="store", type="string", dest="bug_id", help="Specify bug id if no URL is provided in the commit log."), - make_option("--comment", action="store", type="string", dest="comment", help="Text to include in bug comment."), - make_option("--open", action="store_true", default=False, dest="open_bug", help="Open bug in default web browser (Mac only)."), - make_option("--update-only", action="store_true", default=False, dest="update_only", help="Add comment to the bug, but do not close it."), - ] - AbstractDeclarativeCommand.__init__(self, options=options) - - # FIXME: We should be using checkout().changelog_entries_for_revision(...) instead here. - def _fetch_commit_log(self, tool, svn_revision): - if not svn_revision: - return tool.scm().last_svn_commit_log() - return tool.scm().svn_commit_log(svn_revision) - - def _determine_bug_id_and_svn_revision(self, tool, bug_id, svn_revision): - commit_log = self._fetch_commit_log(tool, svn_revision) - - if not bug_id: - bug_id = parse_bug_id(commit_log) - - if not svn_revision: - match = re.search("^r(?P<svn_revision>\d+) \|", commit_log, re.MULTILINE) - if match: - svn_revision = match.group('svn_revision') - - if not bug_id or not svn_revision: - not_found = [] - if not bug_id: - not_found.append("bug id") - if not svn_revision: - not_found.append("svn revision") - error("Could not find %s on command-line or in %s." - % (" or ".join(not_found), "r%s" % svn_revision if svn_revision else "last commit")) - - return (bug_id, svn_revision) - - def execute(self, options, args, tool): - bug_id = options.bug_id - - svn_revision = args and args[0] - if svn_revision: - if re.match("^r[0-9]+$", svn_revision, re.IGNORECASE): - svn_revision = svn_revision[1:] - if not re.match("^[0-9]+$", svn_revision): - error("Invalid svn revision: '%s'" % svn_revision) - - needs_prompt = False - if not bug_id or not svn_revision: - needs_prompt = True - (bug_id, svn_revision) = self._determine_bug_id_and_svn_revision(tool, bug_id, svn_revision) - - log("Bug: <%s> %s" % (tool.bugs.bug_url_for_bug_id(bug_id), tool.bugs.fetch_bug_dictionary(bug_id)["title"])) - log("Revision: %s" % svn_revision) - - if options.open_bug: - tool.user.open_url(tool.bugs.bug_url_for_bug_id(bug_id)) - - if needs_prompt: - if not tool.user.confirm("Is this correct?"): - exit(1) - - bug_comment = bug_comment_from_svn_revision(svn_revision) - if options.comment: - bug_comment = "%s\n\n%s" % (options.comment, bug_comment) - - if options.update_only: - log("Adding comment to Bug %s." % bug_id) - tool.bugs.post_comment_to_bug(bug_id, bug_comment) - else: - log("Adding comment to Bug %s and marking as Resolved/Fixed." % bug_id) - tool.bugs.close_bug_as_fixed(bug_id, bug_comment) - - -# FIXME: Requires unit test. Blocking issue: too complex for now. -class CreateBug(AbstractDeclarativeCommand): - name = "create-bug" - help_text = "Create a bug from local changes or local commits" - argument_names = "[COMMITISH]" - - def __init__(self): - options = [ - steps.Options.cc, - steps.Options.component, - make_option("--no-prompt", action="store_false", dest="prompt", default=True, help="Do not prompt for bug title and comment; use commit log instead."), - make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), - make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), - ] - AbstractDeclarativeCommand.__init__(self, options=options) - - def create_bug_from_commit(self, options, args, tool): - commit_ids = tool.scm().commit_ids_from_commitish_arguments(args) - if len(commit_ids) > 3: - error("Are you sure you want to create one bug with %s patches?" % len(commit_ids)) - - commit_id = commit_ids[0] - - bug_title = "" - comment_text = "" - if options.prompt: - (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() - else: - commit_message = tool.scm().commit_message_for_local_commit(commit_id) - bug_title = commit_message.description(lstrip=True, strip_url=True) - comment_text = commit_message.body(lstrip=True) - comment_text += "---\n" - comment_text += tool.scm().files_changed_summary_for_commit(commit_id) - - diff = tool.scm().create_patch(git_commit=commit_id) - bug_id = tool.bugs.create_bug(bug_title, comment_text, options.component, diff, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - if bug_id and len(commit_ids) > 1: - options.bug_id = bug_id - options.obsolete_patches = False - # FIXME: We should pass through --no-comment switch as well. - PostCommits.execute(self, options, commit_ids[1:], tool) - - def create_bug_from_patch(self, options, args, tool): - bug_title = "" - comment_text = "" - if options.prompt: - (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() - else: - commit_message = tool.checkout().commit_message_for_this_commit(options.git_commit) - bug_title = commit_message.description(lstrip=True, strip_url=True) - comment_text = commit_message.body(lstrip=True) - - diff = tool.scm().create_patch(options.git_commit) - bug_id = tool.bugs.create_bug(bug_title, comment_text, options.component, diff, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - def prompt_for_bug_title_and_comment(self): - bug_title = User.prompt("Bug title: ") - print "Bug comment (hit ^D on blank line to end):" - lines = sys.stdin.readlines() - try: - sys.stdin.seek(0, os.SEEK_END) - except IOError: - # Cygwin raises an Illegal Seek (errno 29) exception when the above - # seek() call is made. Ignoring it seems to cause no harm. - # FIXME: Figure out a way to get avoid the exception in the first - # place. - pass - comment_text = "".join(lines) - return (bug_title, comment_text) - - def execute(self, options, args, tool): - if len(args): - if (not tool.scm().supports_local_commits()): - error("Extra arguments not supported; patch is taken from working directory.") - self.create_bug_from_commit(options, args, tool) - else: - self.create_bug_from_patch(options, args, tool) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py deleted file mode 100644 index bd1fbd6..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (C) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.commands.commandtest import CommandsTest -from webkitpy.tool.commands.upload import * -from webkitpy.tool.mocktool import MockOptions, MockTool - -class UploadCommandsTest(CommandsTest): - def test_commit_message_for_current_diff(self): - tool = MockTool() - expected_stdout = "This is a fake commit message that is at least 50 characters.\n" - self.assert_execute_outputs(CommitMessageForCurrentDiff(), [], expected_stdout=expected_stdout, tool=tool) - - def test_clean_pending_commit(self): - self.assert_execute_outputs(CleanPendingCommit(), []) - - def test_assign_to_committer(self): - tool = MockTool() - expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\nBug 77 is already assigned to foo@foo.com (None).\nBug 76 has no non-obsolete patches, ignoring.\n" - self.assert_execute_outputs(AssignToCommitter(), [], expected_stderr=expected_stderr, tool=tool) - tool.bugs.reassign_bug.assert_called_with(42, "eric@webkit.org", "Attachment 128 was posted by a committer and has review+, assigning to Eric Seidel for commit.") - - def test_obsolete_attachments(self): - expected_stderr = "Obsoleting 2 old patches on bug 42\n" - self.assert_execute_outputs(ObsoleteAttachments(), [42], expected_stderr=expected_stderr) - - def test_post(self): - options = MockOptions() - options.cc = None - options.check_style = True - options.comment = None - options.description = "MOCK description" - options.request_commit = False - options.review = True - options.suggest_reviewers = False - expected_stderr = """Running check-webkit-style -MOCK: user.open_url: file://... -Obsoleting 2 old patches on bug 42 -MOCK add_patch_to_bug: bug_id=42, description=MOCK description, mark_for_review=True, mark_for_commit_queue=False, mark_for_landing=False --- Begin comment -- -None --- End comment -- -MOCK: user.open_url: http://example.com/42 -""" - expected_stdout = "Was that diff correct?\n" - self.assert_execute_outputs(Post(), [42], options=options, expected_stdout=expected_stdout, expected_stderr=expected_stderr) - - def test_land_safely(self): - expected_stderr = "Obsoleting 2 old patches on bug 42\nMOCK add_patch_to_bug: bug_id=42, description=Patch for landing, mark_for_review=False, mark_for_commit_queue=False, mark_for_landing=True\n-- Begin comment --\nNone\n-- End comment --\n" - self.assert_execute_outputs(LandSafely(), [42], expected_stderr=expected_stderr) - - def test_prepare_diff_with_arg(self): - self.assert_execute_outputs(Prepare(), [42]) - - def test_prepare(self): - expected_stderr = "MOCK create_bug\nbug_title: Mock user response\nbug_description: Mock user response\n" - self.assert_execute_outputs(Prepare(), [], expected_stderr=expected_stderr) - - def test_upload(self): - options = MockOptions() - options.cc = None - options.check_style = True - options.comment = None - options.description = "MOCK description" - options.request_commit = False - options.review = True - options.suggest_reviewers = False - expected_stderr = """Running check-webkit-style -MOCK: user.open_url: file://... -Obsoleting 2 old patches on bug 42 -MOCK add_patch_to_bug: bug_id=42, description=MOCK description, mark_for_review=True, mark_for_commit_queue=False, mark_for_landing=False --- Begin comment -- -None --- End comment -- -MOCK: user.open_url: http://example.com/42 -""" - expected_stdout = "Was that diff correct?\n" - self.assert_execute_outputs(Upload(), [42], options=options, expected_stdout=expected_stdout, expected_stderr=expected_stderr) - - def test_mark_bug_fixed(self): - tool = MockTool() - tool._scm.last_svn_commit_log = lambda: "r9876 |" - options = Mock() - options.bug_id = 42 - options.comment = "MOCK comment" - expected_stderr = "Bug: <http://example.com/42> Bug with two r+'d and cq+'d patches, one of which has an invalid commit-queue setter.\nRevision: 9876\nMOCK: user.open_url: http://example.com/42\nAdding comment to Bug 42.\nMOCK bug comment: bug_id=42, cc=None\n--- Begin comment ---\nMOCK comment\n\nCommitted r9876: <http://trac.webkit.org/changeset/9876>\n--- End comment ---\n\n" - expected_stdout = "Is this correct?\n" - self.assert_execute_outputs(MarkBugFixed(), [], expected_stdout=expected_stdout, expected_stderr=expected_stderr, tool=tool, options=options) - - def test_edit_changelog(self): - self.assert_execute_outputs(EditChangeLogs(), []) |