Beispiel #1
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",
            )
Beispiel #2
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)
Beispiel #3
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)
Beispiel #4
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
Beispiel #5
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
Beispiel #6
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, {})
Beispiel #7
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()))
Beispiel #8
0
 def test_transitive_closure_trees(self):
     "transitive closure of a tree, at two non-root nodes, is the two subtrees"
     self.assertEqual(
         self.tree.transitive_closure({"b", "c"}),
         Graph(
             {"b", "c", "d", "e", "f", "g"},
             {
                 ("b", "d", "K"),
                 ("b", "e", "K"),
                 ("c", "f", "N"),
                 ("c", "g", "N"),
             },
         ),
     )
Beispiel #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)
Beispiel #10
0
 def test_transitive_closure_disjoint_edges(self):
     "transitive closure of a disjoint graph keeps those edges"
     self.assertEqual(
         self.disjoint.transitive_closure({"3", "β"}),
         Graph(
             {"1", "2", "3", "β", "γ"},
             {
                 ("2", "1", "red"),
                 ("3", "1", "red"),
                 ("3", "2", "green"),
                 ("β", "γ", "κόκκινο"),
             },
         ),
     )
Beispiel #11
0
 def test_transitive_closure_multi_edges(self):
     "transitive closure of a tree with multiple edges between nodes keeps those edges"
     self.assertEqual(
         self.multi_edges.transitive_closure({"3"}),
         Graph(
             {"1", "2", "3"},
             {
                 ("2", "1", "red"),
                 ("2", "1", "blue"),
                 ("3", "1", "red"),
                 ("3", "2", "blue"),
                 ("3", "2", "green"),
             },
         ),
     )
Beispiel #12
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"])
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)
Beispiel #14
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
                    },
                },
            },
        )
Beispiel #15
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
Beispiel #16
0
 def test_visit_postorder_empty(self):
     "postorder visit of an empty graph is empty"
     self.assertEqual(list(Graph(set(), set()).visit_postorder()), [])
Beispiel #17
0
 def test_transitive_closure_empty(self):
     "transitive closure of an empty set is an empty graph"
     g = Graph({"a", "b", "c"}, {("a", "b", "L"), ("a", "c", "L")})
     self.assertEqual(g.transitive_closure(set()), Graph(set(), set()))
Beispiel #18
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))
Beispiel #19
0
 def test_transitive_closure_disjoint(self):
     "transitive closure of a disjoint set is a subset"
     g = Graph({"a", "b", "c"}, set())
     self.assertEqual(g.transitive_closure({"a", "c"}),
                      Graph({"a", "c"}, set()))
