blob: 06c6d3484897ff92570657e92c61e97348e602ee [file] [log] [blame]
# 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.
import json
DEPS = [
'isolate',
'chromium_checkout',
'chromium_swarming',
'code_coverage',
'depot_tools/gclient',
'recipe_engine/buildbucket',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/properties',
'recipe_engine/python',
'recipe_engine/raw_io',
'recipe_engine/runtime',
'recipe_engine/step',
'swarming_client',
'test_utils',
]
from recipe_engine.recipe_api import Property
from recipe_engine import post_process
PROPERTIES = {
'platforms': Property(default=('win',)),
'custom_trigger_script': Property(default=False),
'show_outputs_ref_in_collect_step': Property(default=True),
'gtest_task': Property(default=False),
'isolated_script_task': Property(default=False),
'merge': Property(default=None),
'trigger_script': Property(default=None),
'named_caches': Property(default=None),
'service_account': Property(default=None),
'wait_for_tasks': Property(default=None),
}
def RunSteps(api, platforms, custom_trigger_script,
show_outputs_ref_in_collect_step, gtest_task, isolated_script_task,
merge, trigger_script, named_caches, service_account,
wait_for_tasks):
# Checkout swarming client.
api.swarming_client.checkout('master')
api.gclient.set_config('chromium')
api.chromium_checkout.ensure_checkout()
# Ensure swarming_client version is fresh enough.
api.chromium_swarming.check_client_version(step_test_data=(0, 8, 6))
# Configure isolate & swarming modules (this is optional).
api.isolate.isolate_server = 'https://isolateserver-dev.appspot.com'
api.chromium_swarming.swarming_server = (
'https://chromium-swarm-dev.appspot.com')
api.chromium_swarming.add_default_tag('master:tryserver')
api.chromium_swarming.default_expiration = 60*60
api.chromium_swarming.default_hard_timeout = 60*60
api.chromium_swarming.default_io_timeout = 20*60
api.chromium_swarming.default_idempotent = True
api.chromium_swarming.default_priority = 30
api.chromium_swarming.default_user = 'joe'
api.chromium_swarming.set_default_env('TESTING', '1')
api.chromium_swarming.verbose = True
api.chromium_swarming.task_output_stdout = 'json'
api.chromium_swarming.service_account_json = (
'/creds/service_accounts/service-account-chromium-builder.json')
api.chromium_swarming.set_default_dimension('inexistent', None)
api.chromium_swarming.show_outputs_ref_in_collect_step = (
show_outputs_ref_in_collect_step)
try:
# Testing ReadOnlyDict.__setattr__() coverage.
api.chromium_swarming.default_dimensions['invalid'] = 'foo'
except TypeError:
pass
try:
api.chromium_swarming.default_env['invalid'] = 'foo'
except TypeError:
pass
# Create a temp dir to put *.isolated files into.
temp_dir = api.path.mkdtemp('hello_isolated_world')
# Prepare a bunch of swarming tasks to run hello_world on multiple platforms.
tasks = []
for platform in platforms:
# Isolate example hello_world.isolate from swarming client repo.
# TODO(vadimsh): Add a thin wrapper around isolate to 'isolate' module?
step_result = api.step(
'archive for %s' % platform, [
'tools/luci-go/isolate',
'archive',
'-isolate',
api.swarming_client.path.join('example', 'payload',
'hello_world.isolate'),
'-verbose',
],
stdout=api.raw_io.output_text())
# TODO(vadimsh): Pass result from isolate though --output-json option.
isolated = step_result.stdout.split()[0].strip()
# Create a task to run the isolated file on swarming, set OS dimension.
# Also generate code coverage for multi-shard case by triggering multiple
# shards on Linux.
if gtest_task:
task = api.chromium_swarming.gtest_task(
name='hello_world', isolated=isolated,
task_output_dir=temp_dir.join('task_output_dir'),
merge=merge)
elif isolated_script_task:
task = api.chromium_swarming.isolated_script_task()
task_request = task.request
task_slice = task_request[0]
task_slice = (task_slice.with_isolated(isolated).
with_env_vars(**{'IS_GTEST': '', 'IS_SCRIPTTEST': 'True'}))
task.request = (task_request.with_slice(0, task_slice).
with_name('hello_world'))
task.task_output_dir = temp_dir.join('task_output_dir')
if merge:
task.merge = merge
task.trigger_script = trigger_script
else:
task = api.chromium_swarming.task(name='hello_world', isolated=isolated,
task_output_dir=temp_dir.join('task_output_dir'),
named_caches=named_caches,
service_account=service_account,
cipd_packages=[
('', 'cool/package', 'vers'),
])
if platform == 'linux':
task.shards = 2
task.shard_indices = range(task.shards)
elif platform == 'mac':
task.shards = 3
task.shard_indices = [1]
else:
task.shards = 1
task.shard_indices = [0]
if custom_trigger_script:
task.trigger_script = {'script': 'custom_trigger.py'}
task_request = task.request
task_slice = task_request[0]
task_dimensions = task_slice.dimensions
task_dimensions['os'] = api.chromium_swarming.prefered_os_dimension(
platform)
task.tags.add('os:' + platform)
ensure_file = task_slice.cipd_ensure_file
if api.swarming_client.get_script_version('swarming.py') >= (0, 8, 6):
ensure_file.add_package('super/awesome/pkg', 'git_revision:deadbeef',
'bin')
task_slice = (task_slice.with_dimensions(**task_dimensions).
with_cipd_ensure_file(ensure_file))
task.request = task_request.with_slice(0, task_slice)
tasks.append(task)
# Launch all tasks.
for task in tasks:
step_results = api.chromium_swarming.trigger_task(task)
for step_result in step_results:
assert len(task.get_task_shard_output_dirs()) == len(task.shard_indices)
# Recipe can do something useful here locally while tasks are
# running on swarming.
api.step('local step', ['echo', 'running something locally'])
if wait_for_tasks:
task_ids = [
task.get_task_ids() for task in tasks
]
api.chromium_swarming.wait_for_finished_task_set(task_ids)
api.chromium_swarming.wait_for_finished_task_set(task_ids)
return
# Wait for all tasks to complete.
for task in tasks:
step_result = api.chromium_swarming.collect_task(task)
api.chromium_swarming.report_stats()
# Cleanup.
api.file.rmtree('remove temp dir', temp_dir)
def GenTests(api):
yield api.test(
'basic',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'archive for linux',
stdout=api.raw_io.output_text('hash_for_linux hello_world.isolated')),
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
api.properties(platforms=('win', 'linux', 'mac')),
)
yield api.test(
'custom_trigger_script',
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
api.properties(platforms=('mac',), custom_trigger_script=True),
api.post_process(post_process.StepCommandContains,
'[trigger] hello_world on Mac-10.13',
['--shard-index', '1']),
api.post_process(post_process.StepCommandContains,
'[trigger] hello_world on Mac-10.13', ['--shards', '3']),
api.post_process(post_process.DropExpectation),
)
yield api.test(
'default_trigger_script',
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
api.properties(platforms=('mac',), custom_trigger_script=False),
api.post_process(post_process.StepCommandContains,
'[trigger] hello_world on Mac-10.13',
['--env', 'GTEST_SHARD_INDEX', '1']),
api.post_process(post_process.StepCommandContains,
'[trigger] hello_world on Mac-10.13',
['--env', 'GTEST_TOTAL_SHARDS', '3']),
api.post_process(post_process.DropExpectation),
)
yield api.test(
'wait_for_tasks',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'archive for linux',
stdout=api.raw_io.output_text('hash_for_linux hello_world.isolated')),
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
# This is probably how you'd use the test api; testing what happens if one
# set of tasks finishes first. This example code doesn't care what is
# returned, but calling code of this usually does.
api.chromium_swarming.wait_for_finished_task_set([
([['110000', '110100']], 1),
([['100000'], ['130000']], 1),
]),
api.properties(platforms=('win', 'linux', 'mac'), wait_for_tasks=True),
api.post_process(
post_process.Filter('wait for tasks', 'wait for tasks (2)')),
)
for exp in [True, False]:
yield api.test(
'basic_luci' + ('_experimental' if exp else ''),
api.runtime(is_luci=True, is_experimental=exp),
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'archive for linux',
stdout=api.raw_io.output_text(
'hash_for_linux hello_world.isolated')),
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
api.properties(platforms=('win', 'linux', 'mac')),
)
yield api.test(
'named_caches',
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
api.properties(
platforms=('mac',),
named_caches={
'foo': 'cache/foo',
'bar': 'cache/bar'
}),
)
yield api.test(
'service_account',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'archive for linux',
stdout=api.raw_io.output_text('hash_for_linux hello_world.isolated')),
api.step_data(
'archive for mac',
stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')),
api.properties(
platforms=(
'win',
'linux',
'mac',
),
service_account='test@example.com'),
)
yield api.test(
'gerrit_trybot',
api.buildbucket.try_build(
project='chromium',
builder='linux',
build_number=1,
git_repo='https://chromium.googlesource.com/chromium/src'),
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
)
yield api.test(
'show_outputs_ref_in_collect_step',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.properties(show_outputs_ref_in_collect_step=False),
)
data = {
'shards': [
{
'': '',
}
]
}
yield api.test(
'gtest_with_outputs_ref',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.canned_summary_output(
api.test_utils.canned_gtest_output(True))),
)
data = {
'shards': [
{
'duration': 120.0,
'state': 'COMPLETED',
}
]
}
yield api.test(
'gtest_with_duration',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.summary(
api.test_utils.canned_gtest_output(True), data)),
)
data = api.chromium_swarming.canned_summary_output_raw(shards=4)
data['shards'][2]['completed_ts'] = '2014-09-25T01:49:23.123'
data['shards'][3]['completed_ts'] = '2014-09-25T01:48:22.345'
yield api.test(
'gtest_with_long_task',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.summary(
api.test_utils.canned_gtest_output(True), data)),
)
yield api.test(
'gtest_with_many_failures',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.canned_summary_output(
api.test_utils.simulated_gtest_output(
failed_test_names=['test-%d' % i for i in xrange(100)]))),
api.properties(gtest_task=True),
)
data = {
'shards': [
{
'abandoned_ts': '2014-09-25T01:41:00.123',
'bot_id': 'vm30',
'completed_ts': None,
'created_ts': '2014-09-25T01:41:00.123',
'duration': 60,
'failure': False,
'id': '148aa78d7aa0100',
'internal_failure': False,
'outputs_ref': None,
'modified_ts': '2014-09-25 01:42:00',
'name': 'heartbeat-canary-2014-09-25_01:41:55-os=Windows',
'outputs': [],
'started_ts': '2014-09-25T01:42:11.123',
'state': 'EXPIRED',
'try_number': None,
'user': 'unknown',
}
],
}
data['shards'][0]['state'] = 'EXPIRED'
yield api.test(
'swarming_expired_new',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data('hello_world on Windows-7-SP1',
api.chromium_swarming.summary(None, data)),
)
yield api.test(
'isolated_script_expired_new',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data('hello_world on Windows-7-SP1',
api.raw_io.output_dir({
'summary.json': json.dumps(data)
})),
api.properties(isolated_script_task=True),
)
data['shards'][0]['state'] = 'TIMED_OUT'
yield api.test(
'swarming_timeout_new',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data('hello_world on Windows-7-SP1',
api.chromium_swarming.summary(None, data)),
)
yield api.test(
'isolated_script_timeout_new',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data('hello_world on Windows-7-SP1',
api.raw_io.output_dir({
'summary.json': json.dumps(data)
})),
api.properties(isolated_script_task=True),
)
data['shards'][0]['state'] = 'COMPLETED'
data['shards'][0]['exit_code'] = '1'
yield api.test(
'isolated_script_non_zero_exit_status',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(data)
}), data)),
api.properties(isolated_script_task=True),
)
data['shards'][0]['state'] = 'TIMED_OUT'
del data['shards'][0]['exit_code']
big_output_dir = {'summary.json': json.dumps(data)}
for i, shard_data in enumerate(
api.test_utils.generate_simplified_json_results(range(4), True, True)):
big_output_dir['%s/output.json' % i] = json.dumps(shard_data)
# Will cause unicode decode error if tried to decode.
big_output_dir['0/binary.png'] = '\x00\x00\x89'
big_output_dir['0/invalid.txt'] = '\x00\x00\x89'
# Large text file
big_output_dir['0/big_text.txt'] = 'lots of text\n' * 2000
yield api.test(
'isolated_large_outdir',
api.step_data(
'archive for win',
stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')),
api.step_data('hello_world on Windows-7-SP1',
api.raw_io.output_dir(big_output_dir)),
api.properties(isolated_script_task=True),
)
summary_data = {
'shards': [
{
'state': 'COMPLETED',
'internal_failure': False,
}
]
}
json_results = {
'interrupted': False,
'version': 3,
'path_delimiter': '/',
'seconds_since_epoch': 0,
'tests': {},
'num_failures_by_type': {},
'links': {'custom_link': 'http://example.com'}
}
yield api.test(
'isolated_script_with_custom_merge',
api.step_data(
'archive for win',
stdout=api.raw_io.output('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(summary_data)
}) + api.json.output(json_results), summary_data)),
api.properties(
isolated_script_task=True,
merge={
'script': '//fake_custom_merge_script.py',
}),
)
yield api.test(
'isolated_script_with_custom_trigger_script',
api.step_data(
'archive for win',
stdout=api.raw_io.output('hash_for_win hello_world.isolated')),
api.step_data(
'hello_world on Windows-7-SP1',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(summary_data)
}) + api.json.output(json_results), summary_data)),
api.properties(
isolated_script_task=True,
trigger_script={
'script': '//fake_custom_trigger_script.py',
'args': ['foo', 'bar'],
}),
api.post_process(
post_process.Filter('[trigger] hello_world on Windows-7-SP1')),
)
summary_data = {
'shards': [
None,
{
'state': 'COMPLETED',
'internal_failure': False,
},
]
}
yield api.test(
'gtest_with_null_shard',
api.step_data(
'archive for linux',
stdout=api.raw_io.output('hash_for_linux hello_world.isolated')),
api.step_data(
'hello_world',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(summary_data)
}) + api.test_utils.canned_gtest_output(False), summary_data)),
api.properties(
platforms=('linux',),
gtest_task=True),
)
yield api.test(
'isolated_script_with_null_shard',
api.step_data(
'archive for linux',
stdout=api.raw_io.output('hash_for_linux hello_world.isolated')),
api.step_data(
'hello_world',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(summary_data)
}), summary_data)),
api.properties(platforms=('linux',), isolated_script_task=True),
)
yield api.test(
'coverage_gtest_with_null_shard',
api.m.code_coverage(use_clang_coverage=True),
api.step_data(
'archive for linux',
stdout=api.raw_io.output('hash_for_linux hello_world.isolated')),
api.step_data(
'hello_world',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(summary_data)
}) + api.test_utils.canned_gtest_output(False), summary_data)),
api.properties(platforms=('linux',), gtest_task=True),
api.post_process(post_process.StepFailure, 'hello_world'),
api.post_process(post_process.DropExpectation))
summary_data_deduped = {
'shards': [
{
'state': 'COMPLETED',
'internal_failure': False,
},
{
'state': 'COMPLETED',
'internal_failure': False,
'deduped_from': None,
},
{
'state': 'COMPLETED',
'internal_failure': False,
'deduped_from': 'deadbeef',
},
]
}
yield api.test(
'gtest_with_deduped_shard',
api.step_data(
'archive for linux',
stdout=api.raw_io.output('hash_for_linux hello_world.isolated')),
api.step_data(
'hello_world',
api.chromium_swarming.summary(
api.raw_io.output_dir({
'summary.json': json.dumps(summary_data_deduped)
}) + api.test_utils.canned_gtest_output(False),
summary_data_deduped)),
api.properties(
platforms=('linux',),
gtest_task=True),
)
missing_duration_data = api.chromium_swarming.canned_summary_output_raw()
del missing_duration_data['shards'][0]['duration']
yield api.test(
'missing_duration',
api.step_data(
'archive for linux',
stdout=api.raw_io.output('hash_for_linux hello_world.isolated')),
api.step_data(
'hello_world',
api.chromium_swarming.summary(
api.test_utils.canned_gtest_output(True), missing_duration_data)),
api.properties(
platforms=('linux',),
gtest_task=True), api.post_process(post_process.StatusSuccess),
api.post_process(post_process.DropExpectation))