|  | # 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) |