Bug 1383880: parse try config during the decision task; r=ahal

This sets the try_mode property, and parses the try message (if given), early
in the decision task and puts the results into the parameters.

The proximate need is to set optimze_target_tasks for some try modes and not
others. This also replaces the existing logic for parsing messages for certain
kinds, and makes the distinction between the different try modes a little
clearer.

MozReview-Commit-ID: AXJEGLh6pEV

--HG--
extra : rebase_source : 03a10610aa3337269fe76a1196bb9b1665e1ab20
extra : source : b53ff084c2d7968a1d9864d1343f2d9381fb652b
This commit is contained in:
Dustin J. Mitchell 2017-08-21 23:14:14 +00:00
Родитель 8009d846d4
Коммит 7e1b6b079a
16 изменённых файлов: 277 добавлений и 200 удалений

Просмотреть файл

@ -21,5 +21,3 @@ jobs-from:
- linux.yml
- macosx.yml
- windows.yml
parse-commit: taskgraph.try_option_syntax:parse_message

Просмотреть файл

@ -9,5 +9,3 @@ transforms:
- taskgraph.transforms.job:transforms
- taskgraph.transforms.coalesce:transforms
- taskgraph.transforms.task:transforms
parse-commit: taskgraph.try_option_syntax:parse_message

Просмотреть файл

@ -32,12 +32,3 @@ task-graph generation infrastructure expects.
The ``transforms`` key in ``kind.yml`` is further documented in
:doc:`transforms`. For more information on how all of this works, consult the
docstrings and comments in the source code itself.
Try option syntax
-----------------
The ``parse-commit`` optional field specified in ``kind.yml`` links to a
function to parse the command line options in the ``--message`` mach parameter.
Currently, the only valid value is ``taskgraph.try_option_syntax:parse_message``.
The parsed arguments are stored in ``config.config['args']``, it corresponds
to the same object returned by ``parse_args`` from ``argparse`` Python module.

Просмотреть файл

@ -79,6 +79,22 @@ Tree Information
associated with this tree. This dictates the names of resources used in the
generated tasks, and those tasks will fail if it is incorrect.
Try Configuration
-----------------
``try_mode``
The mode in which a try push is operating. This can be one of
``"try_task_config"``, ``"try_option_syntax"``, or ``None`` meaning no try
input was provided.
``try_options``
The arguments given as try syntax (as a dictionary), or ``None`` if
``try_mode`` is not ``try_option_syntax``.
``try_task_config``
The contents of the ``try_task_config.json`` file, or ``None`` if
``try_mode`` is not ``try_task_config``.
Target Set
----------
@ -93,10 +109,6 @@ syntax or reading a project-specific configuration file).
apply. This is usually defined internally, as filters are typically
global.
``target_task_labels``
List of task labels to select. Labels not listed will be filtered out.
Enabled on try only.
``target_tasks_method``
The method to use to determine the target task set. This is the suffix of
one of the functions in ``taskcluster/taskgraph/target_tasks.py``.
@ -112,12 +124,3 @@ syntax or reading a project-specific configuration file).
partial updates for nightly releases.
Suitable contents can be generated with ``mach release-history``,
which will print to the console by default.
Morphed Set
-----------
``morph_templates``
Dict of JSON-e templates to apply to each task, keyed by template name.
Values are extra context that will be available to the template under the
``input.<template>`` key. Available templates live in
``taskcluster/taskgraph/templates``. Enabled on try only.

Просмотреть файл

