| # Copyright (c) 2014, Canon Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # 3. Neither the name of Canon Inc. nor the names of |
| # its contributors may be used to endorse or promote products derived |
| # from this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY |
| # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| # DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR |
| # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| """ |
| This script downloads W3C test repositories. |
| """ |
| |
| import json |
| import logging |
| |
| from webkitpy.common.system.filesystem import FileSystem |
| from webkitpy.common.webkit_finder import WebKitFinder |
| from webkitpy.common.checkout.scm.git import Git |
| |
| _log = logging.getLogger(__name__) |
| |
| |
| class TestDownloader(object): |
| |
| @staticmethod |
| def default_options(): |
| options = type('', (), {}) |
| options.fetch = True |
| options.verbose = False |
| options.import_all = False |
| return options |
| |
| @staticmethod |
| def load_test_repositories(filesystem=FileSystem()): |
| webkit_finder = WebKitFinder(filesystem) |
| test_repositories_path = webkit_finder.path_from_webkit_base('LayoutTests', 'imported', 'w3c', 'resources', 'TestRepositories') |
| return json.loads(filesystem.read_text_file(test_repositories_path)) |
| |
| def __init__(self, repository_directory, host, options): |
| self._options = options |
| self._host = host |
| self._filesystem = host.filesystem |
| self._test_suites = [] |
| |
| self.repository_directory = repository_directory |
| |
| self.test_repositories = self.load_test_repositories(self._filesystem) |
| |
| self.paths_to_skip = [] |
| self.paths_to_import = [] |
| for test_repository in self.test_repositories: |
| self.paths_to_skip.extend([self._filesystem.join(test_repository['name'], path) for path in test_repository['paths_to_skip']]) |
| self.paths_to_import.extend([self._filesystem.join(test_repository['name'], path) for path in test_repository['paths_to_import']]) |
| |
| webkit_finder = WebKitFinder(self._filesystem) |
| self.import_expectations_path = webkit_finder.path_from_webkit_base('LayoutTests', 'imported', 'w3c', 'resources', 'import-expectations.json') |
| if not self._filesystem.isfile(self.import_expectations_path): |
| _log.warning('Unable to read import expectation file: %s' % self.import_expectations_path) |
| if not self._options.import_all: |
| self._init_paths_from_expectations() |
| |
| def git(self, test_repository): |
| return Git(test_repository, None, executive=self._host.executive, filesystem=self._filesystem) |
| |
| def checkout_test_repository(self, revision, url, directory): |
| git = None |
| if not self._filesystem.exists(directory): |
| _log.info('Cloning %s into %s...' % (url, directory)) |
| Git.clone(url, directory, self._host.executive) |
| git = self.git(directory) |
| elif self._options.fetch is True: |
| git = self.git(directory) |
| _log.info('Fetching %s...' % url) |
| git.fetch() |
| else: |
| git = self.git(directory) |
| _log.info('Checking out revision ' + revision) |
| git.checkout(revision, not self._options.verbose) |
| |
| def _init_paths_from_expectations(self): |
| import_lines = json.loads(self._filesystem.read_text_file(self.import_expectations_path)) |
| for path, policy in import_lines.items(): |
| if policy == 'skip': |
| self.paths_to_skip.append(path) |
| elif policy == 'import': |
| self.paths_to_import.append(path) |
| else: |
| _log.warning('Problem reading import lines ' + path) |
| |
| def update_import_expectations(self, test_paths): |
| import_lines = json.loads(self._filesystem.read_text_file(self.import_expectations_path)) |
| for path in test_paths: |
| import_lines[path] = 'import' |
| self._filesystem.write_text_file(self.import_expectations_path, json.dumps(import_lines, sort_keys=True, indent=4)) |
| |
| def _add_test_suite_paths(self, test_paths, directory, webkit_path): |
| for name in self._filesystem.listdir(directory): |
| original_path = self._filesystem.join(webkit_path, name) |
| if not name.startswith('.') and not original_path in self.paths_to_skip: |
| test_paths.append(original_path) |
| |
| def _empty_directory(self, directory): |
| if self._filesystem.exists(directory): |
| self._filesystem.rmtree(directory) |
| self._filesystem.maybe_make_directory(directory) |
| |
| def copy_tests(self, destination_directory, test_paths): |
| for test_repository in self.test_repositories: |
| self._empty_directory(self._filesystem.join(destination_directory, test_repository['name'])) |
| |
| copy_paths = [] |
| if test_paths: |
| for path in test_paths: |
| copy_paths.append(path) |
| for path in self.paths_to_import: |
| copy_paths.append(path) |
| else: |
| for test_repository in self.test_repositories: |
| self._add_test_suite_paths(copy_paths, self._filesystem.join(self.repository_directory, test_repository['name']), test_repository['name']) |
| # Handling of tests marked as [ Pass ] in expectations file. |
| for path in self.paths_to_import: |
| if not path in copy_paths: |
| copy_paths.append(path) |
| |
| def longest_path(filesystem, paths): |
| longest_matching_path = "" |
| for path in paths: |
| if path.startswith(longest_matching_path): |
| longest_matching_path = path |
| return longest_matching_path |
| |
| def should_copy_dir(filesystem, directory): |
| relative_path = self._filesystem.relpath(directory, self.repository_directory) |
| if relative_path == ".": |
| return True |
| |
| potential_copy_paths = [copy_directory for copy_directory in copy_paths if relative_path.startswith(copy_directory)] |
| if (not potential_copy_paths): |
| return False |
| |
| potential_skip_paths = [skip_directory for skip_directory in self.paths_to_skip if relative_path.startswith(skip_directory)] |
| if (not potential_skip_paths): |
| return True |
| |
| longest_copy_path = longest_path(filesystem, potential_copy_paths) |
| longest_skip_path = longest_path(filesystem, potential_skip_paths) |
| return longest_copy_path.startswith(longest_skip_path) |
| |
| # Compute directories for which we should copy direct children |
| directories_to_copy = self._filesystem.dirs_under(self.repository_directory, should_copy_dir) |
| |
| def should_copy_file(filesystem, dirname, filename): |
| full_path = filesystem.join(dirname, filename) |
| relative_path = self._filesystem.relpath(full_path, self.repository_directory) |
| if relative_path in copy_paths: |
| return True |
| if relative_path in self.paths_to_skip: |
| return False |
| return dirname in directories_to_copy |
| |
| for source_path in self._filesystem.files_under(self.repository_directory, file_filter=should_copy_file): |
| destination_path = self._filesystem.join(destination_directory, self._filesystem.relpath(source_path, self.repository_directory)) |
| self._filesystem.maybe_make_directory(self._filesystem.dirname(destination_path)) |
| self._filesystem.copyfile(source_path, destination_path) |
| |
| def _git_submodules_description(self, test_repository): |
| directory = self._filesystem.join(self.repository_directory, test_repository['name']) |
| |
| git = self.git(directory) |
| git.init_submodules() |
| |
| submodules = [] |
| submodules_status = [line.strip().split(' ') for line in git.submodules_status().splitlines()] |
| for status in submodules_status: |
| version = status[0] |
| path = status[1].split('/') |
| |
| url = self.git(self._filesystem.join(directory, status[1])).origin_url() |
| if not url.startswith('https://github.com/'): |
| _log.warning('Submodule %s (%s) is not hosted on github' % (status[1], url)) |
| _log.warning('Please ensure that generated URL points to an archive of the module or manually edit its value after the import') |
| url = url[:-4] # to remove .git |
| |
| submodule = {} |
| submodule['path'] = path |
| submodule['url'] = url + '/archive/' + version + '.tar.gz' |
| submodule['url_subpath'] = url.split('/').pop() + '-' + version |
| submodules.append(submodule) |
| |
| git.deinit_submodules() |
| return submodules |
| |
| def generate_git_submodules_description(self, test_repository, filepath): |
| self._filesystem.write_text_file(filepath, json.dumps(self._git_submodules_description(test_repository), sort_keys=True, indent=4)) |
| |
| def generate_gitignore(self, test_repository, destination_directory): |
| rules = [] |
| for submodule in self._git_submodules_description(test_repository): |
| path = list(submodule['path']) |
| path.insert(0, '') |
| rules.append('/'.join(path[:-1]) + '/' + path[-1] + '/') |
| rules.append('/'.join(path[:-1]) + '/.' + path[-1] + '.url') |
| self._filesystem.write_text_file(self._filesystem.join(destination_directory, test_repository['name'], '.gitignore'), '\n'.join(rules)) |
| |
| def clone_tests(self, use_tip_of_tree=False): |
| for test_repository in self.test_repositories: |
| self.checkout_test_repository(test_repository['revision'] if not use_tip_of_tree else 'origin/master', test_repository['url'], self._filesystem.join(self.repository_directory, test_repository['name'])) |
| |
| def download_tests(self, destination_directory, test_paths=[], use_tip_of_tree=False): |
| self.clone_tests(use_tip_of_tree) |
| self.copy_tests(destination_directory, test_paths) |