WebKit Bugzilla
Attachment 343649 Details for
Bug 186823
: Implement EWS real-time patch status updates and tail logging
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch and unit tests
bug-186823-20180626153553.patch (text/plain), 54.77 KB, created by
Daniel Bates
on 2018-06-26 15:35:53 PDT
(
hide
)
Description:
Patch and unit tests
Filename:
MIME Type:
Creator:
Daniel Bates
Created:
2018-06-26 15:35:53 PDT
Size:
54.77 KB
patch
obsolete
>Subversion Revision: 233222 >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 90a02bda3267c94dd4bad60736a227e1543cd0be..55cbbd6d9a1a0ac985708df1e051309605769ca3 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,108 @@ >+2018-06-25 Daniel Bates <dabates@apple.com> >+ >+ Implement EWS real-time patch status updates and tail logging >+ https://bugs.webkit.org/show_bug.cgi?id=186823 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Makes the patch status page (e.g. https://webkit-queues.webkit.org/patch/342789/mac-ews) display >+ real-time status updates, including a tail(1) of the standard output and standard error streams >+ from the bot as it runs commands to build and test a patch. You can either access the patch status >+ page directly using a URL similar to the example one above or, even easier, just click on the EWS >+ bubble of the queue you want to view on the Bugzilla bug page the patch was uploaded to. >+ >+ Real-time updates are written to a TailLog, which is implemented using the AppEngine Channel API. >+ Each TailLog stores the Channel API token (used by the patch status page to connect to the back-end) >+ in memcache keyed off the queue name and attachment id. We use memcache to store this association >+ instead of storing it in the database to complement the transient nature of the Channel API. Memcache >+ may be flushed or items may be evicted from it at any time. >+ >+ A side effect of this change this that the patch status patch now lists events in chronological >+ order from oldest to newest. Currently it lists status updates in reverse chronological order. >+ The change in ordering was made so that the standard output and standard error streams are >+ written as a person would expect in a terminal. >+ >+ * QueueStatusServer/handlers/patch.py: >+ (Patch.get): Always order the status messages in chronological order from oldest to newest. >+ If we have an existing tail log for the specified queue and attachment then include its token >+ as a template variable so that the rendered web page will be able to connect to the stream. >+ * QueueStatusServer/handlers/releaselock.py: >+ (ReleaseLock.post): Expire the tail log when we release the lock for the patch. >+ * QueueStatusServer/handlers/releasepatch.py: >+ (ReleasePatch.post): Expire the tail log when the patch is released from the queue. >+ * QueueStatusServer/handlers/updatestatus.py: >+ (UpdateStatus.post): Create a tail log for the specified queue and attachment if one does not >+ already exist and write the status details to it. We only persist the status details to the >+ database (disk) if the update is not is transient. Transient updates are only written to the >+ tail log and are best used for data that is unnecessary to keep around. We use transient >+ update for streamed standard output and standard error text because the value of such text >+ is time-sensitive (usually only valuable while iterating on a patch before it lands) and >+ the output can be excessive (tens of MBs). EWS will persist a trailing portion of these >+ streams to the database if a patch fails to build of causes tests failures. >+ * QueueStatusServer/index.yaml: Remove the sort order from the database index. >+ * QueueStatusServer/model/taillog.py: Added. >+ (TailLog): >+ (TailLog.__init__): >+ (TailLog.lookup_or_create): Query memcache for an existing channel token. Otherwise, create >+ a new channel and store the token in memcache. >+ (TailLog.lookup): Query memcache for the channel token. >+ (TailLog._is_valid): Convenience function to check if the key still exists in memcache. >+ (TailLog.expire): Remove the key from memcache. >+ (TailLog.write_message): Writes a message to the channel. >+ (TailLog._generate_key): Compute a key from the name of the queue and the attachment id. >+ * QueueStatusServer/stylesheets/common.css: >+ (.code): Stylize transient messages using a monospace font to resemble console output. >+ * QueueStatusServer/templates/patch.html: Connect to to the underlying Channel API to stream >+ tail log updates. >+ * QueueStatusServer/templates/updatestatus.html: Add a checkbox as to whether the update is >+ considered transient and should only appear in the tail log. >+ * Scripts/webkitpy/common/net/statusserver.py: >+ (StatusServer._post_status_to_server): Modified to take a boolean, is_transient, as to whether >+ the status is considered transient. >+ (StatusServer.update_status): Ditto. >+ >+ (WritableStatusServerStatusFileObject): >+ (WritableStatusServerStatusFileObject.__init__): >+ (WritableStatusServerStatusFileObject.__eq__): >+ (WritableStatusServerStatusFileObject.write): >+ File-like object that forwards writes to the status >+ server as transient status updates. >+ >+ * Scripts/webkitpy/common/net/statusserver_mock.py: >+ (MockStatusServer.update_status): >+ * Scripts/webkitpy/common/net/web_mock.py: >+ (MockBrowser.find_control): Added. >+ * Scripts/webkitpy/common/system/executive.py: >+ (Executive.run_and_throw_if_fail): Extract out logic into run_with_teed_output_and_throw_if_fail() >+ and write this function in terms of it. >+ (Executive.run_with_teed_output_and_throw_if_fail): Extracted from Executive.run_and_throw_if_fail(). >+ * Scripts/webkitpy/common/system/executive_mock.py: >+ (MockExecutive.run_with_teed_output_and_throw_if_fail): Added. >+ >+ * Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py: >+ (MockCommitQueue.run_command): >+ (FailingTestCommitQueue.run_command): >+ (MockSimpleTestPlanCommitQueue.run_command): >+ * Scripts/webkitpy/tool/bot/patchanalysistask.py: >+ (PatchAnalysisTaskDelegate.run_command): >+ (PatchAnalysisTask._run_command): >+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py: >+ (AbstractEarlyWarningSystem.run_command): >+ Pass the Attachment object through so that we can log the output of the command with respect to a particular patch. >+ >+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py: >+ (EarlyWarningSystemTest._default_expected_logs): Update expected result now that we send updates for transient events. >+ * Scripts/webkitpy/tool/commands/perfalizer.py: >+ (PerfalizerTask.run_command): Pass the Attachment object through so that we can log the output of the command with >+ respect to a particular patch. >+ * Scripts/webkitpy/tool/commands/queues.py: >+ (AbstractQueue.run_webkit_patch): >+ (CommitQueue.run_command): Pass the Attachment object through so that we can log the output of the command with respect >+ to a particular patch. >+ (StyleQueue.run_command): Ditto. >+ * Scripts/webkitpy/tool/commands/queues_unittest.py: >+ (AbstractQueueTest._assert_run_webkit_patch): Update test now that we send updates for transient events. >+ > 2018-06-26 Daniel Bates <dabates@apple.com> > > EWS should pass --status-host-uses-http when invoking webkit-patch, if needed >diff --git a/Tools/QueueStatusServer/handlers/patch.py b/Tools/QueueStatusServer/handlers/patch.py >index f52780232536173737f3045b6c151f24be21764e..400251d436800b1091374776b244bfdf94c1cfdd 100644 >--- a/Tools/QueueStatusServer/handlers/patch.py >+++ b/Tools/QueueStatusServer/handlers/patch.py >@@ -29,13 +29,14 @@ > from google.appengine.ext import webapp > from google.appengine.ext.webapp import template > >+from model.taillog import TailLog > from model.queuestatus import QueueStatus > > > class Patch(webapp.RequestHandler): > def get(self, attachment_id_string, queue_name=None): > attachment_id = int(attachment_id_string) >- statuses = QueueStatus.all().filter("active_patch_id =", attachment_id).order("-date") >+ statuses = QueueStatus.all().filter("active_patch_id =", attachment_id).order("date") > > bug_id = None > queue_status = {} >@@ -51,4 +52,7 @@ class Patch(webapp.RequestHandler): > "bug_id" : bug_id, > "queue_status" : queue_status, > } >+ tail_log = TailLog.lookup(queue_name, attachment_id) if queue_name else None >+ if tail_log: >+ template_values["tail_log_token"] = tail_log.token > self.response.out.write(template.render("templates/patch.html", template_values)) >diff --git a/Tools/QueueStatusServer/handlers/releaselock.py b/Tools/QueueStatusServer/handlers/releaselock.py >index ebe14942aa89fcf1d01da5901be85d4669af7bfc..ac05b6753eabbc0f264744e915e8fbe72c2a2a6d 100644 >--- a/Tools/QueueStatusServer/handlers/releaselock.py >+++ b/Tools/QueueStatusServer/handlers/releaselock.py >@@ -27,6 +27,7 @@ from google.appengine.ext.webapp import template > from config.queues import work_item_lock_timeout > from handlers.updatebase import UpdateBase > from model.queues import Queue >+from model.taillog import TailLog > > > class ReleaseLock(UpdateBase): >@@ -43,6 +44,7 @@ class ReleaseLock(UpdateBase): > > attachment_id = self._int_from_request("attachment_id") > queue.active_work_items().expire_item(attachment_id) >+ TailLog.expire(queue_name, attachment_id) > > # ReleaseLock is used when a queue neither succeeded nor failed, so it silently releases the patch. > # Let's try other patches before retrying this one, in the interest of fairness, and also because >diff --git a/Tools/QueueStatusServer/handlers/releasepatch.py b/Tools/QueueStatusServer/handlers/releasepatch.py >index bf16b47949f7ac6c343a4762c9de978034860060..e8abfa223bb56f2db0480a5491fba745ecc46861 100644 >--- a/Tools/QueueStatusServer/handlers/releasepatch.py >+++ b/Tools/QueueStatusServer/handlers/releasepatch.py >@@ -34,6 +34,7 @@ from loggers.recordpatchevent import RecordPatchEvent > from model.attachment import Attachment > from model.attachmentdata import AttachmentData > from model.queues import Queue >+from model.taillog import TailLog > > > class ReleasePatch(UpdateBase): >@@ -60,6 +61,7 @@ class ReleasePatch(UpdateBase): > # WorkItems and ActiveWorkItems. > > queue.work_items().remove_work_item(attachment_id) >+ TailLog.expire(queue_name, attachment_id) > RecordPatchEvent.stopped(attachment_id, queue_name, last_status.message) > > queue.active_work_items().expire_item(attachment_id) >diff --git a/Tools/QueueStatusServer/handlers/updatestatus.py b/Tools/QueueStatusServer/handlers/updatestatus.py >index 93a3a9b81c879d73d2380f150a6fd4f5b73fea1e..bbc1fb8bd0007c75fbfd37b12e6ebd8b3ef0eba2 100644 >--- a/Tools/QueueStatusServer/handlers/updatestatus.py >+++ b/Tools/QueueStatusServer/handlers/updatestatus.py >@@ -35,6 +35,7 @@ from loggers.recordbotevent import RecordBotEvent > from loggers.recordpatchevent import RecordPatchEvent > from model.attachment import Attachment > from model.queuestatus import QueueStatus >+from model.taillog import TailLog > > > class UpdateStatus(UpdateBase): >@@ -63,7 +64,16 @@ class UpdateStatus(UpdateBase): > > def post(self): > queue_status = self._queue_status_from_request() >+ tail_log = None >+ if queue_status.queue_name and queue_status.active_patch_id: >+ tail_log = TailLog.lookup_or_create(queue_status.queue_name, queue_status.active_patch_id) >+ if tail_log and bool(self.request.get("is_transient")): >+ tail_log.write_message(queue_status.message, queue_status.bot_id, is_transient=True) >+ self.response.out.write("-1") >+ return > queue_status.put() >+ if tail_log: >+ tail_log.write_message(queue_status.message, queue_status.bot_id, time=queue_status.date, results_file_id=(queue_status.key().id() if queue_status.results_file else None)) > RecordBotEvent.record_activity(queue_status.queue_name, queue_status.bot_id) > if queue_status.active_patch_id: > RecordPatchEvent.updated(queue_status.active_patch_id, queue_status.queue_name, queue_status.message, queue_status.bot_id) >diff --git a/Tools/QueueStatusServer/index.yaml b/Tools/QueueStatusServer/index.yaml >index 565b3620b6f2ce07261c6540effc047982ebae62..ec9209d75ef442ee4b896bb36ec44c015d030ebd 100644 >--- a/Tools/QueueStatusServer/index.yaml >+++ b/Tools/QueueStatusServer/index.yaml >@@ -17,7 +17,6 @@ indexes: > properties: > - name: active_patch_id > - name: date >- direction: desc > > - kind: QueueStatus > properties: >diff --git a/Tools/QueueStatusServer/model/taillog.py b/Tools/QueueStatusServer/model/taillog.py >new file mode 100644 >index 0000000000000000000000000000000000000000..86db7430f1ac01665b141be01c8515a82e7633a1 >--- /dev/null >+++ b/Tools/QueueStatusServer/model/taillog.py >@@ -0,0 +1,83 @@ >+# Copyright (C) 2018 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: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. 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. >+# >+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 json >+ >+from datetime import datetime >+from google.appengine.api import channel >+from google.appengine.api import memcache >+ >+ >+class TailLog(object): >+ _MEMCACHE_PREFIX = "tail-log" >+ >+ def __init__(self, key, token): >+ self._key = key >+ self.token = token >+ >+ @classmethod >+ def lookup_or_create(cls, queue_name, attachment_id): >+ key = cls._generate_key(queue_name, attachment_id) >+ token = memcache.get(key) >+ if token: >+ return cls(key, token) >+ # Three hours should be a reasonable amount of time to build and test a WebKit change. >+ token = channel.create_channel(key, duration_minutes=3 * 60) >+ assert(bool(token)) >+ memcache.set(key, token) >+ return cls(key, token) >+ >+ @classmethod >+ def lookup(cls, queue_name, attachment_id): >+ key = cls._generate_key(queue_name, attachment_id) >+ token = memcache.get(key) >+ if token: >+ return cls(key, token) >+ return None >+ >+ @classmethod >+ def expire(cls, queue_name, attachment_id): >+ # Deletion is a best effort operation. Memcache may be unavailable or the token may have >+ # been evicted due to memory pressure or an explicit flushing of the cache. >+ memcache.delete(cls._generate_key(queue_name, attachment_id)) >+ >+ def _is_valid(self): >+ return memcache.get(self._key) is not None >+ >+ def write_message(self, message, bot_id, time=datetime.now(), results_file_id=None, is_transient=False): >+ if not self._is_valid(): >+ return False >+ message_dict = { >+ "message": message, >+ "timestamp": time, >+ "bot-id": bot_id, >+ "is-transient": bool(is_transient), >+ } >+ if results_file_id: >+ message_dict["results-file-id"] = results_file_id >+ dthandler = lambda obj: obj.isoformat() + "Z" if isinstance(obj, datetime) or isinstance(obj, datetime.date) else None >+ channel.send_message(self._key, json.dumps(message_dict, default=dthandler)) >+ return True >+ >+ @classmethod >+ def _generate_key(cls, queue_name, attachment_id): >+ return "-".join([cls._MEMCACHE_PREFIX, queue_name, str(attachment_id)]) >diff --git a/Tools/QueueStatusServer/stylesheets/common.css b/Tools/QueueStatusServer/stylesheets/common.css >index 33f7c35347cede8f0653b8068ec77739ec7278dc..7f63f464acd7c186b39a2bba6dbf847757bd78a3 100644 >--- a/Tools/QueueStatusServer/stylesheets/common.css >+++ b/Tools/QueueStatusServer/stylesheets/common.css >@@ -45,6 +45,10 @@ tr:hover, li:hover { > background-color: #EEE; > } > >+.code { >+ font-family: monospace; >+} >+ > .status-group { > font-size: 90%; > } >diff --git a/Tools/QueueStatusServer/templates/patch.html b/Tools/QueueStatusServer/templates/patch.html >index 0af28dedf0da3f80132b52579b55746f96e5a37a..20e42300182e742ba1445ff315ecff6bb6e6d008 100644 >--- a/Tools/QueueStatusServer/templates/patch.html >+++ b/Tools/QueueStatusServer/templates/patch.html >@@ -2,7 +2,93 @@ > <html> > <head> > <title>Patch Status</title> >-<link type="text/css" rel="stylesheet" href="/stylesheets/common.css" /> >+<link rel="stylesheet" href="/stylesheets/common.css"> >+<script src="/_ah/channel/jsapi"></script> >+<script> >+const tailLogToken = "{{ tail_log_token }}"; >+var statusLogElement; >+var channel; >+ >+function buildLogLine(messageData) >+{ >+ let statusItem = document.createElement("li"); >+ >+ let statusBotId = document.createElement("span"); >+ statusItem.appendChild(statusBotId); >+ statusBotId.className = "status-bot" >+ statusBotId.textContent = messageData["bot-id"]; >+ statusBotId.innerHTML += " "; >+ >+ let statusMessage = document.createElement("span"); >+ statusItem.appendChild(statusMessage); >+ statusMessage.className = "status-message"; >+ if (messageData["is-transient"]) >+ statusMessage.classList.add("code"); >+ statusMessage.textContent = messageData["message"]; >+ >+ if (messageData["results-file-id"]) { >+ let statusResult = document.createElement("span"); >+ statusItem.appendChild(statusResult); >+ statusResult.className = "status-results"; >+ >+ statusResult.appendChild(document.createTextNode("[")); >+ let statusResultLink = document.createElement("a"); >+ statusResult.appendChild(statusResultLink); >+ statusResultLink.href = "/results/" + messageData["results-file-id"]; >+ statusResultLink.textContent = "results"; >+ statusResult.appendChild(document.createTextNode("]")); >+ } >+ >+ let statusDate = document.createElement("span"); >+ statusItem.appendChild(statusDate); >+ statusDate.className = "status-date"; >+ statusDate.textContent = new Date(messageData["timestamp"]).toLocaleString(); >+ >+ return statusItem; >+} >+ >+function buildErrorLogLine(message) >+{ >+ let statusItem = document.createElement("li"); >+ statusItem.textContent = message; >+ statusItem.innerHTML += " "; >+ >+ statusItem.appendChild(document.createTextNode("[")) >+ let reloadLink = document.createElement("a"); >+ statusItem.appendChild(reloadLink); >+ reloadLink.href="javascript:reload()"; >+ reloadLink.textContent = "reload"; >+ statusItem.appendChild(document.createTextNode("]")) >+ >+ return statusItem; >+} >+ >+function reload() >+{ >+ if (confirm("Are you sure you want to reload?\n\nAll monospace-typed messages will be lost.")) { >+ window.location.reload(); >+ } >+} >+ >+function tailLog() >+{ >+ if (!tailLogToken) >+ return; >+ channel = new goog.appengine.Channel(tailLogToken); >+ let handlers = { >+ "onmessage": (messageEvent) => { statusLogElement.appendChild(buildLogLine(JSON.parse(messageEvent.data))); }, >+ "onclose": () => { statusLogElement.appendChild(buildErrorLogLine("Disconnected.")); }, >+ "onerror": (errorEvent) => { statusLogElement.appendChild(buildErrorLogLine(`Error: ${errorEvent.description} (${errorEvent.code}).`)); }, >+ } >+ channel.open(handlers); >+} >+ >+window.onload = function () >+{ >+ statusLogElement = document.getElementById("status-log"); >+ tailLog() >+} >+</script> > </head> > <body> > <h1> >@@ -14,9 +100,9 @@ > {% for queue_name, statuses in queue_status %} > <div class="status-details"> > <h2>{{ queue_name }}</h2> >- <ul>{% for status in statuses %} >+ <ul id="status-log">{% for status in statuses %} > <li> >- <span class="status-bot">{{ status.bot_id }} </span> >+ <span class="status-bot">{{ status.bot_id }} </span> > <span class="status-message">{{ status.message|force_escape|urlize|webkit_linkify|safe }}</span>{% if status.results_file %} > <span class="status-results">[{{ status.key.id|results_link|safe }}]</span>{% endif %} > <span class="status-date">{{ status.date|timesince }} ago</span> >diff --git a/Tools/QueueStatusServer/templates/updatestatus.html b/Tools/QueueStatusServer/templates/updatestatus.html >index 0f98ba4dba2abb45aeababa61a4f9f2ce5663136..1d4657d84d56ce18d596925c4acc6fc1e507c778 100644 >--- a/Tools/QueueStatusServer/templates/updatestatus.html >+++ b/Tools/QueueStatusServer/templates/updatestatus.html >@@ -12,9 +12,13 @@ Update status for a queue: <input name="queue_name"> > Active Patch Id: > <input name="patch_id"> > </div> >+ <div> >+ Is transient (only show in tail log): >+ <input name="is_transient" type="checkbox" value="1"> >+ </div> > <div> >- Status Text:<br> >- <textarea name="status" rows="3" cols="60"></textarea> >+ Status Text:<br> >+ <textarea name="status" rows="3" cols="60"></textarea> > </div> > <div>Results file: <input type="file" name="results_file"></div> > <div><input type="submit" value="Add Status"></div> >diff --git a/Tools/Scripts/webkitpy/common/net/statusserver.py b/Tools/Scripts/webkitpy/common/net/statusserver.py >index 9aa827461f987f3879fd0553ec886cf9166723d5..7fff89e299af1ff04974dd4871a7d235bcd0209d 100644 >--- a/Tools/Scripts/webkitpy/common/net/statusserver.py >+++ b/Tools/Scripts/webkitpy/common/net/statusserver.py >@@ -98,7 +98,7 @@ class StatusServer: > _log.warn("Attempted to set %s to value exceeding %s characters, truncating." % (field_name, limit)) > self._browser[field_name] = value[:limit] > >- def _post_status_to_server(self, queue_name, status, patch, results_file): >+ def _post_status_to_server(self, queue_name, status, patch, results_file, is_transient): > if results_file: > # We might need to re-wind the file if we've already tried to post it. > results_file.seek(0) >@@ -107,6 +107,7 @@ class StatusServer: > self._browser.open(update_status_url) > self._browser.select_form(name="update_status") > self._browser["queue_name"] = queue_name >+ self._browser.find_control('is_transient').items[0].selected = bool(is_transient) > if self.bot_id: > self._browser["bot_id"] = self.bot_id > self._add_patch(patch) >@@ -193,9 +194,10 @@ class StatusServer: > _log.info("Recording work items: %s for %s" % (high_priority_work_items + work_items, queue_name)) > return NetworkTransaction().run(lambda: self._post_work_items_to_server(queue_name, high_priority_work_items, work_items)) > >- def update_status(self, queue_name, status, patch=None, results_file=None): >+ # Set is_transient to True for status updates that can be discarded at any time. Otherwise, they will be persisted to the data store. >+ def update_status(self, queue_name, status, patch=None, results_file=None, is_transient=False): > _log.info(status) >- return NetworkTransaction().run(lambda: self._post_status_to_server(queue_name, status, patch, results_file)) >+ return NetworkTransaction().run(lambda: self._post_status_to_server(queue_name, status, patch, results_file, is_transient)) > > def update_svn_revision(self, svn_revision_number, broken_bot): > _log.info("SVN revision: %s broke %s" % (svn_revision_number, broken_bot)) >@@ -237,3 +239,19 @@ class StatusServer: > def svn_revision(self, svn_revision_number): > svn_revision_url = '{}/svn-revision/{}'.format(self._server_url(), svn_revision_number) > return self._fetch_url(svn_revision_url) >+ >+ >+class WritableStatusServerStatusFileObject(object): >+ def __init__(self, status_server, queue_name, patch): >+ self._status_server = status_server >+ self._queue_name = queue_name >+ self._patch = patch >+ >+ # For unit testing. >+ def __eq__(self, other): >+ return self._status_server == other._status_server and self._queue_name == other._queue_name and self._patch == other._patch >+ >+ # Callers should pass an already encoded string for writing. >+ def write(self, bytes): >+ if bytes: >+ self._status_server.update_status(self._queue_name, bytes, patch=self._patch, is_transient=True) >diff --git a/Tools/Scripts/webkitpy/common/net/statusserver_mock.py b/Tools/Scripts/webkitpy/common/net/statusserver_mock.py >index 9c90ab25a6b757b62d7eb6bd57d0c79f2c5952d9..7b34ff294f4c0e176db7399109c4054fa7e2e6dd 100644 >--- a/Tools/Scripts/webkitpy/common/net/statusserver_mock.py >+++ b/Tools/Scripts/webkitpy/common/net/statusserver_mock.py >@@ -68,8 +68,8 @@ class MockStatusServer(object): > def submit_to_ews(self, patch_id): > _log.info("MOCK: submit_to_ews: %s" % (patch_id)) > >- def update_status(self, queue_name, status, patch=None, results_file=None): >- _log.info("MOCK: update_status: %s %s" % (queue_name, status)) >+ def update_status(self, queue_name, status, patch=None, results_file=None, is_transient=False): >+ _log.info('MOCK: update_status: {}{} {}'.format(('(transient) ' if is_transient else ''), queue_name, status)) > return 187 > > def update_svn_revision(self, svn_revision, broken_bot): >diff --git a/Tools/Scripts/webkitpy/common/net/web_mock.py b/Tools/Scripts/webkitpy/common/net/web_mock.py >index b41059989ace15296a9a98e3e5a569629041f556..ed8b285e49dff9f112f19c4f2126fc76ff2500ac 100644 >--- a/Tools/Scripts/webkitpy/common/net/web_mock.py >+++ b/Tools/Scripts/webkitpy/common/net/web_mock.py >@@ -29,6 +29,9 @@ > import StringIO > import urllib2 > >+from webkitpy.thirdparty.mock import Mock >+ >+ > class MockWeb(object): > def __init__(self, urls=None, responses=[]): > self.urls = urls or {} >@@ -83,3 +86,8 @@ class MockBrowser(object): > > def set_handle_robots(self, value): > pass >+ >+ def find_control(self, name): >+ control = Mock() >+ control.items = [Mock()] >+ return control >diff --git a/Tools/Scripts/webkitpy/common/system/executive.py b/Tools/Scripts/webkitpy/common/system/executive.py >index 82a01ca8205675935a3531950abefd3180dad5e9..10016a4be97cbed5340936118037844918b08f39 100644 >--- a/Tools/Scripts/webkitpy/common/system/executive.py >+++ b/Tools/Scripts/webkitpy/common/system/executive.py >@@ -125,16 +125,22 @@ class Executive(AbstractExecutive): > # like "build-webkit" where we want to display to the user that we're building > # but still have the output to stuff into a log file. > def run_and_throw_if_fail(self, args, quiet=False, decode_output=True, **kwargs): >+ teed_stdout = sys.stdout >+ if quiet: >+ dev_null = open(os.devnull, 'w') # FIXME: Does this need an encoding? >+ teed_stdout = dev_null >+ try: >+ command_output = self.run_with_teed_output_and_throw_if_fail(args, teed_stdout, decode_output=decode_output, **kwargs) >+ finally: >+ if quiet: >+ dev_null.close() >+ return command_output >+ >+ def run_with_teed_output_and_throw_if_fail(self, args, teed_stdout, decode_output=True, **kwargs): > # Cache the child's output locally so it can be used for error reports. > child_out_file = StringIO.StringIO() >- tee_stdout = sys.stdout >- if quiet: >- dev_null = open(os.devnull, "w") # FIXME: Does this need an encoding? >- tee_stdout = dev_null >- child_stdout = Tee(child_out_file, tee_stdout) >+ child_stdout = Tee(child_out_file, teed_stdout) > exit_code = self._run_command_with_teed_output(args, child_stdout, **kwargs) >- if quiet: >- dev_null.close() > > child_output = child_out_file.getvalue() > child_out_file.close() >diff --git a/Tools/Scripts/webkitpy/common/system/executive_mock.py b/Tools/Scripts/webkitpy/common/system/executive_mock.py >index bc78ffe5a2d8b3b2a3abcd0d99a33a39d4da4167..ab09412821e251405b4c086477271880997321bd 100644 >--- a/Tools/Scripts/webkitpy/common/system/executive_mock.py >+++ b/Tools/Scripts/webkitpy/common/system/executive_mock.py >@@ -99,6 +99,16 @@ class MockExecutive(object): > raise ScriptError("Exception for %s" % args, output="MOCK command output") > return "MOCK output of child process" > >+ def run_with_teed_output_and_throw_if_fail(self, args, tee_stdout, quiet=False, cwd=None, env=None): >+ if self._should_log: >+ env_string = "" >+ if env: >+ env_string = ", env=%s" % env >+ _log.info("MOCK run_with_teed_output_and_throw_if_fail: %s, cwd=%s%s" % (args, cwd, env_string)) >+ if self._should_throw_when_run.intersection(args): >+ raise ScriptError("Exception for %s" % args, output="MOCK command output") >+ return "MOCK output of child process" >+ > def command_for_printing(self, args): > string_args = map(unicode, args) > return " ".join(string_args) >diff --git a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py >index 6bf7924e75e3705173f02c59848aa5102a49147d..3a643dc953e639ec948d734102d692d8e7d47076 100644 >--- a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py >@@ -49,7 +49,7 @@ class MockCommitQueue(CommitQueueTaskDelegate): > self._failure_status_id = 0 > self._flaky_tests = [] > >- def run_command(self, command): >+ def run_command(self, command, patch): > _log.info("run_webkit_patch: %s" % command) > if self._error_plan: > error = self._error_plan.pop(0) >@@ -99,10 +99,10 @@ class FailingTestCommitQueue(MockCommitQueue): > self._test_run_counter = -1 # Special value to indicate tests have never been run. > self._test_failure_plan = test_failure_plan > >- def run_command(self, command): >+ def run_command(self, command, patch): > if command[0] == "build-and-test": > self._test_run_counter += 1 >- MockCommitQueue.run_command(self, command) >+ MockCommitQueue.run_command(self, command, patch) > > def _mock_test_result(self, testname): > return test_results.TestResult(testname, [test_failures.FailureTextMismatch()]) >@@ -130,8 +130,8 @@ class MockSimpleTestPlanCommitQueue(MockCommitQueue): > self._clean_test_results = [clean_test_failures] > self._current_test_results = [] > >- def run_command(self, command): >- MockCommitQueue.run_command(self, command) >+ def run_command(self, command, patch): >+ MockCommitQueue.run_command(self, command, patch) > if command[0] == "build-and-test": > if "--no-clean" in command: > self._current_test_results = self._patch_test_results.pop(0) >diff --git a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py b/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py >index d53e34b603535eee53f256233821aa444bb2cd00..1a6760dff8f6c338f0ef45da809092101c2d882f 100644 >--- a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py >+++ b/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py >@@ -55,7 +55,7 @@ class PatchAnalysisTaskDelegate(object): > def parent_command(self): > raise NotImplementedError("subclasses must implement") > >- def run_command(self, command): >+ def run_command(self, command, patch): > raise NotImplementedError("subclasses must implement") > > def command_passed(self, message, patch): >@@ -97,7 +97,7 @@ class PatchAnalysisTask(object): > if not self.validate(): > raise PatchIsNotValid(self._patch, self.error) > try: >- self._delegate.run_command(command) >+ self._delegate.run_command(command, patch=self._patch) > self._delegate.command_passed(success_message, patch=self._patch) > return True > except ScriptError as e: >diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >index 74ffe8e81cc1fc069aa032b90f0711aad1c4858f..d95b54c699585e14cc45f85d6fd71d07ca08a0dd 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >@@ -130,8 +130,8 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > def parent_command(self): > return self.name > >- def run_command(self, command): >- self.run_webkit_patch(command + [self._deprecated_port.flag()] + (['--architecture=%s' % self._port.architecture()] if self._port.architecture() and self._port.did_override_architecture else [])) >+ def run_command(self, command, patch): >+ self.run_webkit_patch(command + [self._deprecated_port.flag()] + (['--architecture=%s' % self._port.architecture()] if self._port.architecture() and self._port.did_override_architecture else []), patch=patch) > > def command_passed(self, message, patch): > self._update_status(message, patch=patch) >diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py >index bb2b071901396f3add61d1b3258fa366461c7e97..1e1d4f1dd17c513f16a8d83e0ed8b48d97ae19e6 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py >@@ -139,13 +139,19 @@ class EarlyWarningSystemTest(QueuesTest): > string_replacements['status_server_fetch_line'] = status_server_fetch_line > > if ews.should_build: >- build_line = "%(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=%(build_style)s --group=%(group)s --port=%(port)s%(architecture)s\nMOCK: update_status: %(name)s Built patch\n" % string_replacements >+ build_line = """%(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=%(build_style)s --group=%(group)s --port=%(port)s%(architecture)s >+MOCK: update_status: (transient) %(name)s Running: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=%(build_style)s --group=%(group)s --port=%(port)s%(architecture)s >+MOCK: update_status: %(name)s Built patch >+""" % string_replacements > else: > build_line = "" > string_replacements['build_line'] = build_line > > if ews.run_tests: >- run_tests_line = "%(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=%(build_style)s --group=%(group)s --port=%(port)s%(architecture)s\nMOCK: update_status: %(name)s Passed tests\n" % string_replacements >+ run_tests_line = """%(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=%(build_style)s --group=%(group)s --port=%(port)s%(architecture)s >+MOCK: update_status: (transient) %(name)s Running: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=%(build_style)s --group=%(group)s --port=%(port)s%(architecture)s >+MOCK: update_status: %(name)s Passed tests >+""" % string_replacements > else: > run_tests_line = "" > string_replacements['run_tests_line'] = run_tests_line >@@ -160,12 +166,16 @@ class EarlyWarningSystemTest(QueuesTest): > "begin_work_queue": self._default_begin_work_queue_logs(ews.name), > "process_work_item": """MOCK: update_status: %(name)s Started processing patch > %(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com clean --port=%(port)s%(architecture)s >+MOCK: update_status: (transient) %(name)s Running: webkit-patch --status-host=example.com clean --port=%(port)s%(architecture)s > MOCK: update_status: %(name)s Cleaned working directory > %(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com update --port=%(port)s%(architecture)s >+MOCK: update_status: (transient) %(name)s Running: webkit-patch --status-host=example.com update --port=%(port)s%(architecture)s > MOCK: update_status: %(name)s Updated working directory > %(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive %(patch_id)s --port=%(port)s%(architecture)s >+MOCK: update_status: (transient) %(name)s Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive %(patch_id)s --port=%(port)s%(architecture)s > MOCK: update_status: %(name)s Applied patch > %(status_server_fetch_line)sRunning: webkit-patch --status-host=example.com check-patch-relevance --quiet --group=%(group)s --port=%(port)s%(architecture)s >+MOCK: update_status: (transient) %(name)s Running: webkit-patch --status-host=example.com check-patch-relevance --quiet --group=%(group)s --port=%(port)s%(architecture)s > MOCK: update_status: %(name)s Checked relevance of patch > %(build_line)s%(run_tests_line)s%(result_lines)s""" % string_replacements, > "handle_unexpected_error": "Mock error message\n", >diff --git a/Tools/Scripts/webkitpy/tool/commands/perfalizer.py b/Tools/Scripts/webkitpy/tool/commands/perfalizer.py >index cff98d5f5a2d79c8e4ca0ff3c3de115fa512fd02..95e2eed6204096a8a15a2bd5966d46c083372047 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/perfalizer.py >+++ b/Tools/Scripts/webkitpy/tool/commands/perfalizer.py >@@ -141,7 +141,7 @@ class PerfalizerTask(PatchAnalysisTask): > '--output-json-path', self._json_path(), '--description', description] > return self._tool.executive.run_and_throw_if_fail(perf_test_runner_args, cwd=self._tool.scm().checkout_root) > >- def run_command(self, command): >+ def run_command(self, command, patch): > self.run_webkit_patch(command) > > def command_passed(self, message, patch): >diff --git a/Tools/Scripts/webkitpy/tool/commands/queues.py b/Tools/Scripts/webkitpy/tool/commands/queues.py >index 2b33f980e9c60f0cd8063d4fcfb459a4dea56a3f..4848d2a48d7c9d50af2d80dec474d51f81bb51fc 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/queues.py >+++ b/Tools/Scripts/webkitpy/tool/commands/queues.py >@@ -41,6 +41,7 @@ from StringIO import StringIO > from webkitpy.common.config.committervalidator import CommitterValidator > from webkitpy.common.config.ports import DeprecatedPort > from webkitpy.common.net.bugzilla import Bugzilla, Attachment >+from webkitpy.common.net.statusserver import WritableStatusServerStatusFileObject > from webkitpy.common.system.executive import ScriptError > from webkitpy.tool.bot.botinfo import BotInfo > from webkitpy.tool.bot.commitqueuetask import CommitQueueTask, CommitQueueTaskDelegate >@@ -83,7 +84,7 @@ class AbstractQueue(Command, QueueEngineDelegate): > traceback.print_exc() > _log.error("Failed to CC watchers.") > >- def run_webkit_patch(self, args): >+ def run_webkit_patch(self, args, patch): > 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 >@@ -101,8 +102,12 @@ class AbstractQueue(Command, QueueEngineDelegate): > try: > args_for_printing = list(webkit_patch_args) > args_for_printing[0] = 'webkit-patch' # Printing our path for each log is redundant. >- _log.info("Running: %s" % self._tool.executive.command_for_printing(args_for_printing)) >- command_output = self._tool.executive.run_command(webkit_patch_args, cwd=self._tool.scm().checkout_root) >+ message = "Running: %s" % self._tool.executive.command_for_printing(args_for_printing) >+ _log.info(message) >+ >+ teed_stdout = WritableStatusServerStatusFileObject(self._tool.status_server, self.name, patch) >+ teed_stdout.write(message) >+ command_output = self._tool.executive.run_with_teed_output_and_throw_if_fail(webkit_patch_args, teed_stdout, cwd=self._tool.scm().checkout_root) > except ScriptError as e: > # Make sure the whole output gets printed if the command failed. > _log.error(e.message_with_output(output_limit=None)) >@@ -391,8 +396,8 @@ class CommitQueue(PatchProcessingQueue, StepSequenceErrorHandler, CommitQueueTas > > # CommitQueueTaskDelegate methods > >- def run_command(self, command): >- self.run_webkit_patch(command + [self._deprecated_port.flag()]) >+ def run_command(self, command, patch): >+ self.run_webkit_patch(command + [self._deprecated_port.flag()], patch=patch) > > def command_passed(self, message, patch): > self._update_status(message, patch=patch) >@@ -509,8 +514,8 @@ class StyleQueue(AbstractReviewQueue, StyleQueueTaskDelegate): > > # StyleQueueTaskDelegate methods > >- def run_command(self, command): >- self.run_webkit_patch(command) >+ def run_command(self, command, patch): >+ self.run_webkit_patch(command, patch=patch) > > def command_passed(self, message, patch): > self._update_status(message, patch=patch) >diff --git a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py >index ab64143ccfcd4f374b1211663e780db92e49e11c..a954690d341c4d833858b6b4acbe493f0bc7d3a8 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py >@@ -33,6 +33,7 @@ from webkitpy.common.checkout.scm import CheckoutNeedsUpdate > from webkitpy.common.checkout.scm.scm_mock import MockSCM > from webkitpy.common.net.layouttestresults import LayoutTestResults > from webkitpy.common.net.bugzilla import Attachment >+from webkitpy.common.net.statusserver import WritableStatusServerStatusFileObject > from webkitpy.common.system.outputcapture import OutputCapture > from webkitpy.layout_tests.models import test_results > from webkitpy.layout_tests.models import test_failures >@@ -84,12 +85,15 @@ class AbstractQueueTest(CommandsTest): > queue._options = Mock() > queue._options.port = port > >- queue.run_webkit_patch(run_args) >+ patch = Attachment({'id': 1}, None) >+ teed_stdout = WritableStatusServerStatusFileObject(tool.status_server, queue.name, patch) >+ >+ queue.run_webkit_patch(run_args, patch=patch) > 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_command.assert_called_with(expected_run_args, cwd='/mock-checkout') >+ tool.executive.run_with_teed_output_and_throw_if_fail.assert_called_with(expected_run_args, teed_stdout, cwd='/mock-checkout') > > def test_run_webkit_patch(self): > self._assert_run_webkit_patch([1]) >@@ -210,11 +214,11 @@ class SecondThoughtsCommitQueue(TestCommitQueue): > self._reject_patch = False > TestCommitQueue.__init__(self, tool) > >- def run_command(self, command): >+ def run_command(self, command, patch): > # 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) >+ return CommitQueue.run_command(self, command, patch) > > def refetch_patch(self, patch): > if not self._reject_patch: >@@ -246,18 +250,25 @@ class CommitQueueTest(QueuesTest): > expected_logs = { > "begin_work_queue": self._default_begin_work_queue_logs("commit-queue"), > "process_work_item": """Running: webkit-patch --status-host=example.com clean --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com clean --port=mac > MOCK: update_status: commit-queue Cleaned working directory > Running: webkit-patch --status-host=example.com update --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com update --port=mac > MOCK: update_status: commit-queue Updated working directory > Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 --port=mac > MOCK: update_status: commit-queue Applied patch > Running: webkit-patch --status-host=example.com validate-changelog --check-oops --non-interactive 10000 --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com validate-changelog --check-oops --non-interactive 10000 --port=mac > MOCK: update_status: commit-queue ChangeLog validated > Running: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=release --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=release --port=mac > MOCK: update_status: commit-queue Built patch > Running: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=release --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=release --port=mac > MOCK: update_status: commit-queue Passed tests > Running: webkit-patch --status-host=example.com land-attachment --force-clean --non-interactive --parent-command=commit-queue 10000 --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com land-attachment --force-clean --non-interactive --parent-command=commit-queue 10000 --port=mac > MOCK: update_status: commit-queue Landed patch > MOCK: update_status: commit-queue Pass > MOCK: release_work_item: commit-queue 10000 >@@ -283,7 +294,7 @@ MOCK: release_work_item: commit-queue 10000 > } > queue = CommitQueue() > >- def mock_run_webkit_patch(command): >+ def mock_run_webkit_patch(command, patch): > if command[0] == 'clean' or command[0] == 'update': > # We want cleaning to succeed so we can error out on a step > # that causes the commit-queue to reject the patch. >@@ -315,7 +326,7 @@ MOCK: release_work_item: commit-queue 10000 > > queue = CommitQueue(MockCommitQueueTask) > >- def mock_run_webkit_patch(command): >+ def mock_run_webkit_patch(command, patch): > if command[0] == 'clean' or command[0] == 'update': > # We want cleaning to succeed so we can error out on a step > # that causes the commit-queue to reject the patch. >@@ -333,18 +344,25 @@ MOCK: release_work_item: commit-queue 10000 > expected_logs = { > "begin_work_queue": self._default_begin_work_queue_logs("commit-queue"), > "process_work_item": """Running: webkit-patch --status-host=example.com clean --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com clean --port=%(port)s > MOCK: update_status: commit-queue Cleaned working directory > Running: webkit-patch --status-host=example.com update --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com update --port=%(port)s > MOCK: update_status: commit-queue Updated working directory > Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 --port=%(port)s > MOCK: update_status: commit-queue Applied patch > Running: webkit-patch --status-host=example.com validate-changelog --check-oops --non-interactive 10000 --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com validate-changelog --check-oops --non-interactive 10000 --port=%(port)s > MOCK: update_status: commit-queue ChangeLog validated > Running: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=release --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=release --port=%(port)s > MOCK: update_status: commit-queue Built patch > Running: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=release --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=release --port=%(port)s > MOCK: update_status: commit-queue Passed tests > Running: webkit-patch --status-host=example.com land-attachment --force-clean --non-interactive --parent-command=commit-queue 10000 --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com land-attachment --force-clean --non-interactive --parent-command=commit-queue 10000 --port=%(port)s > MOCK: update_status: commit-queue Landed patch > MOCK: update_status: commit-queue Pass > MOCK: release_work_item: commit-queue 10000 >@@ -362,14 +380,19 @@ MOCK: release_work_item: commit-queue 10000 > expected_logs = { > "begin_work_queue": self._default_begin_work_queue_logs("commit-queue"), > "process_work_item": """Running: webkit-patch --status-host=example.com clean --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com clean --port=%(port)s > MOCK: update_status: commit-queue Cleaned working directory > Running: webkit-patch --status-host=example.com update --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com update --port=%(port)s > MOCK: update_status: commit-queue Updated working directory > Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10005 --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10005 --port=%(port)s > MOCK: update_status: commit-queue Applied patch > Running: webkit-patch --status-host=example.com validate-changelog --check-oops --non-interactive 10005 --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com validate-changelog --check-oops --non-interactive 10005 --port=%(port)s > MOCK: update_status: commit-queue ChangeLog validated > Running: webkit-patch --status-host=example.com land-attachment --force-clean --non-interactive --parent-command=commit-queue 10005 --port=%(port)s >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com land-attachment --force-clean --non-interactive --parent-command=commit-queue 10005 --port=%(port)s > MOCK: update_status: commit-queue Landed patch > MOCK: update_status: commit-queue Pass > MOCK: release_work_item: commit-queue 10005 >@@ -415,6 +438,7 @@ MOCK: update_status: commit-queue Tests passed, but commit failed (checkout out > queue._options = Mock() > queue._options.port = None > expected_logs = """Running: webkit-patch --status-host=example.com clean --port=mac >+MOCK: update_status: (transient) commit-queue Running: webkit-patch --status-host=example.com clean --port=mac > MOCK: update_status: commit-queue Cleaned working directory > MOCK: update_status: commit-queue Error: commit-queue did not process patch. Reason: Patch is obsolete. > MOCK: release_work_item: commit-queue 10000 >@@ -478,14 +502,19 @@ class StyleQueueTest(QueuesTest): > "begin_work_queue": self._default_begin_work_queue_logs("style-queue"), > "process_work_item": """MOCK: update_status: style-queue Started processing patch > Running: webkit-patch --status-host=example.com clean >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com clean > MOCK: update_status: style-queue Cleaned working directory > Running: webkit-patch --status-host=example.com update >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com update > MOCK: update_status: style-queue Updated working directory > Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 > MOCK: update_status: style-queue Applied patch > Running: webkit-patch --status-host=example.com apply-watchlist-local 50000 >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com apply-watchlist-local 50000 > MOCK: update_status: style-queue Watchlist applied > Running: webkit-patch --status-host=example.com check-style-local --non-interactive --quiet >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com check-style-local --non-interactive --quiet > MOCK: update_status: style-queue Style checked > MOCK: update_status: style-queue Pass > MOCK: release_work_item: style-queue 10000 >@@ -501,17 +530,22 @@ MOCK: release_work_item: style-queue 10000 > "begin_work_queue": self._default_begin_work_queue_logs("style-queue"), > "process_work_item": """MOCK: update_status: style-queue Started processing patch > Running: webkit-patch --status-host=example.com clean >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com clean > MOCK: update_status: style-queue Cleaned working directory > Running: webkit-patch --status-host=example.com update >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com update > MOCK: update_status: style-queue Updated working directory > Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 > MOCK: update_status: style-queue Applied patch > Running: webkit-patch --status-host=example.com apply-watchlist-local 50000 >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com apply-watchlist-local 50000 > Exception for ['echo', '--status-host=example.com', 'apply-watchlist-local', 50000] > > MOCK command output > MOCK: update_status: style-queue Unabled to apply watchlist > Running: webkit-patch --status-host=example.com check-style-local --non-interactive --quiet >+MOCK: update_status: (transient) style-queue Running: webkit-patch --status-host=example.com check-style-local --non-interactive --quiet > MOCK: update_status: style-queue Style checked > MOCK: update_status: style-queue Pass > MOCK: release_work_item: style-queue 10000
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 186823
:
343111
|
343221
|
343580
|
343622
|
343648
| 343649