| Differences between
and this patch
- a/Tools/ChangeLog +53 lines
Lines 1-3 a/Tools/ChangeLog_sec1
1
2015-01-13  Youenn Fablet  <youenn.fablet@crf.canon.fr>
2
3
        Fix for 134766
4
5
        Add support for automated W3C conformance testing
6
        https://bugs.webkit.org/show_bug.cgi?id=134766
7
8
        Reviewed by NOBODY (OOPS!).
9
10
        Scripts/check-w3c-conformance is the executable to test W3C test conformance.
11
        Results are stored by default in a WebKitBuild sub-folder (see Port.w3c_conformance_results_directory).
12
        Scripts/webkitpy/w3c/conformance_checker.py is the main script.
13
        It uses test importer to clone and import the tests.
14
        It then uses run-webkit-test to run the tests and generate the -expected.txt files for testharness tests.
15
        From the -expected.txt files, it computes whether the test is passing, partially passing or failing.
16
        Results are outputted through TestResultsManager as a json file that can be loaded using w3c_conformance_results.html.
17
        Draft Test expectations and import rules are also generated in the same folder.
18
19
        * Scripts/check-w3c-conformance: Added.
20
        * Scripts/webkitpy/port/base.py:
21
        (Port.w3c_conformance_results_directory):
22
        * Scripts/webkitpy/w3c/conformance_checker.py: Added.
23
        (create_argument_parser):
24
        (ConformanceChecker):
25
        (ConformanceChecker.__init__):
26
        (ConformanceChecker._print_rwt_output_line):
27
        (ConformanceChecker.run_tests):
28
        (ConformanceChecker.results_manager):
29
        (ConformanceChecker.init_test_importer):
30
        (ConformanceChecker.test_suites):
31
        (ConformanceChecker.tests):
32
        (ConformanceChecker.conformance_result):
33
        (ConformanceChecker.check_conformance):
34
        (ConformanceChecker.print_results_to_console):
35
        (ConformanceChecker.write_import_expectation_updates):
36
        (ConformanceChecker.write_test_expectation_updates):
37
        (ConformanceChecker.write_conformance_results):
38
        (configure_logging):
39
        (configure_logging.LogHandler):
40
        (configure_logging.LogHandler.format):
41
        (main):
42
        * Scripts/webkitpy/w3c/test_results_manager.py: Added.
43
        (TestResultsManager):
44
        (TestResultsManager.__init__):
45
        (TestResultsManager.w3c_test_name):
46
        (TestResultsManager._get_test_results):
47
        (TestResultsManager.process_test_results):
48
        (TestResultsManager._js_test_conformance):
49
        (TestResultsManager._conformance_status):
50
        (TestResultsManager.conformance_results):
51
        (TestResultsManager.summary):
52
        (TestResultsManager.write_conformance_results):
53
1
2015-01-10  Michael Catanzaro  <mcatanzaro@igalia.com>
54
2015-01-10  Michael Catanzaro  <mcatanzaro@igalia.com>
2
55
3
        build-webkit: silence output of 'which'
56
        build-webkit: silence output of 'which'
- a/Tools/Scripts/check-w3c-conformance +31 lines
Line 0 a/Tools/Scripts/check-w3c-conformance_sec1
1
#!/usr/bin/env python
2
3
# Copyright (c) 2015, Canon Inc. All rights reserved.
4
#
5
# Redistribution and use in source and binary forms, with or without
6
# modification, are permitted provided that the following conditions
7
# are met:
8
# 1.  Redistributions of source code must retain the above copyright
9
#     notice, this list of conditions and the following disclaimer.
10
# 2.  Redistributions in binary form must reproduce the above copyright
11
#     notice, this list of conditions and the following disclaimer in the
12
#     documentation and/or other materials provided with the distribution.
13
# 3.  Neither the name of Canon Inc. nor the names of
14
#     its contributors may be used to endorse or promote products derived
15
#     from this software without specific prior written permission.
16
#
17
# THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
18
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
# DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
21
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
"""Wrapper around webkitpy/w3c/conformance_checker.py"""
29
from webkitpy.common import multiprocessing_bootstrap
30
31
multiprocessing_bootstrap.run('webkitpy', 'w3c', 'conformance_checker.py')
- a/Tools/Scripts/webkitpy/port/base.py +4 lines
Lines 768-773 class Port(object): a/Tools/Scripts/webkitpy/port/base.py_sec1
768
    def python_unittest_results_directory(self):
768
    def python_unittest_results_directory(self):
769
        return self._build_path('python-unittest-results')
769
        return self._build_path('python-unittest-results')
770
770
771
    def w3c_conformance_results_directory(self):
