Esempio n. 1
0
    def test_round_trip(self):
        graph = TaskGraph(
            tasks={
                "a":
                Task(
                    kind="fancy",
                    label="a",
                    description="Task A",
                    attributes={},
                    dependencies={"prereq": "b"},  # must match edges, below
                    optimization={"skip-unless-has-relevant-tests": None},
                    task={"task": "def"},
                ),
                "b":
                Task(
                    kind="pre",
                    label="b",
                    attributes={},
                    dependencies={},
                    optimization={"skip-unless-has-relevant-tests": None},
                    task={"task": "def2"},
                ),
            },
            graph=Graph(nodes={"a", "b"}, edges={("a", "b", "prereq")}),
        )

        tasks, new_graph = TaskGraph.from_json(graph.to_json())
        self.assertEqual(graph, new_graph)
Esempio n. 2
0
 def inner(tasks):
     label_to_taskid = {k: k + "-tid" for k in tasks}
     for label, task_id in label_to_taskid.items():
         tasks[label].task_id = task_id
     graph = Graph(nodes=set(tasks), edges=set())
     taskgraph = TaskGraph(tasks, graph)
     return taskgraph, label_to_taskid
Esempio n. 3
0
def get_filtered_taskgraph(taskgraph, tasksregex):
    """
    Filter all the tasks on basis of a regular expression
    and returns a new TaskGraph object
    """
    from gecko_taskgraph.graph import Graph
    from gecko_taskgraph.taskgraph import TaskGraph

    # return original taskgraph if no regular expression is passed
    if not tasksregex:
        return taskgraph
    named_links_dict = taskgraph.graph.named_links_dict()
    filteredtasks = {}
    filterededges = set()
    regexprogram = re.compile(tasksregex)

    for key in taskgraph.graph.visit_postorder():
        task = taskgraph.tasks[key]
        if regexprogram.match(task.label):
            filteredtasks[key] = task
            for depname, dep in named_links_dict[key].items():
                if regexprogram.match(dep):
                    filterededges.add((key, dep, depname))
    filtered_taskgraph = TaskGraph(
        filteredtasks, Graph(set(filteredtasks), filterededges)
    )
    return filtered_taskgraph
Esempio n. 4
0
    def test_create_tasks_fails_if_create_fails(self, create_task):
        "creat_tasks fails if a single create_task call fails"
        tasks = {
            "tid-a":
            Task(kind="test",
                 label="a",
                 attributes={},
                 task={"payload": "hello world"}),
        }
        label_to_taskid = {"a": "tid-a"}
        graph = Graph(nodes={"tid-a"}, edges=set())
        taskgraph = TaskGraph(tasks, graph)

        def fail(*args):
            print("UHOH")
            raise RuntimeError("oh noes!")

        create_task.side_effect = fail

        with self.assertRaises(RuntimeError):
            create.create_tasks(
                GRAPH_CONFIG,
                taskgraph,
                label_to_taskid,
                {"level": "4"},
                decision_task_id="decisiontask",
            )
Esempio n. 5
0
    def test_create_tasks(self):
        tasks = {
            "tid-a":
            Task(kind="test",
                 label="a",
                 attributes={},
                 task={"payload": "hello world"}),
            "tid-b":
            Task(kind="test",
                 label="b",
                 attributes={},
                 task={"payload": "hello world"}),
        }
        label_to_taskid = {"a": "tid-a", "b": "tid-b"}
        graph = Graph(nodes={"tid-a", "tid-b"},
                      edges={("tid-a", "tid-b", "edge")})
        taskgraph = TaskGraph(tasks, graph)

        create.create_tasks(
            GRAPH_CONFIG,
            taskgraph,
            label_to_taskid,
            {"level": "4"},
            decision_task_id="decisiontask",
        )

        for tid, task in self.created_tasks.items():
            self.assertEqual(task["payload"], "hello world")
            self.assertEqual(task["schedulerId"], "domain-level-4")
            # make sure the dependencies exist, at least
            for depid in task.get("dependencies", []):
                if depid == "decisiontask":
                    # Don't look for decisiontask here
                    continue
                self.assertIn(depid, self.created_tasks)
Esempio n. 6
0
def make_graph(*tasks_and_edges, **kwargs):
    tasks = {t.label: t for t in tasks_and_edges if isinstance(t, Task)}
    edges = {e for e in tasks_and_edges if not isinstance(e, Task)}
    tg = TaskGraph(tasks, graph.Graph(set(tasks), edges))

    if kwargs.get("deps", True):
        # set dependencies based on edges
        for l, r, name in tg.graph.edges:
            tg.tasks[l].dependencies[name] = r

    return tg
