Title: [243030] trunk/Tools
Revision
243030
Author
[email protected]
Date
2019-03-15 19:48:57 -0700 (Fri, 15 Mar 2019)

Log Message

webkitpy: Upload test results
https://bugs.webkit.org/show_bug.cgi?id=195755
<rdar://problem/48896182>

Reviewed by Aakash Jain.

Establish a new format for uploading results that is not tied to layout tests, apply
that format to webkitpy tests.

* Scripts/webkitpy/common/checkout/scm/git.py:
(Git.native_branch): Return what branch the current checkout is on.
* Scripts/webkitpy/common/checkout/scm/scm_mock.py:
* Scripts/webkitpy/common/checkout/scm/scm_unittest.py:
* Scripts/webkitpy/common/checkout/scm/svn.py:
(SVN.native_branch): Ditto.
* Scripts/webkitpy/common/system/platforminfo.py:
(PlatformInfo.build_version): Return a build version for Mac.
* Scripts/webkitpy/common/system/platforminfo_mock.py:
(MockPlatformInfo.__init__):
(MockPlatformInfo.build_version):
* Scripts/webkitpy/results: Added.
* Scripts/webkitpy/results/__init__.py: Added.
* Scripts/webkitpy/results/options.py: Added.
(upload_options): OptParse list for upload options.
* Scripts/webkitpy/results/upload.py: Added.
(Upload): Class which enforces the upload format expected by the results server.
(Upload.Expectations):
(Upload.create_configuration):
(Upload.create_commit):
(Upload.create_details):
(Upload.create_run_stats):
(Upload.create_test_result):
(Upload.__init__):
(Upload.Encoder): Encode Upload object as json.
(Upload.upload): Upload results to the results server, returning 'True' if the upload is successful.
* Scripts/webkitpy/results/upload_unittest.py: Added.
* Scripts/webkitpy/test/main.py:
(Tester._parse_args): Add upload arguments.
(Tester._run_tests): Allow results to be uploaded.
* Scripts/webkitpy/test/runner.py:
(Runner.__init__): Record which tests were run, rather than just counting them.
(Runner.handle):
* Scripts/webkitpy/test/runner_unittest.py:
(RunnerTest.test_run):
* Scripts/webkitpy/thirdparty/__init__.py:
(AutoinstallImportHook.find_module): Add requests auto-install.
(AutoinstallImportHook._install_requests):
* Scripts/webkitpy/tool/commands/queues_unittest.py:
(PatchProcessingQueueTest.test_upload_results_archive_for_patch): Update os name for testing.

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (243029 => 243030)


--- trunk/Tools/ChangeLog	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/ChangeLog	2019-03-16 02:48:57 UTC (rev 243030)
@@ -1,3 +1,55 @@
+2019-03-15  Jonathan Bedard  <[email protected]>
+
+        webkitpy: Upload test results
+        https://bugs.webkit.org/show_bug.cgi?id=195755
+        <rdar://problem/48896182>
+
+        Reviewed by Aakash Jain.
+
+        Establish a new format for uploading results that is not tied to layout tests, apply
+        that format to webkitpy tests.
+
+        * Scripts/webkitpy/common/checkout/scm/git.py:
+        (Git.native_branch): Return what branch the current checkout is on.
+        * Scripts/webkitpy/common/checkout/scm/scm_mock.py:
+        * Scripts/webkitpy/common/checkout/scm/scm_unittest.py:
+        * Scripts/webkitpy/common/checkout/scm/svn.py:
+        (SVN.native_branch): Ditto.
+        * Scripts/webkitpy/common/system/platforminfo.py:
+        (PlatformInfo.build_version): Return a build version for Mac.
+        * Scripts/webkitpy/common/system/platforminfo_mock.py:
+        (MockPlatformInfo.__init__):
+        (MockPlatformInfo.build_version):
+        * Scripts/webkitpy/results: Added.
+        * Scripts/webkitpy/results/__init__.py: Added.
+        * Scripts/webkitpy/results/options.py: Added.
+        (upload_options): OptParse list for upload options.
+        * Scripts/webkitpy/results/upload.py: Added.
+        (Upload): Class which enforces the upload format expected by the results server.
+        (Upload.Expectations):
+        (Upload.create_configuration):
+        (Upload.create_commit):
+        (Upload.create_details):
+        (Upload.create_run_stats):
+        (Upload.create_test_result):
+        (Upload.__init__):
+        (Upload.Encoder): Encode Upload object as json.
+        (Upload.upload): Upload results to the results server, returning 'True' if the upload is successful.
+        * Scripts/webkitpy/results/upload_unittest.py: Added.
+        * Scripts/webkitpy/test/main.py:
+        (Tester._parse_args): Add upload arguments.
+        (Tester._run_tests): Allow results to be uploaded.
+        * Scripts/webkitpy/test/runner.py:
+        (Runner.__init__): Record which tests were run, rather than just counting them.
+        (Runner.handle):
+        * Scripts/webkitpy/test/runner_unittest.py:
+        (RunnerTest.test_run):
+        * Scripts/webkitpy/thirdparty/__init__.py:
+        (AutoinstallImportHook.find_module): Add requests auto-install.
+        (AutoinstallImportHook._install_requests):
+        * Scripts/webkitpy/tool/commands/queues_unittest.py:
+        (PatchProcessingQueueTest.test_upload_results_archive_for_patch): Update os name for testing.
+
 2019-03-15  Wenson Hsieh  <[email protected]>
 
         [iOS] Crash under -[TestRunnerWKWebView _didShowMenu] while running layout tests on iOS simulator