772
        """Absolute path to the place to store the test results (uses --w3c-conformance-directory)."""
773
        return self._build_path('w3c-conformance-results')
774
771
    def default_results_directory(self):
775
    def default_results_directory(self):
772
        """Absolute path to the default place to store the test results."""
776
        """Absolute path to the default place to store the test results."""
773
        # Results are store relative to the built products to make it easy
777
        # Results are store relative to the built products to make it easy
- a/Tools/Scripts/webkitpy/w3c/conformance_checker.py +262 lines
Line 0 a/Tools/Scripts/webkitpy/w3c/conformance_checker.py_sec1
1
# Copyright (c) 2014, Canon 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
# 3.  Neither the name of Canon Inc. nor the names of
12
#     its contributors may be used to endorse or promote products derived
13
#     from this software without specific prior written permission.
14
#
15
# THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
16
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
# DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
19
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26
"""
27
 This script evaluates W3C tests conformance by:
28
    - importing W3C tests as WebKit tests
29
    - generating expected.txt files (using run-webkit-tests)
30
    - computing whether a js test is passing or not by checking PASS/FAIL assertions outputted within the expected.txt files
31
"""
32
33
import argparse
34
import logging
35
import optparse
36
import sys
37
38
from webkitpy.common.host import Host
39
from webkitpy.common.system.executive import Executive
40
from webkitpy.layout_tests.models.test_expectations import TestExpectations
41
from webkitpy.port import platform_options
42
import webkitpy.w3c.test_importer as test_importer
43
from webkitpy.w3c.test_results_manager import TestResultsManager
44
from webkitpy.w3c.test_downloader import TestDownloader
45
46
_log = logging.getLogger(__name__)
47
48
49
def create_argument_parser():
50
    parser = argparse.ArgumentParser()
51
52
    for opt in platform_options(use_globs=False):
53
        parser.add_argument(opt.get_opt_string(), action=opt.action, dest=opt.dest, const=getattr(opt, 'const'), help=getattr(opt, 'help'))
54
    parser.add_argument('--webkit-test-runner', '-2', action='store_true', default=False,
55
        help='Set WebKit architecture WebKit1 or WebKit2 (default WK1).')
56
57
    parser.add_argument('-q', dest='quiet', action='store_true', default=False,
58
        help='Print minimal log.')
59
    parser.add_argument('-v', dest='verbose', action='store_true', default=False,
60
        help='Print maximal log.')
61
    parser.add_argument('-t', '--test-path', action='append', dest='test_paths', default=[],
62
        help='Import and run only tests in the supplied subdirectory of the w3c_test_directory. Can be supplied multiple times to give multiple paths')
63
    parser.add_argument('-s', '--skip-refresh', action='store_false', default=True, dest='refresh',
64
        help='Disable refresh of tests (git checkout  + import). By default, tests are refreshed.')
65
    parser.add_argument('--no-fetch', action='store_false', dest='fetch', default=True,
66
        help='Do not fetch the CSS/WPT repositories. By default, repositories are fetched if a W3c test directory is not provided')
67
68
    return parser
69
70
71
class ConformanceChecker(object):
72
73
    def __init__(self, port, options):
74
        self._port = port
75
        self._filesystem = port.host.filesystem
76
        self._options = options
77
        self._results_manager = None
78
        self._tests = None
79
        self._conformance_results = None
80
81
        self.layout_w3c_directory = self._filesystem.join('imported', 'w3c')
82
        self.results_directory = self._port.w3c_conformance_results_directory()
83
        self.layout_tests_result_directory = self._filesystem.join(self.results_directory, 'layout-test-results')
84
        self.imported_tests_directory = self._filesystem.join(self.results_directory, 'imported-tests')
85
        self.runned_tests_directory = self._filesystem.join(self.results_directory, 'runned-tests')
86
87
        self.init_test_importer()
88
89
    def _print_rwt_output_line(self, line):
90
        if not self._options.verbose:
91
            if line.startswith('['):
92
                tokens = line.split(' ')
93
                sys.stdout.write('\r' + tokens[0] + ' ' + tokens[1].replace(self.runned_tests_directory, '')[1:] + ' ')
94
                sys.stdout.flush()
95
            elif line == '':
96
                sys.stdout.write('\n')
97
        else:
98
            sys.stdout.write(line)
99
100
    def run_tests(self, tests):
101
        run_webkit_args = [self._port.path_to_script('run-webkit-tests'), '--no-retry-failures', '--no-show-results', '--full-results-html']
102
103
        test_list_filename = self._filesystem.join(self.runned_tests_directory, 'test_list.txt')
104
        self._filesystem.write_text_file(test_list_filename, '\n'.join(tests))
