run-webkit-tests: Report results archive to results.webkit.org
authorjbedard@apple.com <jbedard@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Sep 2019 18:29:12 +0000 (18:29 +0000)
committerjbedard@apple.com <jbedard@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Sep 2019 18:29:12 +0000 (18:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201321

Reviewed by Aakash Jain.

* Scripts/webkitpy/layout_tests/controllers/manager.py:
(Manager.run): After all tests are finish, upload the results archive for each
configuration.
* Scripts/webkitpy/results/upload.py:
(Upload):
(Upload.__init__): Automatically define timestamp.
(Upload.upload_archive): Upload an archive associated with the test run.
* Scripts/webkitpy/results/upload_unittest.py:
(UploadTest.test_buildbot):
(UploadTest):
(UploadTest.test_archive_upload):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249652 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
Tools/Scripts/webkitpy/results/upload.py
Tools/Scripts/webkitpy/results/upload_unittest.py

index 8caba93..0c03de4 100644 (file)
@@ -1,3 +1,22 @@
+2019-09-09  Jonathan Bedard  <jbedard@apple.com>
+
+        run-webkit-tests: Report results archive to results.webkit.org
+        https://bugs.webkit.org/show_bug.cgi?id=201321
+
+        Reviewed by Aakash Jain.
+
+        * Scripts/webkitpy/layout_tests/controllers/manager.py:
+        (Manager.run): After all tests are finish, upload the results archive for each
+        configuration.
+        * Scripts/webkitpy/results/upload.py:
+        (Upload):
+        (Upload.__init__): Automatically define timestamp.
+        (Upload.upload_archive): Upload an archive associated with the test run.
+        * Scripts/webkitpy/results/upload_unittest.py:
+        (UploadTest.test_buildbot):
+        (UploadTest):
+        (UploadTest.test_archive_upload):
+
 2019-09-09  Chris Dumez  <cdumez@apple.com>
 
         Stop using testRunner.setPrivateBrowsingEnabled_DEPRECATED() in http/tests/workers/service/basic-register.html
index 69369c6..a87e939 100644 (file)
@@ -37,6 +37,7 @@ create a final report.
 import json
 import logging
 import random
+import shutil
 import sys
 import time
 from collections import defaultdict, OrderedDict
@@ -244,6 +245,7 @@ class Manager(object):
 
         max_child_processes_for_run = 1
         child_processes_option_value = self._options.child_processes
+        uploads = []
 
         for device_type in device_type_list:
             self._runner._test_is_slow = lambda test_file: self._test_is_slow(test_file, device_type=device_type)
@@ -281,6 +283,7 @@ class Manager(object):
                     configuration=configuration,
                     details=Upload.create_details(options=self._options),
                     commits=self._port.commits_for_upload(),
+                    timestamp=start_time,
                     run_stats=Upload.create_run_stats(
                         start_time=start_time_for_device,
                         end_time=time.time(),
@@ -288,10 +291,12 @@ class Manager(object):
                     ),
                     results=self._results_to_upload_json_trie(self._expectations[device_type], temp_initial_results),
                 )
-                for url in self._options.report_urls:
-                    self._printer.write_update('Uploading to {} ...'.format(url))
-                    if not upload.upload(url, log_line_func=self._printer.writeln):
+                for hostname in self._options.report_urls:
+                    self._printer.write_update('Uploading to {} ...'.format(hostname))
+                    if not upload.upload(hostname, log_line_func=self._printer.writeln):
                         num_failed_uploads += 1
+                    else:
+                        uploads.append(upload)
                 self._printer.writeln('Uploads completed!')
 
             initial_results = initial_results.merge(temp_initial_results) if initial_results else temp_initial_results
@@ -309,6 +314,21 @@ class Manager(object):
 
         end_time = time.time()
         result = self._end_test_run(start_time, end_time, initial_results, retry_results, enabled_pixel_tests_in_retry)
+
+        if self._options.report_urls and uploads:
+            self._printer.writeln('\n')
+            self._printer.write_update('Preparing to upload test archive ...')
+
+            with self._filesystem.mkdtemp() as temp:
+                archive = self._filesystem.join(temp, 'test-archive')
+                shutil.make_archive(archive, 'zip', self._results_directory)
+
+                for upload in uploads:
+                    for hostname in self._options.report_urls:
+                        self._printer.write_update('Uploading archive to {} ...'.format(hostname))
+                        if not upload.upload_archive(hostname, self._filesystem.open_binary_file_for_reading(archive + '.zip'), log_line_func=self._printer.writeln):
+                            num_failed_uploads += 1
+
         if num_failed_uploads:
             result.exit_code = -1
         return result
index 1b49691..ab74cc8 100644 (file)
@@ -25,12 +25,14 @@ import webkitpy.thirdparty.autoinstalled.requests
 import json
 import requests
 import sys
+import time
 
 import platform as host_platform
 
 
 class Upload(object):
     UPLOAD_ENDPOINT = '/api/upload'
