| Differences between
and this patch
- Tools/ChangeLog +83 lines
Lines 1-3 Tools/ChangeLog_sec1
1
2013-02-21  Ryosuke Niwa  <rniwa@webkit.org>
2
3
        Upload results to perf.webkit.org in addition to the one specified by --test-results-server
4
        https://bugs.webkit.org/show_bug.cgi?id=108577
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        Upload results to perf.webkit.org as well as the host specified by --test-results-server.
9
        This is a temporarily measure until we move away from using webkit-perf.appspot.com.
10
11
        This patch adds scm.svn_timestamp to obtain the timestamp of the latest commit present in
12
        a svn checkout or a git clone. This information is embedded in JSON submitted to perf.webkit.org.
13
14
        It also changes the repository names returned by port objects to be properly capitalized
15
        human readable names such as WebKit instead of lowercased names such as webkit. Several users
16
        of this feature has been updated to explicitly lowercase the names.
17
18
19
        * Scripts/webkitpy/common/checkout/scm/git.py:
20
        (Git.svn_timestamp): Added. Obtains the timestamp of the last subverion commit in the clone.
21
        Unfortunately, git's timestamp granularity is seconds so we're losing some information
22
        compared to using a regular subversion client. To make matters worse, git doesn't have any
23
        option to show ISO-format timestamp in UTC so we're going to manually fiddle with timezone.
24
25
        * Scripts/webkitpy/common/checkout/scm/scm.py:
26
        (SCM.svn_timestamp): Added.
27
28
        * Scripts/webkitpy/common/checkout/scm/scm_mock.py:
29
        (MockSCM.svn_timestamp): Added.
30
31
        * Scripts/webkitpy/common/checkout/scm/scm_unittest.py:
32
        (test_svn_timestamp): Added a test for Git.svn_timestamp.
33
34
        * Scripts/webkitpy/common/checkout/scm/svn.py:
35
        (SVN.svn_timestamp): Added. With svn, all we need to do is to use --xml option and parse the
36
        timestamp which is always in UTC.
37
38
        * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
39
        (JSONResultsGeneratorBase._insert_generic_metadata): Lowercase the name. Note that the name
40
        'chromium' needs to be substituted by 'chrome' for historical reasons.
41
42
        * Scripts/webkitpy/layout_tests/port/base.py:
43
        (Port.repository_paths): Return WebKit instead of webkit as noted above.
44
45
        * Scripts/webkitpy/layout_tests/port/chromium.py:
46
        (ChromiumPort.repository_paths): Return Chromium instead of chromium as noted above.
47
48
        * Scripts/webkitpy/performance_tests/perftestsrunner.py:
49
        (PerfTestsRunner.__init__): Store the current time in UTC as well as in local time.
50
        (PerfTestsRunner._collect_tests):
51
52
        (PerfTestsRunner._generate_and_show_results): Retrieve both regular output and one for perf.webkit.org,
53
        and upload them appropriately.
54
55
        (PerfTestsRunner._generate_results_dict): Store WebKit and Chromium revisions at which tests were ran
56
        in revisions_for_perf_webkit and construct an output for perf.webkit.org.
57
58
        (PerfTestsRunner._datetime_in_ES5_compatible_iso_format): Added.
59
60
        (PerfTestsRunner._merge_slave_config_json): Merge slave configuration files into both regular output
61
        and one for perf.webkit.org. Here, we prefix each key with "builder" for perf.webkit.org.
62
        e.g. "processor" would be renamed to "builderProcessor".
63
64
        (PerfTestsRunner._generate_output_files):
65
66
        (PerfTestsRunner._upload_json): Added a remote path as an argument since we upload JSONs to /api/report
67
        on perf.webkit.org whereas we upload it to /api/test/report on webkit-perf.appspot.com. Also added the code
68
        to parse response as JSON when possible since perf.webkit.org returns a JSON response as opposed to
69
        webkit-perf.appspot.com which spits out a plaintext response.
70
71
        * Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py:
72
        (MainTest._test_run_with_json_output.mock_upload_json): Tolerate perf.webkit.org/api/report for now.
73
        (MainTest._test_run_with_json_output): Store a UTC time as perftestrunner would do.
74
        (MainTest.test_run_with_upload_json_should_generate_perf_webkit_json): Added.
75
76
        * Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py:
77
        (MainTest.test_upload_json): Moved from itegrationtest.py since it really is a unit test. Also added test
78
        cases to parse JSON responses.
79
        (MainTest.test_upload_json.MockFileUploader): Refactored.
80
        (MainTest.test_upload_json.MockFileUploader.reset): Added.
81
        (MainTest.test_upload_json.MockFileUploader.__init__):