105
        run_webkit_args.append('--test-list=' + test_list_filename)
106
107
        if self._filesystem.exists(self.layout_tests_result_directory):
108
            self._filesystem.rmtree(self.layout_tests_result_directory)
109
        run_webkit_args.append('--results-directory=' + self.layout_tests_result_directory)
110
111
        run_webkit_args.append('--wptserver-doc-root=' + self._filesystem.join(self.runned_tests_directory, TestDownloader.WPT_NAME))
112
113
        if self._options.platform:
114
            run_webkit_args.append('--' + self._options.platform)
115
        if self._options.webkit_test_runner is True:
116
            run_webkit_args.append('-2')
117
118
        if not self._options.verbose:
119
            run_webkit_args.append('-q')
120
121
        process = Executive().popen(run_webkit_args, stdout=Executive.PIPE, stderr=Executive.STDOUT)
122
        while True:
123
            line = process.stdout.readline()
124
            self._print_rwt_output_line(line)
125
            if line == '':
126
                break
127
        process.wait()
128
129
    def results_manager(self):
130
        return self._results_manager
131
132
    def init_test_importer(self):
133
        args = ['-d', self.imported_tests_directory, '--ignore-expectations']
134
135
        for test_path in self._options.test_paths:
136
            args.append('-t')
137
            args.append(test_path)
138
139
        if not self._options.fetch:
140
            args.append('--no-fetch')
141
142
        if self._options.verbose:
143
            args.append('-v')
144
        elif self._options.quiet:
145
            logging.getLogger('webkitpy.w3c.test_importer').setLevel(logging.WARN)
146
            logging.getLogger('webkitpy.w3c.test_converter').setLevel(logging.WARN)
147
            logging.getLogger('webkitpy.w3c.test_downloader').setLevel(logging.WARN)
148
149
        options, args = test_importer.parse_args(args)
150
        self.test_importer = test_importer.TestImporter(self._port.host, None, options)
151
152
    def test_suites(self):
153
        return self._options.test_paths if self._options.test_paths else self.test_importer.test_downloader().all_test_paths()
154
155
    def tests(self):
156
        test_suites = self.test_suites()
157
        if self._tests is None:
158
            test_suite_paths = [self._filesystem.join(self.runned_tests_directory, test_suite) for test_suite in test_suites]
159
            self._tests = self._port.tests(test_suite_paths)
160
        return self._tests
161
162
    def conformance_result(self, test):
163
        if self._conformance_results is None:
164
            self._conformance_results = self.results_manager().conformance_results()
165
        return self._conformance_results[self.results_manager().w3c_test_name(test)]
166
167
    def check_conformance(self):
168
        if self._options.refresh is True:
169
            _log.info('Importing tests...')
170
            self.test_importer.do_import()
171
        else:
172
            _log.info('Skipping test import...')
173
174
        # Regenerate runned_test_directory to start in a clean state (no -expected.txt files in particular).
175
        self._filesystem.rmtree(self.runned_tests_directory)
176
        self._filesystem.copytree(self.imported_tests_directory, self.runned_tests_directory)
177
178
        _log.info('Running tests...')
179
        self.run_tests(self.tests())
180
181
        _log.info('Parsing test results...')
182
        self._results_manager = TestResultsManager(self._port, self.runned_tests_directory, self.layout_tests_result_directory, self.tests())
183
        self._results_manager.process_test_results()
184
185
        _log.info('Generating conformance results...')
186
        self._filesystem.maybe_make_directory(self.results_directory)
187
        self.write_conformance_results()
188
189
        _log.info('Generating expectation updates...')
190
        self.write_test_expectation_updates(self.results_directory)
191
        self.write_import_expectation_updates(self.results_directory)
192
193
        return True
194
195
    def print_results_to_console(self):
196
        summary = self.results_manager().summary()
197
        _log.info('Run ' + str(summary['total']) + ' tests')
198
        _log.info('Pass: ' + str(summary['num_pass']))
199
        _log.info('Partial: ' + str(summary['num_partial']))
200
        _log.info('Fail: ' + str(summary['num_fail']))
201
        _log.info('Error: ' + str(summary['num_error']))
202
203
    def write_import_expectation_updates(self, directory_path):
204
        import_expectations = []
205
        for test_suite in self.test_suites():
206
            import_expectations.append('# %s [ Pass ]' % self._filesystem.join(self.layout_w3c_directory, test_suite))
207
208
        import_expectations.append('')
209
210
        for test in self.tests():
211
            if not self.conformance_result(test) == 'PASS':
212
                import_expectations.append('%s [ Skip ]' % self._filesystem.join(self.layout_w3c_directory, self.results_manager().w3c_test_name(test)))
