Add support for webkit-test-runner options to WPT importer
https://bugs.webkit.org/show_bug.cgi?id=197826

Reviewed by Alex Christensen.

In case of overwriting an existing test, check if the existing test
contains a <!-- webkit-test-runner --> comment and insert it back if
needed in the new test.

For exporter, forbid creating a WPT PR if there are changes containing
the webkit-test-runner string.

* Scripts/webkitpy/w3c/test_converter.py:
(convert_for_webkit):
(_W3CTestConverter.__init__):
(_W3CTestConverter.add_webkit_test_runner_options_if_needed):
(_W3CTestConverter.handle_starttag):
(_W3CTestConverter.handle_comment):
(_W3CTestConverter.handle_decl):
(_W3CTestConverter.handle_pi):
* Scripts/webkitpy/w3c/test_exporter.py:
(WebPlatformTestExporter.write_git_patch_file):
(WebPlatformTestExporter.make_pull_request):
* Scripts/webkitpy/w3c/test_importer.py:
(TestImporter.webkit_test_runner_options):
(TestImporter):
(TestImporter.add_webkit_test_runner_options_to_content):
(TestImporter.copy_html_file):
(TestImporter.write_html_template):
(TestImporter.write_html_files_for_templated_js_tests):
(TestImporter.import_tests):
* Scripts/webkitpy/w3c/test_importer_unittest.py:
(test_manual_slow_test):
(test_webkit_test_runner_options):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@245287 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index 0b09d51..bea3c4e 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,40 @@
+2019-05-14  Youenn Fablet  <youenn@apple.com>
+
+        Add support for webkit-test-runner options to WPT importer
+        https://bugs.webkit.org/show_bug.cgi?id=197826
+
+        Reviewed by Alex Christensen.
+
+        In case of overwriting an existing test, check if the existing test
+        contains a <!-- webkit-test-runner --> comment and insert it back if
+        needed in the new test.
+
+        For exporter, forbid creating a WPT PR if there are changes containing
+        the webkit-test-runner string.
+
+        * Scripts/webkitpy/w3c/test_converter.py:
+        (convert_for_webkit):
+        (_W3CTestConverter.__init__):
+        (_W3CTestConverter.add_webkit_test_runner_options_if_needed):
+        (_W3CTestConverter.handle_starttag):
+        (_W3CTestConverter.handle_comment):
+        (_W3CTestConverter.handle_decl):
+        (_W3CTestConverter.handle_pi):
+        * Scripts/webkitpy/w3c/test_exporter.py:
+        (WebPlatformTestExporter.write_git_patch_file):
+        (WebPlatformTestExporter.make_pull_request):
+        * Scripts/webkitpy/w3c/test_importer.py:
+        (TestImporter.webkit_test_runner_options):
+        (TestImporter):
+        (TestImporter.add_webkit_test_runner_options_to_content):
+        (TestImporter.copy_html_file):
+        (TestImporter.write_html_template):
+        (TestImporter.write_html_files_for_templated_js_tests):
+        (TestImporter.import_tests):
+        * Scripts/webkitpy/w3c/test_importer_unittest.py:
+        (test_manual_slow_test):
+        (test_webkit_test_runner_options):
+
 2019-05-14  Aakash Jain  <aakash_jain@apple.com>
 
         [ews-app] Status bubble should turn orange when any build step fails
diff --git a/Tools/Scripts/webkitpy/w3c/test_converter.py b/Tools/Scripts/webkitpy/w3c/test_converter.py
index 6e2996e..5274a29 100644
--- a/Tools/Scripts/webkitpy/w3c/test_converter.py
+++ b/Tools/Scripts/webkitpy/w3c/test_converter.py
@@ -38,12 +38,12 @@
 _log = logging.getLogger(__name__)
 
 
-def convert_for_webkit(new_path, filename, reference_support_info, host=Host(), convert_test_harness_links=True):
+def convert_for_webkit(new_path, filename, reference_support_info, host=Host(), convert_test_harness_links=True, webkit_test_runner_options=''):
     """ Converts a file's |contents| so it will function correctly in its |new_path| in Webkit.
 
     Returns the list of modified properties and the modified text if the file was modifed, None otherwise."""
     contents = host.filesystem.read_binary_file(filename)
-    converter = _W3CTestConverter(new_path, filename, reference_support_info, host, convert_test_harness_links)
+    converter = _W3CTestConverter(new_path, filename, reference_support_info, host, convert_test_harness_links, webkit_test_runner_options)
     if filename.endswith('.css'):
         return converter.add_webkit_prefix_to_unprefixed_properties_and_values(contents)
     else:
@@ -53,7 +53,7 @@
 
 
 class _W3CTestConverter(HTMLParser):
-    def __init__(self, new_path, filename, reference_support_info, host=Host(), convert_test_harness_links=True):
+    def __init__(self, new_path, filename, reference_support_info, host=Host(), convert_test_harness_links=True, webkit_test_runner_options=''):
         HTMLParser.__init__(self)
 
         self._host = host
@@ -67,6 +67,8 @@
         self.style_data = []
         self.filename = filename
         self.reference_support_info = reference_support_info
