| OLD | NEW |
| 1 # Copyright (C) 2013 Google Inc. All rights reserved. | 1 # Copyright (C) 2013 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 56 from webkitpy.layout_tests.port.base import Port | 56 from webkitpy.layout_tests.port.base import Port |
| 57 from webkitpy.layout_tests.models.test_expectations import TestExpectations | 57 from webkitpy.layout_tests.models.test_expectations import TestExpectations |
| 58 | 58 |
| 59 | 59 |
| 60 logging.basicConfig() | 60 logging.basicConfig() |
| 61 _log = logging.getLogger(__name__) | 61 _log = logging.getLogger(__name__) |
| 62 _log.setLevel(logging.INFO) | 62 _log.setLevel(logging.INFO) |
| 63 | 63 |
| 64 PLATFORM_DIRECTORY = 'platform' | 64 PLATFORM_DIRECTORY = 'platform' |
| 65 | 65 |
| 66 |
| 66 class LayoutTestsMover(object): | 67 class LayoutTestsMover(object): |
| 67 | 68 |
| 68 def __init__(self, port=None): | 69 def __init__(self, port=None): |
| 69 self._port = port | 70 self._port = port |
| 70 if not self._port: | 71 if not self._port: |
| 71 host = Host() | 72 host = Host() |
| 72 # Given that we use include_overrides=False and model_all_expectatio
ns=True when | 73 # Given that we use include_overrides=False and model_all_expectatio
ns=True when |
| 73 # constructing the TestExpectations object, it doesn't matter which
Port object we use. | 74 # constructing the TestExpectations object, it doesn't matter which
Port object we use. |
| 74 self._port = host.port_factory.get() | 75 self._port = host.port_factory.get() |
| 75 self._port.host.initialize_scm() | 76 self._port.host.initialize_scm() |
| (...skipping 27 matching lines...) Expand all Loading... |
| 103 raise Exception('Destination path %s is not in LayoutTests directory
' % self._destination) | 104 raise Exception('Destination path %s is not in LayoutTests directory
' % self._destination) |
| 104 | 105 |
| 105 # If destination is an existing directory, we move the children of origi
n into destination. | 106 # If destination is an existing directory, we move the children of origi
n into destination. |
| 106 # However, if any of the children of origin would clash with existing ch
ildren of | 107 # However, if any of the children of origin would clash with existing ch
ildren of |
| 107 # destination, we fail. | 108 # destination, we fail. |
| 108 # FIXME: Consider adding support for recursively moving into an existing
directory. | 109 # FIXME: Consider adding support for recursively moving into an existing
directory. |
| 109 if self._filesystem.isdir(self._absolute_destination): | 110 if self._filesystem.isdir(self._absolute_destination): |
| 110 for file_path in self._filesystem.listdir(self._absolute_origin): | 111 for file_path in self._filesystem.listdir(self._absolute_origin): |
| 111 if self._filesystem.exists(self._filesystem.join(self._absolute_
destination, file_path)): | 112 if self._filesystem.exists(self._filesystem.join(self._absolute_
destination, file_path)): |
| 112 raise Exception('Origin path %s clashes with existing destin
ation path %s' % | 113 raise Exception('Origin path %s clashes with existing destin
ation path %s' % |
| 113 (self._filesystem.join(self._origin, file_path), sel
f._filesystem.join(self._destination, file_path))) | 114 (self._filesystem.join(self._origin, file_pa
th), self._filesystem.join(self._destination, file_path))) |
| 114 | 115 |
| 115 def _get_expectations_for_test(self, model, test_path): | 116 def _get_expectations_for_test(self, model, test_path): |
| 116 """Given a TestExpectationsModel object, finds all expectations that mat
ch the specified | 117 """Given a TestExpectationsModel object, finds all expectations that mat
ch the specified |
| 117 test, specified as a relative path. Handles the fact that expectations m
ay be keyed by | 118 test, specified as a relative path. Handles the fact that expectations m
ay be keyed by |
| 118 directory. | 119 directory. |
| 119 """ | 120 """ |
| 120 expectations = set() | 121 expectations = set() |
| 121 if model.has_test(test_path): | 122 if model.has_test(test_path): |
| 122 expectations.add(model.get_expectation_line(test_path)) | 123 expectations.add(model.get_expectation_line(test_path)) |
| 123 test_path = self._filesystem.dirname(test_path) | 124 test_path = self._filesystem.dirname(test_path) |
| 124 while not test_path == '': | 125 while not test_path == '': |
| 125 # The model requires a trailing slash for directories. | 126 # The model requires a trailing slash for directories. |
| 126 test_path_for_model = test_path + '/' | 127 test_path_for_model = test_path + '/' |
| 127 if model.has_test(test_path_for_model): | 128 if model.has_test(test_path_for_model): |
| 128 expectations.add(model.get_expectation_line(test_path_for_model)
) | 129 expectations.add(model.get_expectation_line(test_path_for_model)
) |
| 129 test_path = self._filesystem.dirname(test_path) | 130 test_path = self._filesystem.dirname(test_path) |
| 130 return expectations | 131 return expectations |
| 131 | 132 |
| 132 def _get_expectations(self, model, path): | 133 def _get_expectations(self, model, path): |
| 133 """Given a TestExpectationsModel object, finds all expectations for all
tests under the | 134 """Given a TestExpectationsModel object, finds all expectations for all
tests under the |
| 134 specified relative path. | 135 specified relative path. |
| 135 """ | 136 """ |
| 136 expectations = set() | 137 expectations = set() |
| 137 for test in self._filesystem.files_under(self._filesystem.join(self._lay
out_tests_root, path), dirs_to_skip=['script-tests', 'resources'], | 138 for test in self._filesystem.files_under(self._filesystem.join(self._lay
out_tests_root, path), dirs_to_skip=['script-tests', 'resources'], |
| 138 file_filter=Port.is_test_file): | 139 file_filter=Port.is_test_file): |
| 139 expectations = expectations.union(self._get_expectations_for_test(mo
del, self._filesystem.relpath(test, self._layout_tests_root))) | 140 expectations = expectations.union( |
| 141 self._get_expectations_for_test( |
| 142 model, |
| 143 self._filesystem.relpath( |
| 144 test, |
| 145 self._layout_tests_root))) |
| 140 return expectations | 146 return expectations |
| 141 | 147 |
| 142 @staticmethod | 148 @staticmethod |
| 143 def _clone_expectation_line_for_path(expectation_line, path): | 149 def _clone_expectation_line_for_path(expectation_line, path): |
| 144 """Clones a TestExpectationLine object and updates the clone to apply to
the specified | 150 """Clones a TestExpectationLine object and updates the clone to apply to
the specified |
| 145 relative path. | 151 relative path. |
| 146 """ | 152 """ |
| 147 clone = copy.copy(expectation_line) | 153 clone = copy.copy(expectation_line) |
| 148 clone.original_string = re.compile(expectation_line.name).sub(path, expe
ctation_line.original_string) | 154 clone.original_string = re.compile(expectation_line.name).sub(path, expe
ctation_line.original_string) |
| 149 clone.name = path | 155 clone.name = path |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 # Both the root path and the target of the reference my be subject to th
e move, so there are | 210 # Both the root path and the target of the reference my be subject to th
e move, so there are |
| 205 # four cases to consider. In the case where both or neither are subject
to the move, the | 211 # four cases to consider. In the case where both or neither are subject
to the move, the |
| 206 # reference doesn't need updating. | 212 # reference doesn't need updating. |
| 207 # | 213 # |
| 208 # This is true even if the reference includes superfluous dot segments w
hich mention a moved | 214 # This is true even if the reference includes superfluous dot segments w
hich mention a moved |
| 209 # directory, as dot segments are collapsed during URL normalization. For
example, if | 215 # directory, as dot segments are collapsed during URL normalization. For
example, if |
| 210 # foo.html contains a reference 'bar/../script.js', this remains valid (
though ugly) even if | 216 # foo.html contains a reference 'bar/../script.js', this remains valid (
though ugly) even if |
| 211 # bar/ is moved to baz/, because the reference is always normalized to '
script.js'. | 217 # bar/ is moved to baz/, because the reference is always normalized to '
script.js'. |
| 212 absolute_reference = self._filesystem.normpath(self._filesystem.join(roo
t, reference)) | 218 absolute_reference = self._filesystem.normpath(self._filesystem.join(roo
t, reference)) |
| 213 if self._is_child_path(self._absolute_origin, root) == self._is_child_pa
th(self._absolute_origin, absolute_reference): | 219 if self._is_child_path(self._absolute_origin, root) == self._is_child_pa
th(self._absolute_origin, absolute_reference): |
| 214 return None; | 220 return None |
| 215 | 221 |
| 216 new_root = self._move_path(root, self._absolute_origin, self._absolute_d
estination) | 222 new_root = self._move_path(root, self._absolute_origin, self._absolute_d
estination) |
| 217 new_absolute_reference = self._move_path(absolute_reference, self._absol
ute_origin, self._absolute_destination) | 223 new_absolute_reference = self._move_path(absolute_reference, self._absol
ute_origin, self._absolute_destination) |
| 218 return self._filesystem.relpath(new_absolute_reference, new_root) | 224 return self._filesystem.relpath(new_absolute_reference, new_root) |
| 219 | 225 |
| 220 def _get_all_updated_references(self, references): | 226 def _get_all_updated_references(self, references): |
| 221 """Determines the updated references due to the move. Returns a dictiona
ry that maps from an | 227 """Determines the updated references due to the move. Returns a dictiona
ry that maps from an |
| 222 absolute file path to a dictionary that maps from a reference string to
the corresponding | 228 absolute file path to a dictionary that maps from a reference string to
the corresponding |
| 223 updated reference. | 229 updated reference. |
| 224 """ | 230 """ |
| (...skipping 21 matching lines...) Expand all Loading... |
| 246 for target in updates.keys(): | 252 for target in updates.keys(): |
| 247 regex = re.compile(r'((?:src=|href=|importScripts\(|url\()["\']?)%s(
["\']?)' % target) | 253 regex = re.compile(r'((?:src=|href=|importScripts\(|url\()["\']?)%s(
["\']?)' % target) |
| 248 contents = regex.sub(r'\1%s\2' % updates[target], contents) | 254 contents = regex.sub(r'\1%s\2' % updates[target], contents) |
| 249 self._filesystem.write_binary_file(path, contents) | 255 self._filesystem.write_binary_file(path, contents) |
| 250 self._scm.add(path) | 256 self._scm.add(path) |
| 251 | 257 |
| 252 def _update_test_source_files(self): | 258 def _update_test_source_files(self): |
| 253 def is_test_source_file(filesystem, dirname, basename): | 259 def is_test_source_file(filesystem, dirname, basename): |
| 254 pass_regex = re.compile(r'\.(css|js)$') | 260 pass_regex = re.compile(r'\.(css|js)$') |
| 255 fail_regex = re.compile(r'-expected\.') | 261 fail_regex = re.compile(r'-expected\.') |
| 256 return (Port.is_test_file(filesystem, dirname, basename) or pass_reg
ex.search(basename)) and not fail_regex.search(basename) | 262 return (Port.is_test_file(filesystem, dirname, basename) |
| 263 or pass_regex.search(basename)) and not fail_regex.search(ba
sename) |
| 257 | 264 |
| 258 test_source_files = self._filesystem.files_under(self._layout_tests_root
, file_filter=is_test_source_file) | 265 test_source_files = self._filesystem.files_under(self._layout_tests_root
, file_filter=is_test_source_file) |
| 259 _log.info('Considering %s test source files for references' % len(test_s
ource_files)) | 266 _log.info('Considering %s test source files for references' % len(test_s
ource_files)) |
| 260 references = self._find_references(test_source_files) | 267 references = self._find_references(test_source_files) |
| 261 _log.info('Considering references in %s files' % len(references)) | 268 _log.info('Considering references in %s files' % len(references)) |
| 262 updates = self._get_all_updated_references(references) | 269 updates = self._get_all_updated_references(references) |
| 263 _log.info('Updating references in %s files' % len(updates)) | 270 _log.info('Updating references in %s files' % len(updates)) |
| 264 count = 0 | 271 count = 0 |
| 265 for file_path in updates.keys(): | 272 for file_path in updates.keys(): |
| 266 self._update_file(file_path, updates[file_path]) | 273 self._update_file(file_path, updates[file_path]) |
| (...skipping 17 matching lines...) Expand all Loading... |
| 284 self._scm.move(self._scm_path(origin, directory), self._scm_path(des
tination, directory)) | 291 self._scm.move(self._scm_path(origin, directory), self._scm_path(des
tination, directory)) |
| 285 self._filesystem.rmtree(absolute_origin) | 292 self._filesystem.rmtree(absolute_origin) |
| 286 | 293 |
| 287 def _move_files(self): | 294 def _move_files(self): |
| 288 """Moves the all files that correspond to the move, including platform-s
pecific expected | 295 """Moves the all files that correspond to the move, including platform-s
pecific expected |
| 289 results. | 296 results. |
| 290 """ | 297 """ |
| 291 self._move_directory(self._origin, self._destination) | 298 self._move_directory(self._origin, self._destination) |
| 292 for directory in self._filesystem.listdir(self._filesystem.join(self._la
yout_tests_root, PLATFORM_DIRECTORY)): | 299 for directory in self._filesystem.listdir(self._filesystem.join(self._la
yout_tests_root, PLATFORM_DIRECTORY)): |
| 293 self._move_directory(self._filesystem.join(PLATFORM_DIRECTORY, direc
tory, self._origin), | 300 self._move_directory(self._filesystem.join(PLATFORM_DIRECTORY, direc
tory, self._origin), |
| 294 self._filesystem.join(PLATFORM_DIRECTORY, directory,
self._destination)) | 301 self._filesystem.join(PLATFORM_DIRECTORY, direc
tory, self._destination)) |
| 295 | 302 |
| 296 def _commit_changes(self): | 303 def _commit_changes(self): |
| 297 if not self._scm.supports_local_commits(): | 304 if not self._scm.supports_local_commits(): |
| 298 return | 305 return |
| 299 title = 'Move LayoutTests directory %s to %s' % (self._origin, self._des
tination) | 306 title = 'Move LayoutTests directory %s to %s' % (self._origin, self._des
tination) |
| 300 _log.info('Committing change \'%s\'' % title) | 307 _log.info('Committing change \'%s\'' % title) |
| 301 self._scm.commit_locally_with_message('%s\n\nThis commit was automatical
ly generated by move-layout-tests.' % title, | 308 self._scm.commit_locally_with_message('%s\n\nThis commit was automatical
ly generated by move-layout-tests.' % title, |
| 302 commit_all_working_directory_chang
es=False) | 309 commit_all_working_directory_chang
es=False) |
| 303 | 310 |
| 304 def move(self, origin, destination): | 311 def move(self, origin, destination): |
| 305 self._origin = origin | 312 self._origin = origin |
| 306 self._destination = destination | 313 self._destination = destination |
| 307 self._absolute_origin = self._filesystem.join(self._layout_tests_root, s
elf._origin) | 314 self._absolute_origin = self._filesystem.join(self._layout_tests_root, s
elf._origin) |
| 308 self._absolute_destination = self._filesystem.join(self._layout_tests_ro
ot, self._destination) | 315 self._absolute_destination = self._filesystem.join(self._layout_tests_ro
ot, self._destination) |
| 309 self._validate_input() | 316 self._validate_input() |
| 310 self._update_expectations() | 317 self._update_expectations() |
| 311 self._update_test_source_files() | 318 self._update_test_source_files() |
| 312 self._move_files() | 319 self._move_files() |
| 313 # FIXME: Handle virtual test suites. | 320 # FIXME: Handle virtual test suites. |
| 314 self._commit_changes() | 321 self._commit_changes() |
| 315 | 322 |
| 323 |
| 316 def main(argv): | 324 def main(argv): |
| 317 parser = optparse.OptionParser(description=__doc__) | 325 parser = optparse.OptionParser(description=__doc__) |
| 318 parser.add_option('--origin', | 326 parser.add_option('--origin', |
| 319 help=('The directory of tests to move, as a relative path
from the LayoutTests directory.')) | 327 help=('The directory of tests to move, as a relative path
from the LayoutTests directory.')) |
| 320 parser.add_option('--destination', | 328 parser.add_option('--destination', |
| 321 help=('The new path for the directory of tests, as a relat
ive path from the LayoutTests directory.')) | 329 help=('The new path for the directory of tests, as a relat
ive path from the LayoutTests directory.')) |
| 322 options, _ = parser.parse_args() | 330 options, _ = parser.parse_args() |
| 323 LayoutTestsMover().move(options.origin, options.destination) | 331 LayoutTestsMover().move(options.origin, options.destination) |
| OLD | NEW |