213
214
        self._filesystem.write_text_file(self._filesystem.join(directory_path, 'ImportExpectations'), '\n'.join(import_expectations))
215
216
    def write_test_expectation_updates(self, directory_path):
217
        test_expectations = ['# W3C test infrastructure folders']
218
        for path in self.test_importer.test_downloader().infrastructure_paths:
219
            file_path = self._filesystem.join(self.runned_tests_directory, path)
220
            if (self._filesystem.isdir(file_path)):
221
                test_expectations.append('%s [ Skip ]' % self._filesystem.join(self.layout_w3c_directory, path))
222
        self._filesystem.write_text_file(self._filesystem.join(directory_path, 'TestExpectations'), '\n'.join(test_expectations))
223
224
    def write_conformance_results(self):
225
        test_run_info = {}
226
        test_run_info['port_name'] = self._port.port_name
227
        test_run_info['web_platform_tests_version'] = self.test_importer.test_downloader().tests_version('WPT')
228
        test_run_info['csswg_tests_version'] = self.test_importer.test_downloader().tests_version('CSS')
229
        test_run_info['results_directory'] = self._filesystem.relpath(self.layout_tests_result_directory, self.results_directory)
230
        test_run_info['tests_directory'] = self._filesystem.relpath(self.runned_tests_directory, self.results_directory)
231
        self.results_manager().write_conformance_results(self.results_directory, test_run_info)
232
233
234
def configure_logging():
235
    class LogHandler(logging.StreamHandler):
236
        def format(self, record):
237
            if record.levelno > logging.INFO:
238
                return "%s: %s" % (record.levelname, record.getMessage())
239
            return record.getMessage()
240
241
    logger = logging.getLogger()
242
    logger.setLevel(logging.INFO)
243
    handler = LogHandler()
244
    handler.setLevel(logging.INFO)
245
    logger.addHandler(handler)
246
    return handler
247
248
249
def main(argv, stdout, stderr):
250
    configure_logging()
251
    host = Host()
252
    parser = create_argument_parser()
253
    options, args = parser.parse_known_args(argv)
254
    port = host.port_factory.get(options.platform, optparse.Values({'webkit_test_runner': options.webkit_test_runner}))
255
256
    runner = ConformanceChecker(port, options)
257
    if runner.check_conformance():
258
        runner.print_results_to_console()
259
        _log.info("Results exported in '" + runner.results_directory + "'")
260
261
if '__main__' == __name__:
262
    sys.exit(main(sys.argv[1:], sys.stdout, sys.stderr))
- a/Tools/Scripts/webkitpy/w3c/test_results_manager.py +146 lines
Line 0 a/Tools/Scripts/webkitpy/w3c/test_results_manager.py_sec1
1
# Copyright (c) 2014, Canon 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
# 3.  Neither the name of Canon Inc. nor the names of
12
#     its contributors may be used to endorse or promote products derived
13
#     from this software without specific prior written permission.
14
#
15
# THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
16
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
# DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
19
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26
"""
27
 This script manage JSON and results.html files for W3C test results.
28
"""
29
30
import json
31
32
from webkitpy.layout_tests.layout_package import json_results_generator
33
34
35
class TestResultsManager(object):
36
37
    _JSON_PREFIX = 'ADD_CONFORMANCE_RESULTS('
38
    _JSON_SUFFIX = ');'
39
40
    def __init__(self, port, base_directory, result_dir, tests):
41
        self._port = port
42
        self._filesystem = port.host.filesystem
43
        self._results_directory = result_dir
44
        self._tests = tests
45
46
        # Root directory of W3C imported test
47
        self._base_directory = base_directory
48
49
    def w3c_test_name(self, test):
50
        return self._filesystem.relpath(test, self._base_directory)
51
52
    def _get_test_results(self, tests, test_dict, path):
53
        for key in test_dict.keys():
54
            _path = self._filesystem.join(path, key)
55
            _potential_test = test_dict[key]
56
            # Is it a test entry or a sub-directory
57
            if 'report' in _potential_test:
58
                # Store the test
59
                tests[_path] = _potential_test
60
            else:
61
                self._get_test_results(tests, _potential_test, _path)
62
63
    def process_test_results(self):
64
        test_results = {}
65
        results_json_path = self._filesystem.join(self._results_directory, 'full_results.json')
66
        results_json = json_results_generator.load_json(self._filesystem, results_json_path)
67
        path = self._filesystem.sep if self._filesystem.isabs(self._base_directory) else ''
68
        self._get_test_results(test_results, results_json['tests'], path)
69
        return test_results
70
71
    def _js_test_conformance(self, expected_text):
72
        # Test is said to PASS if at least one line is PASS and no line is FAIL
73
        hasFail = False