Modified: trunk/Tools/Scripts/webkitpy/common/checkout/scm/git.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/common/checkout/scm/git.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/common/checkout/scm/git.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -290,6 +290,14 @@
     def native_revision(self, path):
         return self._run_git(['-C', self.find_checkout_root(path), 'log', '-1', '--pretty=format:%H'])
 
+    def native_branch(self, path):
+        result = self._run_git(['-C', self.find_checkout_root(path), 'rev-parse', '--abbrev-ref', 'HEAD']).rstrip()
+
+        # For git-svn
+        if result.startswith('heads'):
+            return result[6:]
+        return result
+
     def svn_url(self):
         git_command = ['svn', 'info']
         status = self._run_git(git_command)

Modified: trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -87,6 +87,9 @@
     def native_revision(self, path):
         return self.svn_revision(path)
 
+    def native_branch(self, path):
+        return 'trunk'
+
     def timestamp_of_revision(self, path, revision):
         return '2013-02-01 08:48:05 +0000'
 

Modified: trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -909,6 +909,9 @@
         self.assertEqual(self.scm.head_svn_revision(), self.scm.native_revision('.'))
         self.assertEqual(self.scm.native_revision('.'), '5')
 
+    def test_native_branch(self):
+        self.assertEqual(self.scm.native_branch('.'), 'trunk')
+
     def test_propset_propget(self):
         filepath = os.path.join(self.svn_checkout_path, "test_file")
         expected_mime_type = "x-application/foo-bar"
@@ -1112,6 +1115,10 @@
         command = ['git', '-C', scm.checkout_root, 'rev-parse', 'HEAD']
         self.assertEqual(scm.native_revision(scm.checkout_root), run_command(command).strip())
 
+    def test_native_branch(self):
+        scm = self.tracking_scm
+        self.assertEqual('master', scm.native_branch(scm.checkout_root))
+
     def test_rename_files(self):
         scm = self.tracking_scm
 
@@ -1627,6 +1634,9 @@
         command = ['git', '-C', self.git_checkout_path, 'rev-parse', 'HEAD']
         self.assertEqual(self.scm.native_revision(self.git_checkout_path), run_command(command).strip())
 
+    def test_native_branch(self):
+        self.assertEqual('trunk', self.scm.native_branch(self.git_checkout_path))
+
     def test_to_object_name(self):
         relpath = 'test_file_commit1'
         fullpath = os.path.realpath(os.path.join(self.git_checkout_path, relpath))

Modified: trunk/Tools/Scripts/webkitpy/common/checkout/scm/svn.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/common/checkout/scm/svn.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/common/checkout/scm/svn.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -83,7 +83,7 @@
         SCM.__init__(self, cwd, **kwargs)
         self._bogus_dir = None
         if patch_directories == []:
-            raise Exception(message='Empty list of patch directories passed to SCM.__init__')
+            raise Exception('Empty list of patch directories passed to SCM.__init__')
         elif patch_directories == None:
             self._patch_directories = [self._filesystem.relpath(cwd, self.checkout_root)]
         else:
@@ -278,6 +278,14 @@
     def native_revision(self, path):
         return self.svn_revision(path)
 