+        self.webkit_test_runner_options = webkit_test_runner_options
+        self.has_started = False
 
         resources_path = self.path_from_webkit_root('LayoutTests', 'resources')
         resources_relpath = self._filesystem.relpath(resources_path, new_path)
@@ -215,10 +217,18 @@
 
         self.converted_data.append(converted)
 
+    def add_webkit_test_runner_options_if_needed(self):
+        if self.has_started:
+            return
+        self.has_started = True
+        if self.webkit_test_runner_options:
+            self.converted_data[-1] = self.converted_data[-1] + self.webkit_test_runner_options
+
     def handle_starttag(self, tag, attrs):
         if tag == 'style':
             self.in_style_tag = True
         self.convert_attributes_if_needed(tag, attrs)
+        self.add_webkit_test_runner_options_if_needed()
 
     def handle_endtag(self, tag):
         if tag == 'style':
@@ -244,9 +254,12 @@
 
     def handle_comment(self, data):
         self.converted_data.extend(['<!-- ', data, ' -->'])
+        self.add_webkit_test_runner_options_if_needed()
 
     def handle_decl(self, decl):
         self.converted_data.extend(['<!', decl, '>'])
+        self.add_webkit_test_runner_options_if_needed()
 
     def handle_pi(self, data):
         self.converted_data.extend(['<?', data, '>'])
+        self.add_webkit_test_runner_options_if_needed()
diff --git a/Tools/Scripts/webkitpy/w3c/test_exporter.py b/Tools/Scripts/webkitpy/w3c/test_exporter.py
index 0abcee7..d48db75 100644
--- a/Tools/Scripts/webkitpy/w3c/test_exporter.py
+++ b/Tools/Scripts/webkitpy/w3c/test_exporter.py
@@ -182,6 +182,13 @@
             return ''
         # FIXME: We can probably try to use --relative git parameter to not do that replacement.
         patch_data = patch_data.replace(WEBKIT_WPT_DIR + '/', '')
+
+        # FIXME: Support stripping of <!-- webkit-test-runner --> comments.
+        self.has_webkit_test_runner_specific_changes = 'webkit-test-runner' in patch_data
+        if self.has_webkit_test_runner_specific_changes:
+            _log.warning("Patch contains webkit-test-runner specific changes, please remove them before creating a PR")
+            return ''
+
         self._filesystem.write_text_file(patch_file, patch_data)
         return patch_file
 
@@ -306,6 +313,10 @@
         return True
 
     def make_pull_request(self):
+        if self.has_webkit_test_runner_specific_changes:
+            _log.error('Cannot create a WPT PR since it contains webkit test runner specific changes')
+            return
+
         if not self._github:
             _log.info('Skipping pull request because OAuth token was not provided. You can open the pull request manually using the branch ' + self._wpt_fork_branch_github_url)
             return
diff --git a/Tools/Scripts/webkitpy/w3c/test_importer.py b/Tools/Scripts/webkitpy/w3c/test_importer.py
index f92ecd2..e874808 100644
--- a/Tools/Scripts/webkitpy/w3c/test_importer.py
+++ b/Tools/Scripts/webkitpy/w3c/test_importer.py
@@ -360,17 +360,49 @@
             return True
         return self.options.convert_test_harness_links
 
-    def write_html_files_for_templated_js_tests(self, orig_filepath, new_filepath):
+    def _webkit_test_runner_options(self, path):
+        if not(self.filesystem.isfile(path)):
+            return ''
+
+        options_prefix = '<!-- webkit-test-runner'
+        contents = self.filesystem.read_text_file(path).split('\n')
+        if not len(contents):
+            return ''
+        first_line = contents[0]
+
+        return first_line[first_line.index(options_prefix):] if options_prefix in first_line else ''
+
+    def _add_webkit_test_runner_options_to_content(self, content, webkit_test_runner_options):
+        lines = content.split('\n')
+        if not len(lines):
+            return ''
+        lines[0] = lines[0] + webkit_test_runner_options
+        return '\n'.join(lines)
+
+    def _copy_html_file(self, source_filepath, new_filepath):
+        webkit_test_runner_options = self._webkit_test_runner_options(new_filepath)
+        if not webkit_test_runner_options:
+            self.filesystem.copyfile(source_filepath, new_filepath)
+            return
+
+        source_content = self.filesystem.read_text_file(source_filepath)
+        self.filesystem.write_text_file(new_filepath, self._add_webkit_test_runner_options_to_content(source_content, webkit_test_runner_options))
+
+    def _write_html_template(self, new_filepath):
+        webkit_test_runner_options = self._webkit_test_runner_options(new_filepath)
         content = '<!-- This file is required for WebKit test infrastructure to run the templated test -->'
+        self.filesystem.write_text_file(new_filepath, content + webkit_test_runner_options)
+
+    def write_html_files_for_templated_js_tests(self, orig_filepath, new_filepath):
         if (orig_filepath.endswith('.window.js')):
-            self.filesystem.write_text_file(new_filepath.replace('.window.js', '.window.html'), content)
+            self._write_html_template(new_filepath.replace('.window.js', '.window.html'))
             return
         if (orig_filepath.endswith('.worker.js')):
