WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-162458-20170215154807.patch (text/plain), 89.79 KB, created by
Srinivasan Vijayaraghavan
on 2017-02-15 15:50:43 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Srinivasan Vijayaraghavan
Created:
2017-02-15 15:50:43 PST
Size:
89.79 KB
patch
obsolete
>Subversion Revision: 212152 >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 213ef17837929d5c00f8b0f45b8e7248f1ee97b2..297202473517553ae64f21ff1404f5cd86458cfb 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,140 @@ >+2017-02-15 Srinivasan Vijayaraghavan <svijayaraghavan@apple.com> >+ >+ EWS should run JavaScriptCore tests >+ https://bugs.webkit.org/show_bug.cgi?id=162458 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * 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_test_results): Getter. Some of these exist for interopability reasons. >+ (JSCTestResults.test_results): 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._create_jsc_test_results): Creates a JSCTestResults object. >+ (JSCTestResultsReader.results): Invokes _create_jsc_test_results to return 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. >+ (MockAbstractEarlyWarningSystem): Added. >+ (MockAbstractEarlyWarningSystem.test_results): Simulates inconclusive results. >+ (MockAbstractEarlyWarningSystem._create_task): Use MockEarlyWarningSystemTask (not EarlyWarningSystemTask). >+ (MockEarlyWarningSystemTask): Added. >+ (MockEarlyWarningSystemTask._test_patch): Simulates running tests but not getting a conclusive result. >+ * 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-10 Joseph Pecoraro <pecoraro@apple.com> > > [Resource Timing] Enable Resource Timing by default in Tests >diff --git a/Tools/QueueStatusServer/config/messages.py b/Tools/QueueStatusServer/config/messages.py >index 6f9552932ce428dd36fe5090e95777bc48d651e5..a0934e2229d569765de5ae4151e418da3ce0dbb0 100644 >--- a/Tools/QueueStatusServer/config/messages.py >+++ b/Tools/QueueStatusServer/config/messages.py >@@ -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" >diff --git a/Tools/QueueStatusServer/config/queues.py b/Tools/QueueStatusServer/config/queues.py >index 341dd083adfbb8907198beed5eab2193026983f5..0a22ff32c8f03c87149698774f6438ed5fabadc7 100644 >--- a/Tools/QueueStatusServer/config/queues.py >+++ b/Tools/QueueStatusServer/config/queues.py >@@ -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 @@ all_queue_names = [ > "gtk-wk2-ews", > "ios-ews", > "ios-sim-ews", >+ "jsc-ews", > "mac-ews", > "mac-wk2-ews", > "mac-debug-ews", >diff --git a/Tools/QueueStatusServer/handlers/statusbubble.py b/Tools/QueueStatusServer/handlers/statusbubble.py >index c80514ff8789ef694b995714e2eb5d96f8bca782..38bac0399e3043730671527e40d34f153dbac4f0 100644 >--- a/Tools/QueueStatusServer/handlers/statusbubble.py >+++ b/Tools/QueueStatusServer/handlers/statusbubble.py >@@ -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,11 +183,15 @@ class StatusBubble(webapp.RequestHandler): > 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 >diff --git a/Tools/QueueStatusServer/model/queuestatus.py b/Tools/QueueStatusServer/model/queuestatus.py >index d9bb6f65637540c70b9781aed25b8823d9b38396..971e6dc5814ad4b8848c374c5d0cbcbbbe5b86f4 100644 >--- a/Tools/QueueStatusServer/model/queuestatus.py >+++ b/Tools/QueueStatusServer/model/queuestatus.py >@@ -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 @@ class QueueStatus(db.Model, QueuePropertyMixin): > 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 >diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py b/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py >index c5d10fcb189b0a8b519fa55625022a70c486d44b..9774ec8ae873a1b0b8376071806679215809eeec 100644 >--- a/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py >+++ b/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py >@@ -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 @@ class MockSCM(object): > 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 @@ class MockSCM(object): > 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"] >diff --git a/Tools/Scripts/webkitpy/common/config/ews.json b/Tools/Scripts/webkitpy/common/config/ews.json >index 35d4985ab0f93d127b3721e4601cd6d89dfccf3c..e5340fe5c3c21ae81ad1ef7e03f7fb81f3839cff 100644 >--- a/Tools/Scripts/webkitpy/common/config/ews.json >+++ b/Tools/Scripts/webkitpy/common/config/ews.json >@@ -50,5 +50,11 @@ > "port": "mac", > "name": "mac-32bit-ews", > "architecture": "i386" >+ }, >+ "JSC EWS": { >+ "port": "mac", >+ "name": "jsc-ews", >+ "group": "jsc", >+ "runTests": true > } > } >diff --git a/Tools/Scripts/webkitpy/common/config/ports.py b/Tools/Scripts/webkitpy/common/config/ports.py >index a0d50dcb6ea0b5fde0b3c17ef159ceafee1a2e98..1833933ea8f3d1c723f4704bafbce58b7cf133b8 100644 >--- a/Tools/Scripts/webkitpy/common/config/ports.py >+++ b/Tools/Scripts/webkitpy/common/config/ports.py >@@ -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,30 @@ class DeprecatedPort(object): > 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-javascriptcore-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-javascriptcore-tests") >+ command.append("--no-fail-fast") >+ command.append("--json-output=jsc_test_results.json") >+ 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") >diff --git a/Tools/Scripts/webkitpy/common/config/ports_mock.py b/Tools/Scripts/webkitpy/common/config/ports_mock.py >index 61743debb4ebee3756ba7c20f014823540d2dca6..ca1b18a31068423bdce07cdeb7d7d7d199f400ab 100644 >--- a/Tools/Scripts/webkitpy/common/config/ports_mock.py >+++ b/Tools/Scripts/webkitpy/common/config/ports_mock.py >@@ -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 @@ class MockPort(object): > 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): >diff --git a/Tools/Scripts/webkitpy/common/config/ports_unittest.py b/Tools/Scripts/webkitpy/common/config/ports_unittest.py >index 08d545236da3703e928f1ceed1dd9102d9b8622e..16cd7f928251bbc996cb646e4359d73fdb535b26 100644 >--- a/Tools/Scripts/webkitpy/common/config/ports_unittest.py >+++ b/Tools/Scripts/webkitpy/common/config/ports_unittest.py >@@ -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 @@ class DeprecatedPortTest(unittest.TestCase): > 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"]) >diff --git a/Tools/Scripts/webkitpy/common/net/abstracttestresults.py b/Tools/Scripts/webkitpy/common/net/abstracttestresults.py >new file mode 100644 >index 0000000000000000000000000000000000000000..6fcfd0825c3086024c1c82480773c5ff95e8a4ae >--- /dev/null >+++ b/Tools/Scripts/webkitpy/common/net/abstracttestresults.py >@@ -0,0 +1,36 @@ >+# 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 test_results(self): >+ raise NotImplementedError("subclasses must implement") >+ >+ def failing_tests(self): >+ raise NotImplementedError("subclasses must implement") >+ >+ def did_exceed_test_failure_limit(self): >+ raise NotImplementedError("subclasses must implement") >diff --git a/Tools/Scripts/webkitpy/common/net/jsctestresults.py b/Tools/Scripts/webkitpy/common/net/jsctestresults.py >new file mode 100644 >index 0000000000000000000000000000000000000000..4eff3fae52b6783f77d3387b159fdafb0e2ecb50 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/common/net/jsctestresults.py >@@ -0,0 +1,80 @@ >+# 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.results = stress_test_failures[:] >+ if not self.all_api_tests_passed: >+ self.results.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.results) <= set(other.results) >+ >+ def all_passed(self): >+ return self.all_api_tests_passed and not self.stress_test_failures >+ >+ def test_results(self): >+ return self.results >+ >+ def failing_tests(self): >+ return self.results >+ >+ # No defined failure limit for JSC. >+ def did_exceed_test_failure_limit(self): >+ return False >diff --git a/Tools/Scripts/webkitpy/common/net/jsctestresults_unittest.py b/Tools/Scripts/webkitpy/common/net/jsctestresults_unittest.py >new file mode 100644 >index 0000000000000000000000000000000000000000..4cf1149afcd63b5fba6683149bca837ac6eab916 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/common/net/jsctestresults_unittest.py >@@ -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))) >diff --git a/Tools/Scripts/webkitpy/common/net/layouttestresults.py b/Tools/Scripts/webkitpy/common/net/layouttestresults.py >index 7fb67c16b7e1fdb0a64607b55314e2700fa12e9a..70616edb2405ceec513ff8485b755bca92873c0e 100644 >--- a/Tools/Scripts/webkitpy/common/net/layouttestresults.py >+++ b/Tools/Scripts/webkitpy/common/net/layouttestresults.py >@@ -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 @@ def path_for_layout_test(test_name): > # 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: >diff --git a/Tools/Scripts/webkitpy/port/base.py b/Tools/Scripts/webkitpy/port/base.py >index 01e82c2d68a0a55ba772ddd37f6c6d2289e9b587..6a6142a7c179f33a73d3721903e1ad013b96d4ab 100644 >--- a/Tools/Scripts/webkitpy/port/base.py >+++ b/Tools/Scripts/webkitpy/port/base.py >@@ -808,6 +808,9 @@ class Port(object): > 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: >diff --git a/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py b/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py >index fc308b43d1d172ab73b0e3af80d1b139dd3c1032..ecfacd011be883b4bb1e5c7d7dffdbdcb459de35 100644 >--- a/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py >+++ b/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py >@@ -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 @@ class EarlyWarningSystemTask(PatchAnalysisTask): > 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 >diff --git a/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py b/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py >new file mode 100644 >index 0000000000000000000000000000000000000000..a6aef3dae8011fab575f2c543110014e97a6b20a >--- /dev/null >+++ b/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py >@@ -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) >diff --git a/Tools/Scripts/webkitpy/tool/bot/jsctestresultsreader.py b/Tools/Scripts/webkitpy/tool/bot/jsctestresultsreader.py >new file mode 100644 >index 0000000000000000000000000000000000000000..c41c16e70fb439a95f9ad3ad81deae78e5ec5ee3 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/tool/bot/jsctestresultsreader.py >@@ -0,0 +1,47 @@ >+# 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): # File does not exist or can't be read. >+ return None >+ >+ def _create_jsc_test_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) >+ >+ def results(self): >+ return self._create_jsc_test_results() >diff --git a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py b/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py >index 752316416c5f9d0a3e59ef6d05e31785f9174629..dd2f1c59b7673e3b947b689f56d7b0c10adad0a7 100644 >--- a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py >+++ b/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py >@@ -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 @@ class PatchIsNotValid(Exception): > 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 @@ class PatchAnalysisTask(object): > "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 @@ class PatchAnalysisTask(object): > "--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,9 +190,12 @@ class PatchAnalysisTask(object): > "--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. >@@ -206,10 +235,35 @@ class PatchAnalysisTask(object): > # 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 >+ >+ consistent_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 consistent_results.is_subset(clean_tree_results): >+ return True >+ >+ self.failure_status_id = first_failure_status_id >+ return self.report_failure(None, consistent_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 @@ class PatchAnalysisTask(object): > 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,14 +315,16 @@ class PatchAnalysisTask(object): > > 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 > # 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 @@ class PatchAnalysisTask(object): > # 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 >diff --git a/Tools/Scripts/webkitpy/tool/commands/download.py b/Tools/Scripts/webkitpy/tool/commands/download.py >index e5169aac85f3186e84be9d5024592bd7fe6d5dd5..14b55bc3f7c9c10b5df60fc1af87d7ca39e3bc94 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/download.py >+++ b/Tools/Scripts/webkitpy/tool/commands/download.py >@@ -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 BuildAndTest(AbstractSequencedCommand): > ] > > >+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" >diff --git a/Tools/Scripts/webkitpy/tool/commands/download_unittest.py b/Tools/Scripts/webkitpy/tool/commands/download_unittest.py >index 429da1246f8bd76ab02ee633c42d6fb38d3854c5..59c9488c094099b7347e96ef202c360d91946303 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/download_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/download_unittest.py >@@ -89,6 +89,7 @@ class DownloadCommandsTest(CommandsTest): > options.update = True > options.architecture = 'MOCK ARCH' > options.iterate_on_new_tests = 0 >+ options.group = None > return options > > def test_build(self): >diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >index 9bc041860ce67454454e4b251438a9dbe5264513..68f1d80320cb257e26baf8401fb7aa5120419dc1 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >@@ -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.filesystem import FileSystem > 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,7 +56,11 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > > 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 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > 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 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > 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 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > 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,12 +166,13 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > > 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), > '_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 >diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py >index 08887813ce2e93a8ec9da37636ecbde9eff3c9e7..8ec3a2b99a25995e9bdb3e550bb81e0b60121687 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py >@@ -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,62 +43,100 @@ from webkitpy.tool.commands.queuestest import QueuesTest > from webkitpy.tool.mocktool import MockTool, MockOptions > > >-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 >+# 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 > >- ews = TestEWS() >+ >+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_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 MockEarlyWarningSystemTaskForInconclusiveResults(EarlyWarningSystemTask): >+ def _test_patch(self): >+ self._test() >+ results = self._delegate.test_results() >+ return bool(results) >+ >+ >+class MockAbstractEarlyWarningSystemForInconclusiveResults(AbstractEarlyWarningSystem): >+ def _create_task(self, patch): >+ task = MockEarlyWarningSystemTaskForInconclusiveResults(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 > 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,12 @@ MOCK: release_work_item: %(name)s 10000 > self.maxDiff = None > for ews_class in classes: > self._test_ews(ews_class()) >+ >+ # This must use the Mock EWS classes defined above >+ def test_inconclusive_jsc_test_results(self): >+ classes = MockAbstractEarlyWarningSystemForInconclusiveResults.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) >diff --git a/Tools/Scripts/webkitpy/tool/commands/queues.py b/Tools/Scripts/webkitpy/tool/commands/queues.py >index 1c71aacd7530dc728f554909e2d2bbc77d8d11cb..fe2127cfe274edf075ecd2cd239b470ed830f97a 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/queues.py >+++ b/Tools/Scripts/webkitpy/tool/commands/queues.py >@@ -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 @@ class AbstractQueue(Command, QueueEngineDelegate): > watchers = [ > ] > >+ _skip_status = "Skip" > _pass_status = "Pass" > _fail_status = "Fail" > _error_status = "Error" >@@ -244,6 +245,10 @@ class AbstractPatchQueue(AbstractQueue): > 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) > >diff --git a/Tools/Scripts/webkitpy/tool/steps/__init__.py b/Tools/Scripts/webkitpy/tool/steps/__init__.py >index 356acab03ad8274ce9bda6858864ff19b64f4a0a..6c9c09ac52a386b9f0cf363b9c03bbaa4c003861 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/__init__.py >+++ b/Tools/Scripts/webkitpy/tool/steps/__init__.py >@@ -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.applypatchwithlocalcommit import ApplyPatchWithLocalCom > 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 >diff --git a/Tools/Scripts/webkitpy/tool/steps/build.py b/Tools/Scripts/webkitpy/tool/steps/build.py >index f74ffce70941c61e596792e4c60dcd7f8b65e9ec..6d648e34fe3a929a8ca6b71b02a70d9a84325d1a 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/build.py >+++ b/Tools/Scripts/webkitpy/tool/steps/build.py >@@ -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,28 +42,35 @@ class Build(AbstractStep): > 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): > 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) >diff --git a/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py b/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py >new file mode 100644 >index 0000000000000000000000000000000000000000..d56177751949a22b749f1a4cc4370d7cea0dcf6f >--- /dev/null >+++ b/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py >@@ -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 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/JavaScriptCore/", >+ "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): >+ _log.info("This patch has relevant changes.") >+ return True >+ >+ _log.info("This patch does not have relevant changes.") >+ raise ScriptError(message="This patch does not have relevant changes.") >diff --git a/Tools/Scripts/webkitpy/tool/steps/options.py b/Tools/Scripts/webkitpy/tool/steps/options.py >index 3ef47d008a59e050d4ca0c4877ddd1a60731a409..f5e0def32717e306e63169841110b2898b72a1f1 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/options.py >+++ b/Tools/Scripts/webkitpy/tool/steps/options.py >@@ -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 @@ class Options(object): > email = make_option("--email", action="store", type="string", dest="email", help="Email address to use in ChangeLogs.") > force_clean = make_option("--force-clean", action="store_true", 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="store", 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="store", dest="group", default=None, help="") > local_commit = make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch") > non_interactive = make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.") > obsolete_patches = make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one.") >diff --git a/Tools/Scripts/webkitpy/tool/steps/runtests.py b/Tools/Scripts/webkitpy/tool/steps/runtests.py >index 75d72bc9ad2f4e36d45fdac90f1819481cf0288b..ef979e572786f71ec331049ecfb71f6cdfecae95 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/runtests.py >+++ b/Tools/Scripts/webkitpy/tool/steps/runtests.py >@@ -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 @@ class RunTests(AbstractStep): > 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,7 @@ class RunTests(AbstractStep): > 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) >+ self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root) >diff --git a/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py b/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py >index 30006819200ad92d8609642e8f8027d246d87849..d4d99646c2ff05ecddcc8d41e2793f50fcbdf464 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py >@@ -40,7 +40,7 @@ class RunTestsTest(unittest.TestCase): > 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 >diff --git a/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py >index 7d94a13b3463d98a44f949f814cc7121c068ab5d..0f1fcfc9ba88cd96833ee14e798be341d640ff32 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py >@@ -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 @@ from webkitpy.tool import steps > 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 @@ MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitpy'], cwd=/mock-checkout > Running Perl unit tests > MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitperl'], cwd=/mock-checkout > Running JavaScriptCore tests >-MOCK run_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests'], cwd=/mock-checkout >+MOCK run_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests', '--no-fail-fast', '--json-output=jsc_test_results.json'], 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,10 +136,111 @@ MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitpy'], cwd=/mock-checkout > Running Perl unit tests > MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitperl'], cwd=/mock-checkout > Running JavaScriptCore tests >-MOCK run_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests'], cwd=/mock-checkout >+MOCK run_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests', '--no-fail-fast', '--json-output=jsc_test_results.json'], 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 > 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_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests', '--no-fail-fast', '--json-output=jsc_test_results.json', '--release'], 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_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests', '--no-fail-fast', '--json-output=jsc_test_results.json', '--debug'], 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 >+This patch has relevant changes. >+""" >+ 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 >+This patch has relevant changes. >+""" >+ 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)
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 162458
:
297210
|
297239
|
297551
|
298160
|
298714
|
298739
|
298802
|
298807
|
300176
|
300294
|
300461
|
300474
|
300784
|
300838
|
301020
|
301069
|
301117
|
301137
|
301437
|
301572
|
301667
|
301848
|
301849
|
301972
|
302000