| Differences between
and this patch
- Tools/ChangeLog +52 lines
Lines 1-3 Tools/ChangeLog_sec1
1
2019-03-15  Jonathan Bedard  <jbedard@apple.com>
2
3
        webkitpy: Upload test results
4
        https://bugs.webkit.org/show_bug.cgi?id=195755
5
        <rdar://problem/48896182>
6
7
        Reviewed by NOBODY (OOPS!).
8
9
        Establish a new format for uploading results that is not tied to layout tests, apply
10
        that format to webkitpy tests.
11
12
        * Scripts/webkitpy/common/checkout/scm/git.py:
13
        (Git.native_branch): Return what branch the current checkout is on.
14
        * Scripts/webkitpy/common/checkout/scm/scm_mock.py:
15
        * Scripts/webkitpy/common/checkout/scm/scm_unittest.py:
16
        * Scripts/webkitpy/common/checkout/scm/svn.py:
17
        (SVN.native_branch): Ditto.
18
        * Scripts/webkitpy/common/system/platforminfo.py:
19
        (PlatformInfo.build_version): Return a build version for Mac.
20
        * Scripts/webkitpy/common/system/platforminfo_mock.py:
21
        (MockPlatformInfo.__init__):
22
        (MockPlatformInfo.build_version):
23
        * Scripts/webkitpy/results: Added.
24
        * Scripts/webkitpy/results/__init__.py: Added.
25
        * Scripts/webkitpy/results/options.py: Added.
26
        (upload_options): OptParse list for upload options.
27
        * Scripts/webkitpy/results/upload.py: Added.
28
        (Upload): Class which enforces the upload format expected by the results server.
29
        (Upload.Expectations):
30
        (Upload.create_configuration):
31
        (Upload.create_commit):
32
        (Upload.create_details):
33
        (Upload.create_run_stats):
34
        (Upload.create_test_result):
35
        (Upload.__init__):
36
        (Upload.Encoder): Encode Upload object as json.
37
        (Upload.upload): Upload results to the results server, returning 'True' if the upload is successful.
38
        * Scripts/webkitpy/results/upload_unittest.py: Added.
39
        * Scripts/webkitpy/test/main.py:
40
        (Tester._parse_args): Add upload arguments.
41
        (Tester._run_tests): Allow results to be uploaded.
42
        * Scripts/webkitpy/test/runner.py:
43
        (Runner.__init__): Record which tests were run, rather than just counting them.
44
        (Runner.handle):
45
        * Scripts/webkitpy/test/runner_unittest.py:
46
        (RunnerTest.test_run):
47
        * Scripts/webkitpy/thirdparty/__init__.py:
48
        (AutoinstallImportHook.find_module): Add requests auto-install.
49
        (AutoinstallImportHook._install_requests):
50
        * Scripts/webkitpy/tool/commands/queues_unittest.py:
51
        (PatchProcessingQueueTest.test_upload_results_archive_for_patch): Update os name for testing.
52
1
2019-03-15  Shawn Roberts  <sroberts@apple.com>
53
2019-03-15  Shawn Roberts  <sroberts@apple.com>
2
54
3
        Unreviewed, rolling out r242952.
55
        Unreviewed, rolling out r242952.
- Tools/Scripts/webkitpy/results/__init__.py +13 lines
Line 0 Tools/Scripts/webkitpy/results/__init__.py_sec1
1
# Required for Python to search this directory for module files
2
3
# Keep this file free of any code or import statements that could
4
# cause either an error to occur or a log message to be logged.
5
# This ensures that calling code can import initialization code from
6
# webkitpy before any errors or log messages due to code in this file.
7
# Initialization code can include things like version-checking code and
8
# logging configuration code.
9
#
10
# We do not execute any version-checking code or logging configuration
11
# code in this file so that callers can opt-in as they want.  This also
12
# allows different callers to choose different initialization code,
13
# as necessary.
- Tools/Scripts/webkitpy/results/options.py +33 lines
Line 0 Tools/Scripts/webkitpy/results/options.py_sec1
1
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
import optparse
24
25
26
def upload_options():
27
    return [
28
        optparse.make_option('--report', action='append', dest='report_urls', help='URL (or URLs) to report test results to'),
29
        optparse.make_option('--buildbot-master', help='The url of the buildbot master.'),
30
        optparse.make_option('--builder-name', help='The name of the buildbot builder tests were run on.'),
31
        optparse.make_option('--build-number', help='The buildbot build number tests are associated with.'),
32
        optparse.make_option('--buildbot-worker', help='The buildbot worker tests were run on.'),
33
    ]
- Tools/Scripts/webkitpy/results/upload.py +184 lines
Line 0 Tools/Scripts/webkitpy/results/upload.py_sec1
1
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
import webkitpy.thirdparty.autoinstalled.requests
24
25
import json
26
import requests
27
import sys
28
29
import platform as host_platform
30
31
32
class Upload(object):
33
    UPLOAD_ENDPOINT = '/api/upload'
34
    BUILDBOT_DETAILS = ['buildbot-master', 'builder-name', 'build-number', 'buildbot-worker']
35
    VERSION = 0
36
37
    class Expectations:
38
        # These are ordered by priority, meaning that a test which both crashes and has
39
        # a warning should be considered to have crashed.
40
        ORDER = [
41
            'CRASH',
42
            'TIMEOUT',
43
            'IMAGE',   # Image-diff
44
            'AUDIO',   # Audio-diff
45
            'TEXT',    # Text-diff
46
            'FAIL',
47
            'ERROR',
48
            'WARNING',
49
            'PASS',
50
        ]
51
        CRASH, TIMEOUT, IMAGE, AUDIO, TEXT, FAIL, ERROR, WARNING, PASS = ORDER
52
53
    class Encoder(json.JSONEncoder):
54
55
        def default(self, obj):
56
            if not isinstance(obj, Upload):
57
                return super(Upload.Encoder, self).default(obj)
58
59
            if not obj.suite:
60
                raise ValueError('No suite specified to results upload')
61
            if not obj.commits:
62
                raise ValueError('No commits specified to results upload')
63
64
            details = obj.details or obj.create_details()
65
            buildbot_args = [details.get(arg, None) is None for arg in obj.BUILDBOT_DETAILS]
66
            if any(buildbot_args) and not all(buildbot_args):
67
                raise ValueError('All buildbot details must be defined for upload, details missing: {}'.format(', '.join(
68
                    [obj.BUILDBOT_DETAILS[i] for i in xrange(len(obj.BUILDBOT_DETAILS)) if buildbot_args[i]],
69
                )))
70
71
            def unpack_test(current, path_to_test, data):
72
                if len(path_to_test) == 1:
73
                    current[path_to_test[0]] = data
74
                    return
75
                if not current.get(path_to_test[0]):
76
                    current[path_to_test[0]] = {}
77
                unpack_test(current[path_to_test[0]], path_to_test[1:], data)
78
79
            results = {}
80
            for test, data in obj.results.iteritems():
81
                unpack_test(results, test.split('/'), data)
82
83
            result = dict(
84
                version=obj.VERSION,
85
                suite=obj.suite,
86
                configuration=obj.configuration or obj.create_configuration(),
87
                commits=obj.commits,
88
                test_results=dict(
89
                    details=details,
90
                    run_stats=obj.run_stats or obj.create_run_stats(),
91
                    results=results,
92
                ),
93
            )
94
            if obj.timestamp:
95
                result['timestamp'] = obj.timestamp
96
            return result
97
98
    @staticmethod
99
    def create_configuration(
100
            platform=None,
101
            is_simulator=False,
102
            version=None,
103
            architecture=None,
104
            version_name=None,
105
            model=None,
106
            style=None,   # Debug/Production/Release
107
            flavor=None,  # Dumping ground suite-wide configuration changes (ie, GuardMalloc)
108
            sdk=None,
109
        ):
110
111
        # This deviates slightly from the rest of webkitpy, but it allows this file to be entirely portable.
112
        config = dict(
113
            platform=platform or (host_platform.system() if host_platform.system() != 'Darwin' else 'mac').lower(),
114
            is_simulator=is_simulator,
115
            version=version or (host_platform.release() if host_platform.system() != 'Darwin' else host_platform.mac_ver()[0]),
116
            architecture=architecture or host_platform.machine(),
117
        )
118
        optional_data = dict(version_name=version_name, model=model, style=style, flavor=flavor, sdk=sdk)
119
        config.update({key: value for key, value in optional_data.iteritems() if value is not None})
120
        return config
121
122
    def __init__(self, suite=None, configuration=None, commits=[], timestamp=None, details=None, run_stats=None, results={}):
123
        self.suite = suite
124
        self.configuration = configuration
125
        self.commits = commits
126
        self.timestamp = timestamp
127
        self.details = details
128
        self.run_stats = run_stats
129
        self.results = results
130
131
    @staticmethod
132
    def create_commit(repository_id, id, branch=None):
133
        commit = dict(repository_id=repository_id, id=id)
134
        if branch:
135
            commit['branch'] = branch
136
        return commit
137
138
    @staticmethod
139
    def create_details(link=None, options=None, **kwargs):
140
        result = dict(**kwargs)
141
        if link:
142
            result['link'] = link
143
        if not options:
144
            return result
145
146
        for element in Upload.BUILDBOT_DETAILS:
147
            value = getattr(options, element.replace('-', '_'), None)
148
            if value is not None:
149
                result[element] = value
150
        return result
151
152
    @staticmethod
153
    def create_run_stats(start_time=None, end_time=None, tests_skipped=None, **kwargs):
154
        stats = dict(**kwargs)
155
        optional_data = dict(start_time=start_time, end_time=end_time, tests_skipped=tests_skipped)
156
        stats.update({key: value for key, value in optional_data.iteritems() if value is not None})
157
        return stats
158
159
    @staticmethod
160
    def create_test_result(expected=None, actual=None, log=None, **kwargs):
161
        result = dict(**kwargs)
162
163
        # Tests which don't declare expectations or results are assumed to have passed.
164
        optional_data = dict(expected=expected, actual=actual, log=log)
165
        result.update({key: value for key, value in optional_data.iteritems() if value is not None})
166
        return result
167
168
    def upload(self, url, log_line_func=lambda val: sys.stdout.write(val + '\n')):
169
        try:
170
            response = requests.post(url + self.UPLOAD_ENDPOINT, data=json.dumps(self, cls=Upload.Encoder))
171
        except requests.exceptions.ConnectionError:
172
            log_line_func(' ' * 4 + 'Failed to upload to {}, results server not online'.format(url))
173
            return False
174
        except ValueError as e:
175
            log_line_func(' ' * 4 + 'Failed to encode upload data: {}'.format(e))
176
            return False
177
178
        if response.status_code != 200:
179
            log_line_func(' ' * 4 + 'Error uploading to {}:'.format(url))
180
            log_line_func(' ' * 8 + response.json()['description'])
181
            return False
182
183
        log_line_func(' ' * 4 + 'Uploaded results to {}'.format(url))
184
        return True
- Tools/Scripts/webkitpy/results/upload_unittest.py +216 lines
Line 0 Tools/Scripts/webkitpy/results/upload_unittest.py_sec1
1
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
import webkitpy.thirdparty.autoinstalled.requests
24
25
import collections
26
import json
27
import requests
28
import time
29
import unittest
30
31
from webkitpy.results.upload import Upload
32
from webkitpy.thirdparty import mock
33
34
35
class UploadTest(unittest.TestCase):
36
37
    class Options(object):
38
        def __init__(self, **kwargs):
39
            for key, value in kwargs.iteritems():
40
                setattr(self, key, value)
41
42
    class MockResponse(object):
43
        def __init__(self, status_code=200, text=''):
44
            self.status_code = status_code
45
            self.text = text
46
47
        def json(self):
48
            return json.loads(self.text)
49
50
    @staticmethod
51
    def normalize(data):
52
        if isinstance(data, basestring):
53
            return str(data)
54
        elif isinstance(data, collections.Mapping):
55
            return dict(map(UploadTest.normalize, data.iteritems()))
56
        elif isinstance(data, collections.Iterable):
57
            return type(data)(map(UploadTest.normalize, data))
58
        return data
59
60
    @staticmethod
61
    def raise_requests_ConnectionError():
62
        raise requests.exceptions.ConnectionError()
63
64
    def test_encoding(self):
65
        start_time, end_time = time.time() - 3, time.time()
66
        upload = Upload(
67
            suite='webkitpy-tests',
68
            configuration=Upload.create_configuration(
69
                platform='mac',
70
                version='10.13.0',
71
                version_name='High Sierra',
72
                architecture='x86_64',
73
                sdk='17A405',
74
            ),
75
            details=Upload.create_details(link='https://webkit.org'),
76
            commits=[Upload.create_commit(
77
                repository_id='webkit',
78
                id='5',
79
                branch='trunk',
80
            )],
81
            run_stats=Upload.create_run_stats(
82
                start_time=start_time,
83
                end_time=end_time,
84
                tests_skipped=0,
85
            ),
86
            results={
87
                'webkitpy.test1': {},
88
                'webkitpy.test2': Upload.create_test_result(expected=Upload.Expectations.PASS, actual=Upload.Expectations.FAIL),
89
            },
90
        )
91
        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
92
93
        self.assertEqual(generated_dict['version'], 0)
94
        self.assertEqual(generated_dict['suite'], 'webkitpy-tests')
95
        self.assertEqual(generated_dict['configuration'], self.normalize(dict(
96
            platform='mac',
97
            is_simulator=False,
98
            version='10.13.0',
99
            version_name='High Sierra',
100
            architecture='x86_64',
101
            sdk='17A405',
102
        )))
103
        self.assertEqual(generated_dict['commits'], [dict(
104
            repository_id='webkit',
105
            id='5',
106
            branch='trunk',
107
        )])
108
        self.assertEqual(generated_dict['test_results']['details'], self.normalize(dict(link='https://webkit.org')))
109
        self.assertEqual(generated_dict['test_results']['run_stats'], self.normalize(dict(
110
            start_time=start_time,
111
            end_time=end_time,
112
            tests_skipped=0,
113
        )))
114
        self.assertEqual(generated_dict['test_results']['results'], self.normalize({
115
            'webkitpy.test1': {},
116
            'webkitpy.test2': Upload.create_test_result(expected=Upload.Expectations.PASS, actual=Upload.Expectations.FAIL),
117
        }))
118
119
    def test_upload(self):
120
        upload = Upload(
121
            suite='webkitpy-tests',
122
            commits=[Upload.create_commit(
123
                repository_id='webkit',
124
                id='5',
125
                branch='trunk',
126
            )],
127
        )
128
129
        with mock.patch('requests.post', new=lambda url, data: self.MockResponse()):
130
            self.assertTrue(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
131
132
        with mock.patch('requests.post', new=lambda url, data: self.raise_requests_ConnectionError()):
133
            self.assertFalse(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
134
135
        mock_404 = mock.patch('requests.post', new=lambda url, data: self.MockResponse(
136
            status_code=404,
137
            text=json.dumps(dict(description='No such address')),
138
        ))
139
        with mock_404:
140
            self.assertFalse(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
141
142
    def test_packed_test(self):
143
        upload = Upload(
144
            suite='webkitpy-tests',
145
            commits=[Upload.create_commit(
146
                repository_id='webkit',
147
                id='5',
148
                branch='trunk',
149
            )],
150
            results={
151
                'dir1/sub-dir1/test1': Upload.create_test_result(actual=Upload.Expectations.FAIL),
152
                'dir1/sub-dir1/test2': Upload.create_test_result(actual=Upload.Expectations.TIMEOUT),
153
                'dir1/sub-dir2/test3': {},
154
                'dir1/sub-dir2/test4': {},
155
                'dir2/sub-dir3/test5': {},
156
                'dir2/test6': {},
157
            }
158
        )
159
        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
160
        self.assertEqual(generated_dict['test_results']['results'], self.normalize({
161
            'dir1': {
162
                'sub-dir1': {
163
                    'test1': {'actual': Upload.Expectations.FAIL},
164
                    'test2': {'actual': Upload.Expectations.TIMEOUT},
165
                }, 'sub-dir2': {
166
                    'test3': {},
167
                    'test4': {},
168
                }
169
            }, 'dir2': {
170
                'sub-dir3': {'test5': {}},
171
                'test6': {},
172
            },
173
        }))
174
175
    def test_no_suite(self):
176
        upload = Upload(
177
            commits=[Upload.create_commit(
178
                repository_id='webkit',
179
                id='5',
180
                branch='trunk',
181
            )],
182
        )
183
        with self.assertRaises(ValueError):
184
            json.dumps(upload, cls=Upload.Encoder)
185
186
    def test_no_commits(self):
187
        upload = Upload(
188
            suite='webkitpy-tests',
189
        )
190
        with self.assertRaises(ValueError):
191
            json.dumps(upload, cls=Upload.Encoder)
192
193
    def test_buildbot(self):
194
        upload = Upload(
195
            suite='webkitpy-tests',
196
            commits=[Upload.create_commit(
197
                repository_id='webkit',
198
                id='5',
199
                branch='trunk',
200
            )],
201
            details=Upload.create_details(options=self.Options(
202
                buildbot_master='webkit.org',
203
                builder_name='Queue-1',
204
                build_number=1,
205
        )))
206
        with self.assertRaises(ValueError):
207
            json.dumps(upload, cls=Upload.Encoder)
208
209
        upload.details['buildbot-worker'] = 'bot123'
210
        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
211
        self.assertEqual(generated_dict['test_results']['details'], self.normalize({
212
            'buildbot-master': 'webkit.org',
213
            'builder-name': 'Queue-1',
214
            'build-number': 1,
215
            'buildbot-worker': 'bot123',
216
        }))
- Tools/Scripts/webkitpy/common/checkout/scm/git.py +8 lines
Lines 290-295 class Git(SCM, SVNRepository): Tools/Scripts/webkitpy/common/checkout/scm/git.py_sec1
290
    def native_revision(self, path):
290
    def native_revision(self, path):
291
        return self._run_git(['-C', self.find_checkout_root(path), 'log', '-1', '--pretty=format:%H'])
291
        return self._run_git(['-C', self.find_checkout_root(path), 'log', '-1', '--pretty=format:%H'])
292
292
293
    def native_branch(self, path):
294
        result = self._run_git(['-C', self.find_checkout_root(path), 'rev-parse', '--abbrev-ref', 'HEAD']).rstrip()
295
296
        # For git-svn
297
        if result.startswith('heads'):
298
            return result[6:]
299
        return result
300
293
    def svn_url(self):
301
    def svn_url(self):
294
        git_command = ['svn', 'info']
302
        git_command = ['svn', 'info']
295
        status = self._run_git(git_command)
303
        status = self._run_git(git_command)
- Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py +3 lines
Lines 87-92 class MockSCM(object): Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py_sec1
87
    def native_revision(self, path):
87
    def native_revision(self, path):
88
        return self.svn_revision(path)
88
        return self.svn_revision(path)
89
89
90
    def native_branch(self, path):
91
        return 'trunk'
92
90
    def timestamp_of_revision(self, path, revision):
93
    def timestamp_of_revision(self, path, revision):
91
        return '2013-02-01 08:48:05 +0000'
94
        return '2013-02-01 08:48:05 +0000'
92
95
- Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py +10 lines
Lines 909-914 END Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py_sec1
909
        self.assertEqual(self.scm.head_svn_revision(), self.scm.native_revision('.'))
909
        self.assertEqual(self.scm.head_svn_revision(), self.scm.native_revision('.'))
910
        self.assertEqual(self.scm.native_revision('.'), '5')
910
        self.assertEqual(self.scm.native_revision('.'), '5')
911
911
912
    def test_native_branch(self):
913
        self.assertEqual(self.scm.native_branch('.'), 'trunk')
914
912
    def test_propset_propget(self):
915
    def test_propset_propget(self):
913
        filepath = os.path.join(self.svn_checkout_path, "test_file")
916
        filepath = os.path.join(self.svn_checkout_path, "test_file")
914
        expected_mime_type = "x-application/foo-bar"
917
        expected_mime_type = "x-application/foo-bar"
Lines 1112-1117 class GitTest(SCMTest): Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py_sec2
1112
        command = ['git', '-C', scm.checkout_root, 'rev-parse', 'HEAD']
1115
        command = ['git', '-C', scm.checkout_root, 'rev-parse', 'HEAD']
1113
        self.assertEqual(scm.native_revision(scm.checkout_root), run_command(command).strip())
1116
        self.assertEqual(scm.native_revision(scm.checkout_root), run_command(command).strip())
1114
1117
1118
    def test_native_branch(self):
1119
        scm = self.tracking_scm
1120
        self.assertEqual('master', scm.native_branch(scm.checkout_root))
1121
1115
    def test_rename_files(self):
1122
    def test_rename_files(self):
1116
        scm = self.tracking_scm
1123
        scm = self.tracking_scm
1117
1124
Lines 1627-1632 class GitSVNTest(SCMTest): Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py_sec3
1627
        command = ['git', '-C', self.git_checkout_path, 'rev-parse', 'HEAD']
1634
        command = ['git', '-C', self.git_checkout_path, 'rev-parse', 'HEAD']
1628
        self.assertEqual(self.scm.native_revision(self.git_checkout_path), run_command(command).strip())
1635
        self.assertEqual(self.scm.native_revision(self.git_checkout_path), run_command(command).strip())
1629
1636
1637
    def test_native_branch(self):
1638
        self.assertEqual('trunk', self.scm.native_branch(self.git_checkout_path))
1639
1630
    def test_to_object_name(self):
1640
    def test_to_object_name(self):
1631
        relpath = 'test_file_commit1'
1641
        relpath = 'test_file_commit1'
1632
        fullpath = os.path.realpath(os.path.join(self.git_checkout_path, relpath))
1642
        fullpath = os.path.realpath(os.path.join(self.git_checkout_path, relpath))
- Tools/Scripts/webkitpy/common/checkout/scm/svn.py -1 / +9 lines
Lines 83-89 class SVN(SCM, SVNRepository): Tools/Scripts/webkitpy/common/checkout/scm/svn.py_sec1
83
        SCM.__init__(self, cwd, **kwargs)
83
        SCM.__init__(self, cwd, **kwargs)
84
        self._bogus_dir = None
84
        self._bogus_dir = None
85
        if patch_directories == []:
85
        if patch_directories == []:
86
            raise Exception(message='Empty list of patch directories passed to SCM.__init__')
86
            raise Exception('Empty list of patch directories passed to SCM.__init__')
87
        elif patch_directories == None:
87
        elif patch_directories == None:
88
            self._patch_directories = [self._filesystem.relpath(cwd, self.checkout_root)]
88
            self._patch_directories = [self._filesystem.relpath(cwd, self.checkout_root)]
89
        else:
89
        else:
Lines 278-283 class SVN(SCM, SVNRepository): Tools/Scripts/webkitpy/common/checkout/scm/svn.py_sec2
278
    def native_revision(self, path):
278
    def native_revision(self, path):
279
        return self.svn_revision(path)
279
        return self.svn_revision(path)
280
280
281
    def native_branch(self, path):
282
        relative_url = self.value_from_svn_info(path, 'Relative URL')[2:]
283
        if relative_url.startswith('trunk'):
284
            return 'trunk'
285
        elif relative_url.startswith('branch'):
286
            return relative_url.split('/')[1]
287
        raise Exception('{} is not a branch'.format(relative_url.split('/')[0]))
288
281
    def timestamp_of_revision(self, path, revision):
289
    def timestamp_of_revision(self, path, revision):
282
        # We use --xml to get timestamps like 2013-02-08T08:18:04.964409Z
290
        # We use --xml to get timestamps like 2013-02-08T08:18:04.964409Z
283
        repository_root = self.value_from_svn_info(self.checkout_root, 'Repository Root')
291
        repository_root = self.value_from_svn_info(self.checkout_root, 'Repository Root')
- Tools/Scripts/webkitpy/common/system/platforminfo.py +5 lines
Lines 146-151 class PlatformInfo(object): Tools/Scripts/webkitpy/common/system/platforminfo.py_sec1
146
        except:
146
        except:
147
            return sys.maxint
147
            return sys.maxint
148
148
149
    def build_version(self):
150
        if self.is_mac():
151
            return self._executive.run_command(['/usr/bin/sw_vers', '-buildVersion'], return_stderr=False, ignore_errors=True).rstrip()
152
        return None
153
149
    def xcode_sdk_version(self, sdk_name):
154
    def xcode_sdk_version(self, sdk_name):
150
        if self.is_mac():
155
        if self.is_mac():
151
            # Assumes that xcrun does not write to standard output on failure (e.g. SDK does not exist).
156
            # Assumes that xcrun does not write to standard output on failure (e.g. SDK does not exist).
- Tools/Scripts/webkitpy/common/system/platforminfo_mock.py -1 / +6 lines
Lines 32-38 from webkitpy.common.version_name_map im Tools/Scripts/webkitpy/common/system/platforminfo_mock.py_sec1
32
32
33
33
34
class MockPlatformInfo(object):
34
class MockPlatformInfo(object):
35
    def __init__(self, os_name='mac', os_version=Version.from_name('Snow Leopard')):
35
    def __init__(self, os_name='mac', os_version=Version.from_name('High Sierra')):
36
        assert isinstance(os_version, Version)
36
        assert isinstance(os_version, Version)
37
        self.os_name = os_name
37
        self.os_name = os_name
38
        self.os_version = os_version
38
        self.os_version = os_version
Lines 78-83 class MockPlatformInfo(object): Tools/Scripts/webkitpy/common/system/platforminfo_mock.py_sec2
78
    def terminal_width(self):
78
    def terminal_width(self):
79
        return 80
79
        return 80
80
80
81
    def build_version(self):
82
        if self.is_mac():
83
            return '17A405'
84
        return None
85
81
    def xcode_sdk_version(self, sdk_name):
86
    def xcode_sdk_version(self, sdk_name):
82
        return Version(8, 1)
87
        return Version(8, 1)
83
88
- Tools/Scripts/webkitpy/results/__init__.py +13 lines
Line 0 Tools/Scripts/webkitpy/results/__init__.py_sec1
1
# Required for Python to search this directory for module files
2
3
# Keep this file free of any code or import statements that could
4
# cause either an error to occur or a log message to be logged.
5
# This ensures that calling code can import initialization code from
6
# webkitpy before any errors or log messages due to code in this file.
7
# Initialization code can include things like version-checking code and
8
# logging configuration code.
9
#
10
# We do not execute any version-checking code or logging configuration
11
# code in this file so that callers can opt-in as they want.  This also
12
# allows different callers to choose different initialization code,
13
# as necessary.
- Tools/Scripts/webkitpy/results/options.py +33 lines
Line 0 Tools/Scripts/webkitpy/results/options.py_sec1
1
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
import optparse
24
25
26
def upload_options():
27
    return [
28
        optparse.make_option('--report', action='append', dest='report_urls', help='URL (or URLs) to report test results to'),
29
        optparse.make_option('--buildbot-master', help='The url of the buildbot master.'),
30
        optparse.make_option('--builder-name', help='The name of the buildbot builder tests were run on.'),
31
        optparse.make_option('--build-number', help='The buildbot build number tests are associated with.'),
32
        optparse.make_option('--buildbot-worker', help='The buildbot worker tests were run on.'),
33
    ]
- Tools/Scripts/webkitpy/results/upload.py +184 lines
Line 0 Tools/Scripts/webkitpy/results/upload.py_sec1
1
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
import webkitpy.thirdparty.autoinstalled.requests
24
25
import json
26
import requests
27
import sys
28
29
import platform as host_platform
30
31
32
class Upload(object):
33
    UPLOAD_ENDPOINT = '/api/upload'
34
    BUILDBOT_DETAILS = ['buildbot-master', 'builder-name', 'build-number', 'buildbot-worker']
35
    VERSION = 0
36
37
    class Expectations:
38
        # These are ordered by priority, meaning that a test which both crashes and has
39
        # a warning should be considered to have crashed.
40
        ORDER = [
41
            'CRASH',
42
            'TIMEOUT',
43
            'IMAGE',   # Image-diff
44
            'AUDIO',   # Audio-diff
45
            'TEXT',    # Text-diff
46
            'FAIL',
47
            'ERROR',
48
            'WARNING',
49
            'PASS',
50
        ]
51
        CRASH, TIMEOUT, IMAGE, AUDIO, TEXT, FAIL, ERROR, WARNING, PASS = ORDER
52
53
    class Encoder(json.JSONEncoder):
54
55
        def default(self, obj):
56
            if not isinstance(obj, Upload):
57
                return super(Upload.Encoder, self).default(obj)
58
59
            if not obj.suite:
60
                raise ValueError('No suite specified to results upload')
61
            if not obj.commits:
62
                raise ValueError('No commits specified to results upload')
63
64
            details = obj.details or obj.create_details()
65
            buildbot_args = [details.get(arg, None) is None for arg in obj.BUILDBOT_DETAILS]
66
            if any(buildbot_args) and not all(buildbot_args):
67
                raise ValueError('All buildbot details must be defined for upload, details missing: {}'.format(', '.join(
68
                    [obj.BUILDBOT_DETAILS[i] for i in xrange(len(obj.BUILDBOT_DETAILS)) if buildbot_args[i]],
69
                )))
70
71
            def unpack_test(current, path_to_test, data):
72
                if len(path_to_test) == 1:
73
                    current[path_to_test[0]] = data
74
                    return
75
                if not current.get(path_to_test[0]):
76
                    current[path_to_test[0]] = {}
77
                unpack_test(current[path_to_test[0]], path_to_test[1:], data)
78
79
            results = {}
80
            for test, data in obj.results.iteritems():
81
                unpack_test(results, test.split('/'), data)
82
83
            result = dict(
84
                version=obj.VERSION,
85
                suite=obj.suite,
86
                configuration=obj.configuration or obj.create_configuration(),
87
                commits=obj.commits,
88
                test_results=dict(
89
                    details=details,
90
                    run_stats=obj.run_stats or obj.create_run_stats(),
91
                    results=results,
92
                ),
93
            )
94
            if obj.timestamp:
95
                result['timestamp'] = obj.timestamp
96
            return result
97
98
    @staticmethod
99
    def create_configuration(
100
            platform=None,
101
            is_simulator=False,
102
            version=None,
103
            architecture=None,
104
            version_name=None,
105
            model=None,
106
            style=None,   # Debug/Production/Release
107
            flavor=None,  # Dumping ground suite-wide configuration changes (ie, GuardMalloc)
108
            sdk=None,
109
        ):
110
111
        # This deviates slightly from the rest of webkitpy, but it allows this file to be entirely portable.
112
        config = dict(
113
            platform=platform or (host_platform.system() if host_platform.system() != 'Darwin' else 'mac').lower(),
114
            is_simulator=is_simulator,
115
            version=version or (host_platform.release() if host_platform.system() != 'Darwin' else host_platform.mac_ver()[0]),
116
            architecture=architecture or host_platform.machine(),
117
        )
118
        optional_data = dict(version_name=version_name, model=model, style=style, flavor=flavor, sdk=sdk)
119
        config.update({key: value for key, value in optional_data.iteritems() if value is not None})
120
        return config
121
122
    def __init__(self, suite=None, configuration=None, commits=[], timestamp=None, details=None, run_stats=None, results={}):
123
        self.suite = suite
124
        self.configuration = configuration
125
        self.commits = commits
126
        self.timestamp = timestamp
127
        self.details = details
128
        self.run_stats = run_stats
129
        self.results = results
130
131
    @staticmethod
132
    def create_commit(repository_id, id, branch=None):
133
        commit = dict(repository_id=repository_id, id=id)
134
        if branch:
135
            commit['branch'] = branch
136
        return commit
137
138
    @staticmethod
139
    def create_details(link=None, options=None, **kwargs):
140
        result = dict(**kwargs)
141
        if link:
142
            result['link'] = link
143
        if not options:
144
            return result
145
146
        for element in Upload.BUILDBOT_DETAILS:
147
            value = getattr(options, element.replace('-', '_'), None)
148
            if value is not None:
149
                result[element] = value
150
        return result
151
152
    @staticmethod
153
    def create_run_stats(start_time=None, end_time=None, tests_skipped=None, **kwargs):
154
        stats = dict(**kwargs)
155
        optional_data = dict(start_time=start_time, end_time=end_time, tests_skipped=tests_skipped)
156
        stats.update({key: value for key, value in optional_data.iteritems() if value is not None})
157
        return stats
158
159
    @staticmethod
160
    def create_test_result(expected=None, actual=None, log=None, **kwargs):
161
        result = dict(**kwargs)
162
163
        # Tests which don't declare expectations or results are assumed to have passed.
164
        optional_data = dict(expected=expected, actual=actual, log=log)
165
        result.update({key: value for key, value in optional_data.iteritems() if value is not None})
166
        return result
167
168
    def upload(self, url, log_line_func=lambda val: sys.stdout.write(val + '\n')):
169
        try:
170
            response = requests.post(url + self.UPLOAD_ENDPOINT, data=json.dumps(self, cls=Upload.Encoder))
171
        except requests.exceptions.ConnectionError:
172
            log_line_func(' ' * 4 + 'Failed to upload to {}, results server not online'.format(url))
173
            return False
174
        except ValueError as e:
175
            log_line_func(' ' * 4 + 'Failed to encode upload data: {}'.format(e))
176
            return False
177
178
        if response.status_code != 200:
179
            log_line_func(' ' * 4 + 'Error uploading to {}:'.format(url))
180
            log_line_func(' ' * 8 + response.json()['description'])
181
            return False
182
183
        log_line_func(' ' * 4 + 'Uploaded results to {}'.format(url))
184
        return True
- Tools/Scripts/webkitpy/results/upload_unittest.py +216 lines
Line 0 Tools/Scripts/webkitpy/results/upload_unittest.py_sec1
1
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
import webkitpy.thirdparty.autoinstalled.requests
24
25
import collections
26
import json
27
import requests
28
import time
29
import unittest
30
31
from webkitpy.results.upload import Upload
32
from webkitpy.thirdparty import mock
33
34
35
class UploadTest(unittest.TestCase):
36
37
    class Options(object):
38
        def __init__(self, **kwargs):
39
            for key, value in kwargs.iteritems():
40
                setattr(self, key, value)
41
42
    class MockResponse(object):
43
        def __init__(self, status_code=200, text=''):
44
            self.status_code = status_code
45
            self.text = text
46
47
        def json(self):
48
            return json.loads(self.text)
49
50
    @staticmethod
51
    def normalize(data):
52
        if isinstance(data, basestring):
53
            return str(data)
54
        elif isinstance(data, collections.Mapping):
55
            return dict(map(UploadTest.normalize, data.iteritems()))
56
        elif isinstance(data, collections.Iterable):
57
            return type(data)(map(UploadTest.normalize, data))
58
        return data
59
60
    @staticmethod
61
    def raise_requests_ConnectionError():
62
        raise requests.exceptions.ConnectionError()
63
64
    def test_encoding(self):
65
        start_time, end_time = time.time() - 3, time.time()
66
        upload = Upload(
67
            suite='webkitpy-tests',
68
            configuration=Upload.create_configuration(
69
                platform='mac',
70
                version='10.13.0',
71
                version_name='High Sierra',
72
                architecture='x86_64',
73
                sdk='17A405',
74
            ),
75
            details=Upload.create_details(link='https://webkit.org'),
76
            commits=[Upload.create_commit(
77
                repository_id='webkit',
78
                id='5',
79
                branch='trunk',
80
            )],
81
            run_stats=Upload.create_run_stats(
82
                start_time=start_time,
83
                end_time=end_time,
84
                tests_skipped=0,
85
            ),
86
            results={
87
                'webkitpy.test1': {},
88
                'webkitpy.test2': Upload.create_test_result(expected=Upload.Expectations.PASS, actual=Upload.Expectations.FAIL),
89
            },
90
        )
91
        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
92
93
        self.assertEqual(generated_dict['version'], 0)
94
        self.assertEqual(generated_dict['suite'], 'webkitpy-tests')
95
        self.assertEqual(generated_dict['configuration'], self.normalize(dict(
96
            platform='mac',
97
            is_simulator=False,
98
            version='10.13.0',
99
            version_name='High Sierra',
100
            architecture='x86_64',
101
            sdk='17A405',
102
        )))
103
        self.assertEqual(generated_dict['commits'], [dict(
104
            repository_id='webkit',
105
            id='5',
106
            branch='trunk',
107
        )])
108
        self.assertEqual(generated_dict['test_results']['details'], self.normalize(dict(link='https://webkit.org')))
109
        self.assertEqual(generated_dict['test_results']['run_stats'], self.normalize(dict(
110
            start_time=start_time,
111
            end_time=end_time,
112
            tests_skipped=0,
113
        )))
114
        self.assertEqual(generated_dict['test_results']['results'], self.normalize({
115
            'webkitpy.test1': {},
116
            'webkitpy.test2': Upload.create_test_result(expected=Upload.Expectations.PASS, actual=Upload.Expectations.FAIL),
117
        }))
118
119
    def test_upload(self):
120
        upload = Upload(
121
            suite='webkitpy-tests',
122
            commits=[Upload.create_commit(
123
                repository_id='webkit',
124
                id='5',
125
                branch='trunk',
126
            )],
127
        )
128
129
        with mock.patch('requests.post', new=lambda url, data: self.MockResponse()):
130
            self.assertTrue(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
131
132
        with mock.patch('requests.post', new=lambda url, data: self.raise_requests_ConnectionError()):
133
            self.assertFalse(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
134
135
        mock_404 = mock.patch('requests.post', new=lambda url, data: self.MockResponse(
136
            status_code=404,
137
            text=json.dumps(dict(description='No such address')),
138
        ))
139
        with mock_404:
140
            self.assertFalse(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
141
142
    def test_packed_test(self):
143
        upload = Upload(
144
            suite='webkitpy-tests',
145
            commits=[Upload.create_commit(
146
                repository_id='webkit',
147
                id='5',
148
                branch='trunk',
149
            )],
150
            results={
151
                'dir1/sub-dir1/test1': Upload.create_test_result(actual=Upload.Expectations.FAIL),
152
                'dir1/sub-dir1/test2': Upload.create_test_result(actual=Upload.Expectations.TIMEOUT),
153
                'dir1/sub-dir2/test3': {},
154
                'dir1/sub-dir2/test4': {},
155
                'dir2/sub-dir3/test5': {},
156
                'dir2/test6': {},
157
            }
158
        )
159
        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
160
        self.assertEqual(generated_dict['test_results']['results'], self.normalize({
161
            'dir1': {
162
                'sub-dir1': {
163
                    'test1': {'actual': Upload.Expectations.FAIL},
164
                    'test2': {'actual': Upload.Expectations.TIMEOUT},
165
                }, 'sub-dir2': {
166
                    'test3': {},
167
                    'test4': {},
168
                }
169
            }, 'dir2': {
170
                'sub-dir3': {'test5': {}},
171
                'test6': {},
172
            },
173
        }))
174
175
    def test_no_suite(self):
176
        upload = Upload(
177
            commits=[Upload.create_commit(
178
                repository_id='webkit',
179
                id='5',
180
                branch='trunk',
181
            )],
182
        )
183
        with self.assertRaises(ValueError):
184
            json.dumps(upload, cls=Upload.Encoder)
185
186
    def test_no_commits(self):
187
        upload = Upload(
188
            suite='webkitpy-tests',
189
        )
190
        with self.assertRaises(ValueError):
191
            json.dumps(upload, cls=Upload.Encoder)
192
193
    def test_buildbot(self):
194
        upload = Upload(
195
            suite='webkitpy-tests',
196
            commits=[Upload.create_commit(
197
                repository_id='webkit',
198
                id='5',
199
                branch='trunk',
200
            )],
201
            details=Upload.create_details(options=self.Options(
202
                buildbot_master='webkit.org',
203
                builder_name='Queue-1',
204
                build_number=1,
205
        )))
206
        with self.assertRaises(ValueError):
207
            json.dumps(upload, cls=Upload.Encoder)
208
209
        upload.details['buildbot-worker'] = 'bot123'
210
        generated_dict = self.normalize(json.loads(json.dumps(upload, cls=Upload.Encoder)))
211
        self.assertEqual(generated_dict['test_results']['details'], self.normalize({
212
            'buildbot-master': 'webkit.org',
213
            'builder-name': 'Queue-1',
214
            'build-number': 1,
215
            'buildbot-worker': 'bot123',
216
        }))
- Tools/Scripts/webkitpy/test/main.py -7 / +57 lines
Lines 1-6 Tools/Scripts/webkitpy/test/main.py_sec1
1
# Copyright (C) 2012 Google, Inc.
1
# Copyright (C) 2012 Google, Inc.
2
# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2
# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
3
# Copyright (C) 2018 Apple Inc. All rights reserved.
3
# Copyright (C) 2018-2019 Apple Inc. All rights reserved.
4
#
4
#
5
# Redistribution and use in source and binary forms, with or without
5
# Redistribution and use in source and binary forms, with or without
6
# modification, are permitted provided that the following conditions
6
# modification, are permitted provided that the following conditions
Lines 38-54 import traceback Tools/Scripts/webkitpy/test/main.py_sec2
38
import unittest
38
import unittest
39
39
40
from webkitpy.common.system.logutils import configure_logging
40
from webkitpy.common.system.logutils import configure_logging
41
from webkitpy.common.system.executive import Executive, ScriptError
41
from webkitpy.common.system.executive import ScriptError
42
from webkitpy.common.system.filesystem import FileSystem
42
from webkitpy.common.system.filesystem import FileSystem
43
from webkitpy.common.system.systemhost import SystemHost
43
from webkitpy.common.host import Host
44
from webkitpy.port.config import Config
44
from webkitpy.port.config import Config
45
from webkitpy.test.finder import Finder
45
from webkitpy.test.finder import Finder
46
from webkitpy.test.printer import Printer
46
from webkitpy.test.printer import Printer
47
from webkitpy.test.runner import Runner, unit_test_name
47
from webkitpy.test.runner import Runner, unit_test_name
48
from webkitpy.results.upload import Upload
49
from webkitpy.results.options import upload_options
48
50
49
_log = logging.getLogger(__name__)
51
_log = logging.getLogger(__name__)
50
52
51
_host = SystemHost()
53
_host = Host()
52
_webkit_root = None
54
_webkit_root = None
53
55
54
56
Lines 131-136 class Tester(object): Tools/Scripts/webkitpy/test/main.py_sec3
131
            help='Set the configuration to Release')
133
            help='Set the configuration to Release')
132
        parser.add_option_group(configuration_group)
134
        parser.add_option_group(configuration_group)
133
135
136
        upload_group = optparse.OptionGroup(parser, 'Upload Options')
137
        upload_group.add_options(upload_options())
138
        parser.add_option_group(upload_group)
139
134
        parser.add_option('-a', '--all', action='store_true', default=False,
140
        parser.add_option('-a', '--all', action='store_true', default=False,
135
                          help='run all the tests')
141
                          help='run all the tests')
136
        parser.add_option('-c', '--coverage', action='store_true', default=False,
142
        parser.add_option('-c', '--coverage', action='store_true', default=False,
Lines 181-191 class Tester(object): Tools/Scripts/webkitpy/test/main.py_sec4
181
        from webkitpy.thirdparty import autoinstall_everything
187
        from webkitpy.thirdparty import autoinstall_everything
182
        autoinstall_everything()
188
        autoinstall_everything()
183
189
190
        start_time = time.time()
191
        config = Config(_host.executive, self.finder.filesystem)
192
        configuration_to_use = self._options.configuration or config.default_configuration()
184
        if will_run_lldb_webkit_tests:
193
        if will_run_lldb_webkit_tests:
185
            self.printer.write_update('Building lldbWebKitTester ...')
194
            self.printer.write_update('Building lldbWebKitTester ...')
186
            build_lldbwebkittester = self.finder.filesystem.join(_webkit_root, 'Tools', 'Scripts', 'build-lldbwebkittester')
195
            build_lldbwebkittester = self.finder.filesystem.join(_webkit_root, 'Tools', 'Scripts', 'build-lldbwebkittester')
187
            config = Config(_host.executive, self.finder.filesystem)
188
            configuration_to_use = self._options.configuration or config.default_configuration()
189
            try:
196
            try:
190
                _host.executive.run_and_throw_if_fail([build_lldbwebkittester, config.flag_for_configuration(configuration_to_use)], quiet=(not bool(self._options.verbose)))
197
                _host.executive.run_and_throw_if_fail([build_lldbwebkittester, config.flag_for_configuration(configuration_to_use)], quiet=(not bool(self._options.verbose)))
191
            except ScriptError as e:
198
            except ScriptError as e:
Lines 218-223 class Tester(object): Tools/Scripts/webkitpy/test/main.py_sec5
218
        test_runner = Runner(self.printer, loader)
225
        test_runner = Runner(self.printer, loader)
219
        test_runner.run(parallel_tests, self._options.child_processes)
226
        test_runner.run(parallel_tests, self._options.child_processes)
220
        test_runner.run(serial_tests, 1)
227
        test_runner.run(serial_tests, 1)
228
        end_time = time.time()
221
229
222
        self.printer.print_result(time.time() - start)
230
        self.printer.print_result(time.time() - start)
223
231
Lines 232-240 class Tester(object): Tools/Scripts/webkitpy/test/main.py_sec6
232
        if self._options.coverage:
240
        if self._options.coverage:
233
            cov.stop()
241
            cov.stop()
234
            cov.save()
242
            cov.save()
243
244
        failed_uploads = 0
245
        if self._options.report_urls:
246
            self.printer.meter.writeln('\n')
247
            self.printer.write_update('Preparing upload data ...')
248
249
            # Empty test results indicate a PASS.
250
            results = {test: {} for test in test_runner.tests_run}
251
            for test, errors in test_runner.errors:
252
                results[test] = Upload.create_test_result(actual=Upload.Expectations.ERROR, log='/n'.join(errors))
253
            for test, failures in test_runner.failures:
254
                results[test] = Upload.create_test_result(actual=Upload.Expectations.FAIL, log='/n'.join(failures))
255
256
            _host.initialize_scm()
257
            upload = Upload(
258
                suite='webkitpy-tests',
259
                configuration=Upload.create_configuration(
260
                    platform=_host.platform.os_name,
261
                    version=str(_host.platform.os_version),
262
                    version_name=_host.platform.os_version_name(),
263
                    style=configuration_to_use,
264
                    sdk=_host.platform.build_version(),
265
                ),
266
                details=Upload.create_details(options=self._options),
267
                commits=[Upload.create_commit(
268
                    repository_id='webkit',
269
                    id=_host.scm().native_revision(_webkit_root),
270
                    branch=_host.scm().native_branch(_webkit_root),
271
                )],
272
                run_stats=Upload.create_run_stats(
273
                    start_time=start_time,
274
                    end_time=end_time,
275
                    tests_skipped=len(test_runner.tests_run) - len(parallel_tests) - len(serial_tests),
276
                ),
277
                results=results,
278
            )
279
            for url in self._options.report_urls:
280
                self.printer.write_update('Uploading to {} ...'.format(url))
281
                failed_uploads = failed_uploads if upload.upload(url, log_line_func=self.printer.meter.writeln) else (failed_uploads + 1)
282
            self.printer.meter.writeln('Uploads completed!')
283
284
        if self._options.coverage:
235
            cov.report(show_missing=False)
285
            cov.report(show_missing=False)
236
286
237
        return not self.printer.num_errors and not self.printer.num_failures
287
        return not self.printer.num_errors and not self.printer.num_failures and not failed_uploads
238
288
239
    def _check_imports(self, names):
289
    def _check_imports(self, names):
240
        for name in names:
290
        for name in names:
- Tools/Scripts/webkitpy/test/runner.py -2 / +2 lines
Lines 40-46 class Runner(object): Tools/Scripts/webkitpy/test/runner.py_sec1
40
    def __init__(self, printer, loader):
40
    def __init__(self, printer, loader):
41
        self.printer = printer
41
        self.printer = printer
42
        self.loader = loader
42
        self.loader = loader
43
        self.tests_run = 0
43
        self.tests_run = []
44
        self.errors = []
44
        self.errors = []
45
        self.failures = []
45
        self.failures = []
46
        self.worker_factory = lambda caller: _Worker(caller, self.loader)
46
        self.worker_factory = lambda caller: _Worker(caller, self.loader)
Lines 60-66 class Runner(object): Tools/Scripts/webkitpy/test/runner.py_sec2
60
            self.printer.print_started_test(source, test_name)
60
            self.printer.print_started_test(source, test_name)
61
            return
61
            return
62
62
63
        self.tests_run += 1
63
        self.tests_run.append(test_name)
64
        if failures:
64
        if failures:
65
            self.failures.append((test_name, failures))
65
            self.failures.append((test_name, failures))
66
        if errors:
66
        if errors:
- Tools/Scripts/webkitpy/test/runner_unittest.py -1 / +1 lines
Lines 92-97 class RunnerTest(unittest.TestCase): Tools/Scripts/webkitpy/test/runner_unittest.py_sec1
92
                            ('test3 (Foo)', 'E', 'test3\nerred'))
92
                            ('test3 (Foo)', 'E', 'test3\nerred'))
93
        runner = Runner(Printer(stream, options), loader)
93
        runner = Runner(Printer(stream, options), loader)
94
        runner.run(['Foo.test1', 'Foo.test2', 'Foo.test3'], 1)
94
        runner.run(['Foo.test1', 'Foo.test2', 'Foo.test3'], 1)
95
        self.assertEqual(runner.tests_run, 3)
95
        self.assertEqual(len(runner.tests_run), 3)
96
        self.assertEqual(len(runner.failures), 1)
96
        self.assertEqual(len(runner.failures), 1)
97
        self.assertEqual(len(runner.errors), 1)
97
        self.assertEqual(len(runner.errors), 1)
- Tools/Scripts/webkitpy/thirdparty/__init__.py +18 lines
Lines 1-4 Tools/Scripts/webkitpy/thirdparty/__init__.py_sec1
1
# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
1
# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2
# Copyright (C) 2019 Apple Inc. All rights reserved.
2
#
3
#
3
# Redistribution and use in source and binary forms, with or without
4
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# modification, are permitted provided that the following conditions
Lines 114-119 class AutoinstallImportHook(object): Tools/Scripts/webkitpy/thirdparty/__init__.py_sec2
114
            self._install_pytest_timeout()
115
            self._install_pytest_timeout()
115
        elif '.pytest' in fullname:
116
        elif '.pytest' in fullname:
116
            self._install_pytest()
117
            self._install_pytest()
118
        elif '.requests' in fullname:
119
            self._install_requests()
117
120
118
    def _install_mechanize(self):
121
    def _install_mechanize(self):
119
        self._install("https://files.pythonhosted.org/packages/source/m/mechanize/mechanize-0.2.5.tar.gz",
122
        self._install("https://files.pythonhosted.org/packages/source/m/mechanize/mechanize-0.2.5.tar.gz",
Lines 161-166 class AutoinstallImportHook(object): Tools/Scripts/webkitpy/thirdparty/__init__.py_sec3
161
        self._install("https://files.pythonhosted.org/packages/a2/ec/415d0cccc1ed41cd7fdf69ad989da16a8d13057996371004cab4bafc48f3/pytest-3.6.2.tar.gz",
164
        self._install("https://files.pythonhosted.org/packages/a2/ec/415d0cccc1ed41cd7fdf69ad989da16a8d13057996371004cab4bafc48f3/pytest-3.6.2.tar.gz",
162
                              "pytest-3.6.2/src/pytest.py")
165
                              "pytest-3.6.2/src/pytest.py")
163
166
167
    def _install_requests(self):
168
        self._ensure_autoinstalled_dir_is_in_sys_path()
169
        self._install("https://files.pythonhosted.org/packages/06/b8/d1ea38513c22e8c906275d135818fee16ad8495985956a9b7e2bb21942a1/certifi-2019.3.9.tar.gz",
170
                      "certifi-2019.3.9/certifi")
171
        self._install("https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz",
172
                      "chardet-3.0.4/chardet")
173
        self._install("https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz",
174
                      "chardet-3.0.4/chardet")
175
        self._install("https://files.pythonhosted.org/packages/ad/13/eb56951b6f7950cadb579ca166e448ba77f9d24efc03edd7e55fa57d04b7/idna-2.8.tar.gz",
176
                      "idna-2.8/idna")
177
        self._install("https://files.pythonhosted.org/packages/b1/53/37d82ab391393565f2f831b8eedbffd57db5a718216f82f1a8b4d381a1c1/urllib3-1.24.1.tar.gz",
178
                      "urllib3-1.24.1/src/urllib3")
179
        self._install("https://files.pythonhosted.org/packages/52/2c/514e4ac25da2b08ca5a464c50463682126385c4272c18193876e91f4bc38/requests-2.21.0.tar.gz",
180
                      "requests-2.21.0/requests")
181
164
    def _install_pylint(self):
182
    def _install_pylint(self):
165
        self._ensure_autoinstalled_dir_is_in_sys_path()
183
        self._ensure_autoinstalled_dir_is_in_sys_path()
166
        if (not self._fs.exists(self._fs.join(_AUTOINSTALLED_DIR, "pylint")) or
184
        if (not self._fs.exists(self._fs.join(_AUTOINSTALLED_DIR, "pylint")) or
- Tools/Scripts/webkitpy/tool/commands/queues_unittest.py -2 / +2 lines
Lines 183-192 class PatchProcessingQueueTest(CommandsT Tools/Scripts/webkitpy/tool/commands/queues_unittest.py_sec1
183
        queue._options = Mock()
183
        queue._options = Mock()
184
        queue._options.port = None
184
        queue._options.port = None
185
        patch = queue._tool.bugs.fetch_attachment(10001)
185
        patch = queue._tool.bugs.fetch_attachment(10001)
186
        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
186
        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
187
-- Begin comment --
187
-- Begin comment --
188
The attached test failures were seen while running run-webkit-tests on the mock-queue.
188
The attached test failures were seen while running run-webkit-tests on the mock-queue.
189
Port: mac-snowleopard  Platform: MockPlatform 1.0
189
Port: mac-highsierra  Platform: MockPlatform 1.0
190
-- End comment --
190
-- End comment --
191
"""
191
"""
192
        OutputCapture().assert_outputs(self, queue._upload_results_archive_for_patch, [patch, Mock()], expected_logs=expected_logs)
192
        OutputCapture().assert_outputs(self, queue._upload_results_archive_for_patch, [patch, Mock()], expected_logs=expected_logs)

Return to Bug 195755