+    def native_branch(self, path):
+        relative_url = self.value_from_svn_info(path, 'Relative URL')[2:]
+        if relative_url.startswith('trunk'):
+            return 'trunk'
+        elif relative_url.startswith('branch'):
+            return relative_url.split('/')[1]
+        raise Exception('{} is not a branch'.format(relative_url.split('/')[0]))
+
     def timestamp_of_revision(self, path, revision):
         # We use --xml to get timestamps like 2013-02-08T08:18:04.964409Z
         repository_root = self.value_from_svn_info(self.checkout_root, 'Repository Root')

Modified: trunk/Tools/Scripts/webkitpy/common/system/platforminfo.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/common/system/platforminfo.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/common/system/platforminfo.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -146,6 +146,11 @@
         except:
             return sys.maxint
 
+    def build_version(self):
+        if self.is_mac():
+            return self._executive.run_command(['/usr/bin/sw_vers', '-buildVersion'], return_stderr=False, ignore_errors=True).rstrip()
+        return None
+
     def xcode_sdk_version(self, sdk_name):
         if self.is_mac():
             # Assumes that xcrun does not write to standard output on failure (e.g. SDK does not exist).

Modified: trunk/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -32,7 +32,7 @@
 
 
 class MockPlatformInfo(object):
-    def __init__(self, os_name='mac', os_version=Version.from_name('Snow Leopard')):
+    def __init__(self, os_name='mac', os_version=Version.from_name('High Sierra')):
         assert isinstance(os_version, Version)
         self.os_name = os_name
         self.os_version = os_version
@@ -78,6 +78,11 @@
     def terminal_width(self):
         return 80
 
+    def build_version(self):
+        if self.is_mac():
+            return '17A405'
+        return None
+
     def xcode_sdk_version(self, sdk_name):
         return Version(8, 1)
 

Added: trunk/Tools/Scripts/webkitpy/results/__init__.py (0 => 243030)


--- trunk/Tools/Scripts/webkitpy/results/__init__.py	                        (rev 0)
+++ trunk/Tools/Scripts/webkitpy/results/__init__.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -0,0 +1,13 @@
+# Required for Python to search this directory for module files
+
+# Keep this file free of any code or import statements that could
+# cause either an error to occur or a log message to be logged.
+# This ensures that calling code can import initialization code from
+# webkitpy before any errors or log messages due to code in this file.
+# Initialization code can include things like version-checking code and
+# logging configuration code.
+#
+# We do not execute any version-checking code or logging configuration
+# code in this file so that callers can opt-in as they want.  This also
+# allows different callers to choose different initialization code,
+# as necessary.

Added: trunk/Tools/Scripts/webkitpy/results/options.py (0 => 243030)


--- trunk/Tools/Scripts/webkitpy/results/options.py	                        (rev 0)
+++ trunk/Tools/Scripts/webkitpy/results/options.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -0,0 +1,33 @@
+# Copyright (C) 2019 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 optparse
+
+
+def upload_options():
+    return [
+        optparse.make_option('--report', action='', dest='report_urls', help='URL (or URLs) to report test results to'),
+        optparse.make_option('--buildbot-master', help='The url of the buildbot master.'),
+        optparse.make_option('--builder-name', help='The name of the buildbot builder tests were run on.'),
+        optparse.make_option('--build-number', help='The buildbot build number tests are associated with.'),
+        optparse.make_option('--buildbot-worker', help='The buildbot worker tests were run on.'),
+    ]

Added: trunk/Tools/Scripts/webkitpy/results/upload.py (0 => 243030)