74
        hasPass = False
75
        for line in expected_text.splitlines():
76
            if line.startswith('PASS'):
77
                hasPass = True
78
            elif line.startswith('FAIL'):
79
                hasFail = True
80
        if hasPass:
81
            return 'PASS' if not hasFail else 'PARTIAL'
82
        if hasFail:
83
            return 'FAIL'
84
        return 'ERROR'
85
86
    def _conformance_status(self, test_name, test_results):
87
        # FIXME: We should improve reftest/jstest detection.
88
        if not test_name in test_results:
89
            return {'type': 'reftest', 'status': 'PASS'}
90
91
        test_result = test_results[test_name]
92
93
        report = test_result['report']
94
        if report == 'MISSING':
95
            text = self._port.expected_text(test_name)
96
            if not(text is None or len(text) == 0):
97
                return {'type': 'jstest', 'status': self._js_test_conformance(text)}
98
        elif report == 'REGRESSION':
99
            return {'type': 'reftest', 'status': 'FAIL'}
100
101
        reason = test_result['actual']
102
        if reason == 'CRASH' or reason == 'TIMEOUT':
103
            return {'type': 'jstest', 'status': reason}
104
105
        return {'type': 'jstest', 'status': 'ERROR'}
106
107
    def conformance_results(self):
108
        test_wkr_results = self.process_test_results()
109
        results = {}
110
        for test_name in self._tests:
111
            results[self.w3c_test_name(test_name)] = self._conformance_status(test_name, test_wkr_results)
112
        return results
113
114
    def summary(self):
115
        num_pass = num_partial = num_fail = num_error = 0
116
        conformance_results = self.conformance_results()
117
        for key, value in conformance_results.iteritems():
118
            result = value['status']
119
            if result == 'PASS':
120
                num_pass += 1
121
            elif result == 'PARTIAL':
122
                num_partial += 1
123
            elif result == 'FAIL':
124
                num_fail += 1
125
            else:
126
                num_error += 1
127
        summary = {}
128
        summary['total'] = num_pass + num_fail + num_partial + num_error
129
        summary['num_pass'] = num_pass
130
        summary['num_fail'] = num_fail
131
        summary['num_partial'] = num_partial
132
        summary['num_error'] = num_error
133
        return summary
134
135
    def write_conformance_results(self, path, test_run_info):
136
        results = {}
137
        results['conformance_results'] = self.conformance_results()
138
        results.update(test_run_info)
139
140
        json_string = json.dumps(results, indent=4, sort_keys=True, separators=(',', ':'))
141
        json_string = self._JSON_PREFIX + json_string + self._JSON_SUFFIX
142
        self._filesystem.write_text_file(self._filesystem.join(path, 'w3c_conformance_results.json'), json_string)
143
144
        results_file = self._port.path_from_webkit_base('LayoutTests', 'imported', 'w3c', 'resources', 'w3c_conformance_results.html')
145
        if self._filesystem.exists(results_file):
146
            self._filesystem.copyfile(results_file, self._filesystem.join(path, 'w3c_conformance_results.html'))
- a/LayoutTests/imported/w3c/ChangeLog +11 lines
Lines 1-3 a/LayoutTests/imported/w3c/ChangeLog_sec1
1
2015-01-13  Youenn Fablet  <youenn.fablet@crf.canon.fr>
2
3
        Add support for automated W3C conformance testing
4
        https://bugs.webkit.org/show_bug.cgi?id=134766
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        The template to display conformance results.
9
10
        * resources/w3c_conformance_results.html: Added.
11
1
2014-11-14  Said Abou-Hallawa  <sabouhallawa@apple.com>
12
2014-11-14  Said Abou-Hallawa  <sabouhallawa@apple.com>
2
13
3
        Import w3c tests for canvas
14
        Import w3c tests for canvas
- a/LayoutTests/imported/w3c/resources/w3c_conformance_results.html +467 lines
Line 0 a/LayoutTests/imported/w3c/resources/w3c_conformance_results.html_sec1
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<style>
5
#results {
6
    display: table;
7
    padding: 20px;
8
    clear: both;
9
}
10
#summary {
11
    margin-top: 20px;
12
    margin-bottom: 20px;
