Bug 1379163: Implement the "Run Missing Tests" action; r=armenzg,bstack
authorDustin J. Mitchell <dustin@mozilla.com>
Wed, 19 Jul 2017 21:54:13 +0000
changeset 370655 aff22b63df7a936f2f3f94ac86ad78d43123f12f
parent 370654 2e143d567065e0ae90fa11268225b6b5572bdedd
child 370656 03896a3c7a68c0672e1fda121e357f9bc7511e62
push id32231
push usercbook@mozilla.com
push dateTue, 25 Jul 2017 12:20:10 +0000
treeherdermozilla-central@80394cbcae0f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarmenzg, bstack
bugs1379163
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1379163: Implement the "Run Missing Tests" action; r=armenzg,bstack This implements an action to run all test tasks which were optimized out by the decision task. MozReview-Commit-ID: qPflBlxMg7
taskcluster/actions/run_missing_tests.py
taskcluster/actions/util.py
new file mode 100644
--- /dev/null
+++ b/taskcluster/actions/run_missing_tests.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import logging
+from slugid import nice as slugid
+
+from .registry import register_callback_action
+from actions.util import create_task, find_decision_task
+from taskgraph.util.taskcluster import get_artifact
+from taskgraph.util.parameterization import resolve_task_references
+from taskgraph.taskgraph import TaskGraph
+
+logger = logging.getLogger(__name__)
+
+
+@register_callback_action(
+    name='run-missing-tests',
+    title='Run Missing Tests',
+    symbol='rmt',
+    description="""
+    Run tests in the selected push that were optimized away, usually by SETA.
+
+    This action is for use on pushes that will be merged into another branch,
+    to check that optimization hasn't hidden any failures.
+    """,
+    order=100,  # Useful for sheriffs, but not top of the list
+    context=[],  # Applies to any task
+    schema={},
+)
+def run_missing_tests(parameters, input, task_group_id, task_id, task):
+    decision_task_id = find_decision_task(parameters)
+
+    full_task_graph = get_artifact(decision_task_id, "public/full-task-graph.json")
+    _, full_task_graph = TaskGraph.from_json(full_task_graph)
+    target_tasks = get_artifact(decision_task_id, "public/target-tasks.json")
+    label_to_taskid = get_artifact(decision_task_id, "public/label-to-taskid.json")
+
+    # The idea here is to schedule all tasks of the `test` kind that were
+    # targetted but did not appear in the final task-graph -- those were the
+    # optimized tasks.
+    to_run = []
+    already_run = 0
+    for label in target_tasks:
+        task = full_task_graph.tasks[label]
+        if task.kind != 'test':
+            continue  # not a test
+        if label in label_to_taskid:
+            already_run += 1
+            continue
+        to_run.append(task)
+
+    for task in to_run:
+
+        # fix up the task's dependencies, similar to how optimization would
+        # have done in the decision
+        dependencies = {name: label_to_taskid[label]
+                        for name, label in task.dependencies.iteritems()}
+        task_def = resolve_task_references(task.label, task.task, dependencies)
+        task_def.setdefault('dependencies', []).extend(dependencies.itervalues())
+        create_task(slugid(), task_def)
+
+    logger.info('Out of {} test tasks, {} already existed and the action created {}'.format(
+        already_run + len(to_run), already_run, len(to_run)))
--- a/taskcluster/actions/util.py
+++ b/taskcluster/actions/util.py
@@ -3,22 +3,30 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import json
 import sys
 
 from taskgraph import create
-from taskgraph.util.taskcluster import get_session
+from taskgraph.util.taskcluster import get_session, find_task_id
 
 # this is set to true for `mach taskgraph action-callback --test`
 testing = False
 
 
+def find_decision_task(parameters):
+    """Given the parameters for this action, find the taskId of the decision
+    task"""
+    return find_task_id('gecko.v2.{}.pushlog-id.{}.decision'.format(
+        parameters['project'],
+        parameters['pushlog_id']))
+
+
 def create_task(task_id, task_def):
     """Create a new task.  The task definition will have {relative-datestamp':
     '..'} rendered just like in a decision task.  Action callbacks should use
     this function to create new tasks, as it has the additional advantage of
     allowing easy debugging with `mach taskgraph action-callback --test`."""
     if testing:
         json.dump([task_id, task_def], sys.stdout,
                   sort_keys=True, indent=4, separators=(',', ': '))