Beispiel #20
0
class TestGraph(unittest.TestCase):

    tree = Graph(
        {"a", "b", "c", "d", "e", "f", "g"},
        {
            ("a", "b", "L"),
            ("a", "c", "L"),
            ("b", "d", "K"),
            ("b", "e", "K"),
            ("c", "f", "N"),
            ("c", "g", "N"),
        },
    )

    linear = Graph(
        {"1", "2", "3", "4"},
        {
            ("1", "2", "L"),
            ("2", "3", "L"),
            ("3", "4", "L"),
        },
    )

    diamonds = Graph(
        {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
        {
            tuple(x)
            for x in
            "AFL ADL BDL BEL CEL CHL DFL DGL EGL EHL FIL GIL GJL HJL".split()
        },
    )

    multi_edges = Graph(
        {"1", "2", "3", "4"},
        {
            ("2", "1", "red"),
            ("2", "1", "blue"),
            ("3", "1", "red"),
            ("3", "2", "blue"),
            ("3", "2", "green"),
            ("4", "3", "green"),
        },
    )

    disjoint = Graph(
        {"1", "2", "3", "4", "α", "β", "γ"},
        {
            ("2", "1", "red"),
            ("3", "1", "red"),
            ("3", "2", "green"),
            ("4", "3", "green"),
            ("α", "β", "πράσινο"),
            ("β", "γ", "κόκκινο"),
            ("α", "γ", "μπλε"),
        },
    )

    def test_transitive_closure_empty(self):
        "transitive closure of an empty set is an empty graph"
        g = Graph({"a", "b", "c"}, {("a", "b", "L"), ("a", "c", "L")})
        self.assertEqual(g.transitive_closure(set()), Graph(set(), set()))

    def test_transitive_closure_disjoint(self):
        "transitive closure of a disjoint set is a subset"
        g = Graph({"a", "b", "c"}, set())
        self.assertEqual(g.transitive_closure({"a", "c"}),
                         Graph({"a", "c"}, set()))

    def test_transitive_closure_trees(self):
        "transitive closure of a tree, at two non-root nodes, is the two subtrees"
        self.assertEqual(
            self.tree.transitive_closure({"b", "c"}),
            Graph(
                {"b", "c", "d", "e", "f", "g"},
                {
                    ("b", "d", "K"),
                    ("b", "e", "K"),
                    ("c", "f", "N"),
                    ("c", "g", "N"),
                },
            ),
        )

    def test_transitive_closure_multi_edges(self):
        "transitive closure of a tree with multiple edges between nodes keeps those edges"
        self.assertEqual(
            self.multi_edges.transitive_closure({"3"}),
            Graph(
                {"1", "2", "3"},
                {
                    ("2", "1", "red"),
                    ("2", "1", "blue"),
                    ("3", "1", "red"),
                    ("3", "2", "blue"),
                    ("3", "2", "green"),
                },
            ),
        )

    def test_transitive_closure_disjoint_edges(self):
        "transitive closure of a disjoint graph keeps those edges"
        self.assertEqual(
            self.disjoint.transitive_closure({"3", "β"}),
            Graph(
                {"1", "2", "3", "β", "γ"},
                {
                    ("2", "1", "red"),
                    ("3", "1", "red"),
                    ("3", "2", "green"),
                    ("β", "γ", "κόκκινο"),
                },
            ),
        )

    def test_transitive_closure_linear(self):
        "transitive closure of a linear graph includes all nodes in the line"
        self.assertEqual(self.linear.transitive_closure({"1"}), self.linear)

    def test_visit_postorder_empty(self):
        "postorder visit of an empty graph is empty"
        self.assertEqual(list(Graph(set(), set()).visit_postorder()), [])

    def assert_postorder(self, seq, all_nodes):
        seen = set()
        for e in seq:
            for l, r, n in self.tree.edges:
                if l == e:
                    self.assertTrue(r in seen)
            seen.add(e)
        self.assertEqual(seen, all_nodes)

    def test_visit_postorder_tree(self):
        "postorder visit of a tree satisfies invariant"
        self.assert_postorder(self.tree.visit_postorder(), self.tree.nodes)

    def test_visit_postorder_diamonds(self):
        "postorder visit of a graph full of diamonds satisfies invariant"
        self.assert_postorder(self.diamonds.visit_postorder(),
                              self.diamonds.nodes)

    def test_visit_postorder_multi_edges(self):
        "postorder visit of a graph with duplicate edges satisfies invariant"
        self.assert_postorder(self.multi_edges.visit_postorder(),
                              self.multi_edges.nodes)

    def test_visit_postorder_disjoint(self):
        "postorder visit of a disjoint graph satisfies invariant"
        self.assert_postorder(self.disjoint.visit_postorder(),
                              self.disjoint.nodes)

    def assert_preorder(self, seq, all_nodes):
        seen = set()
        for e in seq:
            for l, r, n in self.tree.edges:
                if r == e:
                    self.assertTrue(l in seen)
            seen.add(e)
        self.assertEqual(seen, all_nodes)

    def test_visit_preorder_tree(self):
        "preorder visit of a tree satisfies invariant"
        self.assert_preorder(self.tree.visit_preorder(), self.tree.nodes)

    def test_visit_preorder_diamonds(self):
        "preorder visit of a graph full of diamonds satisfies invariant"
        self.assert_preorder(self.diamonds.visit_preorder(),
                             self.diamonds.nodes)

    def test_visit_preorder_multi_edges(self):
        "preorder visit of a graph with duplicate edges satisfies invariant"
        self.assert_preorder(self.multi_edges.visit_preorder(),
                             self.multi_edges.nodes)

    def test_visit_preorder_disjoint(self):
        "preorder visit of a disjoint graph satisfies invariant"
        self.assert_preorder(self.disjoint.visit_preorder(),
                             self.disjoint.nodes)

    def test_links_dict(self):
        "link dict for a graph with multiple edges is correct"
        self.assertEqual(
            self.multi_edges.links_dict(),
            {
                "2": {"1"},
                "3": {"1", "2"},
                "4": {"3"},
            },
        )

    def test_named_links_dict(self):
        "named link dict for a graph with multiple edges is correct"
        self.assertEqual(
            self.multi_edges.named_links_dict(),
            {
                "2": dict(red="1", blue="1"),
                "3": dict(red="1", blue="2", green="2"),
                "4": dict(green="3"),
            },
        )

    def test_reverse_links_dict(self):
        "reverse link dict for a graph with multiple edges is correct"
        self.assertEqual(
            self.multi_edges.reverse_links_dict(),
            {
                "1": {"2", "3"},
                "2": {"3"},
                "3": {"4"},
            },
        )