--- trunk/Tools/Scripts/webkitpy/results/upload.py	                        (rev 0)
+++ trunk/Tools/Scripts/webkitpy/results/upload.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -0,0 +1,184 @@
+# Copyright (C) 2019 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 webkitpy.thirdparty.autoinstalled.requests
+
+import json
+import requests
+import sys
+
+import platform as host_platform
+
+
+class Upload(object):
+    UPLOAD_ENDPOINT = '/api/upload'
+    BUILDBOT_DETAILS = ['buildbot-master', 'builder-name', 'build-number', 'buildbot-worker']
+    VERSION = 0
+
+    class Expectations:
+        # These are ordered by priority, meaning that a test which both crashes and has
+        # a warning should be considered to have crashed.
+        ORDER = [
+            'CRASH',
+            'TIMEOUT',
+            'IMAGE',   # Image-diff
+            'AUDIO',   # Audio-diff
+            'TEXT',    # Text-diff
+            'FAIL',
+            'ERROR',
+            'WARNING',
+            'PASS',
+        ]
+        CRASH, TIMEOUT, IMAGE, AUDIO, TEXT, FAIL, ERROR, WARNING, PASS = ORDER
+
+    class Encoder(json.JSONEncoder):
+
+        def default(self, obj):
+            if not isinstance(obj, Upload):
+                return super(Upload.Encoder, self).default(obj)
+
+            if not obj.suite:
+                raise ValueError('No suite specified to results upload')
+            if not obj.commits:
+                raise ValueError('No commits specified to results upload')
+
+            details = obj.details or obj.create_details()
+            buildbot_args = [details.get(arg, None) is None for arg in obj.BUILDBOT_DETAILS]
+            if any(buildbot_args) and not all(buildbot_args):
+                raise ValueError('All buildbot details must be defined for upload, details missing: {}'.format(', '.join(
+                    [obj.BUILDBOT_DETAILS[i] for i in xrange(len(obj.BUILDBOT_DETAILS)) if buildbot_args[i]],
+                )))
+
+            def unpack_test(current, path_to_test, data):
+                if len(path_to_test) == 1:
+                    current[path_to_test[0]] = data
+                    return
+                if not current.get(path_to_test[0]):
+                    current[path_to_test[0]] = {}
+                unpack_test(current[path_to_test[0]], path_to_test[1:], data)
+
+            results = {}
+            for test, data in obj.results.iteritems():
+                unpack_test(results, test.split('/'), data)
+
+            result = dict(
+                version=obj.VERSION,
+                suite=obj.suite,
+                configuration=obj.configuration or obj.create_configuration(),
+                commits=obj.commits,
+                test_results=dict(
+                    details=details,
+                    run_stats=obj.run_stats or obj.create_run_stats(),
+                    results=results,
+                ),
+            )
+            if obj.timestamp:
+                result['timestamp'] = obj.timestamp
+            return result
+
+    def __init__(self, suite=None, configuration=None, commits=[], timestamp=None, details=None, run_stats=None, results={}):
+        self.suite = suite
+        self.configuration = configuration
+        self.commits = commits
+        self.timestamp = timestamp
+        self.details = details
+        self.run_stats = run_stats
+        self.results = results
+
+    @staticmethod
+    def create_configuration(
+            platform=None,
+            is_simulator=False,
+            version=None,
+            architecture=None,
+            version_name=None,
+            model=None,
+            style=None,   # Debug/Production/Release
+            flavor=None,  # Dumping ground suite-wide configuration changes (ie, GuardMalloc)
+            sdk=None,
+        ):
+
+        # This deviates slightly from the rest of webkitpy, but it allows this file to be entirely portable.
+        config = dict(
+            platform=platform or (host_platform.system() if host_platform.system() != 'Darwin' else 'mac').lower(),
+            is_simulator=is_simulator,
+            version=version or (host_platform.release() if host_platform.system() != 'Darwin' else host_platform.mac_ver()[0]),
+            architecture=architecture or host_platform.machine(),
+        )
+        optional_data = dict(version_name=version_name, model=model, style=style, flavor=flavor, sdk=sdk)
+        config.update({key: value for key, value in optional_data.iteritems() if value is not None})
+        return config
+
+    @staticmethod
+    def create_commit(repository_id, id, branch=None):
+        commit = dict(repository_id=repository_id, id=id)
+        if branch:
+            commit['branch'] = branch
+        return commit
+
+    @staticmethod
+    def create_details(link=None, options=None, **kwargs):
+        result = dict(**kwargs)
+        if link:
+            result['link'] = link
+        if not options:
+            return result
+
+        for element in Upload.BUILDBOT_DETAILS:
+            value = getattr(options, element.replace('-', '_'), None)
+            if value is not None:
+                result[element] = value
+        return result
+
+    @staticmethod
+    def create_run_stats(start_time=None, end_time=None, tests_skipped=None, **kwargs):
+        stats = dict(**kwargs)
+        optional_data = dict(start_time=start_time, end_time=end_time, tests_skipped=tests_skipped)
+        stats.update({key: value for key, value in optional_data.iteritems() if value is not None})
+        return stats
+
+    @staticmethod
+    def create_test_result(expected=None, actual=None, log=None, **kwargs):
+        result = dict(**kwargs)
+
+        # Tests which don't declare expectations or results are assumed to have passed.
+        optional_data = dict(expected=expected, actual=actual, log=log)
+        result.update({key: value for key, value in optional_data.iteritems() if value is not None})
+        return result
+
+    def upload(self, url, log_line_func=lambda val: sys.stdout.write(val + '\n')):
+        try:
+            response = requests.post(url + self.UPLOAD_ENDPOINT, data="" cls=Upload.Encoder))
+        except requests.exceptions.ConnectionError:
+            log_line_func(' ' * 4 + 'Failed to upload to {}, results server not online'.format(url))
+            return False
+        except ValueError as e:
+            log_line_func(' ' * 4 + 'Failed to encode upload data: {}'.format(e))
+            return False
+
+        if response.status_code != 200:
+            log_line_func(' ' * 4 + 'Error uploading to {}:'.format(url))
+            log_line_func(' ' * 8 + response.json()['description'])
+            return False
+
+        log_line_func(' ' * 4 + 'Uploaded results to {}'.format(url))
+        return True