-            self.filesystem.write_text_file(new_filepath.replace('.worker.js', '.worker.html'), content)
+            self._write_html_template(new_filepath.replace('.worker.js', '.worker.html'))
             return
         if (orig_filepath.endswith('.any.js')):
-            self.filesystem.write_text_file(new_filepath.replace('.any.js', '.any.html'), content)
-            self.filesystem.write_text_file(new_filepath.replace('.any.js', '.any.worker.html'), content)
+            self._write_html_template(new_filepath.replace('.any.js', '.any.html'))
+            self._write_html_template(new_filepath.replace('.any.js', '.any.worker.html'))
             return
 
     def import_tests(self):
@@ -449,7 +481,7 @@
                 mimetype = mimetypes.guess_type(orig_filepath)
                 if should_rewrite_files and ('html' in str(mimetype[0]) or 'xml' in str(mimetype[0])  or 'css' in str(mimetype[0])):
                     try:
-                        converted_file = convert_for_webkit(new_path, filename=orig_filepath, reference_support_info=reference_support_info, host=self.host, convert_test_harness_links=self.should_convert_test_harness_links(subpath))
+                        converted_file = convert_for_webkit(new_path, filename=orig_filepath, reference_support_info=reference_support_info, host=self.host, convert_test_harness_links=self.should_convert_test_harness_links(subpath), webkit_test_runner_options=self._webkit_test_runner_options(new_filepath))
                     except:
                         _log.warn('Failed converting %s', orig_filepath)
                         failed_conversion_files.append(orig_filepath)
@@ -474,6 +506,8 @@
                 elif orig_filepath.endswith('__init__.py') and not self.filesystem.getsize(orig_filepath):
                     # Some bots dislike empty __init__.py.
                     self.write_init_py(new_filepath)
+                elif 'html' in str(mimetype[0]):
+                    self._copy_html_file(orig_filepath, new_filepath)
                 else:
                     self.filesystem.copyfile(orig_filepath, new_filepath)
 
diff --git a/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py b/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
index 47b4620..7b642988 100644
--- a/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
+++ b/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
@@ -304,3 +304,28 @@
         fs = self.import_downloaded_tests(['--no-fetch', '--import-all', '-d', 'w3c'], FAKE_FILES)
         self.assertFalse(fs.exists('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/new-manual.html'))
         self.assertEquals(tests_options, fs.read_text_file('/mock-checkout/LayoutTests/tests-options.json'))
+
+    def test_webkit_test_runner_options(self):
+        FAKE_FILES = {
+            '/mock-checkout/WebKitBuild/w3c-tests/csswg-tests/t/test.html': '<!doctype html><script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>',
+            '/mock-checkout/WebKitBuild/w3c-tests/web-platform-tests/css/test.html': '<!doctype html>\n<script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>',
+            '/mock-checkout/LayoutTests/w3c/web-platform-tests/css/test.html': '<!-- doctype html --><!-- webkit-test-runner [ dummy ] -->',
+            '/mock-checkout/WebKitBuild/w3c-tests/web-platform-tests/t/test.html': '<!doctype html><script src="/resources/testharness.js"></script><script src="/resources/testharnessreport.js"></script>',
+            '/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.html': '<!-- doctype html --><!-- webkit-test-runner [ dummy ] -->',
+            '/mock-checkout/WebKitBuild/w3c-tests/web-platform-tests/t/test.any.js': 'test(() => {}, "empty")',
+            '/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.any.html': '<!-- This file is required for WebKit test infrastructure to run the templated test --><!-- webkit-test-runner [ dummy ] -->',
+            '/mock-checkout/Source/WebCore/css/CSSProperties.json': '',
+            '/mock-checkout/Source/WebCore/css/CSSValueKeywords.in': '',
+        }
+        FAKE_FILES.update(FAKE_REPOSITORY)
+
+        fs = self.import_downloaded_tests(['--no-fetch', '--import-all', '-d', 'w3c'], FAKE_FILES)
+
+        self.assertTrue(fs.exists('/mock-checkout/LayoutTests/w3c/web-platform-tests/css/test.html'))
+        self.assertTrue(fs.exists('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.html'))
+        self.assertTrue(fs.exists('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.any.html'))
+        self.assertTrue(fs.exists('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.any.worker.html'))
+        self.assertTrue('<!-- webkit-test-runner [ dummy ] -->' in fs.read_text_file('/mock-checkout/LayoutTests/w3c/web-platform-tests/css/test.html').split('\n')[0])
+        self.assertTrue('<!-- webkit-test-runner [ dummy ] -->' in fs.read_text_file('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.html').split('\n')[0])
+        self.assertTrue('<!-- webkit-test-runner [ dummy ] -->' in fs.read_text_file('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.any.html').split('\n')[0])
+        self.assertFalse('<!-- webkit-test-runner [ dummy ] -->' in fs.read_text_file('/mock-checkout/LayoutTests/w3c/web-platform-tests/t/test.any.worker.html').split('\n')[0])