82
        (MainTest.test_upload_json.MockFileUploader.upload_single_text_file):
83
1
2013-02-21  Dirk Pranke  <dpranke@chromium.org>
84
2013-02-21  Dirk Pranke  <dpranke@chromium.org>
2
85
3
        [chromium] support the lucid version of freetype on precise in DRT
86
        [chromium] support the lucid version of freetype on precise in DRT
- Tools/Scripts/webkitpy/common/checkout/scm/git.py +16 lines
Lines 27-32 Tools/Scripts/webkitpy/common/checkout/scm/git.py_sec1
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
30
import datetime
30
import logging
31
import logging
31
import os
32
import os
32
import re
33
import re
Lines 253-258 class Git(SCM, SVNRepository): Tools/Scripts/webkitpy/common/checkout/scm/git.py_sec2
253
            return ""
254
            return ""
254
        return str(match.group('svn_revision'))
255
        return str(match.group('svn_revision'))
255
256
257
    def svn_timestamp(self, path):
258
        git_log = self._run_git(['log', '-25', '--date=iso', self.find_checkout_root(path)])
259
        match = re.search("^Date:\s*(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) ([+-])(\d{2})(\d{2})$", git_log, re.MULTILINE)
260
        if not match:
261
            return ""
262
263
        # Manually modify the timezone since Git doesn't have an optiton to show it in UTC.
264
        # Git also truncates milliseconds but we're going to ignore that for now.
265
        time_with_timezone = datetime.datetime(int(match.group(1)), int(match.group(2)), int(match.group(3)),
266
            int(match.group(4)), int(match.group(5)), int(match.group(6)), 0)
267
268
        sign = 1 if match.group(7) == '+' else -1
269
        time_wihout_timezone = time_with_timezone - datetime.timedelta(hours=sign * int(match.group(8)), minutes=int(match.group(9)))
270
        return time_wihout_timezone.strftime('%Y-%m-%dT%H:%M:%SZ')
271
256
    def prepend_svn_revision(self, diff):
272
    def prepend_svn_revision(self, diff):
257
        revision = self.head_svn_revision()
273
        revision = self.head_svn_revision()
258
        if not revision:
274
        if not revision:
- Tools/Scripts/webkitpy/common/checkout/scm/scm.py +3 lines
Lines 169-174 class SCM: Tools/Scripts/webkitpy/common/checkout/scm/scm.py_sec1
169
    def svn_revision(self, path):
169
    def svn_revision(self, path):
170
        self._subclass_must_implement()
170
        self._subclass_must_implement()
171
171
172
    def svn_timestamp(self, path):
173
        self._subclass_must_implement()
174
172
    def create_patch(self, git_commit=None, changed_files=None):
175
    def create_patch(self, git_commit=None, changed_files=None):
173
        self._subclass_must_implement()
176
        self._subclass_must_implement()
174
177
- Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py +3 lines
Lines 84-89 class MockSCM(object): Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py_sec1
84
    def svn_revision(self, path):
84
    def svn_revision(self, path):
85
        return '5678'
85
        return '5678'
86
86
87
    def svn_timestamp(self, path):
88
        return '2013-02-01 08:48:05 +0000'
89
87
    def create_patch(self, git_commit, changed_files=None):
90
    def create_patch(self, git_commit, changed_files=None):
88
        return "Patch1"
91
        return "Patch1"
89
92
- Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py +12 lines
Lines 1577-1579 MOCK run_command: ['git', 'log', '-25', Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py_sec1
1577
1577
1578
    def test_push_local_commits_to_server_without_username_and_with_password(self):
1578
    def test_push_local_commits_to_server_without_username_and_with_password(self):
1579
        self.assertRaises(AuthenticationError, self.make_scm().push_local_commits_to_server, {'password': 'blah'})
1579
        self.assertRaises(AuthenticationError, self.make_scm().push_local_commits_to_server, {'password': 'blah'})
1580
1581
    def test_svn_timestamp(self):
1582
        scm = self.make_scm()
1583
        scm.find_checkout_root = lambda path: ''
1584
        scm._run_git = lambda args: 'Date: 2013-02-08 08:05:49 +0000'
1585
        self.assertEqual(scm.svn_timestamp('some-path'), '2013-02-08T08:05:49Z')
1586
1587
        scm._run_git = lambda args: 'Date: 2013-02-08 01:02:03 +0130'
1588
        self.assertEqual(scm.svn_timestamp('some-path'), '2013-02-07T23:32:03Z')
1589
1590
        scm._run_git = lambda args: 'Date: 2013-02-08 01:55:21 -0800'
1591
        self.assertEqual(scm.svn_timestamp('some-path'), '2013-02-08T09:55:21Z')
- Tools/Scripts/webkitpy/common/checkout/scm/svn.py +6 lines
Lines 246-251 class SVN(SCM, SVNRepository): Tools/Scripts/webkitpy/common/checkout/scm/svn.py_sec1
246
    def svn_revision(self, path):
246
    def svn_revision(self, path):
247
        return self.value_from_svn_info(path, 'Revision')
247
        return self.value_from_svn_info(path, 'Revision')
248
248
249
    def svn_timestamp(self, path):
250
        # We use --xml to get timestamps like 2013-02-08T08:18:04.964409Z
251
        info_output = Executive().run_command([self.executable_name, 'info', '--xml'], cwd=path).rstrip()
252
        match = re.search(r"^<date>(?P<value>.+)</date>$", info_output, re.MULTILINE)
253
        return match.group('value')
254
249
    # FIXME: This method should be on Checkout.
255
    # FIXME: This method should be on Checkout.
250
    def create_patch(self, git_commit=None, changed_files=None):
256
    def create_patch(self, git_commit=None, changed_files=None):
251
        """Returns a byte array (str()) representing the patch file.
257
        """Returns a byte array (str()) representing the patch file.
- Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py -3 / +4 lines
Lines 526-536 class JSONResultsGeneratorBase(object): Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py_sec1
526
        for (name, path) in self._svn_repositories:
526
        for (name, path) in self._svn_repositories:
527
            # Note: for JSON file's backward-compatibility we use 'chrome' rather
527
            # Note: for JSON file's backward-compatibility we use 'chrome' rather
528
            # than 'chromium' here.
528
            # than 'chromium' here.
529
            if name == 'chromium':
529
            lowercase_name = name.lower()
530
                name = 'chrome'
530
            if lowercase_name == 'chromium':
531
                lowercase_name = 'chrome'
531
            self._insert_item_into_raw_list(results_for_builder,
532
            self._insert_item_into_raw_list(results_for_builder,
532
                self._get_svn_revision(path),
533
                self._get_svn_revision(path),
533
                name + 'Revision')
534
                lowercase_name + 'Revision')
534
535
535
        self._insert_item_into_raw_list(results_for_builder,
536
        self._insert_item_into_raw_list(results_for_builder,
536
            int(time.time()),
537
            int(time.time()),
- Tools/Scripts/webkitpy/layout_tests/port/base.py -3 / +3 lines
Lines 1097-1107 class Port(object): Tools/Scripts/webkitpy/layout_tests/port/base.py_sec1
1097
1097
1098
    def repository_paths(self):
1098
    def repository_paths(self):
1099
        """Returns a list of (repository_name, repository_path) tuples of its depending code base.
1099
        """Returns a list of (repository_name, repository_path) tuples of its depending code base.
1100
        By default it returns a list that only contains a ('webkit', <webkitRepossitoryPath>) tuple."""
1100
        By default it returns a list that only contains a ('WebKit', <webkitRepossitoryPath>) tuple."""
1101
1101
1102
        # We use LayoutTest directory here because webkit_base isn't a part webkit repository in Chromium port
1102
        # We use LayoutTest directory here because webkit_base isn't a part of WebKit repository in Chromium port
1103
        # where turnk isn't checked out as a whole.
1103
        # where turnk isn't checked out as a whole.
1104
        return [('webkit', self.layout_tests_dir())]
1104
        return [('WebKit', self.layout_tests_dir())]
1105
1105
1106
    _WDIFF_DEL = '##WDIFF_DEL##'
1106
    _WDIFF_DEL = '##WDIFF_DEL##'
1107
    _WDIFF_ADD = '##WDIFF_ADD##'
1107
    _WDIFF_ADD = '##WDIFF_ADD##'
- Tools/Scripts/webkitpy/layout_tests/port/chromium.py -1 / +1 lines
Lines 364-370 class ChromiumPort(Port): Tools/Scripts/webkitpy/layout_tests/port/chromium.py_sec1
364
364
365
    def repository_paths(self):
365
    def repository_paths(self):
366
        repos = super(ChromiumPort, self).repository_paths()
366
        repos = super(ChromiumPort, self).repository_paths()
367
        repos.append(('chromium', self.path_from_chromium_base('build')))
367
        repos.append(('Chromium', self.path_from_chromium_base('build')))
368
        return repos
368
        return repos
369
369
370
    def _get_crash_log(self, name, pid, stdout, stderr, newer_than):
370
    def _get_crash_log(self, name, pid, stdout, stderr, newer_than):
- Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py -20 / +87 lines
Lines 33-38 import json Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec1
33
import logging
33
import logging
34
import optparse
34
import optparse
35
import time
35
import time
36
import datetime
36
37
37
from webkitpy.common import find_files
38
from webkitpy.common import find_files
38
from webkitpy.common.checkout.scm.detection import SCMDetector
39
from webkitpy.common.checkout.scm.detection import SCMDetector
Lines 67-72 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec2
67
        self._base_path = self._port.perf_tests_dir()
68
        self._base_path = self._port.perf_tests_dir()
68
        self._results = {}
69
        self._results = {}
69
        self._timestamp = time.time()
70
        self._timestamp = time.time()
71
        self._utc_timestamp = datetime.datetime.utcnow()
70
        self._needs_http = None
72
        self._needs_http = None
71
        self._has_http_lock = False
73
        self._has_http_lock = False
72
74
Lines 126-133 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec3
126
        return optparse.OptionParser(option_list=(perf_option_list)).parse_args(args)
128
        return optparse.OptionParser(option_list=(perf_option_list)).parse_args(args)
127
129
128
    def _collect_tests(self):
130
    def _collect_tests(self):
129
        """Return the list of tests found."""
130
131
        test_extensions = ['.html', '.svg']
131
        test_extensions = ['.html', '.svg']
132
        if self._options.replay:
132
        if self._options.replay:
133
            test_extensions.append('.replay')
133
            test_extensions.append('.replay')
Lines 208-231 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec4
208
    def _generate_and_show_results(self):
208
    def _generate_and_show_results(self):
209
        options = self._options
209
        options = self._options
210
        output_json_path = self._output_json_path()
210
        output_json_path = self._output_json_path()
211
        output = self._generate_results_dict(self._timestamp, options.description, options.platform, options.builder_name, options.build_number)
211
        output, perf_webkit_output = self._generate_results_dict(self._timestamp, options.description, options.platform, options.builder_name, options.build_number)
212
212
213
        if options.slave_config_json_path:
213
        if options.slave_config_json_path:
214
            output = self._merge_slave_config_json(options.slave_config_json_path, output)
214
            output, perf_webkit_output = self._merge_slave_config_json(options.slave_config_json_path, output, perf_webkit_output)
215
            if not output:
215
            if not output:
216
                return self.EXIT_CODE_BAD_SOURCE_JSON
216
                return self.EXIT_CODE_BAD_SOURCE_JSON
217
217
218
        output = self._merge_outputs_if_needed(output_json_path, output)
218
        output = self._merge_outputs_if_needed(output_json_path, output)
219
        if not output:
219
        if not output:
220
            return self.EXIT_CODE_BAD_MERGE
220
            return self.EXIT_CODE_BAD_MERGE
221
        perf_webkit_output = [perf_webkit_output]
221
222
222
        results_page_path = self._host.filesystem.splitext(output_json_path)[0] + '.html'
223
        results_page_path = self._host.filesystem.splitext(output_json_path)[0] + '.html'
223
        self._generate_output_files(output_json_path, results_page_path, output)
224
        perf_webkit_json_path = self._host.filesystem.splitext(output_json_path)[0] + '-perf-webkit.json' if options.test_results_server else None
225
        self._generate_output_files(output_json_path, perf_webkit_json_path, results_page_path, output, perf_webkit_output)
224
226
225
        if options.test_results_server:
227
        if options.test_results_server:
226
            if not self._upload_json(options.test_results_server, output_json_path):
228
            if not self._upload_json(options.test_results_server, output_json_path):
227
                return self.EXIT_CODE_FAILED_UPLOADING
229
                return self.EXIT_CODE_FAILED_UPLOADING
228
230
231
            # FIXME: Remove this code once we've made transition to use perf.webkit.org
232
            if not self._upload_json('perf.webkit.org', perf_webkit_json_path, "/api/report"):
233
                return self.EXIT_CODE_FAILED_UPLOADING
234
229
        if options.show_results:
235
        if options.show_results:
230
            self._port.show_results_html_file(results_page_path)
236
            self._port.show_results_html_file(results_page_path)
231
237
Lines 233-241 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec5
233
        contents = {'results': self._results}
239
        contents = {'results': self._results}
234
        if description:
240
        if description:
235
            contents['description'] = description
241
            contents['description'] = description
242
243
        revisions_for_perf_webkit = {}
236
        for (name, path) in self._port.repository_paths():
244
        for (name, path) in self._port.repository_paths():
237
            scm = SCMDetector(self._host.filesystem, self._host.executive).detect_scm_system(path) or self._host.scm()
245
            scm = SCMDetector(self._host.filesystem, self._host.executive).detect_scm_system(path) or self._host.scm()
238
            contents[name + '-revision'] = scm.svn_revision(path)
246
            revision = scm.svn_revision(path)
247
            contents[name.lower() + '-revision'] = revision
248
            revisions_for_perf_webkit[name] = {'revision': str(revision), 'timestamp': scm.svn_timestamp(path)}
239
249
240
        # FIXME: Add --branch or auto-detect the branch we're in
250
        # FIXME: Add --branch or auto-detect the branch we're in
241
        for key, value in {'timestamp': int(timestamp), 'branch': self._default_branch, 'platform': platform,
251
        for key, value in {'timestamp': int(timestamp), 'branch': self._default_branch, 'platform': platform,
Lines 243-262 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec6
243
            if value:
253
            if value:
244
                contents[key] = value
254
                contents[key] = value
245
255
246
        return contents
256
        contents_for_perf_webkit = {
257
            'builderName': builder_name,
258
            'buildNumber': str(build_number),
259
            'buildTime': self._datetime_in_ES5_compatible_iso_format(self._utc_timestamp),
260
            'platform': platform,
261
            'revisions': revisions_for_perf_webkit,
262
            'tests': {}}
247
263
248
    def _merge_slave_config_json(self, slave_config_json_path, output):
264
        for metric_full_name in self._results:
265
            result = self._results[metric_full_name]
266
267
            # We can't reprot results without indivisual measurements.
268
            if not isinstance(result, dict):
269
                continue
270
271
            assert metric_full_name.count(':') <= 1
272
            test_full_name, _, metric = metric_full_name.partition(':')
273
            if not metric:
274
                metric = {'fps': 'FrameRate', 'runs/s': 'Runs', 'ms': 'Time'}[result['unit']]
275
276
            tests = contents_for_perf_webkit['tests']
277
            path = test_full_name.split('/')
278
            for i in range(0, len(path)):
279
                # FIXME: We shouldn't assume HTML extension.
280
                is_last_token = i + 1 == len(path)
281
                url = 'http://trac.webkit.org/browser/trunk/PerformanceTests/' + '/'.join(path[0:i + 1])
282
                if is_last_token:
283
                    url += '.html'
284
285
                tests.setdefault(path[i], {'url': url})
286
                current_test = tests[path[i]]
287
                if is_last_token:
288
                    current_test.setdefault('metrics', {})
289
                    assert metric not in current_test['metrics']
290
                    current_test['metrics'][metric] = {'current': result['values']}
291
                else:
292
                    current_test.setdefault('tests', {})
293
                    tests = current_test['tests']
294
295
        return contents, contents_for_perf_webkit
296
297
    @staticmethod
298
    def _datetime_in_ES5_compatible_iso_format(datetime):
299
        return datetime.strftime('%Y-%m-%dT%H:%M:%S.%f')
300
301
    def _merge_slave_config_json(self, slave_config_json_path, contents, contents_for_perf_webkit):
249
        if not self._host.filesystem.isfile(slave_config_json_path):
302
        if not self._host.filesystem.isfile(slave_config_json_path):
250
            _log.error("Missing slave configuration JSON file: %s" % slave_config_json_path)
303
            _log.error("Missing slave configuration JSON file: %s" % slave_config_json_path)
251
            return None
304
            return None, None
252
305
253
        try:
306
        try:
254
            slave_config_json = self._host.filesystem.open_text_file_for_reading(slave_config_json_path)
307
            slave_config_json = self._host.filesystem.open_text_file_for_reading(slave_config_json_path)
255
            slave_config = json.load(slave_config_json)
308
            slave_config = json.load(slave_config_json)
256
            return dict(slave_config.items() + output.items())
309
            contents = dict(slave_config.items() + contents.items())
310
            for key in slave_config:
311
                contents_for_perf_webkit['builder' + key.capitalize()] = slave_config[key]
312
            return contents, contents_for_perf_webkit
257
        except Exception, error:
313
        except Exception, error:
258
            _log.error("Failed to merge slave configuration JSON file %s: %s" % (slave_config_json_path, error))
314
            _log.error("Failed to merge slave configuration JSON file %s: %s" % (slave_config_json_path, error))
259
        return None
315
        return None, None
260
316
261
    def _merge_outputs_if_needed(self, output_json_path, output):
317
    def _merge_outputs_if_needed(self, output_json_path, output):
262
        if self._options.reset_results or not self._host.filesystem.isfile(output_json_path):
318
        if self._options.reset_results or not self._host.filesystem.isfile(output_json_path):
Lines 268-279 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec7
268
            _log.error("Failed to merge output JSON file %s: %s" % (output_json_path, error))
324
            _log.error("Failed to merge output JSON file %s: %s" % (output_json_path, error))
269
        return None
325
        return None
270
326
271
    def _generate_output_files(self, output_json_path, results_page_path, output):
327
    def _generate_output_files(self, output_json_path, perf_webkit_json_path, results_page_path, output, perf_webkit_output):
272
        filesystem = self._host.filesystem
328
        filesystem = self._host.filesystem
273
329
274
        json_output = json.dumps(output)
330
        json_output = json.dumps(output)
275
        filesystem.write_text_file(output_json_path, json_output)
331
        filesystem.write_text_file(output_json_path, json_output)
276
332
333
        if perf_webkit_json_path:
334
            filesystem.write_text_file(perf_webkit_json_path, json.dumps(perf_webkit_output))
335
277
        if results_page_path:
336
        if results_page_path:
278
            template_path = filesystem.join(self._port.perf_tests_dir(), 'resources/results-template.html')
337
            template_path = filesystem.join(self._port.perf_tests_dir(), 'resources/results-template.html')
279
            template = filesystem.read_text_file(template_path)
338
            template = filesystem.read_text_file(template_path)
Lines 284-305 class PerfTestsRunner(object): Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py_sec8
284
343
285
            filesystem.write_text_file(results_page_path, results_page)
344
            filesystem.write_text_file(results_page_path, results_page)
286
345
287
    def _upload_json(self, test_results_server, json_path, file_uploader=FileUploader):
346
    def _upload_json(self, test_results_server, json_path, host_path="/api/test/report", file_uploader=FileUploader):
288
        uploader = file_uploader("https://%s/api/test/report" % test_results_server, 120)
347
        url = "https://%s%s" % (test_results_server, host_path)
348
        uploader = file_uploader(url, 120)
289
        try:
349
        try:
290
            response = uploader.upload_single_text_file(self._host.filesystem, 'application/json', json_path)
350
            response = uploader.upload_single_text_file(self._host.filesystem, 'application/json', json_path)
291
        except Exception, error:
351
        except Exception, error:
292
            _log.error("Failed to upload JSON file in 120s: %s" % error)
352
            _log.error("Failed to upload JSON file to %s in 120s: %s" % (url, error))
293
            return False
353
            return False
294
354
295
        response_body = [line.strip('\n') for line in response]
355
        response_body = [line.strip('\n') for line in response]
296
        if response_body != ['OK']:
356
        if response_body != ['OK']:
297
            _log.error("Uploaded JSON but got a bad response:")
357
            try:
298
            for line in response_body:
358
                parsed_response = json.loads('\n'.join(response_body))
299
                _log.error(line)
359
            except:
300
            return False
360
                _log.error("Uploaded JSON to %s but got a bad response:" % url)
361
                for line in response_body:
362
                    _log.error(line)
363
                return False
364
            if parsed_response.get('status') != 'OK':
365
                _log.error("Uploaded JSON to %s but got an error:" % url)
366
                _log.error(json.dumps(parsed_response, indent=4))
367
                return False
301
368
302
        _log.info("JSON file uploaded.")
369
        _log.info("JSON file uploaded to %s." % url)
303
        return True
370
        return True
304
371
305
    def _print_status(self, tests, expected, unexpected):
372
    def _print_status(self, tests, expected, unexpected):
- Tools/Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py -39 / +31 lines
Lines 29-34 Tools/Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py_sec1
29
"""Integration tests for run_perf_tests."""
29
"""Integration tests for run_perf_tests."""
30
30
31
import StringIO
31
import StringIO
32
import datetime
32
import json
33
import json
33
import re
34
import re
34
import unittest2 as unittest
35
import unittest2 as unittest
Lines 321-334 class MainTest(unittest.TestCase): Tools/Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py_sec2
321
322
322
        uploaded = [False]
323
        uploaded = [False]
323
324
324
        def mock_upload_json(hostname, json_path):
325
        def mock_upload_json(hostname, json_path, host_path=None):
325
            self.assertEqual(hostname, 'some.host')
326
            # FIXME: Get rid of the hard-coded perf.webkit.org once we've completed the transition.
326
            self.assertEqual(json_path, '/mock-checkout/output.json')
327
            self.assertIn(hostname, ['some.host', 'perf.webkit.org'])
328
            self.assertIn(json_path, ['/mock-checkout/output.json', '/mock-checkout/output-perf-webkit.json'])
329
            self.assertIn(host_path, [None, '/api/report'])
327
            uploaded[0] = upload_suceeds
330
            uploaded[0] = upload_suceeds
328
            return upload_suceeds
331
            return upload_suceeds
329
332
330
        runner._upload_json = mock_upload_json
333
        runner._upload_json = mock_upload_json
331
        runner._timestamp = 123456789
334
        runner._timestamp = 123456789
335
        runner._utc_timestamp = datetime.datetime(2013, 2, 8, 15, 19, 37, 460000)
332
        output_capture = OutputCapture()
336
        output_capture = OutputCapture()
333
        output_capture.capture_output()
337
        output_capture.capture_output()
334
        try:
338
        try:
Lines 521-561 class MainTest(unittest.TestCase): Tools/Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py_sec3
521
525
522
        self._test_run_with_json_output(runner, port.host.filesystem, upload_suceeds=False, expected_exit_code=PerfTestsRunner.EXIT_CODE_FAILED_UPLOADING)
526
        self._test_run_with_json_output(runner, port.host.filesystem, upload_suceeds=False, expected_exit_code=PerfTestsRunner.EXIT_CODE_FAILED_UPLOADING)
523
527
524
    def test_upload_json(self):
528
    def test_run_with_upload_json_should_generate_perf_webkit_json(self):
525
        runner, port = self.create_runner()
529
        runner, port = self.create_runner_and_setup_results_template(args=['--output-json-path=/mock-checkout/output.json',
526
        port.host.filesystem.files['/mock-checkout/some.json'] = 'some content'
530
            '--test-results-server', 'some.host', '--platform', 'platform1', '--builder-name', 'builder1', '--build-number', '123',
527
531
            '--slave-config-json-path=/mock-checkout/slave-config.json'])
528
        called = []
532
        port.host.filesystem.write_text_file('/mock-checkout/slave-config.json', '{"key": "value1"}')
529
        upload_single_text_file_throws = False
530
        upload_single_text_file_return_value = StringIO.StringIO('OK')
531
532
        class MockFileUploader:
533
            def __init__(mock, url, timeout):
534
                self.assertEqual(url, 'https://some.host/api/test/report')
535
                self.assertTrue(isinstance(timeout, int) and timeout)
536
                called.append('FileUploader')
537
538
            def upload_single_text_file(mock, filesystem, content_type, filename):
539
                self.assertEqual(filesystem, port.host.filesystem)
540
                self.assertEqual(content_type, 'application/json')
541
                self.assertEqual(filename, 'some.json')
542
                called.append('upload_single_text_file')
543
                if upload_single_text_file_throws:
544
                    raise "Some exception"
545
                return upload_single_text_file_return_value
546
533
547
        runner._upload_json('some.host', 'some.json', MockFileUploader)
534
        self._test_run_with_json_output(runner, port.host.filesystem, upload_suceeds=True)
548
        self.assertEqual(called, ['FileUploader', 'upload_single_text_file'])
535
        generated_json = json.loads(port.host.filesystem.files['/mock-checkout/output-perf-webkit.json'])
536
        self.assertTrue(isinstance(generated_json, list))
537
        self.assertEqual(len(generated_json), 1)
549
538
550
        output = OutputCapture()
539
        output = generated_json[0]
551
        output.capture_output()
540
        self.maxDiff = None
552
        upload_single_text_file_return_value = StringIO.StringIO('Some error')
541
        self.assertEqual(output['platform'], 'platform1')
553
        runner._upload_json('some.host', 'some.json', MockFileUploader)
542
        self.assertEqual(output['buildNumber'], '123')
554
        _, _, logs = output.restore_output()
543
        self.assertEqual(output['buildTime'], '2013-02-08T15:19:37.460000')
555
        self.assertEqual(logs, 'Uploaded JSON but got a bad response:\nSome error\n')
544
        self.assertEqual(output['builderName'], 'builder1')
556
545
        self.assertEqual(output['builderKey'], 'value1')
557
        # Throwing an exception upload_single_text_file shouldn't blow up _upload_json
546
        self.assertEqual(output['revisions'], {'WebKit': {'revision': '5678', 'timestamp': '2013-02-01 08:48:05 +0000'}})
558
        called = []
547
        self.assertEqual(output['tests'].keys(), ['Bindings'])
559
        upload_single_text_file_throws = True
548
        self.assertEqual(sorted(output['tests']['Bindings'].keys()), ['tests', 'url'])
560
        runner._upload_json('some.host', 'some.json', MockFileUploader)
549
        self.assertEqual(output['tests']['Bindings']['url'], 'http://trac.webkit.org/browser/trunk/PerformanceTests/Bindings')
561
        self.assertEqual(called, ['FileUploader', 'upload_single_text_file'])
550
        self.assertEqual(output['tests']['Bindings']['tests'].keys(), ['event-target-wrapper'])
551
        self.assertEqual(output['tests']['Bindings']['tests']['event-target-wrapper'], {
552
            'url': 'http://trac.webkit.org/browser/trunk/PerformanceTests/Bindings/event-target-wrapper.html',
553
            'metrics': {'Time': {'current': [1486.0, 1471.0, 1510.0, 1505.0, 1478.0, 1490.0]}}})
- Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py +62 lines
Lines 34-39 import re Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py_sec1
34
import unittest2 as unittest
34
import unittest2 as unittest
35
35
36
from webkitpy.common.host_mock import MockHost
36
from webkitpy.common.host_mock import MockHost
37
from webkitpy.common.system.outputcapture import OutputCapture
37
from webkitpy.layout_tests.port.test import TestPort
38
from webkitpy.layout_tests.port.test import TestPort
38
from webkitpy.performance_tests.perftestsrunner import PerfTestsRunner
39
from webkitpy.performance_tests.perftestsrunner import PerfTestsRunner
39
40
Lines 147-149 class MainTest(unittest.TestCase): Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py_sec2
147
        self.assertEqual(options.output_json_path, 'a/output.json')
148
        self.assertEqual(options.output_json_path, 'a/output.json')
148
        self.assertEqual(options.slave_config_json_path, 'a/source.json')
149
        self.assertEqual(options.slave_config_json_path, 'a/source.json')
149
        self.assertEqual(options.test_results_server, 'somehost')
150
        self.assertEqual(options.test_results_server, 'somehost')
151
152
    def test_upload_json(self):
153
        runner, port = self.create_runner()
154
        port.host.filesystem.files['/mock-checkout/some.json'] = 'some content'
155
156
        class MockFileUploader:
157
            called = []
158
            upload_single_text_file_throws = False
159
            upload_single_text_file_return_value = None
160
161
            @classmethod
162
            def reset(cls):
163
                cls.called = []
164
                cls.upload_single_text_file_throws = False
165
                cls.upload_single_text_file_return_value = None
166
167
            def __init__(mock, url, timeout):
168
                self.assertEqual(url, 'https://some.host/some/path')
169
                self.assertTrue(isinstance(timeout, int) and timeout)
170
                mock.called.append('FileUploader')
171
172
            def upload_single_text_file(mock, filesystem, content_type, filename):
173
                self.assertEqual(filesystem, port.host.filesystem)
174
                self.assertEqual(content_type, 'application/json')
175
                self.assertEqual(filename, 'some.json')
176
                mock.called.append('upload_single_text_file')
177
                if mock.upload_single_text_file_throws:
178
                    raise Exception
179
                return mock.upload_single_text_file_return_value
180
181
        MockFileUploader.upload_single_text_file_return_value = StringIO.StringIO('OK')
182
        self.assertTrue(runner._upload_json('some.host', 'some.json', '/some/path', MockFileUploader))
183
        self.assertEqual(MockFileUploader.called, ['FileUploader', 'upload_single_text_file'])
184
185
        MockFileUploader.reset()
186
        MockFileUploader.upload_single_text_file_return_value = StringIO.StringIO('Some error')
187
        output = OutputCapture()
188
        output.capture_output()
189
        self.assertFalse(runner._upload_json('some.host', 'some.json', '/some/path', MockFileUploader))
190
        _, _, logs = output.restore_output()
191
        self.assertEqual(logs, 'Uploaded JSON to https://some.host/some/path but got a bad response:\nSome error\n')
192
193
        # Throwing an exception upload_single_text_file shouldn't blow up _upload_json
194
        MockFileUploader.reset()
195
        MockFileUploader.upload_single_text_file_throws = True
196
        self.assertFalse(runner._upload_json('some.host', 'some.json', '/some/path', MockFileUploader))
197
        self.assertEqual(MockFileUploader.called, ['FileUploader', 'upload_single_text_file'])
198
199
        MockFileUploader.reset()
200
        MockFileUploader.upload_single_text_file_return_value = StringIO.StringIO('{"status": "OK"}')
201
        self.assertTrue(runner._upload_json('some.host', 'some.json', '/some/path', MockFileUploader))
202
        self.assertEqual(MockFileUploader.called, ['FileUploader', 'upload_single_text_file'])
203
204
        MockFileUploader.reset()
205
        MockFileUploader.upload_single_text_file_return_value = StringIO.StringIO('{"status": "SomethingHasFailed", "failureStored": false}')
206
        output = OutputCapture()
207
        output.capture_output()
208
        self.assertFalse(runner._upload_json('some.host', 'some.json', '/some/path', MockFileUploader))
209
        _, _, logs = output.restore_output()
210
        serialized_json = json.dumps({'status': 'SomethingHasFailed', 'failureStored': False}, indent=4)
211
        self.assertEqual(logs, 'Uploaded JSON to https://some.host/some/path but got an error:\n%s\n' % serialized_json)

Return to Bug 108577