Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: sky/tools/webkitpy/w3c/test_importer.py

Issue 675343003: Prune a bunch of webkitpy. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright (C) 2013 Adobe Systems Incorporated. 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 #
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
9 # disclaimer.
10 # 2. Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following
12 # disclaimer in the documentation and/or other materials
13 # provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
16 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
19 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20 # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
24 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
25 # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 # SUCH DAMAGE.
27
28 """
29 This script imports a directory of W3C tests into WebKit.
30
31 This script will import the tests into WebKit following these rules:
32
33 - By default, all tests are imported under tests/w3c/[repo-name].
34
35 - By default, only reftests and jstest are imported. This can be overridden
36 with a -a or --all argument
37
38 - Also by default, if test files by the same name already exist in the
39 destination directory, they are overwritten with the idea that running
40 this script would refresh files periodically. This can also be
41 overridden by a -n or --no-overwrite flag
42
43 - All files are converted to work in WebKit:
44 1. Paths to testharness.js and vendor-prefix.js files are modified to
45 point to Webkit's copy of them in tests/resources, using the
46 correct relative path from the new location.
47 2. All CSS properties requiring the -webkit-vendor prefix are prefixed
48 (the list of what needs prefixes is read from Source/WebCore/CSS/CSS Properties.in).
49 3. Each reftest has its own copy of its reference file following
50 the naming conventions new-run-webkit-tests expects.
51 4. If a reference files lives outside the directory of the test that
52 uses it, it is checked for paths to support files as it will be
53 imported into a different relative position to the test file
54 (in the same directory).
55 5. Any tags with the class "instructions" have style="display:none" add ed
56 to them. Some w3c tests contain instructions to manual testers which we
57 want to strip out (the test result parser only recognizes pure testh arness.js
58 output and not those instructions).
59
60 - Upon completion, script outputs the total number tests imported, broken
61 down by test type
62
63 - Also upon completion, if we are not importing the files in place, each
64 directory where files are imported will have a w3c-import.log file writte n with
65 a timestamp, the W3C Mercurial changeset if available, the list of CSS
66 properties used that require prefixes, the list of imported files, and
67 guidance for future test modification and maintenance. On subsequent
68 imports, this file is read to determine if files have been
69 removed in the newer changesets. The script removes these files
70 accordingly.
71 """
72
73 # FIXME: Change this file to use the Host abstractions rather that os, sys, shut ils, etc.
74
75 import datetime
76 import logging
77 import mimetypes
78 import optparse
79 import os
80 import shutil
81 import sys
82
83 from webkitpy.common.host import Host
84 from webkitpy.common.webkit_finder import WebKitFinder
85 from webkitpy.common.system.executive import ScriptError
86 from webkitpy.layout_tests.models.test_expectations import TestExpectationParser
87 from webkitpy.w3c.test_parser import TestParser
88 from webkitpy.w3c.test_converter import convert_for_webkit
89
90
91 CHANGESET_NOT_AVAILABLE = 'Not Available'
92
93
94 _log = logging.getLogger(__name__)
95
96
97 def main(_argv, _stdout, _stderr):
98 options, args = parse_args()
99 dir_to_import = os.path.normpath(os.path.abspath(args[0]))
100 if len(args) == 1:
101 top_of_repo = dir_to_import
102 else:
103 top_of_repo = os.path.normpath(os.path.abspath(args[1]))
104
105 if not os.path.exists(dir_to_import):
106 sys.exit('Directory %s not found!' % dir_to_import)
107 if not os.path.exists(top_of_repo):
108 sys.exit('Repository directory %s not found!' % top_of_repo)
109 if top_of_repo not in dir_to_import:
110 sys.exit('Repository directory %s must be a parent of %s' % (top_of_repo , dir_to_import))
111
112 configure_logging()
113 test_importer = TestImporter(Host(), dir_to_import, top_of_repo, options)
114 test_importer.do_import()
115
116
117 def configure_logging():
118 class LogHandler(logging.StreamHandler):
119
120 def format(self, record):
121 if record.levelno > logging.INFO:
122 return "%s: %s" % (record.levelname, record.getMessage())
123 return record.getMessage()
124
125 logger = logging.getLogger()
126 logger.setLevel(logging.INFO)
127 handler = LogHandler()
128 handler.setLevel(logging.INFO)
129 logger.addHandler(handler)
130 return handler
131
132
133 def parse_args():
134 parser = optparse.OptionParser(usage='usage: %prog [options] [dir_to_import] [top_of_repo]')
135 parser.add_option('-n', '--no-overwrite', dest='overwrite', action='store_fa lse', default=True,
136 help='Flag to prevent duplicate test files from overwriting existing tes ts. By default, they will be overwritten.')
137 parser.add_option('-a', '--all', action='store_true', default=False,
138 help='Import all tests including reftests, JS tests, and manual/pixel te sts. By default, only reftests and JS tests are imported.')
139 parser.add_option('-d', '--dest-dir', dest='destination', default='w3c',
140 help='Import into a specified directory relative to the tests root. By d efault, files are imported under tests/w3c.')
141 parser.add_option('--ignore-expectations', action='store_true', default=Fals e,
142 help='Ignore the W3CImportExpectations file and import everything.')
143 parser.add_option('--dry-run', action='store_true', default=False,
144 help='Dryrun only (don\'t actually write any results).')
145
146 options, args = parser.parse_args()
147 if len(args) > 2:
148 parser.error('Incorrect number of arguments')
149 elif len(args) == 0:
150 args = (os.getcwd(),)
151 return options, args
152
153
154 class TestImporter(object):
155
156 def __init__(self, host, dir_to_import, top_of_repo, options):
157 self.host = host
158 self.dir_to_import = dir_to_import
159 self.top_of_repo = top_of_repo
160 self.options = options
161
162 self.filesystem = self.host.filesystem
163 self.webkit_finder = WebKitFinder(self.filesystem)
164 self._webkit_root = self.webkit_finder.webkit_base()
165 self.layout_tests_dir = self.webkit_finder.path_from_webkit_base('tests' )
166 self.destination_directory = self.filesystem.normpath(self.filesystem.jo in(self.layout_tests_dir, options.destination,
167 self.filesystem.basename(self.top_of_repo)))
168 self.import_in_place = (self.dir_to_import == self.destination_directory )
169
170 self.changeset = CHANGESET_NOT_AVAILABLE
171
172 self.import_list = []
173
174 def do_import(self):
175 _log.info("Importing %s into %s", self.dir_to_import, self.destination_d irectory)
176 self.find_importable_tests(self.dir_to_import)
177 self.load_changeset()
178 self.import_tests()
179
180 def load_changeset(self):
181 """Returns the current changeset from mercurial or "Not Available"."""
182 try:
183 self.changeset = self.host.executive.run_command(['hg', 'tip']).spli t('changeset:')[1]
184 except (OSError, ScriptError):
185 self.changeset = CHANGESET_NOT_AVAILABLE
186
187 def find_importable_tests(self, directory):
188 # FIXME: use filesystem
189 paths_to_skip = self.find_paths_to_skip()
190
191 for root, dirs, files in os.walk(directory):
192 cur_dir = root.replace(self.layout_tests_dir + '/', '') + '/'
193 _log.info(' scanning ' + cur_dir + '...')
194 total_tests = 0
195 reftests = 0
196 jstests = 0
197
198 DIRS_TO_SKIP = ('.git', '.hg')
199 if dirs:
200 for d in DIRS_TO_SKIP:
201 if d in dirs:
202 dirs.remove(d)
203
204 for path in paths_to_skip:
205 path_base = path.replace(cur_dir, '')
206 path_full = self.filesystem.join(root, path_base)
207 if path_base in dirs:
208 dirs.remove(path_base)
209 if not self.options.dry_run and self.import_in_place:
210 _log.info(" pruning %s" % path_base)
211 self.filesystem.rmtree(path_full)
212
213 copy_list = []
214
215 for filename in files:
216 path_full = self.filesystem.join(root, filename)
217 path_base = path_full.replace(self.layout_tests_dir + '/', '')
218 if path_base in paths_to_skip:
219 if not self.options.dry_run and self.import_in_place:
220 _log.info(" pruning %s" % path_base)
221 self.filesystem.remove(path_full)
222 continue
223 # FIXME: This block should really be a separate function, but th e early-continues make that difficult.
224
225 if filename.startswith('.') or filename.endswith('.pl'):
226 continue # For some reason the w3c repo contains random per l scripts we don't care about.
227
228 fullpath = os.path.join(root, filename)
229
230 mimetype = mimetypes.guess_type(fullpath)
231 if not 'html' in str(mimetype[0]) and not 'xml' in str(mimetype[ 0]):
232 copy_list.append({'src': fullpath, 'dest': filename})
233 continue
234
235 if root.endswith('resources'):
236 copy_list.append({'src': fullpath, 'dest': filename})
237 continue
238
239 test_parser = TestParser(vars(self.options), filename=fullpath)
240 test_info = test_parser.analyze_test()
241 if test_info is None:
242 continue
243
244 if 'reference' in test_info.keys():
245 reftests += 1
246 total_tests += 1
247 test_basename = os.path.basename(test_info['test'])
248
249 # Add the ref file, following WebKit style.
250 # FIXME: Ideally we'd support reading the metadata
251 # directly rather than relying on a naming convention.
252 # Using a naming convention creates duplicate copies of the
253 # reference files.
254 ref_file = os.path.splitext(test_basename)[0] + '-expected'
255 ref_file += os.path.splitext(test_basename)[1]
256
257 copy_list.append({'src': test_info['reference'], 'dest': ref _file})
258 copy_list.append({'src': test_info['test'], 'dest': filename })
259
260 # Update any support files that need to move as well to rema in relative to the -expected file.
261 if 'refsupport' in test_info.keys():
262 for support_file in test_info['refsupport']:
263 source_file = os.path.join(os.path.dirname(test_info ['reference']), support_file)
264 source_file = os.path.normpath(source_file)
265
266 # Keep the dest as it was
267 to_copy = {'src': source_file, 'dest': support_file}
268
269 # Only add it once
270 if not(to_copy in copy_list):
271 copy_list.append(to_copy)
272 elif 'jstest' in test_info.keys():
273 jstests += 1
274 total_tests += 1
275 copy_list.append({'src': fullpath, 'dest': filename})
276 else:
277 total_tests += 1
278 copy_list.append({'src': fullpath, 'dest': filename})
279
280 if not total_tests:
281 # We can skip the support directory if no tests were found.
282 if 'support' in dirs:
283 dirs.remove('support')
284
285 if copy_list:
286 # Only add this directory to the list if there's something to im port
287 self.import_list.append({'dirname': root, 'copy_list': copy_list ,
288 'reftests': reftests, 'jstests': jstests, 'total_tests': tot al_tests})
289
290 def find_paths_to_skip(self):
291 if self.options.ignore_expectations:
292 return set()
293
294 paths_to_skip = set()
295 port = self.host.port_factory.get()
296 w3c_import_expectations_path = self.webkit_finder.path_from_webkit_base( 'tests', 'W3CImportExpectations')
297 w3c_import_expectations = self.filesystem.read_text_file(w3c_import_expe ctations_path)
298 parser = TestExpectationParser(port, full_test_list=(), is_lint_mode=Fal se)
299 expectation_lines = parser.parse(w3c_import_expectations_path, w3c_impor t_expectations)
300 for line in expectation_lines:
301 if 'SKIP' in line.expectations:
302 if line.specifiers:
303 _log.warning("W3CImportExpectations:%s should not have any s pecifiers" % line.line_numbers)
304 continue
305 paths_to_skip.add(line.name)
306 return paths_to_skip
307
308 def import_tests(self):
309 total_imported_tests = 0
310 total_imported_reftests = 0
311 total_imported_jstests = 0
312 total_prefixed_properties = {}
313
314 for dir_to_copy in self.import_list:
315 total_imported_tests += dir_to_copy['total_tests']
316 total_imported_reftests += dir_to_copy['reftests']
317 total_imported_jstests += dir_to_copy['jstests']
318
319 prefixed_properties = []
320
321 if not dir_to_copy['copy_list']:
322 continue
323
324 orig_path = dir_to_copy['dirname']
325
326 subpath = os.path.relpath(orig_path, self.top_of_repo)
327 new_path = os.path.join(self.destination_directory, subpath)
328
329 if not(os.path.exists(new_path)):
330 os.makedirs(new_path)
331
332 copied_files = []
333
334 for file_to_copy in dir_to_copy['copy_list']:
335 # FIXME: Split this block into a separate function.
336 orig_filepath = os.path.normpath(file_to_copy['src'])
337
338 if os.path.isdir(orig_filepath):
339 # FIXME: Figure out what is triggering this and what to do a bout it.
340 _log.error('%s refers to a directory' % orig_filepath)
341 continue
342
343 if not(os.path.exists(orig_filepath)):
344 _log.warning('%s not found. Possible error in the test.', or ig_filepath)
345 continue
346
347 new_filepath = os.path.join(new_path, file_to_copy['dest'])
348
349 if not(os.path.exists(os.path.dirname(new_filepath))):
350 if not self.import_in_place and not self.options.dry_run:
351 os.makedirs(os.path.dirname(new_filepath))
352
353 if not self.options.overwrite and os.path.exists(new_filepath):
354 _log.info(' skipping import of existing file ' + new_filepa th)
355 else:
356 # FIXME: Maybe doing a file diff is in order here for existi ng files?
357 # In other words, there's no sense in overwriting identical files, but
358 # there's no harm in copying the identical thing.
359 _log.info(' importing %s', os.path.relpath(new_filepath, se lf.layout_tests_dir))
360
361 # Only html, xml, or css should be converted
362 # FIXME: Eventually, so should js when support is added for this type of conversion
363 mimetype = mimetypes.guess_type(orig_filepath)
364 if 'html' in str(mimetype[0]) or 'xml' in str(mimetype[0]) or ' css' in str(mimetype[0]):
365 converted_file = convert_for_webkit(new_path, filename=orig_ filepath)
366
367 if not converted_file:
368 if not self.import_in_place and not self.options.dry_run :
369 shutil.copyfile(orig_filepath, new_filepath) # The file was unmodified.
370 else:
371 for prefixed_property in converted_file[0]:
372 total_prefixed_properties.setdefault(prefixed_proper ty, 0)
373 total_prefixed_properties[prefixed_property] += 1
374
375 prefixed_properties.extend(set(converted_file[0]) - set( prefixed_properties))
376 if not self.options.dry_run:
377 outfile = open(new_filepath, 'wb')
378 outfile.write(converted_file[1])
379 outfile.close()
380 else:
381 if not self.import_in_place and not self.options.dry_run:
382 shutil.copyfile(orig_filepath, new_filepath)
383
384 copied_files.append(new_filepath.replace(self._webkit_root, ''))
385
386 if not self.import_in_place and not self.options.dry_run:
387 self.remove_deleted_files(new_path, copied_files)
388 self.write_import_log(new_path, copied_files, prefixed_propertie s)
389
390 _log.info('')
391 _log.info('Import complete')
392 _log.info('')
393 _log.info('IMPORTED %d TOTAL TESTS', total_imported_tests)
394 _log.info('Imported %d reftests', total_imported_reftests)
395 _log.info('Imported %d JS tests', total_imported_jstests)
396 _log.info('Imported %d pixel/manual tests', total_imported_tests - total _imported_jstests - total_imported_reftests)
397 _log.info('')
398 _log.info('Properties needing prefixes (by count):')
399 for prefixed_property in sorted(total_prefixed_properties, key=lambda p: total_prefixed_properties[p]):
400 _log.info(' %s: %s', prefixed_property, total_prefixed_properties[p refixed_property])
401
402 def setup_destination_directory(self):
403 """ Creates a destination directory that mirrors that of the source dire ctory """
404
405 new_subpath = self.dir_to_import[len(self.top_of_repo):]
406
407 destination_directory = os.path.join(self.destination_directory, new_sub path)
408
409 if not os.path.exists(destination_directory):
410 os.makedirs(destination_directory)
411
412 _log.info('Tests will be imported into: %s', destination_directory)
413
414 def remove_deleted_files(self, dir_to_import, new_file_list):
415 previous_file_list = []
416
417 import_log_file = os.path.join(dir_to_import, 'w3c-import.log')
418 if not os.path.exists(import_log_file):
419 return
420
421 import_log = open(import_log_file, 'r')
422 contents = import_log.readlines()
423
424 if 'List of files\n' in contents:
425 list_index = contents.index('List of files:\n') + 1
426 previous_file_list = [filename.strip() for filename in contents[list _index:]]
427
428 deleted_files = set(previous_file_list) - set(new_file_list)
429 for deleted_file in deleted_files:
430 _log.info('Deleting file removed from the W3C repo: %s', deleted_fil e)
431 deleted_file = os.path.join(self._webkit_root, deleted_file)
432 os.remove(deleted_file)
433
434 import_log.close()
435
436 def write_import_log(self, dir_to_import, file_list, prop_list):
437 now = datetime.datetime.now()
438
439 import_log = open(os.path.join(dir_to_import, 'w3c-import.log'), 'w')
440 import_log.write('The tests in this directory were imported from the W3C repository.\n')
441 import_log.write('Do NOT modify these tests directly in Webkit. Instead, push changes to the W3C CSS repo:\n\n')
442 import_log.write('http://hg.csswg.org/test\n\n')
443 import_log.write('Then run the Tools/Scripts/import-w3c-tests in Webkit to reimport\n\n')
444 import_log.write('Do NOT modify or remove this file\n\n')
445 import_log.write('------------------------------------------------------ ------------------\n')
446 import_log.write('Last Import: ' + now.strftime('%Y-%m-%d %H:%M') + '\n' )
447 import_log.write('W3C Mercurial changeset: ' + self.changeset + '\n')
448 import_log.write('------------------------------------------------------ ------------------\n')
449 import_log.write('Properties requiring vendor prefixes:\n')
450 if prop_list:
451 for prop in prop_list:
452 import_log.write(prop + '\n')
453 else:
454 import_log.write('None\n')
455 import_log.write('------------------------------------------------------ ------------------\n')
456 import_log.write('List of files:\n')
457 for item in file_list:
458 import_log.write(item + '\n')
459
460 import_log.close()
OLDNEW
« no previous file with comments | « sky/tools/webkitpy/w3c/test_converter_unittest.py ('k') | sky/tools/webkitpy/w3c/test_importer_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698