Esempio n. 7
0
 def default_matches(self, attributes, parameters):
     method = target_tasks.get_method("default")
     graph = TaskGraph(
         tasks={
             "a": Task(kind="build",
                       label="a",
                       attributes=attributes,
                       task={}),
         },
         graph=Graph(nodes={"a"}, edges=set()),
     )
     return "a" in method(graph, parameters, {})
Esempio n. 8
0
def tg(request):
    if not hasattr(request.module, "TASKS"):
        pytest.fail(
            "'tg' fixture used from a module that didn't define the TASKS variable"
        )

    tasks = request.module.TASKS
    for task in tasks:
        task.setdefault("task", {})
        task["task"].setdefault("tags", {})

    tasks = {t["label"]: Task(**t) for t in tasks}
    return TaskGraph(tasks, Graph(tasks.keys(), set()))
Esempio n. 9
0
 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={"run_on_projects": ["try"]},
              task={}),
     }
     graph = Graph(nodes=set("abc"), edges=set())
     return TaskGraph(tasks, graph)
Esempio n. 10
0
    def test_create_task_without_dependencies(self):
        "a task with no dependencies depends on the decision task"
        tasks = {
            "tid-a":
            Task(kind="test",
                 label="a",
                 attributes={},
                 task={"payload": "hello world"}),
        }
        label_to_taskid = {"a": "tid-a"}
        graph = Graph(nodes={"tid-a"}, edges=set())
        taskgraph = TaskGraph(tasks, graph)

        create.create_tasks(
            GRAPH_CONFIG,
            taskgraph,
            label_to_taskid,
            {"level": "4"},
            decision_task_id="decisiontask",
        )

        for tid, task in self.created_tasks.items():
            self.assertEqual(task.get("dependencies"), ["decisiontask"])
Esempio n. 11
0
    def test_taskgraph_to_json(self):
        tasks = {
            "a":
            Task(
                kind="test",
                label="a",
                description="Task A",
                attributes={"attr": "a-task"},
                task={"taskdef": True},
            ),
            "b":
            Task(
                kind="test",
                label="b",
                attributes={},
                task={"task": "def"},
                optimization={"skip-unless-has-relevant-tests": None},
                # note that this dep is ignored, superseded by that
                # from the taskgraph's edges
                dependencies={"first": "a"},
            ),
        }
        graph = Graph(nodes=set("ab"), edges={("a", "b", "edgelabel")})
        taskgraph = TaskGraph(tasks, graph)

        res = taskgraph.to_json()

        self.assertEqual(
            res,
            {
                "a": {
                    "kind": "test",
                    "label": "a",
                    "description": "Task A",
                    "attributes": {
                        "attr": "a-task",
                        "kind": "test"
                    },
                    "task": {
                        "taskdef": True
                    },
                    "dependencies": {
                        "edgelabel": "b"
                    },
                    "soft_dependencies": [],
                    "if_dependencies": [],
                    "optimization": None,
                },
                "b": {
                    "kind": "test",
                    "label": "b",
                    "description": "",
                    "attributes": {
                        "kind": "test"
                    },
                    "task": {
                        "task": "def"
                    },
                    "dependencies": {},
                    "soft_dependencies": [],
                    "if_dependencies": [],
                    "optimization": {
                        "skip-unless-has-relevant-tests": None
                    },
                },
            },
        )
