Diff
Modified: trunk/Tools/ChangeLog (212578 => 212579)
--- trunk/Tools/ChangeLog 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/ChangeLog 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,3 +1,136 @@
+2017-02-17 Srinivasan Vijayaraghavan <svijayaragha...@apple.com>
+
+ EWS should run _javascript_Core tests
+ https://bugs.webkit.org/show_bug.cgi?id=162458
+
+ Reviewed by Alexey Proskuryakov.
+
+ * QueueStatusServer/config/queues.py: Add jsc-ews queue.
+ * QueueStatusServer/model/queuestatus.py:
+ (QueueStatus.did_skip): Returns whether patch was skipped, based on status.
+ * QueueStatusServer/handlers/statusbubble.py:
+ (StatusBubble._should_show_bubble_for): Add logic to hide jsc-ews bubble if the patch does not touch jsc.
+ * Scripts/webkitpy/common/checkout/scm/scm_mock.py:
+ (MockSCM.__init__): Add _mockChangedFiles variable.
+ (MockSCM.changed_files): Change to use _mockChangedFiles variables.
+ * Scripts/webkitpy/common/config/ews.json: Add config info for JSC EWS.
+ * Scripts/webkitpy/common/config/ports.py: Add support for JSC EWS in Mac Port.
+ (DeprecatedPort._append_build_style_flag): Helper function to append build_style to a command.
+ (DeprecatedPort.build_jsc_command): Added - command to build JSC only (quicker than building all of WebKit).
+ (DeprecatedPort.run_javascriptcore_tests_command): Allow JSC EWS to only run JSC tests.
+ (MacPort.run_webkit_tests_command): Check for JSC.
+ * Scripts/webkitpy/common/config/ports_mock.py:
+ (MockPort.run_javascriptcore_tests_command): Add build_style argument.
+ * Scripts/webkitpy/common/config/ports_unittest.py:
+ (DeprecatedPortTest.test_mac_port): Add unit tests for build-jsc shell command.
+ * Scripts/webkitpy/common/net/abstracttestresults.py: Added.
+ (AbstractTestResults): Abstract superclass of JSCTestResults and JSCTestResults.
+ (AbstractTestResults.test_results): Stub.
+ (AbstractTestResults.failing_tests): Stub.
+ (AbstractTestResults.did_exceed_test_failure_limit): Stub.
+ * Scripts/webkitpy/common/net/jsctestresults.py: Added.
+ (JSCTestResults): Added.
+ (JSCTestResults.intersection): Return a JSCTestResults object with failures common to both input objects.
+ (JSCTestResults.results_from_string): Use json library to parse results.
+ (JSCTestResults.__init__): Initialize members.
+ (JSCTestResults.equals): This enables unit testing.
+ (JSCTestResults.is_subset): Checks if one set of failures is a subset of another.
+ (JSCTestResults.all_passed): Getter.
+ (JSCTestResults.failing_tests): Getter.
+ (JSCTestResults.did_exceed_test_failure_limit): Getter.
+ * Scripts/webkitpy/common/net/jsctestresults_unittest.py: Added.
+ (JSCTestResultsTest): Class to test JSCTestResults.
+ (JSCTestResultsTest.test_results_from_string): Tests parsing.
+ (JSCTestResultsTest.test_intersection_api_tests): Unit test for intersection() class method.
+ (JSCTestResultsTest.test_intersection_stress_tests): Unit test for intersection() class method.
+ (JSCTestResultsTest.test_intersection_general_case): Unit test for intersection() class method.
+ * Scripts/webkitpy/port/base.py:
+ (Port.jsc_results_directory): Returns the directory for the JSC test results JSON file.
+ * Scripts/webkitpy/tool/bot/earlywarningsystemtask.py:
+ (EarlyWarningSystemTask.run): Add check_patch_relevance step.
+ * Scripts/webkitpy/tool/bot/jscews_unittest.py: Added.
+ (MockPatchAnalysisTask): Mocked-out version of PatchAnalysisTask that doesn't run commands.
+ (MockPatchAnalysisTask.__init__): Sets attributes.
+ (MockPatchAnalysisTask._test): Override to not run command.
+ (MockPatchAnalysisTask._build_and_test_without_patch): Override to not run command.
+ (MockPatchAnalysisTask.validate): Assume mocked patch is valid for purposes of testing retry logic.
+ (MockPatchAnalysisTask.test_run_count): Specific to the mocked version, to test retry logic.
+ (MockJSCEarlyWarningSystem): Mocked-out version of AbstarctEarlyWarningSystem so we can provide test results.
+ (MockJSCEarlyWarningSystem.__init__): Sets attributes, also sets group to jsc.
+ (MockJSCEarlyWarningSystem.test_results): Returns test results provided by us, instead of using a JSON reader.
+ (JSCEarlyWarningSystemTest): Class to test retry logic in below situations.
+ (JSCEarlyWarningSystemTest._create_task): Helper function to abstract out common code.
+ (JSCEarlyWarningSystemTest._results_indicate_all_passed): False if input is None or has failures, else True.
+ (JSCEarlyWarningSystemTest.test_success_case): Clean patch on clean tree.
+ (JSCEarlyWarningSystemTest.test_test_failure): Failed patch on clean tree.
+ (JSCEarlyWarningSystemTest.test_fix): Patch that fixes all tree redness.
+ (JSCEarlyWarningSystemTest.test_ineffective_patch): Patch that has same failures as tree.
+ (JSCEarlyWarningSystemTest.test_partially_effective_patch): Patch fixes some failures but adds no new failures.
+ (JSCEarlyWarningSystemTest.test_different_test_failures_in_patch_and_tree): Patch has some failures not in tree.
+ (JSCEarlyWarningSystemTest.test_first_results_could_not_be_read): Patch results not readable.
+ (JSCEarlyWarningSystemTest.test_second_results_could_not_be_read): Patch results not readable on second run.
+ (JSCEarlyWarningSystemTest.test_clean_results_could_not_be_read): Results from clean tree not readable.
+ (JSCEarlyWarningSystemTest.test_flaky_results_on_clean_tree_pass): Patch has one flake and no failures.
+ (JSCEarlyWarningSystemTest.test_flaky_results_on_clean_tree_pass_v2): Patch has one flake and no failures.
+ (JSCEarlyWarningSystemTest.test_flaky_results_on_clean_tree_failure): Patch has flakes and failed tests.
+ (JSCEarlyWarningSystemTest.test_flaky_results_on_red_tree_pass): Patch has same failures as tree, plus a flake.
+ * Scripts/webkitpy/tool/bot/jsctestresultsreader.py: Added.
+ (JSCTestResultsReader): Reads results file.
+ (JSCTestResultsReader.__init__): Sets attributes.
+ (JSCTestResultsReader._read_file_contents): Reads file.
+ (JSCTestResultsReader.results): Reads the results file and returns a JSCTestResults object.
+ * Scripts/webkitpy/tool/bot/patchanalysistask.py:
+ (PatchIsNotApplicable): Exception for when patch doesn't have relevant changes.
+ (PatchAnalysisTask._check_patch_relevance): Added.
+ (PatchAnalysisTask._build): Check for JSC.
+ (PatchAnalysisTask._build_without_patch): Check for JSC.
+ (PatchAnalysisTask._test): Check for JSC.
+ (PatchAnalysisTask._build_and_test_without_patch): Check for JSC.
+ (PatchAnalysisTask._retry_jsc_tests): Retry logic for JSC EWS.
+ (PatchAnalysisTask._retry_layout_tests): Retry logic for layout tests EWS.
+ (PatchAnalysisTask._test_patch): Add retry logic for JSC.
+ * Scripts/webkitpy/tool/commands/download.py:
+ (CheckPatchRelevance): Add check-patch-relevance command.
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ (AbstractEarlyWarningSystem._create_task): Abstract out to enable mocking.
+ (AbstractEarlyWarningSystem.begin_work_queue): Use JSCTestResultsReader not LayoutTestResultsReader in JSC EWS.
+ (AbstractEarlyWarningSystem.review_patch): Handle PatchIsApplicable.
+ (AbstractEarlyWarningSystem.test_results): _layout_test_results_reader -> _test_results_reader.
+ (AbstractEarlyWarningSystem.archive_last_test_results): _layout_test_results_reader -> _test_results_reader.
+ (AbstractEarlyWarningSystem.group): This attribute determines the type of EWS (eg. JSC).
+ (AbstractEarlyWarningSystem.load_ews_classes): Add _group, and make classes of type cls to enable mocking.
+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py:
+ (TestEWS): Sample layout test EWS class used for unit testing.
+ (TestJSCEWS): Sample JSC EWS class used for unit testing.
+ (AbstractEarlyWarningSystemTest.test_failing_tests_message.TestEWS): Add _group.
+ (AbstractEarlyWarningSystemTest.test_failing_jsc_tests_message.TestEWS): Added test for jsc failures message.
+ (AbstractEarlyWarningSystemTest): Add _group variable.
+ (EarlyWarningSystemTest._default_expected_logs): Add check-patch-relevance step, inconclusive logs, and group.
+ (EarlyWarningSystemTest._test_ews): Add logs_are_conclusive option to pass through to default_expected_logs().
+ (EarlyWarningSystemTest.test_inconclusive_test_results): Test not removing patch from queue if not conclusive.
+ (MockAbstractEarlyWarningSystemForInconclusiveJSCResults): Added.
+ (MockAbstractEarlyWarningSystemForInconclusiveJSCResults._test_patch): Simulates running tests but not getting a conclusive result.
+ (MockEarlyWarningSystemTaskForInconclusiveJSCResults): Added.
+ (MockEarlyWarningSystemTaskForInconclusiveJSCResults._create_task): Use MockEarlyWarningSystemTask (not EarlyWarningSystemTask).
+ * Scripts/webkitpy/tool/steps/__init__.py: Add CheckPatchRelevance import.
+ * Scripts/webkitpy/tool/steps/build.py:
+ (Build.options): Check for JSC.
+ (Build.build): Check for JSC.
+ (Build.run): Check for JSC.
+ * Scripts/webkitpy/tool/steps/checkpatchrelevance.py: Added.
+ (CheckPatchRelevance): Added.
+ (CheckPatchRelevance._does_contain_change_in_paths): Abstract function to perform regex matching.
+ (CheckPatchRelevance.run): Check if changed files in patch belong in certain folders.
+ * Scripts/webkitpy/tool/steps/options.py:
+ (Options): Add --group command line option.
+ * Scripts/webkitpy/tool/steps/runtests.py:
+ (RunTests.options): Add group.
+ (RunTests.run): Check for JSC.
+ (RunTests._run_webkit_tests): Check for JSC.
+ (RunTests): Add _group attribute.
+ (RunTests._run_javascriptcore_tests): New.
+ * Scripts/webkitpy/tool/steps/steps_unittest.py: Unit tests.
+
2017-02-17 Aakash Jain <aakash_j...@apple.com>
Fix tools that were broken by Efl removal
Modified: trunk/Tools/QueueStatusServer/config/messages.py (212578 => 212579)
--- trunk/Tools/QueueStatusServer/config/messages.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/QueueStatusServer/config/messages.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2013 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -27,6 +28,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# These must be in sync with webkit-patch's AbstractQueue.
+skip_status = "Skip"
pass_status = "Pass"
fail_status = "Fail"
error_status = "Error"
Modified: trunk/Tools/QueueStatusServer/config/queues.py (212578 => 212579)
--- trunk/Tools/QueueStatusServer/config/queues.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/QueueStatusServer/config/queues.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 Apple Inc. All rights reserved.
+# Copyright (C) 2014, 2017 Apple Inc. All rights reserved.
# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,7 @@
"gtk-wk2-ews",
"ios-ews",
"ios-sim-ews",
+ "jsc-ews",
"mac-ews",
"mac-wk2-ews",
"mac-debug-ews",
Modified: trunk/Tools/QueueStatusServer/handlers/statusbubble.py (212578 => 212579)
--- trunk/Tools/QueueStatusServer/handlers/statusbubble.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/QueueStatusServer/handlers/statusbubble.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -182,12 +183,16 @@
return bubble
def _should_show_bubble_for(self, attachment, queue):
- # Any pending queue is shown.
+ # Any pending queue is shown.
if attachment.position_in_queue(queue):
return True
- # EWS queues are also shown when complete.
- return bool(queue.is_ews() and attachment.status_for_queue(queue))
+ if not queue.is_ews():
+ return False
+
+ status = attachment.status_for_queue(queue)
+ return bool(status and not status.did_skip())
+
def _build_bubbles_for_attachment(self, attachment):
show_submit_to_ews = True
bubbles = []
Modified: trunk/Tools/QueueStatusServer/model/queues.py (212578 => 212579)
--- trunk/Tools/QueueStatusServer/model/queues.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/QueueStatusServer/model/queues.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -78,6 +78,7 @@
display_name = display_name.replace("Wk2", "WK2")
display_name = display_name.replace("Ews", "EWS")
display_name = display_name.replace("Ios", "iOS")
+ display_name = display_name.replace("Jsc", "JSC")
return display_name
_dash_regexp = re.compile("-")
Modified: trunk/Tools/QueueStatusServer/model/queuestatus.py (212578 => 212579)
--- trunk/Tools/QueueStatusServer/model/queuestatus.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/QueueStatusServer/model/queuestatus.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2013 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -40,3 +41,6 @@
message = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)
results_file = db.BlobProperty()
+
+ def did_skip(self):
+ return self.message == messages.skip_status
Modified: trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2011 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -37,6 +38,7 @@
self.added_paths = set()
self._filesystem = filesystem or MockFileSystem()
self._executive = executive or MockExecutive()
+ self._mockChangedFiles = ["MockFile1"]
def add(self, destination_path):
self.add_list([destination_path])
@@ -71,7 +73,7 @@
return self._filesystem.join(self.checkout_root, *comps)
def changed_files(self, git_commit=None):
- return ["MockFile1"]
+ return self._mockChangedFiles
def changed_files_for_revision(self, revision):
return ["MockFile1"]
Modified: trunk/Tools/Scripts/webkitpy/common/config/ews.json (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/config/ews.json 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/common/config/ews.json 2017-02-17 22:41:49 UTC (rev 212579)
@@ -44,5 +44,11 @@
"port": "mac",
"name": "mac-32bit-ews",
"architecture": "i386"
+ },
+ "JSC EWS": {
+ "port": "mac",
+ "name": "jsc-ews",
+ "group": "jsc",
+ "runTests": true
}
}
Modified: trunk/Tools/Scripts/webkitpy/common/config/ports.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/config/ports.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/common/config/ports.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,6 +1,6 @@
# Copyright (C) 2009, Google Inc. All rights reserved.
# Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
-# Copyright (C) 2015 Apple Inc. All rights reserved.
+# Copyright (C) 2015, 2017 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
@@ -95,24 +95,29 @@
def prepare_changelog_command(self):
return self.script_shell_command("prepare-ChangeLog")
- def build_webkit_command(self, build_style=None):
- command = self.script_shell_command("build-webkit")
+ def _append_build_style_flag(self, command, build_style):
if build_style == "debug":
command.append("--debug")
- if build_style == "release":
+ elif build_style == "release":
command.append("--release")
return command
- def run_javascriptcore_tests_command(self):
- return self.script_shell_command("run-_javascript_core-tests")
+ def build_webkit_command(self, build_style=None):
+ command = self.script_shell_command("build-webkit")
+ return self._append_build_style_flag(command, build_style)
+ def build_jsc_command(self, build_style=None):
+ command = self.script_shell_command("build-jsc")
+ return self._append_build_style_flag(command, build_style)
+
+ def run_javascriptcore_tests_command(self, build_style=None):
+ command = self.script_shell_command("run-_javascript_core-tests")
+ command.append("--no-fail-fast")
+ return self._append_build_style_flag(command, build_style)
+
def run_webkit_tests_command(self, build_style=None):
command = self.script_shell_command("run-webkit-tests")
- if build_style == "debug":
- command.append("--debug")
- if build_style == "release":
- command.append("--release")
- return command
+ return self._append_build_style_flag(command, build_style)
def run_python_unittests_command(self):
return self.script_shell_command("test-webkitpy")
Modified: trunk/Tools/Scripts/webkitpy/common/config/ports_mock.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/config/ports_mock.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/common/config/ports_mock.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2011 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -49,7 +50,7 @@
def run_perl_unittests_command(self):
return ['mock-test-webkitperl']
- def run_javascriptcore_tests_command(self):
+ def run_javascriptcore_tests_command(self, build_style=None):
return ['mock-run-javacriptcore-tests']
def run_webkit_tests_command(self, build_style=None):
Modified: trunk/Tools/Scripts/webkitpy/common/config/ports_unittest.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/config/ports_unittest.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/common/config/ports_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -39,6 +40,10 @@
self.assertEqual(MacPort().build_webkit_command(build_style="debug"), DeprecatedPort().script_shell_command("build-webkit") + ["--debug"])
self.assertEqual(MacPort().build_webkit_command(build_style="release"), DeprecatedPort().script_shell_command("build-webkit") + ["--release"])
+ self.assertEqual(MacPort().build_jsc_command(), DeprecatedPort().script_shell_command("build-jsc"))
+ self.assertEqual(MacPort().build_jsc_command(build_style="release"), DeprecatedPort().script_shell_command("build-jsc") + ["--release"])
+ self.assertEqual(MacPort().build_jsc_command(build_style="debug"), DeprecatedPort().script_shell_command("build-jsc") + ["--debug"])
+
def test_gtk_wk2_port(self):
self.assertEqual(GtkWK2Port().flag(), "--port=gtk-wk2")
self.assertEqual(GtkWK2Port().run_webkit_tests_command(), DeprecatedPort().script_shell_command("run-webkit-tests") + ["--gtk"])
Added: trunk/Tools/Scripts/webkitpy/common/net/abstracttestresults.py (0 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/net/abstracttestresults.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/common/net/abstracttestresults.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -0,0 +1,33 @@
+# Copyright (C) 2017 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 logging
+
+_log = logging.getLogger(__name__)
+
+
+class AbstractTestResults(object):
+ def failing_tests(self):
+ raise NotImplementedError("subclasses must implement")
+
+ def did_exceed_test_failure_limit(self):
+ raise NotImplementedError("subclasses must implement")
Added: trunk/Tools/Scripts/webkitpy/common/net/jsctestresults.py (0 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/net/jsctestresults.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/common/net/jsctestresults.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -0,0 +1,77 @@
+# Copyright (C) 2017 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
+import logging
+
+from webkitpy.common.net.abstracttestresults import AbstractTestResults
+
+_log = logging.getLogger(__name__)
+
+
+class JSCTestResults(AbstractTestResults):
+ def __init__(self, all_api_tests_passed, stress_test_failures):
+ self._all_api_tests_passed = all_api_tests_passed
+ self._stress_test_failures = stress_test_failures
+
+ self._failing_test_names = stress_test_failures[:]
+ if not self._all_api_tests_passed:
+ self._failing_test_names.append('apiTests')
+
+ @classmethod
+ def intersection(cls, first, second):
+ intersection_api_tests_passed = first._all_api_tests_passed or second._all_api_tests_passed
+ intersection_stress_test_failures = list(set(first._stress_test_failures) & set(second._stress_test_failures))
+ return cls(intersection_api_tests_passed, intersection_stress_test_failures)
+
+ @classmethod
+ def results_from_string(cls, string):
+ if not string:
+ return None
+
+ try:
+ parsed_results = json.loads(string)
+ except ValueError:
+ _log.error('Invalid JSON results')
+ return None
+
+ if 'allApiTestsPassed' not in parsed_results or 'stressTestFailures' not in parsed_results:
+ return None
+
+ return cls(parsed_results['allApiTestsPassed'], parsed_results['stressTestFailures'])
+
+ def equals(self, other):
+ return (self._all_api_tests_passed == other._all_api_tests_passed and
+ set(self._stress_test_failures) == set(other._stress_test_failures))
+
+ def is_subset(self, other):
+ return set(self._failing_test_names) <= set(other._failing_test_names)
+
+ def all_passed(self):
+ return self._all_api_tests_passed and not self._stress_test_failures
+
+ def failing_tests(self):
+ return self._failing_test_names
+
+ # No defined failure limit for JSC.
+ def did_exceed_test_failure_limit(self):
+ return False
Added: trunk/Tools/Scripts/webkitpy/common/net/jsctestresults_unittest.py (0 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/net/jsctestresults_unittest.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/common/net/jsctestresults_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -0,0 +1,75 @@
+# Copyright (C) 2017 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 unittest
+
+from webkitpy.common.net.jsctestresults import JSCTestResults
+
+
+class JSCTestResultsTest(unittest.TestCase):
+ def test_results_from_string(self):
+ none_item = None
+ empty_json = ''
+ invalid_json = '{"allApiTestsPassed":'
+ incomplete_json_v1 = '{"allApiTestsPassed": true}'
+ incomplete_json_v2 = '{"stressTestFailures":[]}'
+ self.assertEqual(None, JSCTestResults.results_from_string(none_item))
+ self.assertEqual(None, JSCTestResults.results_from_string(empty_json))
+ self.assertEqual(None, JSCTestResults.results_from_string(invalid_json))
+ self.assertEqual(None, JSCTestResults.results_from_string(incomplete_json_v1))
+ self.assertEqual(None, JSCTestResults.results_from_string(incomplete_json_v2))
+
+ no_failures_string = '{"allApiTestsPassed": true, "stressTestFailures":[]}'
+ no_failures_results = JSCTestResults(True, [])
+ self.assertTrue(no_failures_results.equals(JSCTestResults.results_from_string(no_failures_string)))
+
+ api_test_failures_string = '{"allApiTestsPassed": false, "stressTestFailures":[]}'
+ api_test_failures_results = JSCTestResults(False, [])
+ self.assertTrue(api_test_failures_results.equals(JSCTestResults.results_from_string(api_test_failures_string)))
+
+ many_failures_string = '{"allApiTestsPassed": false, "stressTestFailures":["es6.yaml/es6/typed_arrays_Int16Array.js.default", "es6.yaml/es6/typed_arrays_Int8Array.js.default"]}'
+ many_failures_results = JSCTestResults(False, ["es6.yaml/es6/typed_arrays_Int16Array.js.default", "es6.yaml/es6/typed_arrays_Int8Array.js.default"])
+ self.assertTrue(many_failures_results.equals(JSCTestResults.results_from_string(many_failures_string)))
+
+ self.assertFalse(no_failures_results == api_test_failures_results)
+ self.assertFalse(api_test_failures_results == many_failures_results)
+
+ def test_intersection_api_tests(self):
+ results1 = JSCTestResults(False, [])
+ results2 = JSCTestResults(True, [])
+
+ expected_intersection = JSCTestResults(True, [])
+ self.assertTrue(expected_intersection.equals(JSCTestResults.intersection(results1, results2)))
+
+ def test_intersection_stress_tests(self):
+ results1 = JSCTestResults(True, ['failure1', 'failure2'])
+ results2 = JSCTestResults(True, ['failure1', 'failure3'])
+
+ expected_intersection = JSCTestResults(True, ['failure1'])
+ self.assertTrue(expected_intersection.equals(JSCTestResults.intersection(results1, results2)))
+
+ def test_intersection_general_case(self):
+ results1 = JSCTestResults(True, ['failure1', 'failure2'])
+ results2 = JSCTestResults(False, ['failure1'])
+
+ expected_intersection = JSCTestResults(True, ['failure1'])
+ self.assertTrue(expected_intersection.equals(JSCTestResults.intersection(results1, results2)))
Modified: trunk/Tools/Scripts/webkitpy/common/net/layouttestresults.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/common/net/layouttestresults.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/common/net/layouttestresults.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -28,6 +28,7 @@
import logging
+from webkitpy.common.net.abstracttestresults import AbstractTestResults
from webkitpy.common.net.resultsjsonparser import ParsedJSONResults
from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup, SoupStrainer
from webkitpy.layout_tests.models import test_results
@@ -45,7 +46,7 @@
# FIXME: This should be unified with ResultsSummary or other NRWT layout tests code
# in the layout_tests package.
# This doesn't belong in common.net, but we don't have a better place for it yet.
-class LayoutTestResults(object):
+class LayoutTestResults(AbstractTestResults):
@classmethod
def results_from_string(cls, string):
if not string:
Modified: trunk/Tools/Scripts/webkitpy/port/base.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/port/base.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/port/base.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -808,6 +808,9 @@
inverse of relative_test_filename()."""
return self._filesystem.join(self.layout_tests_dir(), test_name)
+ def jsc_results_directory(self):
+ return self._build_path()
+
def results_directory(self):
"""Absolute path to the place to store the test results (uses --results-directory)."""
if not self._results_directory:
Modified: trunk/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -26,7 +27,7 @@
# (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.bot.patchanalysistask import PatchAnalysisTask, PatchAnalysisTaskDelegate, UnableToApplyPatch, PatchIsNotValid
+from webkitpy.tool.bot.patchanalysistask import PatchAnalysisTask, PatchAnalysisTaskDelegate, UnableToApplyPatch, PatchIsNotValid, PatchIsNotApplicable
class EarlyWarningSystemTaskDelegate(PatchAnalysisTaskDelegate):
@@ -58,6 +59,8 @@
return False
if not self._apply():
raise UnableToApplyPatch(self._patch)
+ if not self._check_patch_relevance():
+ raise PatchIsNotApplicable(self._patch)
if not self._build():
if not self._build_without_patch():
return False
Added: trunk/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py (0 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -0,0 +1,218 @@
+# Copyright (C) 2017 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 logging
+import unittest
+
+from webkitpy.common.net.jsctestresults import JSCTestResults
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.bot.patchanalysistask import *
+from webkitpy.tool.commands.earlywarningsystem import AbstractEarlyWarningSystem
+from webkitpy.tool.mocktool import MockTool
+
+_log = logging.getLogger(__name__)
+
+
+class MockPatchAnalysisTask(PatchAnalysisTask):
+ def __init__(self, delegate, patch, patches_passed_all_tests):
+ self._delegate = delegate
+ self._patch = patch
+ self._script_error = None
+ self._results_archive_from_patch_test_run = None
+ self._results_from_patch_test_run = None
+ self.error = None
+ self._patches_passed_all_tests = patches_passed_all_tests
+ self._test_run_count = 0
+ self.failure_status_id = 0
+
+ def _test(self):
+ self._test_run_count += 1
+
+ if self._patches_passed_all_tests.pop() == True:
+ return True
+ self._script_error = ScriptError('Regression test')
+ return False
+
+ def _build_and_test_without_patch(self):
+ self._test_run_count += 1
+ return True
+
+ def validate(self):
+ return True
+
+ def test_run_count(self):
+ return self._test_run_count
+
+
+# This is the delegate to be used with MockPatchAnalysisTask, above.
+class MockJSCEarlyWarningSystem(AbstractEarlyWarningSystem):
+ def __init__(self, first_test_results, second_test_results, clean_test_results):
+ AbstractEarlyWarningSystem.__init__(self)
+ self._group = 'jsc'
+ self._results_in_order = [clean_test_results, second_test_results, first_test_results]
+
+ def test_results(self):
+ return self._results_in_order.pop()
+
+
+class JSCEarlyWarningSystemTest(unittest.TestCase):
+ def _results_indicate_all_passed(self, results):
+ if results == None:
+ return False
+ return results.all_passed()
+
+ def _create_task(self, first_test_results, second_test_results, clean_test_results):
+ queue = MockJSCEarlyWarningSystem(first_test_results, second_test_results, clean_test_results)
+ tool = MockTool(log_executive=True)
+ patch = tool.bugs.fetch_attachment(10000)
+ patches_passed_all_tests = map(self._results_indicate_all_passed, [second_test_results, first_test_results])
+ return MockPatchAnalysisTask(queue, patch, patches_passed_all_tests)
+
+ def test_success_case(self):
+ first_test_results = JSCTestResults(True, [])
+ second_test_results = JSCTestResults(True, [])
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 1)
+ self.assertTrue(return_value)
+
+ def test_test_failure(self):
+ first_test_results = JSCTestResults(True, ['Fail.js'])
+ second_test_results = JSCTestResults(True, ['Fail.js'])
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ with self.assertRaises(ScriptError):
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 3)
+
+ def test_fix(self):
+ first_test_results = JSCTestResults(True, [])
+ second_test_results = JSCTestResults(True, [])
+ clean_test_results = JSCTestResults(True, ['Fail.js'])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 1)
+ self.assertTrue(return_value)
+
+ def test_ineffective_patch(self):
+ first_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
+ second_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
+ clean_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 3)
+ self.assertTrue(return_value)
+
+ def test_partially_effective_patch(self):
+ first_test_results = JSCTestResults(True, ['failure2.js'])
+ second_test_results = JSCTestResults(True, ['failure2.js'])
+ clean_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 3)
+ self.assertTrue(return_value)
+
+ def test_different_test_failures_in_patch_and_tree(self):
+ first_test_results = JSCTestResults(False, [])
+ second_test_results = JSCTestResults(False, [])
+ clean_test_results = JSCTestResults(True, ['failure1.js', 'failure2.js'])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ with self.assertRaises(ScriptError):
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 3)
+
+ def test_first_results_could_not_be_read(self):
+ first_test_results = None
+ second_test_results = JSCTestResults(True, [])
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 1)
+ self.assertFalse(return_value)
+
+ def test_second_results_could_not_be_read(self):
+ first_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
+ second_test_results = None
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 2)
+ self.assertFalse(return_value)
+
+ def test_clean_results_could_not_be_read(self):
+ first_test_results = JSCTestResults(True, ['failure2.js'])
+ second_test_results = JSCTestResults(True, ['failure2.js'])
+ clean_test_results = None
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertEqual(task.test_run_count(), 3)
+ self.assertFalse(return_value)
+
+ def test_flaky_results_on_clean_tree_pass(self):
+ first_test_results = JSCTestResults(True, ['failure2.js'])
+ second_test_results = JSCTestResults(True, [])
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertTrue(return_value)
+ self.assertEqual(task.test_run_count(), 2)
+
+ def test_flaky_results_on_clean_tree_pass_v2(self):
+ first_test_results = JSCTestResults(True, [])
+ second_test_results = JSCTestResults(True, ['failure2.js'])
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertTrue(return_value)
+ self.assertEqual(task.test_run_count(), 1)
+
+ def test_flaky_results_on_clean_tree_failure(self):
+ first_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
+ second_test_results = JSCTestResults(True, ['failure2.js'])
+ clean_test_results = JSCTestResults(True, [])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ with self.assertRaises(ScriptError):
+ task._test_patch()
+ self.assertEqual(task.test_run_count(), 3)
+
+ def test_flaky_results_on_red_tree_pass(self):
+ first_test_results = JSCTestResults(True, ['failure1.js'])
+ second_test_results = JSCTestResults(True, ['failure1.js', 'failure2.js'])
+ clean_test_results = JSCTestResults(True, ['failure1.js'])
+ task = self._create_task(first_test_results, second_test_results, clean_test_results)
+
+ return_value = task._test_patch()
+ self.assertTrue(return_value)
+ self.assertEqual(task.test_run_count(), 3)
Added: trunk/Tools/Scripts/webkitpy/tool/bot/jsctestresultsreader.py (0 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/bot/jsctestresultsreader.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/tool/bot/jsctestresultsreader.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -0,0 +1,44 @@
+# Copyright (C) 2017 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 logging
+
+from webkitpy.common.net.jsctestresults import JSCTestResults
+
+_log = logging.getLogger(__name__)
+
+
+class JSCTestResultsReader(object):
+ def __init__(self, host, results_directory):
+ self._host = host
+ self._results_directory = results_directory
+
+ def _read_file_contents(self, path):
+ try:
+ return self._host.filesystem.read_text_file(path)
+ except (IOError, KeyError):
+ return None
+
+ def results(self):
+ results_path = self._host.filesystem.join(self._results_directory, 'jsc_test_results.json')
+ contents = self._read_file_contents(results_path)
+ return JSCTestResults.results_from_string(contents)
Modified: trunk/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (c) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -28,6 +29,7 @@
from webkitpy.common.system.executive import ScriptError
from webkitpy.common.net.layouttestresults import LayoutTestResults
+from webkitpy.common.net.jsctestresults import JSCTestResults
class UnableToApplyPatch(Exception):
@@ -43,6 +45,11 @@
self.failure_message = failure_message
+class PatchIsNotApplicable(Exception):
+ def __init__(self, patch):
+ Exception.__init__(self)
+ self.patch = patch
+
class PatchAnalysisTaskDelegate(object):
def parent_command(self):
raise NotImplementedError("subclasses must implement")
@@ -122,28 +129,44 @@
"Applied patch",
"Patch does not apply")
+ def _check_patch_relevance(self):
+ args = [
+ "check-patch-relevance",
+ ]
+
+ if hasattr(self._delegate, 'group'):
+ args.append("--group=%s" % self._delegate.group())
+
+ return self._run_command(args, "Checked relevance of patch", "Patch was not relevant")
+
def _build(self):
- return self._run_command([
+ args = [
"build",
"--no-clean",
"--no-update",
"--build-style=%s" % self._delegate.build_style(),
- ],
- "Built patch",
- "Patch does not build")
+ ]
+ if hasattr(self._delegate, 'group'):
+ args.append("--group=%s" % self._delegate.group())
+
+ return self._run_command(args, "Built patch", "Patch does not build")
+
def _build_without_patch(self):
- return self._run_command([
+ args = [
"build",
"--force-clean",
"--no-update",
"--build-style=%s" % self._delegate.build_style(),
- ],
- "Able to build without patch",
- "Unable to build without patch")
+ ]
+ if hasattr(self._delegate, 'group'):
+ args.append("--group=%s" % self._delegate.group())
+
+ return self._run_command(args, "Able to build without patch", "Unable to build without patch")
+
def _test(self):
- return self._run_command([
+ args = [
"build-and-test",
"--no-clean",
"--no-update",
@@ -151,12 +174,15 @@
"--test",
"--non-interactive",
"--build-style=%s" % self._delegate.build_style(),
- ],
- "Passed tests",
- "Patch does not pass tests")
+ ]
+ if hasattr(self._delegate, 'group'):
+ args.append("--group=%s" % self._delegate.group())
+
+ return self._run_command(args, "Passed tests", "Patch does not pass tests")
+
def _build_and_test_without_patch(self):
- return self._run_command([
+ args = [
"build-and-test",
"--force-clean",
"--no-update",
@@ -164,10 +190,13 @@
"--test",
"--non-interactive",
"--build-style=%s" % self._delegate.build_style(),
- ],
- "Able to pass tests without patch",
- "Unable to pass tests without patch (tree is red?)")
+ ]
+ if hasattr(self._delegate, 'group'):
+ args.append("--group=%s" % self._delegate.group())
+
+ return self._run_command(args, "Able to pass tests without patch", "Unable to pass tests without patch (tree is red?)")
+
def _land(self):
# Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should.
return self._run_command([
@@ -206,10 +235,35 @@
# also present without the patch, so we don't need to defer.
return False
- def _test_patch(self):
+ # FIXME: Abstract out common parts of the retry logic.
+ def _retry_jsc_tests(self):
+ first_results = self._delegate.test_results()
+ first_script_error = self._script_error
+ first_failure_status_id = self.failure_status_id
+ if first_results is None:
+ return False
+
if self._test():
return True
+ second_results = self._delegate.test_results()
+ second_script_error = self._script_error
+ if second_results is None:
+ return False
+ consistently_failing_test_results = JSCTestResults.intersection(first_results, second_results)
+
+ self._build_and_test_without_patch()
+ clean_tree_results = self._delegate.test_results()
+ if clean_tree_results is None:
+ return False
+
+ if consistently_failing_test_results.is_subset(clean_tree_results):
+ return True
+
+ self.failure_status_id = first_failure_status_id
+ return self.report_failure(None, consistently_failing_test_results, first_script_error)
+
+ def _retry_layout_tests(self):
# Note: archive_last_test_results deletes the results directory, making these calls order-sensitve.
# We could remove this dependency by building the test_results from the archive.
first_results = self._delegate.test_results()
@@ -240,11 +294,13 @@
return self.report_failure(first_results_archive, first_results, first_script_error)
if second_results.did_exceed_test_failure_limit():
- self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive, first_script_error, first_failure_status_id)
+ self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive,
+ first_script_error, first_failure_status_id)
return False
if first_results.did_exceed_test_failure_limit():
- self._should_defer_patch_or_throw(second_results.failing_test_results(), second_results_archive, second_script_error, second_failure_status_id)
+ self._should_defer_patch_or_throw(second_results.failing_test_results(), second_results_archive,
+ second_script_error, second_failure_status_id)
return False
if self._results_failed_different_tests(first_results, second_results):
@@ -259,7 +315,8 @@
tests_that_consistently_failed = first_failing_results_set.intersection(second_failing_results_set)
if tests_that_consistently_failed:
- if self._should_defer_patch_or_throw(tests_that_consistently_failed, first_results_archive, first_script_error, first_failure_status_id):
+ if self._should_defer_patch_or_throw(tests_that_consistently_failed, first_results_archive,
+ first_script_error, first_failure_status_id):
return False # Defer patch
# At this point we know that at least one test flaked, but no consistent failures
@@ -266,7 +323,8 @@
# were introduced. This is a bit of a grey-zone.
return False # Defer patch
- if self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive, first_script_error, first_failure_status_id):
+ if self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive,
+ first_script_error, first_failure_status_id):
return False # Defer patch
# At this point, we know that the first and second runs had the exact same failures,
@@ -274,6 +332,15 @@
# that the patch is good.
return True
+ def _test_patch(self):
+ if self._test():
+ return True
+
+ if hasattr(self._delegate, 'group') and self._delegate.group() == "jsc":
+ return self._retry_jsc_tests()
+ else:
+ return self._retry_layout_tests()
+
def results_archive_from_patch_test_run(self, patch):
assert(self._patch.id() == patch.id()) # PatchAnalysisTask is not currently re-useable.
return self._results_archive_from_patch_test_run
Modified: trunk/Tools/Scripts/webkitpy/tool/commands/download.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/commands/download.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/download.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,5 +1,5 @@
# Copyright (c) 2009, 2011 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
+# Copyright (c) 2009, 2017 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
@@ -87,6 +87,14 @@
]
+class CheckPatchRelevance(AbstractSequencedCommand):
+ name = "check-patch-relevance"
+ help_text = "Check if this patch needs to be tested"
+ steps = [
+ steps.CheckPatchRelevance,
+ ]
+
+
class Land(AbstractSequencedCommand):
name = "land"
help_text = "Land the current working directory diff and updates the associated bug if any"
Modified: trunk/Tools/Scripts/webkitpy/tool/commands/download_unittest.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/commands/download_unittest.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/download_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -89,6 +89,7 @@
options.update = True
options.architecture = 'MOCK ARCH'
options.iterate_on_new_tests = 0
+ options.group = None
return options
def test_build(self):
Modified: trunk/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (c) 2009 Google Inc. All rights reserved.
+# Copyright (c) 2017 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
@@ -28,6 +29,7 @@
import json
import logging
+import os
from optparse import make_option
from webkitpy.common.config.committers import CommitterList
@@ -36,7 +38,8 @@
from webkitpy.common.system.executive import ScriptError
from webkitpy.tool.bot.earlywarningsystemtask import EarlyWarningSystemTask, EarlyWarningSystemTaskDelegate
from webkitpy.tool.bot.layouttestresultsreader import LayoutTestResultsReader
-from webkitpy.tool.bot.patchanalysistask import UnableToApplyPatch, PatchIsNotValid
+from webkitpy.tool.bot.jsctestresultsreader import JSCTestResultsReader
+from webkitpy.tool.bot.patchanalysistask import UnableToApplyPatch, PatchIsNotValid, PatchIsNotApplicable
from webkitpy.tool.bot.queueengine import QueueEngine
from webkitpy.tool.commands.queues import AbstractReviewQueue
@@ -53,8 +56,12 @@
def begin_work_queue(self):
AbstractReviewQueue.begin_work_queue(self)
- self._layout_test_results_reader = LayoutTestResultsReader(self._tool, self._port.results_directory(), self._log_directory())
+ if self.group() == "jsc":
+ self._test_results_reader = JSCTestResultsReader(self._tool, self._port.jsc_results_directory())
+ else:
+ self._test_results_reader = LayoutTestResultsReader(self._tool, self._port.results_directory(), self._log_directory())
+
def _failing_tests_message(self, task, patch):
results = task.results_from_patch_test_run(patch)
@@ -79,8 +86,12 @@
tool.bugs.add_cc_to_bug(patch.bug_id(), self.watchers)
tool.bugs.set_flag_on_attachment(patch.id(), "commit-queue", "-", message)
+ # This exists for mocking
+ def _create_task(self, patch):
+ return EarlyWarningSystemTask(self, patch, self._options.run_tests)
+
def review_patch(self, patch):
- task = EarlyWarningSystemTask(self, patch, self._options.run_tests)
+ task = self._create_task(patch)
try:
succeeded = task.run()
if not succeeded:
@@ -93,6 +104,9 @@
except UnableToApplyPatch, e:
self._did_error(patch, "%s unable to apply patch." % self.name)
return False
+ except PatchIsNotApplicable, e:
+ self._did_skip(patch)
+ return False
except ScriptError, e:
self._post_reject_message_on_bug(self._tool, patch, task.failure_status_id, self._failing_tests_message(task, patch))
results_archive = task.results_archive_from_patch_test_run(patch)
@@ -117,14 +131,17 @@
return self._update_status(message, patch=patch, results_file=failure_log)
def test_results(self):
- return self._layout_test_results_reader.results()
+ return self._test_results_reader.results()
def archive_last_test_results(self, patch):
- return self._layout_test_results_reader.archive(patch)
+ return self._test_results_reader.archive(patch)
def build_style(self):
return self._build_style
+ def group(self):
+ return self._group
+
def refetch_patch(self, patch):
return self._tool.bugs.fetch_attachment(patch.id())
@@ -149,7 +166,7 @@
classes = []
for name, config in ewses.iteritems():
- classes.append(type(name.encode('utf-8').translate(None, ' -'), (AbstractEarlyWarningSystem,), {
+ classes.append(type(name.encode('utf-8').translate(None, ' -'), (cls,), {
'name': config.get('name', config['port'] + '-ews'),
'port_name': config['port'],
'architecture': config.get('architecture', None),
@@ -156,5 +173,6 @@
'_build_style': config.get('style', "release"),
'watchers': config.get('watchers', []),
'run_tests': config.get('runTests', cls.run_tests),
+ '_group': config.get('group', None),
}))
return classes
Modified: trunk/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -29,6 +30,7 @@
from webkitpy.thirdparty.mock import Mock
from webkitpy.common.host import Host
from webkitpy.common.host_mock import MockHost
+from webkitpy.common.net.jsctestresults import JSCTestResults
from webkitpy.common.net.layouttestresults import LayoutTestResults
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.layout_tests.models import test_results
@@ -41,40 +43,79 @@
from webkitpy.tool.mocktool import MockTool, MockOptions
+# Needed to define port_name, used in AbstractEarlyWarningSystem.__init__
+class TestEWS(AbstractEarlyWarningSystem):
+ port_name = "win" # Needs to be a port which port/factory understands.
+ _build_style = None
+ _group = None
+
+
+class TestJSCEWS(AbstractEarlyWarningSystem):
+ port_name = "mac" # Needs to be a port which port/factory understands.
+ _build_style = None
+ _group = "jsc"
+
+
class AbstractEarlyWarningSystemTest(QueuesTest):
- def test_failing_tests_message(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.
- _build_style = None
-
- ews = TestEWS()
+ def _test_message(self, ews, results, message):
ews.bind_to_tool(MockTool())
ews.host = MockHost()
ews._options = MockOptions(port=None, confirm=False)
OutputCapture().assert_outputs(self, ews.begin_work_queue, expected_logs=self._default_begin_work_queue_logs(ews.name))
task = Mock()
- task.results_from_patch_test_run = lambda a: LayoutTestResults([test_results.TestResult("foo.html", failures=[test_failures.FailureTextMismatch()]),
- test_results.TestResult("bar.html", failures=[test_failures.FailureTextMismatch()])],
- did_exceed_test_failure_limit=False)
+ task.results_from_patch_test_run = results
patch = ews._tool.bugs.fetch_attachment(10000)
- self.assertMultiLineEqual(ews._failing_tests_message(task, patch), "New failing tests:\nfoo.html\nbar.html")
+ self.assertMultiLineEqual(ews._failing_tests_message(task, patch), message)
+ def test_failing_tests_message(self):
+ ews = TestEWS()
+ results = lambda a: LayoutTestResults([test_results.TestResult("foo.html", failures=[test_failures.FailureTextMismatch()]),
+ test_results.TestResult("bar.html", failures=[test_failures.FailureTextMismatch()])],
+ did_exceed_test_failure_limit=False)
+ message = "New failing tests:\nfoo.html\nbar.html"
+ self._test_message(ews, results, message)
+ def test_failing_jsc_tests_message(self):
+ ews = TestJSCEWS()
+ results = lambda a: JSCTestResults(False, ["es6.yaml/es6/typed_arrays_Int8Array.js.default", "es6.yaml/es6/typed_arrays_Uint8Array.js.default"])
+ message = "New failing tests:\nes6.yaml/es6/typed_arrays_Int8Array.js.default\nes6.yaml/es6/typed_arrays_Uint8Array.js.default\napiTests"
+ self._test_message(ews, results, message)
+
+
+class MockEarlyWarningSystemTaskForInconclusiveJSCResults(EarlyWarningSystemTask):
+ def _test_patch(self):
+ self._test()
+ results = self._delegate.test_results()
+ return bool(results)
+
+
+class MockAbstractEarlyWarningSystemForInconclusiveJSCResults(AbstractEarlyWarningSystem):
+ def _create_task(self, patch):
+ task = MockEarlyWarningSystemTaskForInconclusiveJSCResults(self, patch, self._options.run_tests)
+ return task
+
+
class EarlyWarningSystemTest(QueuesTest):
- def _default_expected_logs(self, ews):
+ def _default_expected_logs(self, ews, conclusive):
string_replacements = {
"name": ews.name,
"port": ews.port_name,
"architecture": " --architecture=%s" % ews.architecture if ews.architecture else "",
"build_style": ews.build_style(),
+ "group": ews.group(),
}
if ews.run_tests:
- run_tests_line = "Running: webkit-patch --status-host=example.com build-and-test --no-clean --no-update --test --non-interactive --build-style=%(build_style)s --port=%(port)s%(architecture)s\n" % string_replacements
+ run_tests_line = "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\n" % string_replacements
else:
run_tests_line = ""
string_replacements['run_tests_line'] = run_tests_line
+ if conclusive:
+ result_lines = "MOCK: update_status: %(name)s Pass\nMOCK: release_work_item: %(name)s 10000\n" % string_replacements
+ else:
+ result_lines = "MOCK: release_lock: %(name)s 10000\n" % string_replacements
+ string_replacements['result_lines'] = result_lines
+
expected_logs = {
"begin_work_queue": self._default_begin_work_queue_logs(ews.name),
"process_work_item": """MOCK: update_status: %(name)s Started processing patch
@@ -81,22 +122,21 @@
Running: webkit-patch --status-host=example.com clean --port=%(port)s%(architecture)s
Running: webkit-patch --status-host=example.com update --port=%(port)s%(architecture)s
Running: webkit-patch --status-host=example.com apply-attachment --no-update --non-interactive 10000 --port=%(port)s%(architecture)s
-Running: webkit-patch --status-host=example.com build --no-clean --no-update --build-style=%(build_style)s --port=%(port)s%(architecture)s
-%(run_tests_line)sMOCK: update_status: %(name)s Pass
-MOCK: release_work_item: %(name)s 10000
-""" % string_replacements,
+Running: webkit-patch --status-host=example.com check-patch-relevance --group=%(group)s --port=%(port)s%(architecture)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
+%(run_tests_line)s%(result_lines)s""" % string_replacements,
"handle_unexpected_error": "Mock error message\n",
"handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
return expected_logs
- def _test_ews(self, ews):
+ def _test_ews(self, ews, results_are_conclusive=True):
ews.bind_to_tool(MockTool())
ews.host = MockHost()
options = Mock()
options.port = None
options.run_tests = ews.run_tests
- self.assert_queue_outputs(ews, expected_logs=self._default_expected_logs(ews), options=options)
+ self.assert_queue_outputs(ews, expected_logs=self._default_expected_logs(ews, results_are_conclusive), options=options)
def test_ewses(self):
classes = AbstractEarlyWarningSystem.load_ews_classes()
@@ -104,3 +144,11 @@
self.maxDiff = None
for ews_class in classes:
self._test_ews(ews_class())
+
+ def test_inconclusive_jsc_test_results(self):
+ classes = MockAbstractEarlyWarningSystemForInconclusiveJSCResults.load_ews_classes()
+ self.assertTrue(classes)
+ self.maxDiff = None
+ test_classes = filter(lambda x: x.run_tests and x.group == "jsc", classes)
+ for ews_class in test_classes:
+ self._test_ews(ews_class(), False)
Modified: trunk/Tools/Scripts/webkitpy/tool/commands/queues.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/commands/queues.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/queues.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,5 +1,5 @@
# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
+# Copyright (c) 2009, 2017 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
@@ -62,6 +62,7 @@
watchers = [
]
+ _skip_status = "Skip"
_pass_status = "Pass"
_fail_status = "Fail"
_error_status = "Error"
@@ -244,6 +245,10 @@
self._update_status(message, patch)
self._release_work_item(patch)
+ def _did_skip(self, patch):
+ self._update_status(self._skip_status, patch)
+ self._release_work_item(patch)
+
def _unlock_patch(self, patch):
self._tool.status_server.release_lock(self.name, patch)
Modified: trunk/Tools/Scripts/webkitpy/tool/steps/__init__.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/__init__.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/__init__.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -33,6 +34,7 @@
from webkitpy.tool.steps.applywatchlist import ApplyWatchList
from webkitpy.tool.steps.attachtobug import AttachToBug
from webkitpy.tool.steps.build import Build
+from webkitpy.tool.steps.checkpatchrelevance import CheckPatchRelevance
from webkitpy.tool.steps.checkstyle import CheckStyle
from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
from webkitpy.tool.steps.closebug import CloseBug
Modified: trunk/Tools/Scripts/webkitpy/tool/steps/build.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/build.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/build.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -41,20 +42,24 @@
Options.build,
Options.quiet,
Options.build_style,
+ Options.group,
]
- def build(self, build_style):
+ def build(self, build_style, group):
environment = self._tool.copy_current_environment()
environment.disable_gcc_smartquotes()
environment.disable_jhbuild_VT100_output()
env = environment.to_dictionary()
- build_webkit_command = self._tool.deprecated_port().build_webkit_command(build_style=build_style)
+ if group == "jsc":
+ build_command = self._tool.deprecated_port().build_jsc_command(build_style=build_style)
+ else:
+ build_command = self._tool.deprecated_port().build_webkit_command(build_style=build_style)
if self._options.architecture:
- build_webkit_command += ['ARCHS=%s' % self._options.architecture]
+ build_command += ['ARCHS=%s' % self._options.architecture]
- self._tool.executive.run_and_throw_if_fail(build_webkit_command, self._options.quiet,
+ self._tool.executive.run_and_throw_if_fail(build_command, self._options.quiet,
cwd=self._tool.scm().checkout_root, env=env)
def run(self, state):
@@ -61,8 +66,11 @@
if not self._options.build:
return
_log.info("Building WebKit")
+
+ group = self._options.group
+
if self._options.build_style == "both":
- self.build("debug")
- self.build("release")
+ self.build("debug", group)
+ self.build("release", group)
else:
- self.build(self._options.build_style)
+ self.build(self._options.build_style, group)
Added: trunk/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py (0 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -0,0 +1,74 @@
+# Copyright (C) 2017 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 logging
+import re
+
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+
+_log = logging.getLogger(__name__)
+
+
+class CheckPatchRelevance(AbstractStep):
+ @classmethod
+ def options(cls):
+ return AbstractStep.options() + [
+ Options.group,
+ ]
+
+ jsc_paths = [
+ "JSTests/",
+ "Source/_javascript_Core/",
+ "Source/WTF/"
+ "Source/bmalloc/",
+ ]
+
+ group_to_paths_mapping = {
+ 'jsc': jsc_paths,
+ }
+
+ def _changes_are_relevant(self, changed_files):
+ # In the default case, all patches are relevant
+ if self._options.group is None:
+ return True
+
+ patterns = self.group_to_paths_mapping[self._options.group]
+
+ for changed_file in changed_files:
+ for pattern in patterns:
+ if re.search(pattern, changed_file, re.IGNORECASE):
+ return True
+
+ return False
+
+ def run(self, state):
+ _log.info("Checking relevance of patch")
+
+ change_list = self._tool.scm().changed_files()
+
+ if self._changes_are_relevant(change_list):
+ return True
+
+ _log.info("This patch does not have relevant changes.")
+ raise ScriptError(message="This patch does not have relevant changes.")
Modified: trunk/Tools/Scripts/webkitpy/tool/steps/options.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/options.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/options.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -46,6 +47,7 @@
email = make_option("--email", action="" type="string", dest="email", help="Email address to use in ChangeLogs.")
force_clean = make_option("--force-clean", action="" dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)")
git_commit = make_option("-g", "--git-commit", action="" dest="git_commit", help="Operate on a local commit. If a range, the commits are squashed into one. <ref>.... includes the working copy changes. UPSTREAM can be used for the upstream/tracking branch.")
+ group = make_option("--group", action="" dest="group", default=None, help="")
local_commit = make_option("--local-commit", action="" dest="local_commit", default=False, help="Make a local commit for each applied patch")
non_interactive = make_option("--non-interactive", action="" dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.")
obsolete_patches = make_option("--no-obsolete", action="" dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one.")
Modified: trunk/Tools/Scripts/webkitpy/tool/steps/runtests.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/runtests.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/runtests.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -53,9 +54,14 @@
Options.iterate_on_new_tests,
Options.non_interactive,
Options.quiet,
+ Options.group,
]
def run(self, state):
+ if self._options.group == "jsc":
+ self._run_javascriptcore_tests()
+ return
+
if self._options.iterate_on_new_tests:
_log.info("Running run-webkit-tests on new tests")
self._run_webkit_tests(self._options.iterate_on_new_tests)
@@ -143,3 +149,11 @@
args.append("--iterations=%d" % iterate_on_new_tests)
self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root)
+
+ def _run_javascriptcore_tests(self):
+ args = self._tool.deprecated_port().run_javascriptcore_tests_command(self._options.build_style)
+
+ results_directory = self._tool.port_factory.get().jsc_results_directory()
+ results_file_path = self._tool.filesystem.join(results_directory, "jsc_test_results.json")
+ args.append("--json-output=%s" % results_file_path)
+ self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root)
Modified: trunk/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -40,7 +40,7 @@
tool = MockTool(log_executive=True)
tool._deprecated_port.run_python_unittests_command = lambda: None
tool._deprecated_port.run_perl_unittests_command = lambda: None
- step = RunTests(tool, MockOptions(test=True, non_interactive=True, quiet=False, build_style="release", iterate_on_new_tests=0))
+ step = RunTests(tool, MockOptions(test=True, non_interactive=True, quiet=False, build_style="release", iterate_on_new_tests=0, group=None))
if sys.platform != "cygwin":
expected_logs = """Running bindings generation tests
Modified: trunk/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py (212578 => 212579)
--- trunk/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py 2017-02-17 22:39:45 UTC (rev 212578)
+++ trunk/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py 2017-02-17 22:41:49 UTC (rev 212579)
@@ -1,4 +1,5 @@
# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (C) 2017 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
@@ -28,6 +29,7 @@
import unittest
+from webkitpy.common.system.executive import ScriptError
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.common.config.ports import DeprecatedPort
from webkitpy.tool.mocktool import MockOptions, MockTool
@@ -38,6 +40,7 @@
class StepsTest(unittest.TestCase):
def _step_options(self):
options = MockOptions()
+ options.group = None
options.non_interactive = True
options.port = 'MOCK port'
options.quiet = True
@@ -111,7 +114,7 @@
Running Perl unit tests
MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitperl'], cwd=/mock-checkout
Running _javascript_Core tests
-MOCK run_and_throw_if_fail: ['Tools/Scripts/run-_javascript_core-tests'], cwd=/mock-checkout
+MOCK run_and_throw_if_fail: ['Tools/Scripts/run-_javascript_core-tests', '--no-fail-fast'], cwd=/mock-checkout
Running bindings generation tests
MOCK run_and_throw_if_fail: ['Tools/Scripts/run-bindings-tests'], cwd=/mock-checkout
Running run-webkit-tests
@@ -133,7 +136,7 @@
Running Perl unit tests
MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitperl'], cwd=/mock-checkout
Running _javascript_Core tests
-MOCK run_and_throw_if_fail: ['Tools/Scripts/run-_javascript_core-tests'], cwd=/mock-checkout
+MOCK run_and_throw_if_fail: ['Tools/Scripts/run-_javascript_core-tests', '--no-fail-fast'], cwd=/mock-checkout
Running bindings generation tests
MOCK run_and_throw_if_fail: ['Tools/Scripts/run-bindings-tests'], cwd=/mock-checkout
Running run-webkit-tests
@@ -140,3 +143,104 @@
MOCK run_and_throw_if_fail: ['Tools/Scripts/run-webkit-tests', '--debug', '--quiet'], cwd=/mock-checkout
"""
OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_runtests_jsc(self):
+ mock_options = self._step_options()
+ mock_options.non_interactive = False
+ mock_options.build_style = "release"
+ mock_options.group = "jsc"
+ step = steps.RunTests(MockTool(log_executive=True), mock_options)
+ tool = MockTool(log_executive=True)
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.RunTests(tool, mock_options)
+ expected_logs = """MOCK run_command: ['perl', 'Tools/Scripts/webkit-build-directory', '--configuration', '--release', '--mac'], cwd=/mock-checkout
+MOCK run_and_throw_if_fail: ['Tools/Scripts/run-_javascript_core-tests', '--no-fail-fast', '--release', '--json-output=/MOCK output of child process/jsc_test_results.json'], cwd=/mock-checkout
+"""
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_runtests_jsc_debug(self):
+ mock_options = self._step_options()
+ mock_options.non_interactive = False
+ mock_options.build_style = "debug"
+ mock_options.group = "jsc"
+ tool = MockTool(log_executive=True)
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.RunTests(tool, mock_options)
+ expected_logs = """MOCK run_command: ['perl', 'Tools/Scripts/webkit-build-directory', '--configuration', '--release', '--mac'], cwd=/mock-checkout
+MOCK run_and_throw_if_fail: ['Tools/Scripts/run-_javascript_core-tests', '--no-fail-fast', '--debug', '--json-output=/MOCK output of child process/jsc_test_results.json'], cwd=/mock-checkout
+"""
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_build_jsc_debug(self):
+ mock_options = self._step_options()
+ mock_options.non_interactive = False
+ mock_options.build_style = "debug"
+ mock_options.build = True
+ mock_options.architecture = True
+ mock_options.group = "jsc"
+ tool = MockTool(log_executive=True)
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.Build(tool, mock_options)
+ expected_logs = """Building WebKit
+MOCK run_and_throw_if_fail: ['Tools/Scripts/build-jsc', '--debug', 'ARCHS=True'], cwd=/mock-checkout, env={'LC_ALL': 'C', 'TERM': 'none', 'MOCK_ENVIRON_COPY': '1'}
+"""
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_build_jsc(self):
+ mock_options = self._step_options()
+ mock_options.non_interactive = False
+ mock_options.build_style = "release"
+ mock_options.build = True
+ mock_options.architecture = True
+ mock_options.group = "jsc"
+ tool = MockTool(log_executive=True)
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.Build(tool, mock_options)
+ expected_logs = """Building WebKit
+MOCK run_and_throw_if_fail: ['Tools/Scripts/build-jsc', '--release', 'ARCHS=True'], cwd=/mock-checkout, env={'LC_ALL': 'C', 'TERM': 'none', 'MOCK_ENVIRON_COPY': '1'}
+"""
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_patch_relevant(self):
+ self.maxDiff = None
+ mock_options = self._step_options()
+ tool = MockTool(log_executive=True)
+ tool.scm()._mockChangedFiles = ["JSTests/MockFile1", "ChangeLog"]
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.CheckPatchRelevance(tool, mock_options)
+ expected_logs = """Checking relevance of patch
+"""
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_patch_relevant_jsc(self):
+ self.maxDiff = None
+ mock_options = self._step_options()
+ mock_options.group = "jsc"
+ tool = MockTool(log_executive=True)
+ tool.scm()._mockChangedFiles = ["JSTests/MockFile1", "ChangeLog"]
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.CheckPatchRelevance(tool, mock_options)
+ expected_logs = """Checking relevance of patch
+"""
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
+
+ def test_patch_not_relevant_jsc(self):
+ self.maxDiff = None
+ mock_options = self._step_options()
+ mock_options.group = "jsc"
+ tool = MockTool(log_executive=True)
+ tool.scm()._mockChangedFiles = ["Tools/ChangeLog", "Tools/Scripts/webkitpy/tool/steps/steps_unittest.py"]
+ # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
+ tool._deprecated_port = DeprecatedPort()
+ step = steps.CheckPatchRelevance(tool, mock_options)
+ expected_logs = """Checking relevance of patch
+This patch does not have relevant changes.
+"""
+ with self.assertRaises(ScriptError):
+ OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)