13
}
14
15
.test, .firstline {
16
    display: table;
17
}
18
.testname, .testresult, .firstLine > div {
19
    background-color: lavender;
20
    border-bottom:solid 1px white;
21
}
22
23
.firstLineCell{
24
    display: table-cell;
25
    text-align: center;
26
    width: 800px;
27
}
28
29
.secondLineCell {
30
    display: table-cell;
31
    padding-left: 50px;
32
    width: 750px;
33
}
34
35
.testname {
36
    display: table-cell;
37
    width: 900px;
38
    margin-left:20px;
39
}
40
41
.testnamelink {
42
    float:left;
43
    margin-right:10px;
44
}
45
46
.testresultlink  {
47
    float: left;
48
}
49
50
.testresultlink a {
51
    text-transform: capitalize;
52
}
53
54
.bugfilinglink  {
55
    text-align: right;
56
}
57
58
.port {
59
    display: table-cell;
60
    width: 250px;
61
    text-align: center;
62
}
63
64
.testresult {
65
    display: table-cell;
66
    width: 150px;
67
    text-align: center;
68
}
69
70
.testresult:not(:last-child) {
71
    padding-right: 100px;
72
}
73
74
.partial, .pass, .fail, .timeout, .error {
75
    text-align: center;
76
}
77
.pass a {
78
    color: green;
79
}
80
81
.partial a {
82
    color: yellow;
83
}
84
85
.fail a {
86
    color: orange;
87
}
88
89
.timeout a {
90
    color: red;
91
}
92
93
.crash a {
94
    color: red;
95
}
96
97
.error a {
98
    color: red;
99
}
100
101
.error-results-hist {
102
    background-color: red;
103
    float: left; 
104
}
105
 
106
.fail-results-hist {
107
    background-color: orange;
108
    float: left;
109
}
110
111
.partial-results-hist {
112
    background-color: yellow;
113
    float: left;
114
}
115
.pass-results-hist {
116
    background-color: green;
117
    float: left;
118
}
119
120
.module-name {
121
    display:table-cell;
122
    width: 800px;
123
}
124
125
.number-of-tests {
126
    display:inline-block;
127
    width: 50px;
128
}
129
130
.module-results {
131
    text-align: center;
132
    display:table-cell;
133
    width: 250px;
134
}
135
136
.histogram {
137
    display:inline-block;
138
    margin-left: 40px;
139
    margin-top: 5px;
140
    width:150px;
141
}
142
143
.module{
144
    background-color: lavender;
145
    border:solid 1px white;
146
    display:table;
147
}
148
149
.module-summary, .secondLine {
150
    display:table;
151
    background: lavender;
152
    border:solid 1px white;
153
    height: 25px;
154
    cursor:pointer;
155
}
156
157
.box{
158
    display: table;
159
}
160
161
.hidden-data {
162
    display: none;
163
}
164
165
.legend > div {
166
    float: left;
167
    margin-right: 5px;
168
    min-width: 20px;
169
}
170
171
.legend {
172
}
173
</style>
174
<script>
175
176
function testsDirectory(results)
177
{
178
    return (results.root_directory ? results.root_directory : results.port_name) + "/" + 
179
            (results.tests_directory ? results.tests_directory : "imported-tests")
180
}
181
182
function resultsDirectory(results)
183
{
184
    return (results.root_directory ? results.root_directory : results.port_name) + "/" + 
185
            (results.results_directory ? results.results_directory : "layout-test-results")
186
}
187
188
function githubTestURL(testname, results)
189
{
190
    return "https://github.com/w3c/web-platform-tests/blob/" + results.tests_version + "/" + testname
191
}
192
193
function bugzillaURL(title, comment)
194
{
195
    return "https://bugs.webkit.org/enter_bug.cgi?assigned_to=webkit-unassigned%40lists.webkit.org&product=WebKit&form_name=enter_bug&short_desc=" + title + "&comment=" + comment
196
}
197
198
function testFileLink(testname, results)
199
{
200
    return "<div class='testnamelink'><a href='" + testsDirectory(results) + "/" + testname + "'>" + testname + "</a></div>"
201
        + "<div class='githublink'>(<a href='" + githubTestURL(testname, results) + "'>github</a>)</div>" 
202
}
203
204
function testResultFileLink(testname, result, results)
205
{
206
    var suffix = "." + testname.split('.').pop()
207
    var new_suffix = "-expected.txt"
208
    result_status = result.status.toLowerCase().trim()
209
    if (result_status == "crash")
210
        new_suffix = "-crash-log.txt"
211
    if (result.type == "reftest")
212
        new_suffix = "-diffs.html"
213
    
214
    var link = "<div class='testresultlink'><a href='" + testsDirectory(results) + "/" + testname.replace(suffix, new_suffix) + "'>" + result_status + "</a></div>"
215
216
    if (result != "pass") {
217
        var title = encodeURIComponent("W3C " + testname + " test is " + result_status + "ing")
218
        var comment = encodeURIComponent(githubTestURL(testname, results) + " is " + result_status + "ing on " + results.port_name.toUpperCase() + ".")
219
        link += "<div class='bugfilinglink'><a href='" + bugzillaURL(title, comment) + "'>file bug</a></div>"
220
    }
221
222
    return link
223
}
224
225
function testSuiteStateHistogram(numError, numFail, numPartial, numPass)
226
{
227
    var total = numError + numPass + numPartial + numFail
228
229
    var html = '<div class="module-results">'
230
231
    html += '<div class="number-of-tests">' + numPass + '/' + total + '</div>'
232
233
    html += '<div class="histogram">'
234
    if (numError)
235
        html += '<div class="error-results-hist" style="width:' + (100 * numError / total) + '%">&nbsp;</div>'
236
    if (numFail)
237
        html += '<div class="fail-results-hist" style="width:' + (100 * numFail / total)  + '%">&nbsp;</div>'
238
    if (numPartial)
239
        html += '<div class="partial-results-hist" style="width:' + (100 * numPartial / total) + '%">&nbsp;</div>'
240
    if (numPass)
241
        html += '<div class="pass-results-hist" style="width:' + (100 * numPass / total) + '%">&nbsp;</div>'
242
    html += '</div>'
243
244
    return html + '</div>'
245
}
246
247
function testsuiteModules(results)
248
 {
249
    var modules = {}
250
    // first try to only show module from URL hash
251
    var hash = window.location.hash.substring(1)
252
    var hasTest = false
253
    if (hash.length) {
254
        Object.keys(results.conformance_results).sort().forEach(function(key, i) {
255
            var testName = key
256
            if (testName.indexOf(hash) == 0) {
257
                hasTest = true
258
                var items = testName.split("/")
259
                var name = items[0]
260
                if (modules[name])
261
                    modules[name].push(key)
262
                else
263
                    modules[name] = [key]
264
            }
265
        })
266
    }
267
    if (hasTest)
268
        return modules
269
270
    // If no tests, show all modules
271
    Object.keys(results.conformance_results).sort().forEach(function(key, i) {
272
        var testName = key
273
        var items = testName.split("/")
274
        var name = items[0]
275
        // In case of html module, we split display in sub-modules
276
        if (name == 'web-platform-tests' || name == 'csswg-tests' || name == 'html')
277
            name += '/' + items[1]
278
        if (modules[name])
279
            modules[name].push(key)
280
        else
281
            modules[name] = [key]
282
    })
283
    return modules
284
 }