Added: trunk/Tools/Scripts/webkitpy/results/upload_unittest.py (0 => 243030)


--- trunk/Tools/Scripts/webkitpy/results/upload_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/webkitpy/results/upload_unittest.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -0,0 +1,216 @@
+# Copyright (C) 2019 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 webkitpy.thirdparty.autoinstalled.requests
+
+import collections
+import json
+import requests
+import time
+import unittest
+
+from webkitpy.results.upload import Upload
+from webkitpy.thirdparty import mock
+
+
+class UploadTest(unittest.TestCase):
+
+    class Options(object):
+        def __init__(self, **kwargs):
+            for key, value in kwargs.iteritems():
+                setattr(self, key, value)
+
+    class MockResponse(object):
+        def __init__(self, status_code=200, text=''):
+            self.status_code = status_code
+            self.text = text
+
+        def json(self):
+            return json.loads(self.text)
+
+    @staticmethod
+    def normalize(data):
+        if isinstance(data, basestring):
+            return str(data)
+        elif isinstance(data, collections.Mapping):
+            return dict(map(UploadTest.normalize, data.iteritems()))
+        elif isinstance(data, collections.Iterable):
+            return type(data)(map(UploadTest.normalize, data))
+        return data
+
+    @staticmethod
+    def raise_requests_ConnectionError():
+        raise requests.exceptions.ConnectionError()
+
+    def test_encoding(self):
+        start_time, end_time = time.time() - 3, time.time()
+        upload = Upload(
+            suite='webkitpy-tests',
+            configuration=Upload.create_configuration(
+                platform='mac',
+                version='10.13.0',
+                version_name='High Sierra',
+                architecture='x86_64',
+                sdk='17A405',
+            ),
+            details=Upload.create_details(link='https://webkit.org'),
+            commits=[Upload.create_commit(
+                repository_id='webkit',
+                id='5',
+                branch='trunk',
+            )],
+            run_stats=Upload.create_run_stats(
+                start_time=start_time,
+                end_time=end_time,
+                tests_skipped=0,
+            ),
+            results={
+                'webkitpy.test1': {},
+                'webkitpy.test2': Upload.create_test_result(expected=Upload.Expectations.PASS, actual=Upload.Expectations.FAIL),
+            },
+        )
+        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
+
+        self.assertEqual(generated_dict['version'], 0)
+        self.assertEqual(generated_dict['suite'], 'webkitpy-tests')
+        self.assertEqual(generated_dict['configuration'], self.normalize(dict(
+            platform='mac',
+            is_simulator=False,
+            version='10.13.0',
+            version_name='High Sierra',
+            architecture='x86_64',
+            sdk='17A405',
+        )))
+        self.assertEqual(generated_dict['commits'], [dict(
+            repository_id='webkit',
+            id='5',
+            branch='trunk',
+        )])
+        self.assertEqual(generated_dict['test_results']['details'], self.normalize(dict(link='https://webkit.org')))
+        self.assertEqual(generated_dict['test_results']['run_stats'], self.normalize(dict(
+            start_time=start_time,
+            end_time=end_time,
+            tests_skipped=0,
+        )))
+        self.assertEqual(generated_dict['test_results']['results'], self.normalize({
+            'webkitpy.test1': {},
+            'webkitpy.test2': Upload.create_test_result(expected=Upload.Expectations.PASS, actual=Upload.Expectations.FAIL),
+        }))
+
+    def test_upload(self):
+        upload = Upload(
+            suite='webkitpy-tests',
+            commits=[Upload.create_commit(
+                repository_id='webkit',
+                id='5',
+                branch='trunk',
+            )],
+        )
+
+        with mock.patch('requests.post', new=lambda url, data: self.MockResponse()):
+            self.assertTrue(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
+
+        with mock.patch('requests.post', new=lambda url, data: self.raise_requests_ConnectionError()):
+            self.assertFalse(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
+
+        mock_404 = mock.patch('requests.post', new=lambda url, data: self.MockResponse(
+            status_code=404,
+            text=json.dumps(dict(description='No such address')),
+        ))
+        with mock_404:
+            self.assertFalse(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
+
+    def test_packed_test(self):
+        upload = Upload(
+            suite='webkitpy-tests',
+            commits=[Upload.create_commit(
+                repository_id='webkit',
+                id='5',
+                branch='trunk',
+            )],
+            results={
+                'dir1/sub-dir1/test1': Upload.create_test_result(actual=Upload.Expectations.FAIL),
+                'dir1/sub-dir1/test2': Upload.create_test_result(actual=Upload.Expectations.TIMEOUT),
+                'dir1/sub-dir2/test3': {},
+                'dir1/sub-dir2/test4': {},
+                'dir2/sub-dir3/test5': {},
+                'dir2/test6': {},
+            }
+        )
+        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
+        self.assertEqual(generated_dict['test_results']['results'], self.normalize({
+            'dir1': {
+                'sub-dir1': {
+                    'test1': {'actual': Upload.Expectations.FAIL},
+                    'test2': {'actual': Upload.Expectations.TIMEOUT},
+                }, 'sub-dir2': {
+                    'test3': {},
+                    'test4': {},
+                }
+            }, 'dir2': {
+                'sub-dir3': {'test5': {}},
+                'test6': {},
+            },
+        }))
+
+    def test_no_suite(self):
+        upload = Upload(
+            commits=[Upload.create_commit(
+                repository_id='webkit',
+                id='5',
+                branch='trunk',
+            )],
+        )
+        with self.assertRaises(ValueError):
+            json.dumps(upload, cls=Upload.Encoder)
+
+    def test_no_commits(self):
+        upload = Upload(
+            suite='webkitpy-tests',
+        )
+        with self.assertRaises(ValueError):
+            json.dumps(upload, cls=Upload.Encoder)
+
+    def test_buildbot(self):
+        upload = Upload(
+            suite='webkitpy-tests',
+            commits=[Upload.create_commit(
+                repository_id='webkit',
+                id='5',
+                branch='trunk',
+            )],
+            details=Upload.create_details(options=self.Options(
+                buildbot_master='webkit.org',
+                builder_name='Queue-1',
+                build_number=1,
+        )))
+        with self.assertRaises(ValueError):
+            json.dumps(upload, cls=Upload.Encoder)
+
+        upload.details['buildbot-worker'] = 'bot123'
+        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
+        self.assertEqual(generated_dict['test_results']['details'], self.normalize({
+            'buildbot-master': 'webkit.org',
+            'builder-name': 'Queue-1',
+            'build-number': 1,
+            'buildbot-worker': 'bot123',
+        }))

Modified: trunk/Tools/Scripts/webkitpy/test/main.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/test/main.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/test/main.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -1,6 +1,6 @@
 # Copyright (C) 2012 Google, Inc.
 # Copyright (C) 2010 Chris Jerdonek ([email protected])
-# Copyright (C) 2018 Apple Inc. All rights reserved.
+# Copyright (C) 2018-2019 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -38,17 +38,19 @@
 import unittest
 
 from webkitpy.common.system.logutils import configure_logging
-from webkitpy.common.system.executive import Executive, ScriptError
+from webkitpy.common.system.executive import ScriptError
 from webkitpy.common.system.filesystem import FileSystem
-from webkitpy.common.system.systemhost import SystemHost
+from webkitpy.common.host import Host
 from webkitpy.port.config import Config
 from webkitpy.test.finder import Finder
 from webkitpy.test.printer import Printer
 from webkitpy.test.runner import Runner, unit_test_name
+from webkitpy.results.upload import Upload
+from webkitpy.results.options import upload_options
 
 _log = logging.getLogger(__name__)
 
-_host = SystemHost()
+_host = Host()
 _webkit_root = None
 
 
@@ -131,6 +133,10 @@
             help='Set the configuration to Release')
         parser.add_option_group(configuration_group)
 
+        upload_group = optparse.OptionGroup(parser, 'Upload Options')
+        upload_group.add_options(upload_options())
+        parser.add_option_group(upload_group)
+
         parser.add_option('-a', '--all', action='', default=False,
                           help='run all the tests')
         parser.add_option('-c', '--coverage', action='', default=False,
@@ -181,11 +187,12 @@
         from webkitpy.thirdparty import autoinstall_everything
         autoinstall_everything()
 
+        start_time = time.time()
+        config = Config(_host.executive, self.finder.filesystem)
+        configuration_to_use = self._options.configuration or config.default_configuration()
         if will_run_lldb_webkit_tests:
             self.printer.write_update('Building lldbWebKitTester ...')
             build_lldbwebkittester = self.finder.filesystem.join(_webkit_root, 'Tools', 'Scripts', 'build-lldbwebkittester')
-            config = Config(_host.executive, self.finder.filesystem)
-            configuration_to_use = self._options.configuration or config.default_configuration()
             try:
                 _host.executive.run_and_throw_if_fail([build_lldbwebkittester, config.flag_for_configuration(configuration_to_use)], quiet=(not bool(self._options.verbose)))
             except ScriptError as e:
@@ -218,6 +225,7 @@
         test_runner = Runner(self.printer, loader)
         test_runner.run(parallel_tests, self._options.child_processes)
         test_runner.run(serial_tests, 1)
+        end_time = time.time()
 
         self.printer.print_result(time.time() - start)
 
@@ -232,9 +240,51 @@
         if self._options.coverage:
             cov.stop()
             cov.save()
+
+        failed_uploads = 0
+        if self._options.report_urls:
+            self.printer.meter.writeln('\n')
+            self.printer.write_update('Preparing upload data ...')
+
+            # Empty test results indicate a PASS.
+            results = {test: {} for test in test_runner.tests_run}
+            for test, errors in test_runner.errors:
+                results[test] = Upload.create_test_result(actual=Upload.Expectations.ERROR, log='/n'.join(errors))
+            for test, failures in test_runner.failures:
+                results[test] = Upload.create_test_result(actual=Upload.Expectations.FAIL, log='/n'.join(failures))
+
+            _host.initialize_scm()
+            upload = Upload(
+                suite='webkitpy-tests',
+                configuration=Upload.create_configuration(
+                    platform=_host.platform.os_name,
+                    version=str(_host.platform.os_version),
+                    version_name=_host.platform.os_version_name(),
+                    style=configuration_to_use,
+                    sdk=_host.platform.build_version(),
+                ),
+                details=Upload.create_details(options=self._options),
+                commits=[Upload.create_commit(
+                    repository_id='webkit',
+                    id=_host.scm().native_revision(_webkit_root),
+                    branch=_host.scm().native_branch(_webkit_root),
+                )],
+                run_stats=Upload.create_run_stats(
+                    start_time=start_time,
+                    end_time=end_time,
+                    tests_skipped=len(test_runner.tests_run) - len(parallel_tests) - len(serial_tests),
+                ),
+                results=results,
+            )
+            for url in self._options.report_urls:
+                self.printer.write_update('Uploading to {} ...'.format(url))
+                failed_uploads = failed_uploads if upload.upload(url, log_line_func=self.printer.meter.writeln) else (failed_uploads + 1)
+            self.printer.meter.writeln('Uploads completed!')
+
+        if self._options.coverage:
             cov.report(show_missing=False)
 
-        return not self.printer.num_errors and not self.printer.num_failures
+        return not self.printer.num_errors and not self.printer.num_failures and not failed_uploads
 
     def _check_imports(self, names):
         for name in names:

Modified: trunk/Tools/Scripts/webkitpy/test/runner.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/test/runner.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/test/runner.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -40,7 +40,7 @@
     def __init__(self, printer, loader):
         self.printer = printer
         self.loader = loader
-        self.tests_run = 0
+        self.tests_run = []
         self.errors = []
         self.failures = []
         self.worker_factory = lambda caller: _Worker(caller, self.loader)
@@ -60,7 +60,7 @@
             self.printer.print_started_test(source, test_name)
             return
 
-        self.tests_run += 1
+        self.tests_run.append(test_name)
         if failures:
             self.failures.append((test_name, failures))
         if errors:

Modified: trunk/Tools/Scripts/webkitpy/test/runner_unittest.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/test/runner_unittest.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/test/runner_unittest.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -92,6 +92,6 @@
                             ('test3 (Foo)', 'E', 'test3\nerred'))
         runner = Runner(Printer(stream, options), loader)
         runner.run(['Foo.test1', 'Foo.test2', 'Foo.test3'], 1)
-        self.assertEqual(runner.tests_run, 3)
+        self.assertEqual(len(runner.tests_run), 3)
         self.assertEqual(len(runner.failures), 1)
         self.assertEqual(len(runner.errors), 1)

Modified: trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -1,4 +1,5 @@
 # Copyright (C) 2010 Chris Jerdonek ([email protected])
+# Copyright (C) 2019 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -114,6 +115,8 @@
             self._install_pytest_timeout()
         elif '.pytest' in fullname:
             self._install_pytest()
+        elif '.requests' in fullname:
+            self._install_requests()
 
     def _install_mechanize(self):
         self._install("https://files.pythonhosted.org/packages/source/m/mechanize/mechanize-0.2.5.tar.gz",
@@ -161,6 +164,21 @@
         self._install("https://files.pythonhosted.org/packages/a2/ec/415d0cccc1ed41cd7fdf69ad989da16a8d13057996371004cab4bafc48f3/pytest-3.6.2.tar.gz",
                               "pytest-3.6.2/src/pytest.py")
 
+    def _install_requests(self):
+        self._ensure_autoinstalled_dir_is_in_sys_path()
+        self._install("https://files.pythonhosted.org/packages/06/b8/d1ea38513c22e8c906275d135818fee16ad8495985956a9b7e2bb21942a1/certifi-2019.3.9.tar.gz",
+                      "certifi-2019.3.9/certifi")
+        self._install("https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz",
+                      "chardet-3.0.4/chardet")
+        self._install("https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz",
+                      "chardet-3.0.4/chardet")
+        self._install("https://files.pythonhosted.org/packages/ad/13/eb56951b6f7950cadb579ca166e448ba77f9d24efc03edd7e55fa57d04b7/idna-2.8.tar.gz",
+                      "idna-2.8/idna")
+        self._install("https://files.pythonhosted.org/packages/b1/53/37d82ab391393565f2f831b8eedbffd57db5a718216f82f1a8b4d381a1c1/urllib3-1.24.1.tar.gz",
+                      "urllib3-1.24.1/src/urllib3")
+        self._install("https://files.pythonhosted.org/packages/52/2c/514e4ac25da2b08ca5a464c50463682126385c4272c18193876e91f4bc38/requests-2.21.0.tar.gz",
+                      "requests-2.21.0/requests")
+
     def _install_pylint(self):
         self._ensure_autoinstalled_dir_is_in_sys_path()
         if (not self._fs.exists(self._fs.join(_AUTOINSTALLED_DIR, "pylint")) or

Modified: trunk/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py (243029 => 243030)


--- trunk/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py	2019-03-16 02:21:45 UTC (rev 243029)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py	2019-03-16 02:48:57 UTC (rev 243030)
@@ -183,10 +183,10 @@
         queue._options = Mock()
         queue._options.port = None
         patch = queue._tool.bugs.fetch_attachment(10001)
-        expected_logs = """MOCK add_attachment_to_bug: bug_id=50000, description=Archive of layout-test-results from bot for mac-snowleopard filename=layout-test-results.zip mimetype=None
+        expected_logs = """MOCK add_attachment_to_bug: bug_id=50000, description=Archive of layout-test-results from bot for mac-highsierra filename=layout-test-results.zip mimetype=None
 -- Begin comment --
 The attached test failures were seen while running run-webkit-tests on the mock-queue.
-Port: mac-snowleopard  Platform: MockPlatform 1.0
+Port: mac-highsierra  Platform: MockPlatform 1.0
 -- End comment --
 """
         OutputCapture().assert_outputs(self, queue._upload_results_archive_for_patch, [patch, Mock()], expected_logs=expected_logs)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to