blob: 970d37637fca66b694999769466e54ae2b6204a1 [file]
# Copyright 2014 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.
from contextlib import contextmanager
from recipe_engine.post_process import DropExpectation, MustRun
from dataclasses import dataclass
from recipe_engine.recipe_api import RecipeScriptApi
from recipe_engine.recipe_test_api import RecipeTestApi
from RECIPE_MODULES.build import builder_group, reclient
from RECIPE_MODULES.depot_tools import bot_update, depot_tools, gclient
from RECIPE_MODULES.recipe_engine import (
buildbucket,
cas,
context,
file,
legacy_annotation,
path,
platform,
properties,
step,
swarming,
)
@dataclass
class DEPS(RecipeScriptApi):
bot_update: bot_update.API
buildbucket: buildbucket.API
builder_group: builder_group.API
cas: cas.API
context: context.API
depot_tools: depot_tools.API
file: file.API
gclient: gclient.API
legacy_annotation: legacy_annotation.API
path: path.API
platform: platform.API
properties: properties.API
reclient: reclient.API
step: step.API
swarming: swarming.API
@dataclass
class TEST_DEPS(RecipeTestApi):
bot_update: bot_update.TEST_API
buildbucket: buildbucket.TEST_API
builder_group: builder_group.TEST_API
context: context.TEST_API
file: file.TEST_API
gclient: gclient.TEST_API
legacy_annotation: legacy_annotation.TEST_API
path: path.TEST_API
platform: platform.TEST_API
properties: properties.TEST_API
reclient: reclient.TEST_API
step: step.TEST_API
swarming: swarming.TEST_API
# Maps from triggering builder to triggered builder for swarming.
swarming_dimensions = {
'linux_64-newlib-arm_qemu-pnacl-dbg': {
'builder': 'odroid_32-newlib-arm_hw-pnacl-dbg',
'pool': 'luci.flex.ci',
},
'linux_64-newlib-arm_qemu-pnacl-opt': {
'builder': 'odroid_32-newlib-arm_hw-pnacl-opt',
'pool': 'luci.flex.ci',
},
'linux_64-newlib-arm_qemu-pnacl-buildonly-spec': {
'builder': 'odroid_32-newlib-arm_hw-pnacl-spec',
'pool': 'luci.flex.ci',
},
'nacl-arm_opt': {
'builder': 'nacl-arm_hw_opt',
'pool': 'luci.flex.try',
},
'nacl-arm_perf': {
'builder': 'nacl-arm_hw_perf',
'pool': 'luci.flex.try',
},
}
swarming_collection_step_name = 'collect hardware tests'
class FileInfo:
def __init__(self, input_path, output_dir, is_dir):
self.input_path = input_path
self.output_dir = output_dir
self.is_dir = is_dir
def CheckoutSteps(api: DEPS):
api.gclient.set_config('nacl')
update_result = api.bot_update.ensure_checkout()
api.gclient.runhooks()
return update_result
def ExecBuildSteps(api: DEPS, source_dir, env):
with api.context(cwd=source_dir, env=env):
with api.depot_tools.on_path():
cmd = [
'vpython3',
'-u',
source_dir.joinpath('buildbot', 'buildbot_selector.py'),
]
api.legacy_annotation('annotated steps', cmd)
def AnnotatedStepsSteps(
api: DEPS, got_revision, source_dir, compiled_sources_path
):
use_reclient = api.reclient.instance
# Default environment; required by all builders.
env = {
'BETWEEN_BUILDERS': str(compiled_sources_path),
'BOT_TYPE': 'builder_bot',
'BUILDBOT_MASTERNAME': api.builder_group.for_current,
'BUILDBOT_BUILDERNAME': api.buildbucket.builder_name,
'BUILDBOT_REVISION': api.buildbucket.gitiles_commit.id,
'BUILDBOT_BUILDNUMBER': api.buildbucket.build.number,
'BUILDBOT_GOT_REVISION': got_revision,
'BUILDBOT_SLAVE_TYPE': api.properties['slavetype'],
'PYTHONPATH': str(api.repo_resource('scripts')),
}
if use_reclient:
env.update({'USE_RECLIENT': '1'})
with api.reclient.process(
'compile', '', source_dir, deps_cache_by_step=False
):
ExecBuildSteps(api, source_dir, env)
else:
ExecBuildSteps(api, source_dir, env)
def UploadFilesToCAS(api: DEPS, files):
"""Pushes files up to the RBE-CAS."""
with api.step.nest('Upload isolates'):
isolate_dir = api.path.mkdtemp('isolate_directory')
for file_info in files:
output_dir = isolate_dir / file_info.output_dir
if file_info.is_dir:
api.file.copytree(
"Copying tree: {}".format(file_info.input_path),
file_info.input_path,
output_dir,
)
return api.cas.archive('archive files', isolate_dir, isolate_dir)
def ParseSwarmingResults(api: DEPS, builder_name, results):
"""Called after swarming.collect() to produce proper step results."""
success = True
message = 'Starting execution on {}'.format(builder_name)
step = api.step(message, cmd=None)
step.presentation.logs['output'] = []
for result in results:
if result.state not in (
api.swarming.TaskState.COMPLETED,
api.swarming.TaskState.TIMED_OUT,
):
api.step.active_result.presentation.status = 'EXCEPTION'
success = False
result.analyze()
if result.state == api.swarming.TaskState.TIMED_OUT or not result.success:
step.presentation.status = 'EXCEPTION'
success = False
if result.output is not None:
for line in result.output.splitlines():
if line.startswith('@@@BUILD_STEP'):
# Cut out the '@@@BUILD_STEP' start and the '@@@' end.
step = api.step(line[14:-3], cmd=None)
step.presentation.logs['output'] = []
elif line == '@@@STEP_FAILURE@@@':
step.presentation.status = 'FAILURE'
success = False
else:
step.presentation.logs['output'].append(line)
if not success:
fail_text = 'hardware test failure on {}'.format(builder_name)
raise api.step.StepFailure(fail_text)
def TriggerHardwareTests(
api: DEPS, got_revision, source_dir, compiled_sources_path, dimensions
):
"""Triggers tests on ARM hardware bots with precompiled sources."""
# Isolate required files
isolated_files = [
FileInfo(source_dir, 'native_client', True),
FileInfo(compiled_sources_path, 'native_client/between_builders', True),
FileInfo(api.path.start_dir / 'third_party', 'third_party', True),
FileInfo(api.path.start_dir / 'testing', 'testing', True),
# The ARM bots need the linux_arm toolchain.
FileInfo(
source_dir.joinpath('toolchain', 'linux_x86'),
'native_client/toolchain/linux_arm',
True,
),
]
isolated_digest = UploadFilesToCAS(api, isolated_files)
environment_vars = {
'BUILDBOT_MASTERNAME': api.builder_group.for_current,
'BUILDBOT_BUILDERNAME': dimensions['builder'],
'BUILDBOT_SLAVE_TYPE': api.properties['slavetype'],
'PYTHONPATH': str(api.repo_resource('scripts')),
'BOT_TYPE': 'arm_hw_bot',
}
# Generate the swarming request
request = api.swarming.task_request().with_name(dimensions['builder'])
request = request.with_slice(
0,
request[0]
.with_command(['python3', 'buildbot/buildbot_selector.py'])
.with_relative_cwd('native_client')
.with_dimensions(**dimensions)
.with_env_vars(**environment_vars)
.with_cas_input_root(isolated_digest)
.with_expiration_secs(60 * 120)
.with_execution_timeout_secs(60 * 60),
)
metadata = api.swarming.trigger('Trigger hardware tests', requests=[request])
# Collect the result of the task.
output_directory = api.path.mkdtemp('swarming-output')
results = api.swarming.collect(
swarming_collection_step_name,
metadata,
output_dir=output_directory,
timeout='180m',
)
ParseSwarmingResults(api, dimensions['builder'], results)
def RunSteps(api: DEPS):
update_result = CheckoutSteps(api)
got_revision = update_result.properties['got_revision']
source_dir = update_result.source_root.path
compiled_sources_path = api.path.mkdtemp('between_builders')
AnnotatedStepsSteps(api, got_revision, source_dir, compiled_sources_path)
if api.buildbucket.builder_name in swarming_dimensions:
TriggerHardwareTests(
api,
got_revision,
source_dir,
compiled_sources_path,
swarming_dimensions[api.buildbucket.builder_name],
)
def GenTests(api: TEST_DEPS):
git_repo = (
'https://chromium.googlesource.com/native_client/src/native_client.git'
)
triggering_builder_name = 'linux_64-newlib-arm_qemu-pnacl-dbg'
yield api.test(
'linux_triggering_arm',
api.platform('linux', 64),
api.builder_group.for_current('client.nacl'),
api.buildbucket.ci_build(
builder=triggering_builder_name,
git_repo=git_repo,
revision='a' * 40,
build_number=1234,
),
api.properties(slavetype='BuilderTester'),
)
yield api.test(
'linux_triggering_failed',
api.platform('linux', 64),
api.builder_group.for_current('client.nacl'),
api.buildbucket.ci_build(
builder=triggering_builder_name,
git_repo=git_repo,
revision='a' * 40,
build_number=1234,
),
api.properties(slavetype='BuilderTester'),
api.step_data('annotated steps', api.legacy_annotation.failure_step),
api.expect_status('FAILURE'),
)
failed_output = '@@@BUILD_STEP fake_step@@@\nfake_output\n@@@STEP_FAILURE@@@'
failed_result = api.swarming.task_result(
id='0',
name=swarming_dimensions[triggering_builder_name]['builder'],
state=api.swarming.TaskState.COMPLETED,
output=failed_output,
failure=True,
)
yield api.test(
'linux_triggering_arm_with_collect_COMPLETED_and_failed',
api.platform('linux', 64),
api.builder_group.for_current('client.nacl'),
api.buildbucket.ci_build(
builder=triggering_builder_name,
git_repo=git_repo,
revision='a' * 40,
build_number=1234,
),
api.properties(slavetype='BuilderTester'),
api.override_step_data(
swarming_collection_step_name, api.swarming.collect([failed_result])
),
api.expect_status('FAILURE'),
)
timeout_result = api.swarming.task_result(
id='0',
name=swarming_dimensions[triggering_builder_name]['builder'],
state=api.swarming.TaskState.TIMED_OUT,
)
yield api.test(
'linux_triggering_arm_with_collect_TIMED_OUT',
api.platform('linux', 64),
api.builder_group.for_current('client.nacl'),
api.buildbucket.ci_build(
builder=triggering_builder_name,
git_repo=git_repo,
revision='a' * 40,
build_number=1234,
),
api.properties(slavetype='BuilderTester'),
api.override_step_data(
swarming_collection_step_name, api.swarming.collect([timeout_result])
),
api.expect_status('FAILURE'),
)
died_result = api.swarming.task_result(
id='0',
name=swarming_dimensions[triggering_builder_name]['builder'],
state=api.swarming.TaskState.BOT_DIED,
)
yield api.test(
'linux_triggering_arm_with_collect_BOT_DIED',
api.platform('linux', 64),
api.builder_group.for_current('client.nacl'),
api.buildbucket.ci_build(
builder=triggering_builder_name,
git_repo=git_repo,
revision='a' * 40,
build_number=1234,
),
api.properties(slavetype='BuilderTester'),
api.override_step_data(
swarming_collection_step_name, api.swarming.collect([died_result])
),
api.expect_status('INFRA_FAILURE'),
)
yield api.test(
'reclient_linux',
api.platform('linux', 64),
api.builder_group.for_current('client.nacl'),
api.buildbucket.ci_build(
builder='nacl-precise_64-newlib-x86_64-pnacl',
git_repo=git_repo,
revision='a' * 40,
build_number=1234,
),
api.properties(slavetype='BuilderTester'),
api.properties(
**{
'$build/reclient': {
'instance': 'fake-reclient-instance',
}
},
),
api.post_process(MustRun, 'preprocess for reclient'),
api.post_process(DropExpectation),
)