@ -17,6 +17,7 @@ from .generator import TaskGraphGenerator
from .create import create_tasks
from .parameters import Parameters
from .taskgraph import TaskGraph
from .try_option_syntax import parse_message
from .actions import render_actions_json
from taskgraph.util.partials import populate_release_history
from . import GECKO
@ -36,10 +37,6 @@ ARTIFACTS_DIR = 'artifacts'
PER_PROJECT_PARAMETERS = {
'try': {
'target_tasks_method': 'try_tasks',
# Always perform optimization. This makes it difficult to use try
# pushes to run a task that would otherwise be optimized, but is a
# compromise to avoid essentially disabling optimization in try.
'optimize_target_tasks': True,
# By default, the `try_option_syntax` `target_task_method` ignores this
# parameter, and enables/disables nightlies depending whether
# `--include-nightly` is specified in the commit message.
@ -168,8 +165,6 @@ def get_decision_parameters(options):
'check_servo',
'target_tasks_method',
]
parameters['target_task_labels'] = []
parameters['morph_templates'] = {}
# owner must be an email, but sometimes (e.g., for ffxbld) it is not, in which
# case, fake it
@ -191,15 +186,6 @@ def get_decision_parameters(options):
"for this project".format(project, __file__))
parameters.update(PER_PROJECT_PARAMETERS['default'])
# morph_templates and target_task_labels are only used on try, so don't
# bother loading them elsewhere
task_config_file = os.path.join(GECKO, 'try_task_config.json')
if project == 'try' and os.path.isfile(task_config_file):
with open(task_config_file, 'r') as fh:
task_config = json.load(fh)
parameters['morph_templates'] = task_config.get('templates', {})
parameters['target_task_labels'] = task_config.get('tasks')
# `target_tasks_method` has higher precedence than `project` parameters
if options.get('target_tasks_method'):
parameters['target_tasks_method'] = options['target_tasks_method']
@ -211,6 +197,42 @@ def get_decision_parameters(options):
if 'nightly' in parameters.get('target_tasks_method', ''):
parameters['release_history'] = populate_release_history('Firefox', project)
# if try_task_config.json is present, load it
task_config_file = os.path.join(os.getcwd(), 'try_task_config.json')
# load try settings
parameters['try_mode'] = None
if os.path.isfile(task_config_file):
parameters['try_mode'] = 'try_task_config'
with open(task_config_file, 'r') as fh:
parameters['try_task_config'] = json.load(fh)
else:
parameters['try_task_config'] = None
if 'try:' in parameters['message']:
parameters['try_mode'] = 'try_option_syntax'
args = parse_message(parameters['message'])
parameters['try_options'] = args
else:
parameters['try_options'] = None
parameters['optimize_target_tasks'] = {
# The user has explicitly requested a set of jobs, so run them all
# regardless of optimization. Their dependencies can be optimized,
# though.
'try_task_config': False,
# Always perform optimization. This makes it difficult to use try
# pushes to run a task that would otherwise be optimized, but is a
# compromise to avoid essentially disabling optimization in try.
# to run tasks that would otherwise be optimized, ues try_task_config.
'try_option_syntax': True,
# since no try jobs have been specified, the standard target task will
# be applied, and tasks should be optimized out of that.
None: True,
}[parameters['try_mode']]
return Parameters(parameters)

Просмотреть файл

@ -42,12 +42,6 @@ class Kind(object):
loader = self._get_loader()
config = copy.deepcopy(self.config)
if 'parse-commit' in self.config:
parse_commit = find_object(config['parse-commit'])
config['args'] = parse_commit(parameters['message'])
else:
config['args'] = None
kind_dependencies = config.get('kind-dependencies', [])
kind_dependencies_tasks = [task for task in loaded_tasks
if task.kind in kind_dependencies]

Просмотреть файл

@ -283,8 +283,10 @@ def morph(taskgraph, label_to_taskid, parameters):
morphs = [
add_index_tasks,
add_s3_uploader_task,
apply_jsone_templates(parameters.get('morph_templates')),
]
if parameters['try_mode'] == 'try_task_config':
morphs.append(apply_jsone_templates(parameters['try_task_config'].get('templates')))
for m in morphs:
taskgraph, label_to_taskid = m(taskgraph, label_to_taskid)
return taskgraph, label_to_taskid

Просмотреть файл