+    ARCHIVE_UPLOAD_ENDPOINT = '/api/upload/archive'
     BUILDBOT_DETAILS = ['buildbot-master', 'builder-name', 'build-number', 'buildbot-worker']
     VERSION = 0
 
@@ -99,7 +101,7 @@ class Upload(object):
         self.suite = suite
         self.configuration = configuration
         self.commits = commits
-        self.timestamp = timestamp
+        self.timestamp = int(timestamp or time.time())
         self.details = details
         self.run_stats = run_stats
         self.results = results
@@ -165,20 +167,51 @@ class Upload(object):
         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')):
+    def upload(self, hostname, log_line_func=lambda val: sys.stdout.write(val + '\n')):
         try:
-            response = requests.post(url + self.UPLOAD_ENDPOINT, data=json.dumps(self, cls=Upload.Encoder))
+            response = requests.post('{}{}'.format(hostname, self.UPLOAD_ENDPOINT), data=json.dumps(self, cls=Upload.Encoder))
         except requests.exceptions.ConnectionError:
-            log_line_func(' ' * 4 + 'Failed to upload to {}, results server not online'.format(url))
+            log_line_func(' ' * 4 + 'Failed to upload to {}, results server not online'.format(hostname))
             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'])
+            log_line_func(' ' * 4 + 'Error uploading to {}'.format(hostname))
+            log_line_func(' ' * 8 + response.json().get('description'))
             return False
 
-        log_line_func(' ' * 4 + 'Uploaded results to {}'.format(url))
+        log_line_func(' ' * 4 + 'Uploaded results to {}'.format(hostname))
+        return True
+
+    def upload_archive(self, hostname, archive, log_line_func=lambda val: sys.stdout.write(val + '\n')):
+        try:
+            meta_data = dict(
+                version=self.VERSION,
+                suite=self.suite,
+                configuration=json.dumps(self.configuration or self.create_configuration()),
+                commits=json.dumps(self.commits),
+            )
+            if self.timestamp:
+                meta_data['timestamp'] = self.timestamp
+            response = requests.post(
+                '{}{}'.format(hostname, self.ARCHIVE_UPLOAD_ENDPOINT),
+                data=meta_data,
+                files=dict(file=archive),
+            )
+
+        except requests.exceptions.ConnectionError:
+            log_line_func(' ' * 4 + 'Failed to upload test archive to {}, results server not online'.format(hostname))
+            return False
+        except ValueError as e:
+            log_line_func(' ' * 4 + 'Failed to encode archive reference data: {}'.format(e))
+            return False
+
+        if response.status_code != 200:
+            log_line_func(' ' * 4 + 'Error uploading archive to {}'.format(hostname))
+            log_line_func(' ' * 8 + response.json().get('description'))
+            return False
+
+        log_line_func(' ' * 4 + 'Uploaded test archive to {}'.format(hostname))
         return True
index 49ce31b..19b4067 100644 (file)
@@ -127,17 +127,19 @@ class UploadTest(unittest.TestCase):
         )
 
         with mock.patch('requests.post', new=lambda url, data: self.MockResponse()):
-            self.assertTrue(upload.upload('https://webkit.org/results', log_line_func=lambda _: None))
+            self.assertTrue(upload.upload('https://results.webkit.org', 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))
+            lines = []
+            self.assertFalse(upload.upload('https://results.webkit.org', log_line_func=lambda line: lines.append(line)))
+            self.assertEqual([' ' * 4 + 'Failed to upload to https://results.webkit.org, results server not online'], lines)
 
         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))
+            self.assertFalse(upload.upload('https://results.webkit.org', log_line_func=lambda _: None))
 
     def test_packed_test(self):
         upload = Upload(
@@ -214,3 +216,33 @@ class UploadTest(unittest.TestCase):
             'build-number': 1,
             'buildbot-worker': 'bot123',
         }))
+
+    def test_archive_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, files: self.MockResponse()):
+            self.assertTrue(upload.upload_archive('https://results.webkit.org', archive='content', log_line_func=lambda _: None))
+
+        with mock.patch('requests.post', new=lambda url, data, files: self.raise_requests_ConnectionError()):
+            lines = []
+            self.assertFalse(upload.upload_archive('https://results.webkit.org', archive='content', log_line_func=lambda line: lines.append(line)))
+            self.assertEqual([' ' * 4 + 'Failed to upload test archive to https://results.webkit.org, results server not online'], lines)
+
+        mock_404 = mock.patch('requests.post', new=lambda url, data, files: self.MockResponse(
+            status_code=404,
+            text=json.dumps(dict(description='No such address')),
+        ))
+        with mock_404:
+            lines = []
+            self.assertFalse(upload.upload_archive('https://results.webkit.org', archive='content', log_line_func=lambda line: lines.append(line)))
+            self.assertEqual([
+                ' ' * 4 + 'Error uploading archive to https://results.webkit.org',
+                ' ' * 8 + 'No such address',
+            ], lines)