285
286
function toggleTests(div)
287
{
288
    var box = div.nextElementSibling
289
    if (box)
290
        box.className = (box.className == 'box') ? 'hidden-data' : 'box'
291
}
292
293
function incrementSummary(result, summary)
294
{
295
    if (result.status == "PASS")
296
        summary.numPass++
297
    else if (result.status == "PARTIAL")
298
        summary.numPartial++
299
    else if (result.status == "FAIL")
300
        summary.numFail++
301
    else
302
        summary.numError++
303
}
304
305
function computeGlobalConformance(results)
306
{
307
    var summary = {numPass: 0, numPartial: 0, numFail: 0, numError: 0}
308
    for (var test in results.conformance_results) {
309
        if (results.conformance_results.hasOwnProperty(test))
310
            incrementSummary(results.conformance_results[test], summary)
311
    }
312
    return summary
313
}
314
315
function computeStatistics(resultsSummary, result, results)
316
{
317
    var summary = resultsSummary[results.port_name]
318
319
    if (!summary)
320
        resultsSummary[results.port_name] = summary = {numPass: 0, numPartial: 0, numFail: 0, numError: 0}
321
322
    incrementSummary(result, summary)
323
}
324
325
function addTestSuiteListOfResults(moduleName, tests, listOfResults, isOpen)
326
{
327
    var innerHTML = ""
328
    var resultsSummary =  {}
329
    tests.forEach(function(key, i) {
330
        innerHTML += "<div class='test'><div class='testname'>" + testFileLink(key, listOfResults[0]) + "</div>"
331
        listOfResults.forEach(function(results) {
332
            var result = results.conformance_results[key]
333
            if (!result) {
334
                result = "Missing"
335
                console.log("WARNING: test " + key + " is missing")
336
            }
337
            innerHTML += "<div class='testresult " + result.status.toLowerCase() + "'>" + testResultFileLink(key, result, results) + "</div>"
338
            computeStatistics(resultsSummary, result, results)
339
        })
340
        innerHTML += "</div>"
341
    })
342
    var html = '<div class="module"><div class="module-summary" onclick="toggleTests(event.currentTarget)"><div class="module-name">' + moduleName + '</div>'
343
    listOfResults.forEach(function(results) {
344
        var summary = resultsSummary[results.port_name]
345
        html += testSuiteStateHistogram(summary.numError, summary.numFail, summary.numPartial, summary.numPass)
346
    })
347
    html += '</div><div class="' + (isOpen ? "box" : "hidden-data") + '">'
348
    html += innerHTML
349
    html += '</div></div>'
350
    document.getElementById("results").innerHTML += html
351
}
352
353
function writeSummary()
354
{
355
    var html = "<div>W3C conformance results from run-webkit-test ("
356
    listOfResults.forEach(function(results) {
357
        html += "<a href='" + resultsDirectory(results) + "'>" + results.port_name + "</a> &nbsp;"
358
    })
359
    html += ")</div>"
360
361
    html += "<div> Generated <a href='TestExpectations'>test expectations</a> and <a href='ImportExpectations'>import expectations</a>./div>"
362
363
    if (listOfResults.length > 1) {
364
        var sameTestVersion = true
365
        listOfResults.forEach(function(results) {
366
            if (listOfResults[0].tests_version != results.tests_version) {
367
                sameTestVersion = false
368
                html += "<div>Warning: W3C test version are not the same for all platforms: "
369
                html += listOfResults[0].tests_version + " vs " + results.tests_version + " of platform " + results.port_name
370
                console.log(results)
371
                html += "</div>"
372
            }
373
        })
374
        if (!sameTestVersion) {
375
            html += "<div>Warning: W3C test version are not the same for all platforms: "
376
            html += listOfResults[0].tests_version + " vs " + results.tests_version + " of platform " + results.port_name
377
            console.log(results)
378
            html += "</div>"
379
        }
380
    }
381
382
    document.getElementById("summary").innerHTML = html
383
}
384
385
function displayResults()
386
{
387
    setTimeout(function()  {
388
        var firstLineHTML = "<div class='firstLine'><div class='firstLineCell'>Module/Test Name</div>"
389
        listOfResults.forEach(function(results) {
390
            firstLineHTML += "<div class='port'>" + results.port_name.toUpperCase() + "</div>"
391
        })
392
        firstLineHTML += "</div>"
393
        var secondLineHTML = "<div class='secondLine'><div class='secondLineCell'>Overall conformance</div>"
394
        listOfResults.forEach(function(results) {
395
            summary = computeGlobalConformance(results)
396
            secondLineHTML += testSuiteStateHistogram(summary.numError, summary.numFail, summary.numPartial, summary.numPass)
397
        })
398
        secondLineHTML += "</div>"
399
400
        document.getElementById("results").innerHTML = firstLineHTML + secondLineHTML
401
        var modules =  testsuiteModules(listOfResults[0])
402
        var keys = Object.keys(modules)
403
        keys.sort().forEach(function(key, i) {
404
            addTestSuiteListOfResults(key, modules[key], listOfResults, keys.length == 1)
405
        })
406
        writeSummary(listOfResults)
407
    }, 0)
408
}
409
410
 