@ -21,7 +21,6 @@ PARAMETER_NAMES = set([
'include_nightly',
'level',
'message',
'morph_templates',
'moz_build_date',
'optimize_target_tasks',
'owner',
@ -29,13 +28,10 @@ PARAMETER_NAMES = set([
'pushdate',
'pushlog_id',
'release_history',
'target_task_labels',
'target_tasks_method',
])
TRY_ONLY_PARAMETERS = set([
'morph_templates',
'target_task_labels',
'try_mode',
'try_options',
'try_task_config',
])
@ -45,7 +41,7 @@ class Parameters(ReadOnlyDict):
names = set(self)
msg = []
missing = PARAMETER_NAMES - TRY_ONLY_PARAMETERS - names
missing = PARAMETER_NAMES - names
if missing:
msg.append("missing parameters: " + ", ".join(missing))

Просмотреть файл

@ -6,12 +6,9 @@
from __future__ import absolute_import, print_function, unicode_literals
import os
from taskgraph import try_option_syntax
from taskgraph.util.attributes import match_run_on_projects
here = os.path.abspath(os.path.dirname(__file__))
_target_task_methods = {}
@ -53,17 +50,14 @@ def standard_filter(task, parameters):
def _try_task_config(full_task_graph, parameters):
if not parameters.get('target_task_labels'):
return []
return [t.label for t in full_task_graph.tasks.itervalues()
if t.label in parameters['target_task_labels']]
requested_tasks = parameters['try_task_config']['tasks']
return list(set(requested_tasks) & full_task_graph.graph.nodes)
def _try_option_syntax(full_task_graph, parameters):
"""Generate a list of target tasks based on try syntax in
parameters['message'] and, for context, the full task graph."""
options = try_option_syntax.TryOptionSyntax(parameters['message'], full_task_graph)
options = try_option_syntax.TryOptionSyntax(parameters, full_task_graph)
target_tasks_labels = [t.label for t in full_task_graph.tasks.itervalues()
if options.task_matches(t)]
@ -110,19 +104,23 @@ def _try_option_syntax(full_task_graph, parameters):
@_target_task('try_tasks')
def target_tasks_try(full_task_graph, parameters):
labels = _try_task_config(full_task_graph, parameters)
if 'try:' in parameters['message'] or not labels:
labels.extend(_try_option_syntax(full_task_graph, parameters))
return labels
try_mode = parameters['try_mode']
if try_mode == 'try_task_config':
return _try_task_config(full_task_graph, parameters)
elif try_mode == 'try_option_syntax':
return _try_option_syntax(full_task_graph, parameters)
else:
# With no try mode, we would like to schedule everything (following
# run_on_projects) and let optimization trim it down. But optimization
# isn't yet up to the task, so instead we use try_option_syntax with
# an empty message (which basically just schedules `-j`objs)
return _try_option_syntax(full_task_graph, parameters)
@_target_task('default')
def target_tasks_default(full_task_graph, parameters):
"""Target the tasks which have indicated they should be run on this project
via the `run_on_projects` attributes."""
return [l for l, t in full_task_graph.tasks.iteritems()
if standard_filter(t, parameters)]

Просмотреть файл

@ -12,7 +12,7 @@ import unittest
import tempfile
from taskgraph import decision
from mozunit import main
from mozunit import main, MockedOpen
class TestDecision(unittest.TestCase):
@ -44,5 +44,53 @@ class TestDecision(unittest.TestCase):
decision.ARTIFACTS_DIR = 'artifacts'
class TestGetDecisionParameters(unittest.TestCase):
def setUp(self):
self.options = {
'base_repository': 'https://hg.mozilla.org/mozilla-unified',
'head_repository': 'https://hg.mozilla.org/mozilla-central',
'head_rev': 'abcd',
'head_ref': 'ef01',
'message': '',
'project': 'mozilla-central',
'pushlog_id': 143,
'pushdate': 1503691511,
'owner': 'nobody@mozilla.com',
'level': 3,
}
def test_simple_options(self):
params = decision.get_decision_parameters(self.options)
self.assertEqual(params['pushlog_id'], 143)
self.assertEqual(params['build_date'], 1503691511)
self.assertEqual(params['moz_build_date'], '20170825200511')
self.assertEqual(params['try_mode'], None)
self.assertEqual(params['try_options'], None)
self.assertEqual(params['try_task_config'], None)
def test_no_email_owner(self):
self.options['owner'] = 'ffxbld'
params = decision.get_decision_parameters(self.options)
self.assertEqual(params['owner'], 'ffxbld@noreply.mozilla.org')
def test_try_options(self):
self.options['message'] = 'try: -b do -t all'
params = decision.get_decision_parameters(self.options)
self.assertEqual(params['try_mode'], 'try_option_syntax')
self.assertEqual(params['try_options']['build_types'], 'do')
self.assertEqual(params['try_options']['unittests'], 'all')
self.assertEqual(params['try_task_config'], None)
def test_try_task_config(self):
ttc = {'tasks': ['a', 'b'], 'templates': {}}
ttc_file = os.path.join(os.getcwd(), 'try_task_config.json')
with MockedOpen({ttc_file: json.dumps(ttc)}):
params = decision.get_decision_parameters(self.options)
self.assertEqual(params['try_mode'], 'try_task_config')
self.assertEqual(params['try_options'], None)
self.assertEqual(params['try_task_config'], ttc)
if __name__ == '__main__':
main()

Просмотреть файл

@ -59,6 +59,7 @@ class TestGenerator(unittest.TestCase):
parameters = {
'_kinds': kinds,
'target_tasks_method': 'test_method',
'try_mode': None,
}
return WithFakeKind('/root', parameters)

Просмотреть файл

@ -4,6 +4,7 @@
from __future__ import absolute_import, print_function, unicode_literals
import contextlib
import unittest
from taskgraph import target_tasks
@ -65,43 +66,56 @@ class TestTargetTasks(unittest.TestCase):
self.assertFalse(self.default_matches([], 'mozilla-inbound'))
self.assertFalse(self.default_matches([], 'baobab'))
def test_try_tasks(self):
def make_task_graph(self):
tasks = {
'a': Task(kind=None, label='a', attributes={}, task={}),
'b': Task(kind=None, label='b', attributes={'at-at': 'yep'}, task={}),
'c': Task(kind=None, label='c', attributes={}, task={}),
'c': Task(kind=None, label='c', attributes={'run_on_projects': ['try']}, task={}),
}
graph = Graph(nodes=set('abc'), edges=set())
tg = TaskGraph(tasks, graph)
method = target_tasks.get_method('try_tasks')
params = {
'message': '',
'target_task_labels': [],
}
return TaskGraph(tasks, graph)
@contextlib.contextmanager
def fake_TryOptionSyntax(self):
orig_TryOptionSyntax = try_option_syntax.TryOptionSyntax
try:
try_option_syntax.TryOptionSyntax = FakeTryOptionSyntax
# no try specifier
self.assertEqual(method(tg, params), ['b'])
# try syntax only
params['message'] = 'try: me'
self.assertEqual(method(tg, params), ['b'])
# try task config only
params['message'] = ''
params['target_task_labels'] = ['c']
self.assertEqual(method(tg, params), ['c'])
# both syntax and config
params['message'] = 'try: me'
self.assertEqual(set(method(tg, params)), set(['b', 'c']))
yield
finally:
try_option_syntax.TryOptionSyntax = orig_TryOptionSyntax
def test_just_try_it(self):
"try_mode = None runs try optoin syntax with no options"
tg = self.make_task_graph()
method = target_tasks.get_method('try_tasks')
with self.fake_TryOptionSyntax():
params = {
'try_mode': None,
'message': '',
}
self.assertEqual(method(tg, params), ['b'])
def test_try_option_syntax(self):
"try_mode = try_option_syntax uses TryOptionSyntax"
tg = self.make_task_graph()
method = target_tasks.get_method('try_tasks')
with self.fake_TryOptionSyntax():
params = {
'try_mode': 'try_option_syntax',
'message': 'try: -p all',
}
self.assertEqual(method(tg, params), ['b'])
def test_try_task_config(self):
"try_mode = try_task_config uses the try config"
tg = self.make_task_graph()
method = target_tasks.get_method('try_tasks')
params = {
'try_mode': 'try_task_config',
'try_task_config': {'tasks': ['a']},
}
self.assertEqual(method(tg, params), ['a'])
if __name__ == '__main__':
main()

Просмотреть файл

@ -6,7 +6,7 @@ from __future__ import absolute_import, print_function, unicode_literals
import unittest
from taskgraph.try_option_syntax import TryOptionSyntax
from taskgraph.try_option_syntax import TryOptionSyntax, parse_message
from taskgraph.try_option_syntax import RIDEALONG_BUILDS
from taskgraph.graph import Graph
from taskgraph.taskgraph import TaskGraph
@ -63,145 +63,136 @@ graph_with_jobs = TaskGraph(tasks, Graph(set(tasks), set()))
class TestTryOptionSyntax(unittest.TestCase):
def test_empty_message(self):
"Given an empty message, it should return an empty value"
tos = TryOptionSyntax('', graph_with_jobs)
self.assertEqual(tos.build_types, [])
self.assertEqual(tos.jobs, [])
self.assertEqual(tos.unittests, [])
self.assertEqual(tos.talos, [])
self.assertEqual(tos.platforms, [])
self.assertEqual(tos.trigger_tests, 0)
self.assertEqual(tos.talos_trigger_tests, 0)
self.assertEqual(tos.env, [])
self.assertFalse(tos.profile)
self.assertIsNone(tos.tag)
self.assertFalse(tos.no_retry)
def test_message_without_try(self):
"Given a non-try message, it should return an empty value"
tos = TryOptionSyntax('Bug 1234: frobnicte the foo', graph_with_jobs)
self.assertEqual(tos.build_types, [])
self.assertEqual(tos.jobs, [])
self.assertEqual(tos.unittests, [])
self.assertEqual(tos.talos, [])
self.assertEqual(tos.platforms, [])
self.assertEqual(tos.trigger_tests, 0)
self.assertEqual(tos.talos_trigger_tests, 0)
self.assertEqual(tos.env, [])
self.assertFalse(tos.profile)
self.assertIsNone(tos.tag)
self.assertFalse(tos.no_retry)
def test_unknown_args(self):
"unknown arguments are ignored"
tos = TryOptionSyntax('try: --doubledash -z extra', graph_with_jobs)
parameters = {'try_options': parse_message('try: --doubledash -z extra')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
# equilvant to "try:"..
self.assertEqual(tos.build_types, [])
self.assertEqual(tos.jobs, None)
def test_apostrophe_in_message(self):
"apostrophe does not break parsing"
tos = TryOptionSyntax('Increase spammy log\'s log level. try: -b do', graph_with_jobs)
parameters = {'try_options': parse_message('Increase spammy log\'s log level. try: -b do')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.build_types), ['debug', 'opt'])
def test_b_do(self):
"-b do should produce both build_types"
tos = TryOptionSyntax('try: -b do', graph_with_jobs)
parameters = {'try_options': parse_message('try: -b do')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.build_types), ['debug', 'opt'])
def test_b_d(self):
"-b d should produce build_types=['debug']"
tos = TryOptionSyntax('try: -b d', graph_with_jobs)
parameters = {'try_options': parse_message('try: -b d')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.build_types), ['debug'])
def test_b_o(self):
"-b o should produce build_types=['opt']"
tos = TryOptionSyntax('try: -b o', graph_with_jobs)
parameters = {'try_options': parse_message('try: -b o')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.build_types), ['opt'])
def test_build_o(self):
"--build o should produce build_types=['opt']"
tos = TryOptionSyntax('try: --build o', graph_with_jobs)
parameters = {'try_options': parse_message('try: --build o')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.build_types), ['opt'])
def test_b_dx(self):
"-b dx should produce build_types=['debug'], silently ignoring the x"
tos = TryOptionSyntax('try: -b dx', graph_with_jobs)
parameters = {'try_options': parse_message('try: -b dx')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.build_types), ['debug'])
def test_j_job(self):
"-j somejob sets jobs=['somejob']"
tos = TryOptionSyntax('try: -j somejob', graph_with_jobs)
parameters = {'try_options': parse_message('try: -j somejob')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.jobs), ['somejob'])
def test_j_jobs(self):
"-j job1,job2 sets jobs=['job1', 'job2']"
tos = TryOptionSyntax('try: -j job1,job2', graph_with_jobs)
parameters = {'try_options': parse_message('try: -j job1,job2')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.jobs), ['job1', 'job2'])
def test_j_all(self):
"-j all sets jobs=None"
tos = TryOptionSyntax('try: -j all', graph_with_jobs)
parameters = {'try_options': parse_message('try: -j all')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.jobs, None)
def test_j_twice(self):
"-j job1 -j job2 sets jobs=job1, job2"
tos = TryOptionSyntax('try: -j job1 -j job2', graph_with_jobs)
parameters = {'try_options': parse_message('try: -j job1 -j job2')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.jobs), sorted(['job1', 'job2']))
def test_p_all(self):
"-p all sets platforms=None"
tos = TryOptionSyntax('try: -p all', graph_with_jobs)
parameters = {'try_options': parse_message('try: -p all')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.platforms, None)
def test_p_linux(self):
"-p linux sets platforms=['linux', 'linux-l10n']"
tos = TryOptionSyntax('try: -p linux', graph_with_jobs)
parameters = {'try_options': parse_message('try: -p linux')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.platforms, ['linux', 'linux-l10n'])
def test_p_linux_win32(self):
"-p linux,win32 sets platforms=['linux', 'linux-l10n', 'win32']"
tos = TryOptionSyntax('try: -p linux,win32', graph_with_jobs)
parameters = {'try_options': parse_message('try: -p linux,win32')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.platforms), ['linux', 'linux-l10n', 'win32'])
def test_p_expands_ridealongs(self):
"-p linux,linux64 includes the RIDEALONG_BUILDS"
tos = TryOptionSyntax('try: -p linux,linux64', graph_with_jobs)
parameters = {'try_options': parse_message('try: -p linux,linux64')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
platforms = set(['linux'] + RIDEALONG_BUILDS['linux'])
platforms |= set(['linux64'] + RIDEALONG_BUILDS['linux64'])
self.assertEqual(sorted(tos.platforms), sorted(platforms))
def test_u_none(self):
"-u none sets unittests=[]"
tos = TryOptionSyntax('try: -u none', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u none')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), [])
def test_u_all(self):
"-u all sets unittests=[..whole list..]"
tos = TryOptionSyntax('try: -u all', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u all')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([{'test': t} for t in unittest_tasks]))
def test_u_single(self):
"-u mochitest-webgl sets unittests=[mochitest-webgl]"
tos = TryOptionSyntax('try: -u mochitest-webgl', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u mochitest-webgl')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([{'test': 'mochitest-webgl'}]))
def test_u_alias(self):
"-u mochitest-gl sets unittests=[mochitest-webgl]"
tos = TryOptionSyntax('try: -u mochitest-gl', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u mochitest-gl')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([{'test': 'mochitest-webgl'}]))
def test_u_multi_alias(self):
"-u e10s sets unittests=[all e10s unittests]"
tos = TryOptionSyntax('try: -u e10s', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u e10s')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': t} for t in unittest_tasks if 'e10s' in t
]))
def test_u_commas(self):
"-u mochitest-webgl,gtest sets unittests=both"
tos = TryOptionSyntax('try: -u mochitest-webgl,gtest', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u mochitest-webgl,gtest')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'mochitest-webgl'},
{'test': 'gtest'},
@ -209,21 +200,24 @@ class TestTryOptionSyntax(unittest.TestCase):
def test_u_chunks(self):
"-u gtest-3,gtest-4 selects the third and fourth chunk of gtest"
tos = TryOptionSyntax('try: -u gtest-3,gtest-4', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest-3,gtest-4')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'gtest', 'only_chunks': set('34')},
]))
def test_u_platform(self):
"-u gtest[linux] selects the linux platform for gtest"
tos = TryOptionSyntax('try: -u gtest[linux]', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest[linux]')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'gtest', 'platforms': ['linux']},
]))
def test_u_platforms(self):
"-u gtest[linux,win32] selects the linux and win32 platforms for gtest"
tos = TryOptionSyntax('try: -u gtest[linux,win32]', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest[linux,win32]')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'gtest', 'platforms': ['linux', 'win32']},
]))
@ -231,7 +225,8 @@ class TestTryOptionSyntax(unittest.TestCase):
def test_u_platforms_pretty(self):
"""-u gtest[Ubuntu] selects the linux, linux64, linux64-asan, linux64-stylo-disabled,
and linux64-stylo-sequential platforms for gtest"""
tos = TryOptionSyntax('try: -u gtest[Ubuntu]', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest[Ubuntu]')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'gtest', 'platforms': ['linux32', 'linux64', 'linux64-asan',
'linux64-stylo-disabled', 'linux64-stylo-sequential']},
@ -239,7 +234,8 @@ class TestTryOptionSyntax(unittest.TestCase):
def test_u_platforms_negated(self):
"-u gtest[-linux] selects all platforms but linux for gtest"
tos = TryOptionSyntax('try: -u gtest[-linux]', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest[-linux]')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
all_platforms = set([x.attributes['test_platform'] for x in unittest_tasks.values()])
self.assertEqual(sorted(tos.unittests[0]['platforms']), sorted(
[x for x in all_platforms if x != 'linux']
@ -247,83 +243,99 @@ class TestTryOptionSyntax(unittest.TestCase):
def test_u_platforms_negated_pretty(self):
"-u gtest[Ubuntu,-x64] selects just linux for gtest"
tos = TryOptionSyntax('try: -u gtest[Ubuntu,-x64]', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest[Ubuntu,-x64]')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'gtest', 'platforms': ['linux32']},
]))
def test_u_chunks_platforms(self):
"-u gtest-1[linux,win32] selects the linux and win32 platforms for chunk 1 of gtest"
tos = TryOptionSyntax('try: -u gtest-1[linux,win32]', graph_with_jobs)
parameters = {'try_options': parse_message('try: -u gtest-1[linux,win32]')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.unittests), sorted([
{'test': 'gtest', 'platforms': ['linux', 'win32'], 'only_chunks': set('1')},
]))
def test_t_none(self):
"-t none sets talos=[]"
tos = TryOptionSyntax('try: -t none', graph_with_jobs)
parameters = {'try_options': parse_message('try: -t none')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.talos), [])
def test_t_all(self):
"-t all sets talos=[..whole list..]"
tos = TryOptionSyntax('try: -t all', graph_with_jobs)
parameters = {'try_options': parse_message('try: -t all')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.talos), sorted([{'test': t} for t in talos_tasks]))
def test_t_single(self):
"-t mochitest-webgl sets talos=[mochitest-webgl]"
tos = TryOptionSyntax('try: -t mochitest-webgl', graph_with_jobs)
parameters = {'try_options': parse_message('try: -t mochitest-webgl')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(sorted(tos.talos), sorted([{'test': 'mochitest-webgl'}]))
# -t shares an implementation with -u, so it's not tested heavily
def test_trigger_tests(self):
"--rebuild 10 sets trigger_tests"
tos = TryOptionSyntax('try: --rebuild 10', graph_with_jobs)
parameters = {'try_options': parse_message('try: --rebuild 10')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.trigger_tests, 10)
def test_talos_trigger_tests(self):
"--rebuild-talos 10 sets talos_trigger_tests"
tos = TryOptionSyntax('try: --rebuild-talos 10', graph_with_jobs)
parameters = {'try_options': parse_message('try: --rebuild-talos 10')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.talos_trigger_tests, 10)
def test_interactive(self):
"--interactive sets interactive"
tos = TryOptionSyntax('try: --interactive', graph_with_jobs)
parameters = {'try_options': parse_message('try: --interactive')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.interactive, True)
def test_all_email(self):
"--all-emails sets notifications"
tos = TryOptionSyntax('try: --all-emails', graph_with_jobs)
parameters = {'try_options': parse_message('try: --all-emails')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.notifications, 'all')
def test_fail_email(self):
"--failure-emails sets notifications"
tos = TryOptionSyntax('try: --failure-emails', graph_with_jobs)
parameters = {'try_options': parse_message('try: --failure-emails')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.notifications, 'failure')
def test_no_email(self):
"no email settings don't set notifications"
tos = TryOptionSyntax('try:', graph_with_jobs)
parameters = {'try_options': parse_message('try:')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.notifications, None)
def test_setenv(self):
"--setenv VAR=value adds a environment variables setting to env"
tos = TryOptionSyntax('try: --setenv VAR1=value1 --setenv VAR2=value2', graph_with_jobs)
parameters = {'try_options': parse_message(
'try: --setenv VAR1=value1 --setenv VAR2=value2')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.env, ['VAR1=value1', 'VAR2=value2'])
def test_profile(self):
"--geckoProfile sets profile to true"
tos = TryOptionSyntax('try: --geckoProfile', graph_with_jobs)
parameters = {'try_options': parse_message('try: --geckoProfile')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertTrue(tos.profile)
def test_tag(self):
"--tag TAG sets tag to TAG value"
tos = TryOptionSyntax('try: --tag tagName', graph_with_jobs)
parameters = {'try_options': parse_message('try: --tag tagName')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertEqual(tos.tag, 'tagName')
def test_no_retry(self):
"--no-retry sets no_retry to true"
tos = TryOptionSyntax('try: --no-retry', graph_with_jobs)
parameters = {'try_options': parse_message('try: --no-retry')}
tos = TryOptionSyntax(parameters, graph_with_jobs)
self.assertTrue(tos.no_retry)

Просмотреть файл

@ -44,8 +44,10 @@ def set_defaults(config, jobs):
@transforms.add
def set_env(config, jobs):
"""Set extra environment variables from try command line."""
env = {}
if config.params['try_mode'] == 'try_option_syntax':
env = config.params['try_options']['env'] or {}
for job in jobs:
env = config.config['args'].env
if env:
job_env = {}
if 'worker' in job:

Просмотреть файл

@ -759,8 +759,11 @@ def set_retry_exit_status(config, tests):
@transforms.add
def set_profile(config, tests):
"""Set profiling mode for tests."""
profile = None
if config.params['try_mode'] == 'try_option_syntax':
profile = config.params['try_options']['profile']
for test in tests:
if config.config['args'].profile and test['suite'] == 'talos':
if profile and test['suite'] == 'talos':
test['mozharness']['extra-options'].append('--geckoProfile')
yield test
@ -768,8 +771,10 @@ def set_profile(config, tests):
@transforms.add
def set_tag(config, tests):
"""Set test for a specific tag."""
tag = None
if config.params['try_mode'] == 'try_option_syntax':
tag = config.params['try_options']['tag']
for test in tests:
tag = config.config['args'].tag
if tag:
test['mozharness']['extra-options'].extend(['--tag', tag])
yield test
@ -821,6 +826,7 @@ def set_worker_type(config, tests):
# during the taskcluster migration, this is a bit tortured, but it
# will get simpler eventually!
test_platform = test['test-platform']
try_options = config.params['try_options'] if config.params['try_options'] else {}
if test.get('worker-type'):
# This test already has its worker type defined, so just use that (yields below)
pass
@ -835,7 +841,7 @@ def set_worker_type(config, tests):
WINDOWS_WORKER_TYPES[test_platform.split('/')[0]][test['virtualization']]
elif test_platform.startswith('linux') or test_platform.startswith('android'):
if test.get('suite', '') == 'talos' and test['build-platform'] != 'linux64-ccov/opt':
if config.config['args'].taskcluster_worker:
if try_options.get('taskcluster_worker'):
test['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
else:
test['worker-type'] = 'buildbot-bridge/buildbot-bridge'

Просмотреть файл

@ -257,16 +257,14 @@ def parse_message(message):
# In order to run test jobs multiple times
parser.add_argument('--rebuild', dest='trigger_tests', type=int, default=1)
args, _ = parser.parse_known_args(parts)
return args
return vars(args)
class TryOptionSyntax(object):
def __init__(self, message, full_task_graph):
def __init__(self, parameters, full_task_graph):
"""
Parse a "try syntax" formatted commit message. This is the old "-b do -p
win32 -u all" format. Aliases are applied to map short names to full
names.
Apply the try options in parameters.
The resulting object has attributes:
@ -305,28 +303,22 @@ class TryOptionSyntax(object):
self.tag = None
self.no_retry = False
parts = split_try_msg(message)
if not parts:
return None
args = parse_message(message)
assert args is not None
self.jobs = self.parse_jobs(args.jobs)
self.build_types = self.parse_build_types(args.build_types, full_task_graph)
self.platforms = self.parse_platforms(args.platforms, full_task_graph)
options = parameters['try_options']
self.jobs = self.parse_jobs(options['jobs'])
self.build_types = self.parse_build_types(options['build_types'], full_task_graph)
self.platforms = self.parse_platforms(options['platforms'], full_task_graph)
self.unittests = self.parse_test_option(
"unittest_try_name", args.unittests, full_task_graph)
self.talos = self.parse_test_option("talos_try_name", args.talos, full_task_graph)
self.trigger_tests = args.trigger_tests
self.interactive = args.interactive
self.notifications = args.notifications
self.talos_trigger_tests = args.talos_trigger_tests
self.env = args.env
self.profile = args.profile
self.tag = args.tag
self.no_retry = args.no_retry
self.include_nightly = args.include_nightly
"unittest_try_name", options['unittests'], full_task_graph)
self.talos = self.parse_test_option("talos_try_name", options['talos'], full_task_graph)
self.trigger_tests = options['trigger_tests']
self.interactive = options['interactive']
self.notifications = options['notifications']
self.talos_trigger_tests = options['talos_trigger_tests']
self.env = options['env']
self.profile = options['profile']
self.tag = options['tag']
self.no_retry = options['no_retry']
self.include_nightly = options['include_nightly']
def parse_jobs(self, jobs_arg):
if not jobs_arg or jobs_arg == ['all']: