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-20170209000230.patch (text/plain), 84.66 KB, created by
Srinivasan Vijayaraghavan
on 2017-02-09 00:04:55 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Srinivasan Vijayaraghavan
Created:
2017-02-09 00:04:55 PST
Size:
84.66 KB
patch
obsolete
>Subversion Revision: 211662 >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index a1326b16f87dc93f692652ca6742ae9238ae7d2a..72f3932da8294be62e4e5c251118928f8ffc661d 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,128 @@ >+2017-02-08 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/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.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/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.stress_test_failures): Getter. >+ (JSCTestResults.all_api_tests_passed): Getter. >+ (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/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._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. >+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py: >+ (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-03 Simon Fraser <simon.fraser@apple.com> > > Correctly set the "inWindow" flag for TileControllers that aren't the page tiles, and clarify "usingTiledBacking" logic >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..273f8dff06c69b48305b938330ec502e003b3b75 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,19 @@ 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)) >+ >+ # EWS queues are also shown when complete >+ # But JSC EWS bubble is not shown if the patch is not relevant to JSC. >+ if not queue.is_ews(): >+ return False >+ >+ status = attachment.status_for_queue(queue) >+ if queue.name() == "jsc-ews": >+ return not status.message.startswith("Patch not applicable") >+ return bool(status) > > def _build_bubbles_for_attachment(self, attachment): > show_submit_to_ews = True >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..d37ed9d1c5e2550e48834e7ef00bae4c24cbd784 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 >@@ -103,8 +103,21 @@ class DeprecatedPort(object): > command.append("--release") > return command > >- def run_javascriptcore_tests_command(self): >- return self.script_shell_command("run-javascriptcore-tests") >+ def build_jsc_command(self, build_style=None): >+ command = self.script_shell_command("build-jsc") >+ if build_style == "debug": >+ command.append("--debug") >+ if build_style == "release": >+ command.append("--release") >+ return command >+ >+ 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") >+ if (build_style == "debug"): >+ command.append("--debug") >+ return command > > def run_webkit_tests_command(self, build_style=None): > command = self.script_shell_command("run-webkit-tests") >@@ -150,7 +163,10 @@ class IOSSimulatorWK2Port(DeprecatedPort): > class MacPort(DeprecatedPort): > port_flag_name = "mac" > >- def run_webkit_tests_command(self, build_style=None): >+ def run_webkit_tests_command(self, build_style=None, test_suite=None): >+ if (test_suite == "jsc"): >+ return super(MacPort, self).run_javascriptcore_tests_command(build_style) >+ > command = super(MacPort, self).run_webkit_tests_command(build_style) > command.append("--dump-render-tree") > return command >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/jsctestresults.py b/Tools/Scripts/webkitpy/common/net/jsctestresults.py >new file mode 100644 >index 0000000000000000000000000000000000000000..be6eb0e30f6736ba9942cf0f8c168d32132aa7b5 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/common/net/jsctestresults.py >@@ -0,0 +1,86 @@ >+# 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 >+ >+_log = logging.getLogger(__name__) >+ >+ >+class JSCTestResults(object): >+ @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: >+ 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 __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._test_results = stress_test_failures[:] >+ if not self._all_api_tests_passed: >+ self._test_results.append("apiTests") >+ >+ 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.test_results()) <= set(other.test_results()) >+ >+ def stress_test_failures(self): >+ return self._stress_test_failures >+ >+ def all_api_tests_passed(self): >+ return self._all_api_tests_passed >+ >+ def all_passed(self): >+ return self._all_api_tests_passed and not self._stress_test_failures >+ >+ def failing_test_results(self): >+ return self.stress_test_failures() >+ >+ def test_results(self): >+ return self._test_results >+ >+ def failing_tests(self): >+ return self._test_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..18ef444a568e460880459176c5ffac06f15f17b6 >--- /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_string = "" >+ 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_string)) >+ 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/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..7730de1113eb8c592e38ff651a0da6bc16ff0249 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/tool/bot/jscews_unittest.py >@@ -0,0 +1,220 @@ >+# 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__) >+ >+ >+# FIXME: It would be better semantics to use a MockEarlyWarningSystemTask here instead, but we already have one. >+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 = 42 >+ >+ def _test(self): >+ self._test_run_count += 1 >+ >+ if self._patches_passed_all_tests.pop() == True: >+ return True >+ else: >+ 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.assertEqual(return_value, True) >+ >+ 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.assertEqual(return_value, True) >+ >+ 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.assertEqual(return_value, True) >+ >+ 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.assertEqual(return_value, True) >+ >+ 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.assertEqual(return_value, False) >+ >+ 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.assertEqual(return_value, False) >+ >+ 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.assertEqual(return_value, False) >+ >+ 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.assertEqual(return_value, True) >+ 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.assertEqual(return_value, True) >+ 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): >+ return_value = 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.assertEqual(return_value, True) >+ 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..a56bd05dfa77459d46c37be85d44918948a06666 >--- /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..8e9b971bfde34f2e3a101e8aab3d6cd97ca0dc5b 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,53 @@ 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 +183,18 @@ 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 +202,15 @@ 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. >@@ -210,69 +254,98 @@ class PatchAnalysisTask(object): > if self._test(): > return True > >- # 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() >- first_results_archive = self._delegate.archive_last_test_results(self._patch) >- first_script_error = self._script_error >- first_failure_status_id = self.failure_status_id >- >- if self._test() and not first_results.did_exceed_test_failure_limit(): >- # Only report flaky tests if we were successful at parsing results.json and archiving results. >- if first_results and first_results_archive: >- self._report_flaky_tests(first_results.failing_test_results(), first_results_archive) >- return True >+ # FIXME: Abstract out the retry logic instead of casing on the test suite here. >+ if hasattr(self._delegate, 'group') and self._delegate.group() == "jsc": >+ 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 > >- second_results = self._delegate.test_results() >- second_results_archive = self._delegate.archive_last_test_results(self._patch) >- second_script_error = self._script_error >- second_failure_status_id = self.failure_status_id >+ 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) > >- if second_results.did_exceed_test_failure_limit() and first_results.did_exceed_test_failure_limit(): > self._build_and_test_without_patch() > clean_tree_results = self._delegate.test_results() >- >- if (len(first_results.failing_tests()) - len(clean_tree_results.failing_tests())) <= 5: >+ 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) > >- return self.report_failure(first_results_archive, first_results, first_script_error) >+ else: >+ # 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() >+ first_results_archive = self._delegate.archive_last_test_results(self._patch) >+ first_script_error = self._script_error >+ first_failure_status_id = self.failure_status_id > >- 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) >- return False >+ if self._test() and not first_results.did_exceed_test_failure_limit(): >+ # Only report flaky tests if we were successful at parsing results.json and archiving results. >+ if first_results and first_results_archive: >+ self._report_flaky_tests(first_results.failing_test_results(), first_results_archive) >+ return True > >- 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) >- return False >+ second_results = self._delegate.test_results() >+ second_results_archive = self._delegate.archive_last_test_results(self._patch) >+ second_script_error = self._script_error >+ second_failure_status_id = self.failure_status_id >+ >+ if second_results.did_exceed_test_failure_limit() and first_results.did_exceed_test_failure_limit(): >+ self._build_and_test_without_patch() >+ clean_tree_results = self._delegate.test_results() >+ >+ if (len(first_results.failing_tests()) - len(clean_tree_results.failing_tests())) <= 5: >+ return False >+ >+ self.failure_status_id = first_failure_status_id >+ >+ return self.report_failure(first_results_archive, first_results, first_script_error) > >- if self._results_failed_different_tests(first_results, second_results): >- first_failing_results_set = frozenset(first_results.failing_test_results()) >- second_failing_results_set = frozenset(second_results.failing_test_results()) >+ 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) >+ 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) >+ return False > >- tests_that_only_failed_first = first_failing_results_set.difference(second_failing_results_set) >- self._report_flaky_tests(tests_that_only_failed_first, first_results_archive) >+ if self._results_failed_different_tests(first_results, second_results): >+ first_failing_results_set = frozenset(first_results.failing_test_results()) >+ second_failing_results_set = frozenset(second_results.failing_test_results()) > >- tests_that_only_failed_second = second_failing_results_set.difference(first_failing_results_set) >- self._report_flaky_tests(tests_that_only_failed_second, second_results_archive) >+ tests_that_only_failed_first = first_failing_results_set.difference(second_failing_results_set) >+ self._report_flaky_tests(tests_that_only_failed_first, first_results_archive) > >- 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): >- return False # Defer patch >+ tests_that_only_failed_second = second_failing_results_set.difference(first_failing_results_set) >+ self._report_flaky_tests(tests_that_only_failed_second, second_results_archive) > >- # 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 >+ 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): >+ 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): >- 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 > >- # At this point, we know that the first and second runs had the exact same failures, >- # and that those failures are all present on the clean tree, so we can say with certainty >- # that the patch is good. >- return True >+ 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, >+ # and that those failures are all present on the clean tree, so we can say with certainty >+ # that the patch is good. >+ return True > > def results_archive_from_patch_test_run(self, patch): > assert(self._patch.id() == patch.id()) # PatchAnalysisTask is not currently re-useable. >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/earlywarningsystem.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >index 9bc041860ce67454454e4b251438a9dbe5264513..86b18655f2ea249a2a61b5b452ed834fa2a7236e 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,12 @@ 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": >+ dir = os.path.dirname(os.path.dirname(os.path.dirname(self._port.results_directory()))) >+ self._test_results_reader = JSCTestResultsReader(self._tool, dir) >+ 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 +87,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 +105,10 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > except UnableToApplyPatch, e: > self._did_error(patch, "%s unable to apply patch." % self.name) > return False >+ except PatchIsNotApplicable, e: >+ self._update_status("Patch not applicable to %s." % self.name, patch) >+ self._release_work_item(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 +133,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 +168,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..5a963f73163d56bc80fe269794d2c134e7ea8c91 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,11 +30,13 @@ > 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 > from webkitpy.layout_tests.models import test_failures > from webkitpy.port.factory import PortFactory >+from webkitpy.tool.bot.earlywarningsystemtask import EarlyWarningSystemTask > from webkitpy.tool.bot.queueengine import QueueEngine > from webkitpy.tool.commands.earlywarningsystem import * > from webkitpy.tool.commands.queues import PatchProcessingQueue >@@ -41,12 +44,28 @@ from webkitpy.tool.commands.queuestest import QueuesTest > from webkitpy.tool.mocktool import MockTool, MockOptions > > >+class MockEarlyWarningSystemTask(EarlyWarningSystemTask): >+ def _test_patch(self): >+ self._test() >+ return False >+ >+ >+class MockAbstractEarlyWarningSystem(AbstractEarlyWarningSystem): >+ def test_results(self): >+ return None >+ >+ def _create_task(self, patch): >+ task = MockEarlyWarningSystemTask(self, patch, self._options.run_tests) >+ return task >+ >+ > 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 >+ _group = None > > ews = TestEWS() > ews.bind_to_tool(MockTool()) >@@ -60,43 +79,66 @@ class AbstractEarlyWarningSystemTest(QueuesTest): > patch = ews._tool.bugs.fetch_attachment(10000) > self.assertMultiLineEqual(ews._failing_tests_message(task, patch), "New failing tests:\nfoo.html\nbar.html") > >+ def test_failing_jsc_tests_message(self): >+ # Needed to define port_name, used in AbstractEarlyWarningSystem.__init__ >+ class TestEWS(AbstractEarlyWarningSystem): >+ port_name = "mac" # Needs to be a port which port/factory understands. >+ _build_style = None >+ _group = "jsc" >+ >+ ews = TestEWS() >+ 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: JSCTestResults(False, ["es6.yaml/es6/typed_arrays_Int8Array.js.default", "es6.yaml/es6/typed_arrays_Uint8Array.js.default"]) >+ patch = ews._tool.bugs.fetch_attachment(10000) >+ self.assertMultiLineEqual(ews._failing_tests_message(task, patch), "New failing tests:\nes6.yaml/es6/typed_arrays_Int8Array.js.default\nes6.yaml/es6/typed_arrays_Uint8Array.js.default\napiTests") >+ > > 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 +146,11 @@ MOCK: release_work_item: %(name)s 10000 > self.maxDiff = None > for ews_class in classes: > self._test_ews(ews_class()) >+ >+ def test_inconclusive_test_results(self): >+ classes = MockAbstractEarlyWarningSystem.load_ews_classes() >+ self.assertTrue(classes) >+ self.maxDiff = None >+ test_classes = filter(lambda x: x.run_tests, classes) >+ for ews_class in test_classes: >+ self._test_ews(ews_class(), False) >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..3c6cf0b1de018c132b7f86da2cb907e2fda067ae 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,37 @@ 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 = None >+ if hasattr(self._options, 'group'): >+ 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..82f745e34db9d69b2445348844685966ea663252 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/tool/steps/checkpatchrelevance.py >@@ -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/JavaScriptCore/", >+ "Source/WTF/" >+ "Source/bmalloc/", >+ ] >+ >+ def _does_contain_change_in_paths(self, files, patterns): >+ for item in files: >+ for pattern in patterns: >+ if re.search(pattern, item, re.IGNORECASE): >+ return True >+ >+ return False >+ >+ def run(self, state): >+ _log.info("Checking relevance of patch") >+ >+ group = None >+ if hasattr(self._options, 'group'): >+ group = self._options.group >+ >+ change_list = self._tool.scm().changed_files() >+ >+ if group == "jsc": >+ relevant = self._does_contain_change_in_paths(change_list, self.jsc_paths) >+ else: >+ relevant = True >+ >+ if relevant is True: >+ _log.info("This patch has relevant changes.") >+ return True >+ else: >+ _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..8b2240e2a8c705cc272548f13eda2639302f198b 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 hasattr(self._options, "group") and 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/steps_unittest.py b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py >index 7d94a13b3463d98a44f949f814cc7121c068ab5d..c3300c85944cde21ad9d0b3e56ded88013eb6ade 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 >@@ -111,7 +113,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 +135,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'], 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