blob: dc1edf397b9275ff9601f3e6b4daa1c8932c7b9b [file] [log] [blame]
# 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.
"""Recipe for engine repository tests."""
import contextlib
import copy
from recipe_engine import recipe_api
from PB.recipes.flutter.engine.engine import InputProperties
from PB.recipes.flutter.engine.engine import EnvProperties
DEPS = [
'depot_tools/depot_tools',
'depot_tools/gclient',
'flutter/display_util',
'flutter/flutter_deps',
'flutter/os_utils',
'flutter/osx_sdk',
'flutter/repo_util',
'flutter/retry',
'flutter/shard_util',
'flutter/test_utils',
'flutter/web_util',
'fuchsia/cas_util',
'fuchsia/goma',
'recipe_engine/buildbucket',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/runtime',
'recipe_engine/step',
'recipe_engine/swarming',
]
GIT_REPO = 'https://flutter.googlesource.com/mirrors/engine'
PROPERTIES = InputProperties
ENV_PROPERTIES = EnvProperties
@contextlib.contextmanager
def SetupXcode(api):
# See cr-buildbucket.cfg for how the version is passed in.
# https://github.com/flutter/infra/blob/master/config/cr-buildbucket.cfg#L148
with api.osx_sdk('ios'):
yield
def Build(api, config, *targets):
checkout = GetCheckoutPath(api)
build_dir = checkout.join('out/%s' % config)
goma_jobs = api.properties['goma_jobs']
ninja_args = [api.depot_tools.ninja_path, '-j', goma_jobs, '-C', build_dir]
ninja_args.extend(targets)
with api.goma.build_with_goma():
name = 'build %s' % ' '.join([config] + list(targets))
api.step(name, ninja_args)
def Archive(api, target):
checkout = GetCheckoutPath(api)
build_dir = checkout.join('out', target)
cas_dir = api.path.mkdtemp('cas-directory')
cas_engine = cas_dir.join(target)
api.file.copytree('Copy host_debug_unopt', build_dir, cas_engine)
return api.cas_util.upload(cas_dir, step_name='Archive Flutter Engine Test CAS')
def RunGN(api, *args):
checkout = GetCheckoutPath(api)
gn_cmd = ['python', checkout.join('flutter/tools/gn'), '--goma']
gn_cmd.extend(args)
api.step('gn %s' % ' '.join(args), gn_cmd)
def GetCheckoutPath(api):
return api.path['cache'].join('builder', 'src')
def CleanUpProcesses(api):
# This is to clean up leaked processes.
api.os_utils.kill_processes()
# Collect memory/cpu/process after task execution.
api.os_utils.collect_os_info()
def RunSteps(api, properties, env_properties):
# Collect memory/cpu/process before task execution.
api.os_utils.collect_os_info()
"""Steps to checkout flutter engine and execute web tests."""
cache_root = api.path['cache'].join('builder')
checkout = GetCheckoutPath(api)
if properties.clobber:
api.file.rmtree('Clobber cache', cache_root)
api.file.rmtree('Clobber build output', checkout.join('out'))
api.file.ensure_directory('Ensure checkout cache', cache_root)
api.goma.ensure()
dart_bin = checkout.join(
'third_party', 'dart', 'tools', 'sdks', 'dart-sdk', 'bin'
)
android_home = checkout.join('third_party', 'android_tools', 'sdk')
env = {
'GOMA_DIR': api.goma.goma_dir,
'ANDROID_HOME': str(android_home),
'CHROME_NO_SANDBOX': 'true',
'ENGINE_PATH': cache_root,
'FLUTTER_PREBUILT_DART_SDK': 'True',
}
env_prefixes = {'PATH': [dart_bin]}
api.flutter_deps.certs(env, env_prefixes)
# Enable long path support on Windows.
api.os_utils.enable_long_paths()
# Checkout source code and build
api.repo_util.engine_checkout(cache_root, env, env_prefixes)
# Ensure required deps are installed
api.flutter_deps.required_deps(
env, env_prefixes, api.properties.get('dependencies', [])
)
with api.context(cwd=cache_root, env=env,
env_prefixes=env_prefixes), api.depot_tools.on_path():
api.gclient.runhooks()
target_name = 'host_debug_unopt'
gn_flags = ['--unoptimized', '--full-dart-sdk', '--prebuilt-dart-sdk']
felt_cmd = [
checkout.join('out', target_name, 'dart-sdk', 'bin', 'dart'),
'dev/felt.dart'
]
cas_hash = ''
builds = []
if api.platform.is_linux:
RunGN(api, *gn_flags)
Build(api, target_name)
# Archieve the engine. Start the drones. Due to capacity limits we are
# Only using the drones on the Linux for now.
# Archive build directory into CAS.
cas_hash = Archive(api, target_name)
# Schedule builds.
# TODO(nurhan): Currently this recipe only shards Linux. The web drones
# recipe is written in a way that it can also support sharding for
# macOS and Windows OSes. When more resources are available or when
# MWE or WWE builders start running more than 1 hour, also shard those
# builders.
builds = schedule_builds_on_linux(api, cas_hash)
elif api.platform.is_mac:
# TODO: remove xcode context condition when GN is clear from xcode:
# https://github.com/flutter/flutter/issues/105805
if api.properties.get('$flutter/osx_sdk'):
with SetupXcode(api):
RunGN(api, *gn_flags)
Build(api, target_name)
else:
RunGN(api, *gn_flags)
Build(api, target_name)
else:
# Platform = windows.
RunGN(api, *gn_flags)
Build(api, target_name)
if api.platform.is_win:
felt_cmd = [
checkout.join(
'flutter', 'lib', 'web_ui', 'dev', 'felt_windows.bat'
)
]
# Update dart packages and run tests.
local_dart = checkout.join('out', target_name, 'dart-sdk', 'bin', 'dart')
with api.context(
cwd=checkout.join('flutter', 'web_sdk', 'web_engine_tester')):
api.step('dart pub get in web_engine_tester', [local_dart, 'pub', 'get'])
with api.context(cwd=checkout.join('flutter', 'lib', 'web_ui')):
api.step('dart pub get in web_engine_tester', [local_dart, 'pub', 'get'])
felt_licenses = copy.deepcopy(felt_cmd)
felt_licenses.append('check-licenses')
api.step('felt licenses', felt_licenses)
if api.platform.is_linux:
# TODO(nurhan): Web engine analysis can also be part of felt and used
# in a shard.
web_engine_analysis_cmd = [
checkout.join(
'flutter', 'lib', 'web_ui', 'dev', 'web_engine_analysis.sh'
),
]
api.step('web engine analysis', web_engine_analysis_cmd)
builds = api.shard_util.collect_builds(builds)
api.display_util.display_builds(
step_name='display builds',
builds=builds,
raise_on_failure=True,
)
CleanUpProcesses(api)
elif api.platform.is_mac:
with recipe_api.defer_results():
felt_test = copy.deepcopy(felt_cmd)
felt_test.append('test')
felt_test.append('--require-skia-gold')
felt_test.append('--browser=safari')
api.step(
api.test_utils.test_step_name('Run tests on macOS Safari'), felt_test
)
CleanUpProcesses(api)
else:
api.web_util.chrome(checkout)
felt_test = copy.deepcopy(felt_cmd)
felt_test.append('test')
api.step(api.test_utils.test_step_name('felt test chrome'), felt_test)
CleanUpProcesses(api)
def schedule_builds_on_linux(api, cas_hash):
"""Schedules one subbuild per subshard."""
reqs = []
# For running Chrome Unit tests:
command_name = 'chrome-unit-linux'
# These are the required dependencies.
web_dependencies = ['chrome']
# These are the felt commands which will be used.
command_args = ['test', '--browser=chrome', '--require-skia-gold']
addShardTask(
api, reqs, command_name, web_dependencies, command_args, cas_hash
)
# For running Firefox Unit tests:
command_name = 'firefox-unit-linux'
# We don't need extra dependencies since felt tools handles firefox itself.
# TODO(nurhan): Use cipd packages for Firefox. As we are doing for chrome
# still respect to the version from browser_lock.yaml.
web_dependencies = []
# These are the felt commands which will be used.
command_args = ['test', '--browser=firefox']
addShardTask(
api, reqs, command_name, web_dependencies, command_args, cas_hash
)
return api.buildbucket.schedule(reqs)
def addShardTask(
api, reqs, command_name, web_dependencies, command_args, cas_hash
):
# These are dependencies specified in the yaml file. We want to pass them down
# to drones so they also install these dependencies.
inherited_dependencies = [{'dependency': d['dependency']} for d in api.properties.get('dependencies', [])]
drone_props = {
'command_name': command_name, 'web_dependencies': web_dependencies,
'command_args': command_args, 'local_engine_cas_hash': cas_hash,
'inherited_dependencies': inherited_dependencies,
}
git_url = GIT_REPO
git_ref = api.buildbucket.gitiles_commit.ref
if 'git_url' in api.properties and 'git_ref' in api.properties:
git_url = api.properties['git_url']
git_ref = api.properties['git_ref']
drone_props['git_url'] = git_url
if not git_ref:
drone_props['git_ref'] = 'refs/heads/master'
else:
drone_props['git_ref'] = git_ref
req = api.buildbucket.schedule_request(
swarming_parent_run_id=api.swarming.task_id,
builder='Linux Web Drone',
properties=drone_props,
priority=25,
exe_cipd_version=api.properties.get('exe_cipd_version', 'refs/heads/main')
)
reqs.append(req)
def GenTests(api):
browser_yaml_file = {
'required_driver_version': {'chrome': 84},
'chrome': {'Linux': '768968', 'Mac': '768985', 'Win': '768975'}
}
yield api.test('linux-post-submit') + api.properties(
goma_jobs='200'
) + api.platform('linux', 64) + api.runtime(is_experimental=False)
yield api.test(
'windows-post-submit',
api.step_data(
'read browser lock yaml.parse', api.json.output(browser_yaml_file)
), api.properties(goma_jobs='200'), api.platform('win', 64)
) + api.runtime(is_experimental=False)
yield api.test(
'mac-post-submit-with-xcode',
api.properties(goma_jobs='200', **{'$flutter/osx_sdk': {
'sdk_version': 'deadbeef', 'toolchain_ver': '123abc'
}}), api.platform('mac', 64)
) + api.runtime(is_experimental=False)
yield api.test(
'mac-post-submit',
api.properties(goma_jobs='200'), api.platform('mac', 64)
) + api.runtime(is_experimental=False)
yield api.test('linux-pre-submit') + api.properties(
goma_jobs='200',
git_url='https://mygitrepo',
git_ref='refs/pull/1/head',
clobber=True
) + api.platform('linux', 64) + api.runtime(is_experimental=False)