Esempio n. 12
0
class TestTaskGraph(unittest.TestCase):

    maxDiff = None

    def test_taskgraph_to_json(self):
        tasks = {
            "a":
            Task(
                kind="test",
                label="a",
                description="Task A",
                attributes={"attr": "a-task"},
                task={"taskdef": True},
            ),
            "b":
            Task(
                kind="test",
                label="b",
                attributes={},
                task={"task": "def"},
                optimization={"skip-unless-has-relevant-tests": None},
                # note that this dep is ignored, superseded by that
                # from the taskgraph's edges
                dependencies={"first": "a"},
            ),
        }
        graph = Graph(nodes=set("ab"), edges={("a", "b", "edgelabel")})
        taskgraph = TaskGraph(tasks, graph)

        res = taskgraph.to_json()

        self.assertEqual(
            res,
            {
                "a": {
                    "kind": "test",
                    "label": "a",
                    "description": "Task A",
                    "attributes": {
                        "attr": "a-task",
                        "kind": "test"
                    },
                    "task": {
                        "taskdef": True
                    },
                    "dependencies": {
                        "edgelabel": "b"
                    },
                    "soft_dependencies": [],
                    "if_dependencies": [],
                    "optimization": None,
                },
                "b": {
                    "kind": "test",
                    "label": "b",
                    "description": "",
                    "attributes": {
                        "kind": "test"
                    },
                    "task": {
                        "task": "def"
                    },
                    "dependencies": {},
                    "soft_dependencies": [],
                    "if_dependencies": [],
                    "optimization": {
                        "skip-unless-has-relevant-tests": None
                    },
                },
            },
        )

    def test_round_trip(self):
        graph = TaskGraph(
            tasks={
                "a":
                Task(
                    kind="fancy",
                    label="a",
                    description="Task A",
                    attributes={},
                    dependencies={"prereq": "b"},  # must match edges, below
                    optimization={"skip-unless-has-relevant-tests": None},
                    task={"task": "def"},
                ),
                "b":
                Task(
                    kind="pre",
                    label="b",
                    attributes={},
                    dependencies={},
                    optimization={"skip-unless-has-relevant-tests": None},
                    task={"task": "def2"},
                ),
            },
            graph=Graph(nodes={"a", "b"}, edges={("a", "b", "prereq")}),
        )

        tasks, new_graph = TaskGraph.from_json(graph.to_json())
        self.assertEqual(graph, new_graph)

    simple_graph = TaskGraph(
        tasks={
            "a":
            Task(
                kind="fancy",
                label="a",
                attributes={},
                dependencies={"prereq": "b"},  # must match edges, below
                optimization={"skip-unless-has-relevant-tests": None},
                task={"task": "def"},
            ),
            "b":
            Task(
                kind="pre",
                label="b",
                attributes={},
                dependencies={},
                optimization={"skip-unless-has-relevant-tests": None},
                task={"task": "def2"},
            ),
        },
        graph=Graph(nodes={"a", "b"}, edges={("a", "b", "prereq")}),
    )

    def test_contains(self):
        assert "a" in self.simple_graph
        assert "c" not in self.simple_graph
Esempio n. 13
0
def make_opt_graph(*tasks_and_edges):
    tasks = {t.task_id: t for t in tasks_and_edges if isinstance(t, Task)}
    edges = {e for e in tasks_and_edges if not isinstance(e, Task)}
    return TaskGraph(tasks, graph.Graph(set(tasks), edges))
Esempio n. 14
0
def get_subgraph(
    target_task_graph,
    removed_tasks,
    replaced_tasks,
    label_to_taskid,
    decision_task_id,
):
    """
    Return the subgraph of target_task_graph consisting only of
    non-optimized tasks and edges between them.

    To avoid losing track of taskIds for tasks optimized away, this method
    simultaneously substitutes real taskIds for task labels in the graph, and
    populates each task definition's `dependencies` key with the appropriate
    taskIds.  Task references are resolved in the process.
    """

    # check for any dependency edges from included to removed tasks
    bad_edges = [(l, r, n) for l, r, n in target_task_graph.graph.edges
                 if l not in removed_tasks and r in removed_tasks]
    if bad_edges:
        probs = ", ".join(f"{l} depends on {r} as {n} but it has been removed"
                          for l, r, n in bad_edges)
        raise Exception("Optimization error: " + probs)

    # fill in label_to_taskid for anything not removed or replaced
    assert replaced_tasks <= set(label_to_taskid)
    for label in sorted(target_task_graph.graph.nodes - removed_tasks -
                        set(label_to_taskid)):
        label_to_taskid[label] = slugid()

    # resolve labels to taskIds and populate task['dependencies']
    tasks_by_taskid = {}
    named_links_dict = target_task_graph.graph.named_links_dict()
    omit = removed_tasks | replaced_tasks
    for label, task in target_task_graph.tasks.items():
        if label in omit:
            continue
        task.task_id = label_to_taskid[label]
        named_task_dependencies = {
            name: label_to_taskid[label]
            for name, label in named_links_dict.get(label, {}).items()
        }

        # Add remaining soft dependencies
        if task.soft_dependencies:
            named_task_dependencies.update({
                label: label_to_taskid[label]
                for label in task.soft_dependencies
                if label in label_to_taskid and label not in omit
            })

        task.task = resolve_task_references(
            task.label,
            task.task,
            task_id=task.task_id,
            decision_task_id=decision_task_id,
            dependencies=named_task_dependencies,
        )
        deps = task.task.setdefault("dependencies", [])
        deps.extend(sorted(named_task_dependencies.values()))
        tasks_by_taskid[task.task_id] = task

    # resolve edges to taskIds
    edges_by_taskid = ((label_to_taskid.get(left), label_to_taskid.get(right),
                        name) for (left, right,
                                   name) in target_task_graph.graph.edges)
    # ..and drop edges that are no longer entirely in the task graph
    #   (note that this omits edges to replaced tasks, but they are still in task.dependnecies)
    edges_by_taskid = {(left, right, name)
                       for (left, right, name) in edges_by_taskid
                       if left in tasks_by_taskid and right in tasks_by_taskid}

    return TaskGraph(tasks_by_taskid,
                     Graph(set(tasks_by_taskid), edges_by_taskid))