411
listOfResults = [] 
412
function ADD_CONFORMANCE_RESULTS(results)
413
{
414
    results.root_directory = dirs[counter]
415
    listOfResults.push(results)
416
    setTimeout(loadScripts, 0)    
417
}
418
419
var counter
420
var dirs
421
function loadScripts(directories)
422
{
423
    if (directories) {
424
        dirs = directories
425
        counter = -1
426
    }
427
    if (counter < dirs.length - 1) {
428
        document.getElementById('summary').innerHTML = "Loading " + dirs[++counter] + "..."
429
        var head= document.getElementsByTagName('head')[0]
430
        var script= document.createElement('script')
431
        script.type= 'text/javascript'
432
        script.src= dirs[counter] + '/w3c_conformance_results.json'
433
        head.appendChild(script)
434
    }
435
    else {
436
        document.getElementById('summary').innerHTML = "Displaying results..."
437
        setTimeout(displayResults, 0)
438
    }
439
}
440
441
</script>
442
</head>
443
<body>
444
<div id="summary">
445
    Loading...
446
</div>
447
<div class="legend">
448
    <div class="error-results-hist legend">&nbsp;</div>
449
    <div>Error, Timeout or Crash</div>
450
    <div class="fail-results-hist legend">&nbsp;</div>
451
    <div> All assertions failing</div>
452
    <div class="partial-results-hist legend">&nbsp;</div>
453
    <div> Some assertions passing</div>
454
    <div class="pass-results-hist legend">&nbsp;</div>
455
    <div> All assertions passing</div>
456
</div>
457
<div id="results">
458
</div>
459
460
<!-- Add as many folder containing w3c_conformance_results.json as needed in loadScripts array to display and compare the results -->
461
<script>
462
    setTimeout(function() {
463
        loadScripts(["."])
464
    }, 0)
465
</script>
466
</body>
467
</html>

Return to Bug 134766