| # Copyright 2020 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import copy |
| from recipe_engine import recipe_api |
| |
| |
| class WebUtilsApi(recipe_api.RecipeApi): |
| """Utilities to use when running flutter web engine tests.""" |
| |
| def firefox_driver(self, checkout): |
| """Downloads the latest version of the Firefox web driver from CIPD.""" |
| # Download the driver for Firefox. |
| firefox_driver_path = checkout.join('flutter', 'lib', 'web_ui', |
| '.dart_tool', 'drivers', 'firefox') |
| pkgdriver = self.m.cipd.EnsureFile() |
| pkgdriver.add_package( |
| 'flutter_internal/browser-drivers/firefoxdriver-linux', 'latest') |
| self.m.cipd.ensure(firefox_driver_path, pkgdriver) |
| |
| def chrome(self, checkout): |
| """Downloads Chrome from CIPD. |
| |
| The chrome version to be used will be read from a file on the repo side. |
| """ |
| browser_lock_yaml_file = checkout.join('flutter', 'lib', 'web_ui', 'dev', |
| 'browser_lock.yaml') |
| with self.m.context(cwd=checkout): |
| result = self.m.yaml.read( |
| 'read browser lock yaml', |
| browser_lock_yaml_file, |
| self.m.json.output(), |
| ) |
| browser_lock_content = result.json.output |
| platform = self.m.platform.name.capitalize() |
| binary = browser_lock_content['chrome'][platform] |
| chrome_path = checkout.join('flutter', 'lib', 'web_ui', '.dart_tool', |
| 'chrome', '%s' % binary) |
| # Using the binary number since the repos side file uses binary names. |
| # See: flutter/engine/blob/master/lib/web_ui/dev/browser_lock.yaml |
| # Chrome also uses these binary numbers for archiving different versions. |
| chrome_pkg = self.m.cipd.EnsureFile() |
| chrome_pkg.add_package('flutter_internal/browsers/chrome/${platform}', |
| binary) |
| self.m.cipd.ensure(chrome_path, chrome_pkg) |
| |
| def chrome_driver(self, checkout): |
| """Downloads Chrome web driver from CIPD. |
| |
| The driver version to be used will be read from a file on the repo side. |
| """ |
| # Get driver version from the engine repo. |
| # See: flutter/engine/blob/master/lib/web_ui/dev/browser_lock.yaml |
| browser_lock_yaml_file = checkout.join('flutter', 'lib', 'web_ui', 'dev', |
| 'browser_lock.yaml') |
| with self.m.context(cwd=checkout): |
| result = self.m.yaml.read( |
| 'read browser lock yaml', |
| browser_lock_yaml_file, |
| self.m.json.output(), |
| ) |
| browser_lock_content = result.json.output |
| version = browser_lock_content['required_driver_version']['chrome'] |
| chrome_driver_path = checkout.join('flutter', 'lib', 'web_ui', '.dart_tool', |
| 'drivers', 'chrome', '%s' % version) |
| chrome_pkgdriver = self.m.cipd.EnsureFile() |
| chrome_pkgdriver.add_package( |
| 'flutter_internal/browser-drivers/chrome/${platform}', |
| 'latest-%s' % version) |
| self.m.cipd.ensure(chrome_driver_path, chrome_pkgdriver) |
| |
| def clone_goldens_repo(self, checkout): |
| """Clone the repository that keeps golden files. |
| |
| The repository name and the reference SHA will be read from a file on the |
| repo side. |
| """ |
| builder_root = self.m.path['cache'].join('builder') |
| goldens = builder_root.join('goldens') |
| self.m.file.ensure_directory('mkdir goldens', goldens) |
| golden_yaml_file = checkout.join('flutter', 'lib', 'web_ui', 'dev', |
| 'goldens_lock.yaml') |
| with self.m.context(cwd=builder_root): |
| # Use golden_lock.yaml file to read url of the goldens repository and |
| # the revision number to checkout. |
| # https://github.com/flutter/engine/blob/master/lib/web_ui/dev/goldens_lock.yaml |
| # This file is used by web engine developers. The engine developers update |
| # the flutter/goldens.git repo when they need changes. Later change the |
| # revision number on this file. |
| result = self.m.yaml.read( |
| 'read yaml', |
| golden_yaml_file, |
| self.m.json.output(), |
| ) |
| # The content of the file is expected to be: |
| # |
| # repository: https://github.com/flutter/goldens.git |
| # revision: b6efc75885c23f0b5c485d8bd659ed339feec9ec |
| golden_lock_content = result.json.output |
| repo = golden_lock_content['repository'] |
| revision_number = golden_lock_content['revision'] |
| with self.m.context(cwd=goldens): |
| self.m.git.checkout( |
| repo, |
| dir_path=goldens, |
| ref=revision_number, |
| recursive=True, |
| set_got_revision=True) |
| golden_files = checkout.join('flutter', 'lib', 'web_ui', '.dart_tool', |
| 'goldens') |
| self.m.file.copytree('copy goldens', goldens, golden_files) |
| |
| def upload_failing_goldens(self, checkout, browser): |
| """Upload the failed goldens files to a gcs bucket. |
| |
| Parse the logs to determine which golden tests are failed. Upload expected |
| and actual golden files to a gcs bucket. Display links to html pages where |
| developer can compare actual vs expected images. |
| """ |
| logs_path = checkout.join('flutter', 'lib', 'web_ui', '.dart_tool', |
| 'test_results') |
| tests_info_file_path = logs_path.join('info.txt') |
| self.m.file.write_text( |
| 'write info file', |
| tests_info_file_path, |
| 'tests for %s' % self.m.platform.name, |
| 'tests for windows', |
| ) |
| |
| if not self.m.properties.get( |
| 'gcs_goldens_bucket') or self.m.runtime.is_experimental: |
| # This is to avoid trying to upload files when 'gcs_goldens_bucket' is |
| # missing or when running from led. |
| return |
| |
| bucket_id = self.m.buildbucket.build.id |
| self.m.gsutil.upload( |
| bucket=self.m.properties['gcs_goldens_bucket'], |
| source=logs_path, |
| dest='%s/%s/%s' % ('web_engine', bucket_id, browser), |
| link_name='archive goldens', |
| args=['-r'], |
| multithreaded=True, |
| name='upload goldens %s' % bucket_id, |
| unauthenticated_url=True) |
| html_files = self.m.file.glob_paths( |
| 'html goldens', |
| source=logs_path, |
| pattern='*.html', |
| test_data=['a.html']) |
| with self.m.step.nest('Failed golden links') as presentation: |
| for html_file in html_files: |
| base_name = self.m.path.basename(html_file) |
| url = 'https://storage.googleapis.com/%s/web_engine/%s/%s/%s' % ( |
| self.m.properties['gcs_goldens_bucket'], bucket_id, browser, |
| base_name) |
| presentation.links[base_name] = url |
| |
| def prepare_dependencies(self, checkout): |
| """Install all the required dependencies for a given felt test.""" |
| deps = self.m.properties.get('dependencies', []) |
| available_deps = { |
| 'chrome': self.chrome, |
| 'chrome_driver': self.chrome_driver, |
| 'firefox_driver': self.firefox_driver, |
| 'goldens_repo': self.clone_goldens_repo, |
| } |
| for dep in deps: |
| dep_funct = available_deps.get(dep) |
| if not dep_funct: |
| raise ValueError('Dependency %s not available.' % dep) |
| dep_funct(checkout) |