Esempio n. 15
0
def create_tasks(
    graph_config,
    to_run,
    full_task_graph,
    label_to_taskid,
    params,
    decision_task_id,
    suffix="",
    modifier=lambda t: t,
):
    """Create new tasks.  The task definition will have {relative-datestamp':
    '..'} rendered just like in a decision task.  Action callbacks should use
    this function to create new tasks,
    allowing easy debugging with `mach taskgraph action-callback --test`.
    This builds up all required tasks to run in order to run the tasks requested.

    Optionally this function takes a `modifier` function that is passed in each
    task before it is put into a new graph. It should return a valid task. Note
    that this is passed _all_ tasks in the graph, not just the set in to_run. You
    may want to skip modifying tasks not in your to_run list.

    If `suffix` is given, then it is used to give unique names to the resulting
    artifacts.  If you call this function multiple times in the same action,
    pass a different suffix each time to avoid overwriting artifacts.

    If you wish to create the tasks in a new group, leave out decision_task_id.

    Returns an updated label_to_taskid containing the new tasks"""
    if suffix != "":
        suffix = f"-{suffix}"
    to_run = set(to_run)

    #  Copy to avoid side-effects later
    full_task_graph = copy.deepcopy(full_task_graph)
    label_to_taskid = label_to_taskid.copy()

    target_graph = full_task_graph.graph.transitive_closure(to_run)
    target_task_graph = TaskGraph(
        {l: modifier(full_task_graph[l])
         for l in target_graph.nodes}, target_graph)
    target_task_graph.for_each_task(update_parent)
    if decision_task_id and decision_task_id != os.environ.get("TASK_ID"):
        target_task_graph.for_each_task(update_dependencies)
    optimized_task_graph, label_to_taskid = optimize_task_graph(
        target_task_graph,
        to_run,
        params,
        to_run,
        decision_task_id,
        existing_tasks=label_to_taskid,
    )
    write_artifact(f"task-graph{suffix}.json", optimized_task_graph.to_json())
    write_artifact(f"label-to-taskid{suffix}.json", label_to_taskid)
    write_artifact(f"to-run{suffix}.json", list(to_run))
    create.create_tasks(
        graph_config,
        optimized_task_graph,
        label_to_taskid,
        params,
        decision_task_id,
    )
    return label_to_taskid
Esempio n. 16
0
RIDEALONG_BUILDS = {
    "linux": ["linux-ridealong"],
    "linux64": ["linux64-ridealong"],
}

GRAPH_CONFIG = {
    "try": {"ridealong-builds": RIDEALONG_BUILDS},
}

for r in RIDEALONG_BUILDS.values():
    tasks.update({k: v for k, v in [unittest_task(n + "-test", n) for n in r]})

unittest_tasks = {k: v for k, v in tasks.items() if "unittest_try_name" in v.attributes}
talos_tasks = {k: v for k, v in tasks.items() if "talos_try_name" in v.attributes}
graph_with_jobs = TaskGraph(tasks, Graph(set(tasks), set()))


class TestTryOptionSyntax(unittest.TestCase):
    def test_unknown_args(self):
        "unknown arguments are ignored"
        parameters = parse_message("try: --doubledash -z extra")
        tos = TryOptionSyntax(parameters, graph_with_jobs, GRAPH_CONFIG)
        # equilvant to "try:"..
        self.assertEqual(tos.build_types, [])
        self.assertEqual(tos.jobs, [])

    def test_apostrophe_in_message(self):
        "apostrophe does not break parsing"
        parameters = parse_message("Increase spammy log's log level. try: -b do")
        tos = TryOptionSyntax(parameters, graph_with_jobs, GRAPH_CONFIG)