blob: 853ea46a0b1da49349ff92bbc8a41cc827454ad4 [file] [log] [blame]
# Copyright 2013 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 recipe_engine.post_process import Filter, DoesNotRun, DropExpectation
from recipe_engine.recipe_api import Property
DEPS = [
'archive',
'chromium',
'depot_tools/gclient',
'depot_tools/infra_paths',
'recipe_engine/context',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/runtime',
'recipe_engine/step',
'recipe_engine/url',
'swarming_client',
'recipe_engine/time',
'depot_tools/tryserver',
'v8',
]
PROPERTIES = {
# One of Release|Debug.
'build_config': Property(default=None, kind=str),
# Name of a gclient custom_var to set to 'True'.
'set_gclient_var': Property(default=None, kind=str),
# One of intel|arm|mips.
'target_arch': Property(default=None, kind=str),
# One of android|fuchsia|linux|mac|win.
'target_platform': Property(default=None, kind=str),
}
def RunSteps(api, build_config, set_gclient_var, target_arch, target_platform):
v8 = api.v8
bot_config = v8.update_bot_config(
v8.bot_config_by_buildername(),
build_config, target_arch, target_platform,
)
v8.apply_bot_config(bot_config)
v8.set_gclient_custom_var(set_gclient_var)
# Opt out of using gyp environment variables.
api.chromium.c.use_gyp_env = False
additional_trigger_properties = {}
test_spec = v8.EMPTY_TEST_SPEC
tests = v8.create_tests()
# Tests from V8-side test specs have precedence.
tests = v8.dedupe_tests(v8.extra_tests_from_properties(), tests)
if v8.is_pure_swarming_tester:
api.swarming_client.checkout()
# Simulate a v8 update on slim swarming testers. The revision
# property is mandatory. The commit position is required by gatekeeper.
api.step.active_result.presentation.properties['got_revision'] = (
api.properties['revision'])
api.step.active_result.presentation.properties['got_revision_cp'] = (
api.properties.get('parent_got_revision_cp'))
v8.set_up_swarming()
else:
# Make sure we don't run a non-pure swarming tester on a subdir slave.
# Subdir slaves have the name pattern 'slaveN-c3#M'.
assert '#' not in api.properties.get('bot_id', ''), (
'Can only use pure swarming testers on subdir slaves.')
if api.platform.is_win:
api.chromium.taskkill()
if v8.generate_sanitizer_coverage:
# When collecting code coverage, we need to sync to the revision that
# fits to the patch for the line numbers to match.
revision = v8.calculate_patch_base_gerrit()
update_step = v8.checkout(revision=revision, suffix='with patch base')
else:
update_step = v8.checkout()
update_properties = update_step.json.output['properties']
if update_properties.get('got_swarming_client_revision'):
additional_trigger_properties['parent_got_swarming_client_revision'] = (
update_properties['got_swarming_client_revision'])
v8.set_up_swarming()
v8.runhooks()
if v8.generate_gcov_coverage:
v8.init_gcov_coverage()
test_spec = v8.read_test_spec()
# Tests from V8-side test specs have precedence.
tests = v8.dedupe_tests(v8.extra_tests_from_test_spec(test_spec), tests)
if v8.should_build:
v8.compile(test_spec)
if v8.should_upload_build:
v8.upload_build()
v8.maybe_create_clusterfuzz_archive(update_step)
if v8.should_download_build:
v8.download_build()
if v8.should_test:
test_results = v8.runtests(tests)
v8.maybe_bisect(test_results)
if not api.tryserver.is_tryserver and test_results.is_negative:
# Let the overall build fail for failures and flakes.
raise api.step.StepFailure('Failures or flakes in build.')
if api.tryserver.is_tryserver and test_results.has_failures:
# Let tryjobs fail for failures only.
raise api.step.StepFailure('Failures in tryjob.')
if v8.generate_gcov_coverage:
v8.upload_gcov_coverage_report()
v8.maybe_trigger(test_spec=test_spec, **additional_trigger_properties)
def GenTests(api):
for mastername, _, buildername, _ in api.v8.iter_builders('v8'):
yield api.v8.test(mastername, buildername)
yield (
api.v8.test(
'client.v8.branches',
'V8 Linux - beta branch',
'branch_sync_failure',
) +
api.step_data('bot_update', retcode=1)
)
yield (
api.v8.test(
'client.v8',
'V8 Linux',
'swarming_collect_failure',
) +
api.step_data('Check', retcode=1)
)
# Minimal v8-side test spec for simulating most recipe features.
test_spec = """
{
"tests": [
{"name": "v8testing"},
{"name": "test262_variants", "test_args": ["--extra-flags=--flag"]},
],
}
""".strip()
# Simulate a tryjob triggered by the CQ for setting up different swarming
# default tags.
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'triggered_by_cq',
parent_test_spec=test_spec,
requester='commit-bot@chromium.org',
patch_project='v8',
blamelist=['dude@chromium.org'],
)
)
# Simulate a tryjob triggered by the tryserver for setting up different
# swarming default tags.
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'triggered_by_ts',
parent_test_spec=test_spec,
requester='dude@chromium.org',
patch_project='v8',
blamelist=['dude@chromium.org'],
)
)
# Test usage of test filters. They're used when the buildbucket
# job gets a property 'testfilter', which is expected to be a json list of
# test-filter strings.
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'test_filter',
parent_test_spec=test_spec,
testfilter=['mjsunit/regression/*', 'intl/foo', 'intl/bar'],
extra_flags='--trace_gc --turbo_stats',
)
)
# Test extra properties on a builder bot to ensure it triggers the tester
# with the right properties.
yield (
api.v8.test(
'tryserver.v8',
'v8_win64_rel_ng',
'test_filter_builder',
testfilter=['mjsunit/regression/*', 'intl/foo', 'intl/bar'],
extra_flags='--trace_gc --turbo_stats',
) +
api.post_process(Filter('trigger'))
)
# Test using extra flags with a bot that already uses some extra flags as
# positional argument.
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'positional_extra_flags',
parent_test_spec=test_spec,
extra_flags=['--trace_gc', '--turbo_stats'],
)
)
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'failures',
parent_test_spec=test_spec,
) +
api.override_step_data(
'Check', api.v8.output_json(has_failures=True))
)
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'flakes',
parent_test_spec=test_spec,
) +
api.override_step_data(
'Check', api.v8.output_json(has_failures=True, flakes=True))
)
def TestFailures(wrong_results, flakes):
results_suffix = "_wrong_results" if wrong_results else ""
flakes_suffix = "_flakes" if flakes else ""
return (
api.v8.test(
'client.v8',
'V8 Linux64 - internal snapshot',
'test_failures%s%s' % (results_suffix, flakes_suffix),
) +
api.override_step_data(
'Check', api.v8.output_json(
has_failures=True, wrong_results=wrong_results, flakes=flakes))
)
yield TestFailures(wrong_results=False, flakes=False)
yield TestFailures(wrong_results=False, flakes=True)
yield (
TestFailures(wrong_results=True, flakes=False) +
api.expect_exception('AssertionError')
)
yield (
api.v8.test(
'client.v8',
'V8 Linux64 - internal snapshot',
'empty_json',
) +
api.override_step_data('Check', api.json.output([])) +
api.expect_exception('AssertionError')
)
yield (
api.v8.test(
'client.v8',
'V8 Linux64 - internal snapshot',
'one_failure',
) +
api.override_step_data('Check', api.v8.one_failure())
)
yield (
api.v8.test(
'client.v8',
'V8 Linux64',
'one_failure_build_env_not_supported',
) +
api.override_step_data('Check', api.v8.one_failure()) +
api.properties(parent_build_environment=None)
)
yield (
api.v8.test(
'client.v8',
'V8 Fuzzer',
'fuzz_archive',
) +
api.step_data('Fuzz', retcode=1)
)
# Bisect over range a1, a2, a3. Assume a2 is the culprit. Steps:
# Bisect a0 -> no failures.
# Bisect a2 -> failures.
# Bisect a1 -> no failures.
# Report culprit a2.
yield (
api.v8.test(
'client.v8',
'V8 Linux - predictable',
'bisect',
) +
api.v8.fail('Check - d8') +
api.v8.fail('Bisect a2.Retry') +
api.time.step(120)
)
# The same as above, but overriding changes.
yield (
api.v8.test(
'client.v8',
'V8 Linux - predictable',
'bisect_override_changes',
) +
api.properties(
override_changes=[
{'revision': 'a1'},
{'revision': 'a2'},
{'revision': 'a3'},
],
) +
api.v8.fail('Check - d8') +
api.v8.fail('Bisect a2.Retry') +
api.time.step(120)
)
# Disable bisection, because the failing test is too long compared to the
# overall test time.
yield (
api.v8.test(
'client.v8',
'V8 Linux - predictable',
'bisect_tests_too_long',
) +
api.v8.fail('Check - d8') +
api.time.step(7)
)
# Bisect over range a1, a2, a3. Assume a2 is the culprit.
# Same as above with a swarming builder_tester.
yield (
api.v8.test(
'client.v8',
'V8 Linux - shared',
'bisect_swarming',
) +
api.v8.fail('Check') +
api.v8.fail('Bisect a2.Retry') +
api.time.step(120)
)
# Bisect over range a1, a2, a3. Assume a3 is the culprit. This is a tester
# and the build for a2 is not available. Steps:
# Bisect a0 -> no failures.
# Bisect a1 -> no failures.
# Report a2 and a3 as possible culprits.
yield (
api.v8.test(
'client.v8',
'V8 Linux64',
'bisect_tester_swarming',
) +
api.v8.fail('Check') +
api.time.step(120)
)
# Same as above with a slim swarming tester.
yield (
api.v8.test(
'client.v8',
'V8 Linux64 - custom snapshot - debug',
'slim_bisect_tester_swarming',
) +
api.v8.fail('Mjsunit') +
api.override_step_data(
'Bisect a0.gsutil download isolated json',
api.json.output({'mjsunit': '[dummy hash for bisection]'}),
) +
api.override_step_data(
'Bisect a1.gsutil download isolated json',
api.json.output({'mjsunit': '[dummy hash for bisection]'}),
) +
api.time.step(120)
)
# Same as above with a windows bot. Regression test making sure that
# the swarming hashes are searched in a windows bucket.
yield (
api.v8.test(
'client.v8',
'V8 Win32',
'bisect',
) +
api.v8.fail('Check') +
api.post_process(Filter().include_re(r'.*check build.*')) +
api.time.step(120)
)
# Disable bisection due to a recurring failure. Steps:
# Bisect a0 -> failures.
yield (
api.v8.test(
'client.v8',
'V8 Linux - predictable',
'bisect_recurring_failure',
) +
api.v8.fail('Check - d8') +
api.v8.fail('Bisect a0.Retry') +
api.time.step(120)
)
# Disable bisection due to less than two changes.
yield (
api.v8.test(
'client.v8',
'V8 Linux - predictable',
'bisect_one_change',
) +
api.v8.fail('Check - d8') +
api.url.json(
'Bisect.Fetch changes', api.v8.example_one_buildbot_change()) +
api.override_step_data(
'Bisect.Get change range',
api.v8.example_bisection_range_one_change(),
) +
api.time.step(120)
)
# Explicitly highlight slow tests not marked as slow.
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng_triggered',
'slow_tests',
parent_test_spec=test_spec,
requester='commit-bot@chromium.org',
patch_project='v8',
blamelist=['dude@chromium.org'],
) +
api.override_step_data(
'Check', api.v8.output_json(unmarked_slow_test=True))
)
# Test tryjob with named cache.
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng',
'with_cache',
requester='commit-bot@chromium.org',
blamelist=['dude@chromium.org'],
path_config='generic',
)
)
# Test using build_id (replaces buildnumber in LUCI world).
yield (
api.v8.test(
'tryserver.v8',
'v8_linux_rel_ng',
'with_build_id',
build_id='buildbucket/cr-buildbucket.appspot.com/1234567890',
)
)
# Test reading a pyl test-spec from the V8 repository. The additional test
# targets should be isolated and the tests should be executed.
test_spec = """
{
"swarming_dimensions": {
"pool": "noodle",
"gpu": "quantum",
},
"swarming_task_attrs": {
"priority": 25,
"hard_timeout": 7200,
},
"tests": [
{
"name": "mjsunit",
"variant": "sweet",
"shards": 2,
},
{
"name": "mjsunit",
"variant": "sour",
"suffix": "everything",
"test_args": ["--extra-flags", "--flag1 --flag2"],
# This tests that the default pool dimension above is overridden.
"swarming_dimensions": {"pool": "override"},
# This tests that the default priority above is overridden.
"swarming_task_attrs": {"priority": 100},
},
],
}
""".strip()
yield (
api.v8.test(
'client.v8',
'V8 Mac64',
'with_test_spec',
) +
# On the recipe side, we use api.path['checkout'].join('infra', ...),
# however due to the way path module's test_api works, this has to be
# manually expanded to path in terms of builder_cache as it is done by the
# bot_update.ensure_checkout step.
api.path.exists(api.path['builder_cache'].join(
'V8_Mac64', 'v8', 'infra', 'testing', 'builders.pyl')) +
api.override_step_data(
'read test spec',
api.v8.example_test_spec('V8 Mac64', test_spec),
) +
api.post_process(
Filter()
.include('read test spec')
.include('isolate tests')
.include_re(r'.*Mjsunit.*')
)
)
# As above but on a builder. The additional test targets should be isolated
# and the tests should be passed as properties to the triggered testers in
# the trigger step.
yield (
api.v8.test(
'client.v8',
'V8 Linux - nosnap builder',
'with_test_spec',
) +
# On the recipe side, we use api.path['checkout'].join('infra', ...),
# however due to the way path module's test_api works, this has to be
# manually expanded to path in terms of builder_cache as it is done by the
# bot_update.ensure_checkout step.
api.path.exists(api.path['builder_cache'].join(
'V8_Linux___nosnap_builder', 'v8', 'infra', 'testing',
'builders.pyl')) +
api.override_step_data(
'read test spec',
api.v8.example_test_spec('V8 Linux - nosnap', test_spec),
) +
api.post_process(Filter(
'read test spec',
'generate_build_files',
'isolate tests',
'trigger',
))
)
# As above but on a tester. The additional tests passed as property from the
# builder should be executed.
yield (
api.v8.test(
'client.v8',
'V8 Linux - nosnap',
'with_test_spec',
parent_test_spec=test_spec,
) +
api.post_process(Filter().include_re(r'.*Mjsunit.*'))
)
# Test that uploading/downloading binaries happens to/from experimental GS
# folder when running in LUCI experimental mode and that internal proxy
# builder is not triggered.
yield (
api.v8.test('client.v8', 'V8 Linux - builder', 'experimental') +
api.runtime(is_luci=False, is_experimental=True) +
api.post_process(DoesNotRun, 'trigger (2)') +
api.post_process(Filter(
'gsutil upload', 'package build', 'perf dashboard post'))
)
yield (
api.v8.test('client.v8', 'V8 Linux - presubmit', 'experimental') +
api.runtime(is_luci=True, is_experimental=True) +
api.post_process(Filter('extract build'))
)
# Test that swarming tasks scheduled from experimental builders have low prio.
yield (
api.v8.test('client.v8', 'V8 Linux', 'experimental') +
api.runtime(is_luci=True, is_experimental=True) +
api.post_process(Filter('[trigger] Check'))
)
# Test triggering CI child builders on LUCI.
yield (
api.v8.test('client.v8', 'V8 Linux - builder', 'trigger_on_luci') +
api.runtime(is_luci=True, is_experimental=False) +
api.post_process(Filter('trigger'))
)
# Test using set_gclient_var property.
yield (
api.v8.test('client.v8', 'V8 Linux - builder', 'set_gclient_var',
set_gclient_var='download_gcmole') +
api.v8.check_in_param(
'bot_update',
'--spec-path', '\'custom_vars\': {\'download_gcmole\': \'True\'}') +
api.post_process(DropExpectation)
)
# Test using source side properties. The properties we set make no sense at
# all. We merely test that they will override the properties specified on
# the infra side.
yield (
api.v8.test('client.v8', 'V8 Linux - builder', 'src_side_properties',
build_config='Debug', target_arch='arm',
target_platform='fuchsia') +
api.v8.check_in_param(
'bot_update', '--spec-path', 'target_cpu = [\'arm\', \'arm64\']') +
api.v8.check_in_param(
'bot_update', '--spec-path', 'target_os = [\'fuchsia\']') +
api.v8.check_in_any_arg('generate_build_files', 'Debug') +
api.v8.check_in_any_arg('compile', 'Debug') +
api.post_process(DropExpectation)
)
# Test uploading coverage reports is done to experimental bucket.
yield (
api.v8.test('client.v8', 'V8 Linux64 - gcov coverage', 'experimental') +
api.runtime(is_luci=True, is_experimental=True) +
api.post_process(Filter('gsutil coverage report'))
)