vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / tests / orchestrator / workflows / api / test_task_graph.py
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import pytest
17
18 from aria.orchestrator.workflows.api import task_graph, task
19
20
21 class MockTask(task.BaseTask):
22     def __init__(self):
23         super(MockTask, self).__init__(ctx={})
24
25
26 @pytest.fixture
27 def graph():
28     return task_graph.TaskGraph(name='mock-graph')
29
30
31 class TestTaskGraphTasks(object):
32
33     def test_add_task(self, graph):
34         task = MockTask()
35         add_result = graph.add_tasks(task)
36         assert add_result == [task]
37         tasks = [t for t in graph.tasks]
38         assert len(tasks) == 1
39         assert tasks[0] == task
40
41     def test_add_empty_group(self, graph):
42         result = graph.add_tasks([])
43         assert result == []
44
45     def test_add_group(self, graph):
46         tasks = [MockTask(), MockTask(), MockTask()]
47         added_tasks = graph.add_tasks(*tasks)
48         assert added_tasks == tasks
49
50     def test_add_partially_existing_group(self, graph):
51         task = MockTask()
52         graph.add_tasks(task)
53         tasks = [MockTask(), task, MockTask()]
54         added_tasks = graph.add_tasks(*tasks)
55         assert added_tasks == [tasks[0], tasks[2]]
56
57     def test_add_recursively_group(self, graph):
58         recursive_group = [MockTask(), MockTask()]
59         tasks = [MockTask(), recursive_group, MockTask()]
60         added_tasks = graph.add_tasks(tasks)
61         assert added_tasks == [tasks[0], recursive_group[0], recursive_group[1], tasks[2]]
62
63     def test_add_existing_task(self, graph):
64         task = MockTask()
65         graph.add_tasks(task)
66         # adding a task already in graph - should have no effect, and return False
67         add_result = graph.add_tasks(task)
68         assert add_result == []
69         tasks = [t for t in graph.tasks]
70         assert len(tasks) == 1
71         assert tasks[0] == task
72
73     def test_remove_task(self, graph):
74         task = MockTask()
75         other_task = MockTask()
76         graph.add_tasks(task)
77         graph.add_tasks(other_task)
78         graph.remove_tasks(other_task)
79         tasks = [t for t in graph.tasks]
80         assert len(tasks) == 1
81         assert tasks[0] == task
82
83     def test_remove_tasks_with_dependency(self, graph):
84         task = MockTask()
85         dependent_task = MockTask()
86         graph.add_tasks(task)
87         graph.add_tasks(dependent_task)
88         graph.add_dependency(dependent_task, task)
89         remove_result = graph.remove_tasks(dependent_task)
90         assert remove_result == [dependent_task]
91         tasks = [t for t in graph.tasks]
92         assert len(tasks) == 1
93         assert tasks[0] == task
94         # asserting no dependencies are left for the dependent task
95         assert len(list(graph.get_dependencies(task))) == 0
96
97     def test_remove_empty_group(self, graph):
98         result = graph.remove_tasks([])
99         assert result == []
100
101     def test_remove_group(self, graph):
102         tasks = [MockTask(), MockTask(), MockTask()]
103         graph.add_tasks(*tasks)
104         removed_tasks = graph.remove_tasks(*tasks)
105         assert removed_tasks == tasks
106
107     def test_remove_partially_existing_group(self, graph):
108         task = MockTask()
109         graph.add_tasks(task)
110         tasks = [MockTask(), task, MockTask()]
111         removed_tasks = graph.remove_tasks(*tasks)
112         assert removed_tasks == [task]
113
114     def test_remove_recursively_group(self, graph):
115         recursive_group = [MockTask(), MockTask()]
116         tasks = [MockTask(), recursive_group, MockTask()]
117         graph.add_tasks(tasks)
118         removed_tasks = graph.remove_tasks(tasks)
119         assert removed_tasks == [tasks[0], recursive_group[0], recursive_group[1], tasks[2]]
120
121     def test_remove_nonexistent_task(self, graph):
122         task = MockTask()
123         task_not_in_graph = MockTask()
124         graph.add_tasks(task)
125         # removing a task not in graph - should have no effect, and return False
126         remove_result = graph.remove_tasks(task_not_in_graph)
127         assert remove_result == []
128         tasks = [t for t in graph.tasks]
129         assert len(tasks) == 1
130         assert tasks[0] == task
131
132     def test_has_task(self, graph):
133         task = MockTask()
134         graph.add_tasks(task)
135         assert graph.has_tasks(task) is True
136
137     def test_has_nonexistent_task(self, graph):
138         task = MockTask()
139         task_not_in_graph = MockTask()
140         graph.add_tasks(task)
141         assert graph.has_tasks(task_not_in_graph) is False
142
143     def test_has_empty_group(self, graph):
144         # the "empty task" is in the graph
145         assert graph.has_tasks([]) is True
146
147     def test_has_group(self, graph):
148         tasks = [MockTask(), MockTask(), MockTask()]
149         graph.add_tasks(*tasks)
150         assert graph.has_tasks(*tasks) is True
151
152     def test_has_partially_existing_group(self, graph):
153         task = MockTask()
154         graph.add_tasks(task)
155         tasks = [MockTask(), task, MockTask()]
156         assert graph.has_tasks(tasks) is False
157
158     def test_has_recursively_group(self, graph):
159         recursive_group = [MockTask(), MockTask()]
160         tasks = [MockTask(), recursive_group, MockTask()]
161         graph.add_tasks(tasks)
162         assert graph.has_tasks(tasks) is True
163
164     def test_get_task(self, graph):
165         task = MockTask()
166         graph.add_tasks(task)
167         assert graph.get_task(task.id) == task
168
169     def test_get_nonexistent_task(self, graph):
170         task = MockTask()
171         task_not_in_graph = MockTask()
172         graph.add_tasks(task)
173         with pytest.raises(task_graph.TaskNotInGraphError):
174             graph.get_task(task_not_in_graph.id)
175
176
177 class TestTaskGraphGraphTraversal(object):
178
179     def test_tasks_iteration(self, graph):
180         task = MockTask()
181         other_task = MockTask()
182         graph.add_tasks(task)
183         graph.add_tasks(other_task)
184         tasks = [t for t in graph.tasks]
185         assert set(tasks) == set([task, other_task])
186
187     def test_get_dependents(self, graph):
188         task = MockTask()
189         dependent_task_1 = MockTask()
190         dependent_task_2 = MockTask()
191         transitively_dependent_task = MockTask()
192
193         graph.add_tasks(task)
194         graph.add_tasks(dependent_task_1)
195         graph.add_tasks(dependent_task_2)
196         graph.add_tasks(transitively_dependent_task)
197
198         graph.add_dependency(dependent_task_1, task)
199         graph.add_dependency(dependent_task_2, task)
200         graph.add_dependency(transitively_dependent_task, dependent_task_2)
201
202         dependent_tasks = list(graph.get_dependents(task))
203         # transitively_dependent_task not expected to appear in the result
204         assert set(dependent_tasks) == set([dependent_task_1, dependent_task_2])
205
206     def test_get_task_empty_dependents(self, graph):
207         task = MockTask()
208         other_task = MockTask()
209         graph.add_tasks(task)
210         graph.add_tasks(other_task)
211         dependent_tasks = list(graph.get_dependents(task))
212         assert len(dependent_tasks) == 0
213
214     def test_get_nonexistent_task_dependents(self, graph):
215         task = MockTask()
216         task_not_in_graph = MockTask()
217         graph.add_tasks(task)
218         with pytest.raises(task_graph.TaskNotInGraphError):
219             list(graph.get_dependents(task_not_in_graph))
220
221     def test_get_dependencies(self, graph):
222         task = MockTask()
223         dependency_task_1 = MockTask()
224         dependency_task_2 = MockTask()
225         transitively_dependency_task = MockTask()
226
227         graph.add_tasks(task)
228         graph.add_tasks(dependency_task_1)
229         graph.add_tasks(dependency_task_2)
230         graph.add_tasks(transitively_dependency_task)
231
232         graph.add_dependency(task, dependency_task_1)
233         graph.add_dependency(task, dependency_task_2)
234         graph.add_dependency(dependency_task_2, transitively_dependency_task)
235
236         dependency_tasks = list(graph.get_dependencies(task))
237         # transitively_dependency_task not expected to appear in the result
238         assert set(dependency_tasks) == set([dependency_task_1, dependency_task_2])
239
240     def test_get_task_empty_dependencies(self, graph):
241         task = MockTask()
242         other_task = MockTask()
243         graph.add_tasks(task)
244         graph.add_tasks(other_task)
245         dependency_tasks = list(graph.get_dependencies(task))
246         assert len(dependency_tasks) == 0
247
248     def test_get_nonexistent_task_dependencies(self, graph):
249         task = MockTask()
250         task_not_in_graph = MockTask()
251         graph.add_tasks(task)
252         with pytest.raises(task_graph.TaskNotInGraphError):
253             list(graph.get_dependencies(task_not_in_graph))
254
255
256 class TestTaskGraphDependencies(object):
257
258     def test_add_dependency(self, graph):
259         task = MockTask()
260         dependency_task = MockTask()
261         unrelated_task = MockTask()
262         graph.add_tasks(task)
263         graph.add_tasks(dependency_task)
264         graph.add_tasks(unrelated_task)
265         graph.add_dependency(task, dependency_task)
266         add_result = graph.has_dependency(task, dependency_task)
267         assert add_result is True
268         dependency_tasks = list(graph.get_dependencies(task))
269         assert len(dependency_tasks) == 1
270         assert dependency_tasks[0] == dependency_task
271
272     def test_add_existing_dependency(self, graph):
273         task = MockTask()
274         dependency_task = MockTask()
275         graph.add_tasks(task)
276         graph.add_tasks(dependency_task)
277         graph.add_dependency(task, dependency_task)
278         add_result = graph.has_dependency(task, dependency_task)
279         # adding a dependency already in graph - should have no effect, and return False
280         assert add_result is True
281         graph.add_dependency(task, dependency_task)
282         add_result = graph.has_dependency(task, dependency_task)
283         assert add_result is True
284         dependency_tasks = list(graph.get_dependencies(task))
285         assert len(dependency_tasks) == 1
286         assert dependency_tasks[0] == dependency_task
287
288     def test_add_dependency_nonexistent_dependent(self, graph):
289         task = MockTask()
290         task_not_in_graph = MockTask()
291         graph.add_tasks(task)
292         with pytest.raises(task_graph.TaskNotInGraphError):
293             graph.add_dependency(task_not_in_graph, task)
294
295     def test_add_dependency_nonexistent_dependency(self, graph):
296         task = MockTask()
297         task_not_in_graph = MockTask()
298         graph.add_tasks(task)
299         with pytest.raises(task_graph.TaskNotInGraphError):
300             graph.add_dependency(task, task_not_in_graph)
301
302     def test_add_dependency_empty_dependent(self, graph):
303         task = MockTask()
304         graph.add_tasks(task)
305         # expecting add_dependency result to be False - no dependency has been created
306         assert set(graph.tasks) == set((task,))
307
308     def test_add_dependency_empty_dependency(self, graph):
309         task = MockTask()
310         graph.add_tasks(task)
311         # expecting add_dependency result to be False - no dependency has been created
312         assert set(graph.tasks) == set((task,))
313
314     def test_add_dependency_dependent_group(self, graph):
315         task = MockTask()
316         group_tasks = [MockTask() for _ in xrange(3)]
317         graph.add_tasks(task)
318         graph.add_tasks(*group_tasks)
319         graph.add_dependency(group_tasks, task)
320         assert graph.has_dependency(group_tasks[0], task) is True
321         assert graph.has_dependency(group_tasks[1], task) is True
322         assert graph.has_dependency(group_tasks[2], task) is True
323
324     def test_add_dependency_dependency_group(self, graph):
325         task = MockTask()
326         group_tasks = [MockTask() for _ in xrange(3)]
327         graph.add_tasks(task)
328         graph.add_tasks(*group_tasks)
329         graph.add_dependency(task, group_tasks)
330         assert graph.has_dependency(task, group_tasks[0]) is True
331         assert graph.has_dependency(task, group_tasks[1]) is True
332         assert graph.has_dependency(task, group_tasks[2]) is True
333
334     def test_add_dependency_between_groups(self, graph):
335         group_1_tasks = [MockTask() for _ in xrange(3)]
336         group_2_tasks = [MockTask() for _ in xrange(3)]
337         graph.add_tasks(*group_1_tasks)
338         graph.add_tasks(*group_2_tasks)
339         graph.add_dependency(group_1_tasks, group_2_tasks)
340         for group_2_task in group_2_tasks:
341             assert graph.has_dependency(group_1_tasks[0], group_2_task) is True
342             assert graph.has_dependency(group_1_tasks[1], group_2_task) is True
343             assert graph.has_dependency(group_1_tasks[2], group_2_task) is True
344
345     def test_add_dependency_dependency_group_with_some_existing_dependencies(self, graph):
346         task = MockTask()
347         group_tasks = [MockTask() for _ in xrange(3)]
348         graph.add_tasks(task)
349         graph.add_tasks(*group_tasks)
350         # adding a dependency on a specific task manually,
351         # before adding a dependency on the whole parallel
352         graph.add_dependency(task, group_tasks[1])
353         graph.add_dependency(task, group_tasks)
354         assert graph.has_dependency(task, group_tasks[0]) is True
355         assert graph.has_dependency(task, group_tasks[1]) is True
356         assert graph.has_dependency(task, group_tasks[2]) is True
357
358     def test_add_existing_dependency_between_groups(self, graph):
359         group_1_tasks = [MockTask() for _ in xrange(3)]
360         group_2_tasks = [MockTask() for _ in xrange(3)]
361         graph.add_tasks(*group_1_tasks)
362         graph.add_tasks(*group_2_tasks)
363         graph.add_dependency(group_1_tasks, group_2_tasks)
364         add_result = graph.has_dependency(group_1_tasks, group_2_tasks)
365         assert add_result is True
366         # adding a dependency already in graph - should have no effect, and return False
367         graph.add_dependency(group_1_tasks, group_2_tasks)
368         add_result = graph.has_dependency(group_1_tasks, group_2_tasks)
369         assert add_result is True
370         for group_2_task in group_2_tasks:
371             assert graph.has_dependency(group_1_tasks[0], group_2_task) is True
372             assert graph.has_dependency(group_1_tasks[1], group_2_task) is True
373             assert graph.has_dependency(group_1_tasks[2], group_2_task) is True
374
375     def test_has_dependency(self, graph):
376         task = MockTask()
377         dependency_task = MockTask()
378         graph.add_tasks(task)
379         graph.add_tasks(dependency_task)
380         graph.add_dependency(task, dependency_task)
381         assert graph.has_dependency(task, dependency_task) is True
382
383     def test_has_nonexistent_dependency(self, graph):
384         task = MockTask()
385         other_task = MockTask()
386         graph.add_tasks(task)
387         graph.add_tasks(other_task)
388         assert graph.has_dependency(task, other_task) is False
389
390     def test_has_dependency_nonexistent_dependent(self, graph):
391         task = MockTask()
392         task_not_in_graph = MockTask()
393         graph.add_tasks(task)
394         with pytest.raises(task_graph.TaskNotInGraphError):
395             graph.has_dependency(task_not_in_graph, task)
396
397     def test_has_dependency_nonexistent_dependency(self, graph):
398         task = MockTask()
399         task_not_in_graph = MockTask()
400         graph.add_tasks(task)
401         with pytest.raises(task_graph.TaskNotInGraphError):
402             graph.has_dependency(task, task_not_in_graph)
403
404     def test_has_dependency_empty_dependent(self, graph):
405         task = MockTask()
406         graph.add_tasks(task)
407         # expecting has_dependency result to be False - dependency in an empty form
408         assert graph.has_dependency([], task) is False
409
410     def test_has_dependency_empty_dependency(self, graph):
411         task = MockTask()
412         graph.add_tasks(task)
413         # expecting has_dependency result to be True - dependency in an empty form
414         assert graph.has_dependency(task, []) is False
415
416     def test_has_dependency_dependent_group(self, graph):
417         task = MockTask()
418         group_tasks = [MockTask() for _ in xrange(3)]
419         graph.add_tasks(task)
420         graph.add_tasks(*group_tasks)
421         assert graph.has_dependency(group_tasks, task) is False
422         graph.add_dependency(group_tasks, task)
423         assert graph.has_dependency(group_tasks, task) is True
424
425     def test_has_dependency_dependency_parallel(self, graph):
426         task = MockTask()
427         group_tasks = [MockTask() for _ in xrange(3)]
428         graph.add_tasks(task)
429         graph.add_tasks(*group_tasks)
430         assert graph.has_dependency(task, group_tasks) is False
431         graph.add_dependency(task, group_tasks)
432         assert graph.has_dependency(task, group_tasks) is True
433
434     def test_has_dependency_between_groups(self, graph):
435         group_1_tasks = [MockTask() for _ in xrange(3)]
436         group_2_tasks = [MockTask() for _ in xrange(3)]
437         graph.add_tasks(*group_1_tasks)
438         graph.add_tasks(*group_2_tasks)
439         assert graph.has_dependency(group_2_tasks, group_1_tasks) is False
440         graph.add_dependency(group_2_tasks, group_1_tasks)
441         assert graph.has_dependency(group_2_tasks, group_1_tasks) is True
442
443     def test_has_dependency_dependency_parallel_with_some_existing_dependencies(self, graph):
444         task = MockTask()
445         parallel_tasks = [MockTask() for _ in xrange(3)]
446         graph.add_tasks(task)
447         parallel = graph.add_tasks(*parallel_tasks)
448         graph.add_dependency(task, parallel_tasks[1])
449         # only a partial dependency exists - has_dependency is expected to return False
450         assert graph.has_dependency(task, parallel) is False
451
452     def test_has_nonexistent_dependency_between_groups(self, graph):
453         group_1_tasks = [MockTask() for _ in xrange(3)]
454         group_2_tasks = [MockTask() for _ in xrange(3)]
455         graph.add_tasks(*group_1_tasks)
456         graph.add_tasks(*group_2_tasks)
457         assert graph.has_dependency(group_1_tasks, group_2_tasks) is False
458
459     def test_remove_dependency(self, graph):
460         task = MockTask()
461         dependency_task = MockTask()
462         another_dependent_task = MockTask()
463         graph.add_tasks(task)
464         graph.add_tasks(dependency_task)
465         graph.add_tasks(another_dependent_task)
466         graph.add_dependency(task, dependency_task)
467         graph.add_dependency(another_dependent_task, dependency_task)
468
469         graph.remove_dependency(task, dependency_task)
470         remove_result = graph.has_dependency(task, dependency_task)
471         assert remove_result is False
472         assert graph.has_dependency(task, dependency_task) is False
473         assert graph.has_dependency(another_dependent_task, dependency_task) is True
474
475     def test_remove_nonexistent_dependency(self, graph):
476         task = MockTask()
477         dependency_task = MockTask()
478         graph.add_tasks(task)
479         graph.add_tasks(dependency_task)
480         # removing a dependency not in graph - should have no effect, and return False
481         graph.remove_dependency(task, dependency_task)
482         remove_result = graph.has_dependency(task, dependency_task)
483         assert remove_result is False
484         tasks = [t for t in graph.tasks]
485         assert set(tasks) == set([task, dependency_task])
486
487     def test_remove_dependency_nonexistent_dependent(self, graph):
488         task = MockTask()
489         task_not_in_graph = MockTask()
490         graph.add_tasks(task)
491         with pytest.raises(task_graph.TaskNotInGraphError):
492             graph.remove_dependency(task_not_in_graph, task)
493
494     def test_remove_dependency_nonexistent_dependency(self, graph):
495         # in this test the dependency *task* is not in the graph, not just the dependency itself
496         task = MockTask()
497         task_not_in_graph = MockTask()
498         graph.add_tasks(task)
499         with pytest.raises(task_graph.TaskNotInGraphError):
500             graph.remove_dependency(task, task_not_in_graph)
501
502     def test_remove_dependency_empty_dependent(self, graph):
503         task = MockTask()
504         graph.add_tasks(task)
505         # expecting remove_dependency result to be False - no dependency has been created
506         graph.remove_dependency([], task)
507         assert set(graph.tasks) == set((task,))
508
509     def test_remove_dependency_empty_dependency(self, graph):
510         task = MockTask()
511         graph.add_tasks(task)
512         # expecting remove_dependency result to be False - no dependency has been created
513         graph.remove_dependency(task, [])
514         assert set(graph.tasks) == set((task,))
515
516     def test_remove_dependency_dependent_group(self, graph):
517         task = MockTask()
518         group_tasks = [MockTask() for _ in xrange(3)]
519         graph.add_tasks(task)
520         graph.add_tasks(*group_tasks)
521         graph.add_dependency(group_tasks, task)
522         graph.remove_dependency(group_tasks, task)
523         remove_result = graph.has_dependency(group_tasks, task)
524         assert remove_result is False
525         assert graph.has_dependency(group_tasks[0], task) is False
526         assert graph.has_dependency(group_tasks[1], task) is False
527         assert graph.has_dependency(group_tasks[2], task) is False
528
529     def test_remove_dependency_dependency_group(self, graph):
530         task = MockTask()
531         group_tasks = [MockTask() for _ in xrange(3)]
532         graph.add_tasks(task)
533         graph.add_tasks(*group_tasks)
534         graph.add_dependency(task, group_tasks)
535         graph.remove_dependency(task, group_tasks)
536         remove_result = graph.has_dependency(task, group_tasks)
537         assert remove_result is False
538         assert graph.has_dependency(task, group_tasks[0]) is False
539         assert graph.has_dependency(task, group_tasks[1]) is False
540         assert graph.has_dependency(task, group_tasks[2]) is False
541
542     def test_remove_dependency_between_groups(self, graph):
543         group_1_tasks = [MockTask() for _ in xrange(3)]
544         group_2_tasks = [MockTask() for _ in xrange(3)]
545         graph.add_tasks(*group_1_tasks)
546         graph.add_tasks(*group_2_tasks)
547         graph.add_dependency(group_2_tasks, group_1_tasks)
548         graph.remove_dependency(group_2_tasks, group_1_tasks)
549         remove_result = graph.has_dependency(group_2_tasks, group_1_tasks)
550         assert remove_result is False
551         for group_2_task in group_2_tasks:
552             assert graph.has_dependency(group_2_task, group_1_tasks[0]) is False
553             assert graph.has_dependency(group_2_task, group_1_tasks[1]) is False
554             assert graph.has_dependency(group_2_task, group_1_tasks[2]) is False
555
556     def test_remove_dependency_dependency_group_with_some_existing_dependencies(self, graph):
557         task = MockTask()
558         group_tasks = [MockTask() for _ in xrange(3)]
559         graph.add_tasks(task)
560         graph.add_tasks(*group_tasks)
561         graph.add_dependency(task, group_tasks[1])
562         graph.remove_dependency(task, group_tasks)
563         remove_result = graph.has_dependency(task, group_tasks)
564         # only a partial dependency exists - remove_dependency is expected to return False
565         assert remove_result is False
566         # no dependencies are expected to have changed
567         assert graph.has_dependency(task, group_tasks[0]) is False
568         assert graph.has_dependency(task, group_tasks[1]) is True
569         assert graph.has_dependency(task, group_tasks[2]) is False
570
571     def test_remove_nonexistent_dependency_between_groups(self, graph):
572         group_1_tasks = [MockTask() for _ in xrange(3)]
573         group_2_tasks = [MockTask() for _ in xrange(3)]
574         graph.add_tasks(*group_1_tasks)
575         graph.add_tasks(*group_2_tasks)
576         # removing a dependency not in graph - should have no effect, and return False
577         graph.remove_dependency(group_2_tasks, group_1_tasks)
578         remove_result = graph.has_dependency(group_2_tasks, group_1_tasks)
579         assert remove_result is False
580
581     # nested tests
582
583     def test_group_with_nested_sequence(self, graph):
584         all_tasks = [MockTask() for _ in xrange(5)]
585         graph.add_tasks(all_tasks[0],
586                         graph.sequence(all_tasks[1], all_tasks[2], all_tasks[3]),
587                         all_tasks[4])
588         assert set(graph.tasks) == set(all_tasks)
589
590         # tasks[2] and tasks[3] should each have a single dependency; the rest should have none
591         assert len(list(graph.get_dependencies(all_tasks[0]))) == 0
592         assert len(list(graph.get_dependencies(all_tasks[1]))) == 0
593         assert set(graph.get_dependencies(all_tasks[2])) == set([all_tasks[1]])
594         assert set(graph.get_dependencies(all_tasks[3])) == set([all_tasks[2]])
595         assert len(list(graph.get_dependencies(all_tasks[4]))) == 0
596
597     def test_group_with_nested_group(self, graph):
598         tasks = [MockTask() for _ in xrange(5)]
599         graph.add_tasks(tasks[0], (tasks[1], tasks[2], tasks[3]), tasks[4])
600         graph_tasks = [t for t in graph.tasks]
601         assert set(graph_tasks) == set(tasks)
602         # none of the tasks should have any dependencies
603         for i in xrange(len(tasks)):
604             assert len(list(graph.get_dependencies(tasks[i]))) == 0
605
606     def test_group_with_recursively_nested_group(self, graph):
607         recursively_nested_tasks = [MockTask(), MockTask(), MockTask()]
608         nested_tasks = [MockTask(), MockTask(), MockTask(), recursively_nested_tasks]
609         tasks = [MockTask(), MockTask(), MockTask(), nested_tasks]
610         graph.add_tasks(*tasks)
611
612         assert set(graph.tasks) == set(tasks[:3] + nested_tasks[:3] + recursively_nested_tasks)
613         for tasks_list in [tasks, nested_tasks, recursively_nested_tasks]:
614             for i in xrange(len(tasks_list[:3])):
615                 assert len(list(graph.get_dependencies(tasks_list[i]))) == 0
616
617     def test_group_with_recursively_nested_group_and_interdependencies(self, graph):
618         recursively_nested_tasks = [MockTask(), MockTask(), MockTask()]
619         nested_tasks = [MockTask(), MockTask(), MockTask(), recursively_nested_tasks]
620         tasks = [MockTask(), MockTask(), MockTask(), nested_tasks]
621         graph.add_tasks(*tasks)
622
623         graph.add_dependency(tasks[2], nested_tasks[2])
624         graph.add_dependency(nested_tasks[1], recursively_nested_tasks[0])
625         graph.add_dependency(recursively_nested_tasks[1], tasks[0])
626
627         assert set(graph.tasks) == set(tasks[:3] + nested_tasks[:3] + recursively_nested_tasks)
628         assert set(graph.get_dependencies(tasks[0])) == set()
629         assert set(graph.get_dependencies(tasks[1])) == set()
630         assert set(graph.get_dependencies(tasks[2])) == set([nested_tasks[2]])
631
632         assert set(graph.get_dependencies(nested_tasks[0])) == set()
633         assert set(graph.get_dependencies(nested_tasks[1])) == set([recursively_nested_tasks[0]])
634         assert set(graph.get_dependencies(nested_tasks[2])) == set()
635
636         assert set(graph.get_dependencies(recursively_nested_tasks[0])) == set()
637         assert set(graph.get_dependencies(recursively_nested_tasks[1])) == set([tasks[0]])
638         assert set(graph.get_dependencies(recursively_nested_tasks[2])) == set()
639
640
641 class TestTaskGraphSequence(object):
642
643     def test_sequence(self, graph):
644         tasks = [MockTask(), MockTask(), MockTask()]
645         graph.sequence(*tasks)
646         graph_tasks = [t for t in graph.tasks]
647         assert set(graph_tasks) == set(tasks)
648         assert len(list(graph.get_dependencies(tasks[0]))) == 0
649         assert set(graph.get_dependencies(tasks[1])) == set([tasks[0]])
650         assert set(graph.get_dependencies(tasks[2])) == set([tasks[1]])
651
652     def test_sequence_with_some_tasks_and_dependencies_already_in_graph(self, graph):
653         # tests both that tasks which werent previously in graph get inserted, and
654         # that existing tasks don't get re-added to graph
655         tasks = [MockTask(), MockTask(), MockTask()]
656         # insert some tasks and dependencies to the graph
657         graph.add_tasks(tasks[1])
658         graph.add_tasks(tasks[2])
659         graph.add_dependency(tasks[2], tasks[1])
660
661         graph.sequence(*tasks)
662         graph_tasks = [t for t in graph.tasks]
663         assert set(graph_tasks) == set(tasks)
664         assert len(list(graph.get_dependencies(tasks[0]))) == 0
665         assert set(graph.get_dependencies(tasks[1])) == set([tasks[0]])
666         assert set(graph.get_dependencies(tasks[2])) == set([tasks[1]])
667
668     def test_sequence_with_nested_sequence(self, graph):
669         tasks = [MockTask() for _ in xrange(5)]
670         graph.sequence(tasks[0], graph.sequence(tasks[1], tasks[2], tasks[3]), tasks[4])
671         graph_tasks = [t for t in graph.tasks]
672         assert set(graph_tasks) == set(tasks)
673         # first task should have no dependencies
674         assert len(list(graph.get_dependencies(tasks[0]))) == 0
675         assert len(list(graph.get_dependencies(tasks[1]))) == 1
676         assert len(list(graph.get_dependencies(tasks[2]))) == 2
677         assert len(list(graph.get_dependencies(tasks[3]))) == 2
678         assert len(list(graph.get_dependencies(tasks[4]))) == 3
679
680     def test_sequence_with_nested_group(self, graph):
681         tasks = [MockTask() for _ in xrange(5)]
682         graph.sequence(tasks[0], (tasks[1], tasks[2], tasks[3]), tasks[4])
683         graph_tasks = [t for t in graph.tasks]
684         assert set(graph_tasks) == set(tasks)
685         # first task should have no dependencies
686         assert len(list(graph.get_dependencies(tasks[0]))) == 0
687         # rest of the tasks (except last) should have a single dependency - the first task
688         for i in xrange(1, 4):
689             assert set(graph.get_dependencies(tasks[i])) == set([tasks[0]])
690         # last task should have have a dependency on all tasks except for the first one
691         assert set(graph.get_dependencies(tasks[4])) == set([tasks[1], tasks[2], tasks[3]])
692
693     def test_sequence_with_recursively_nested_group(self, graph):
694         recursively_nested_group = [MockTask(), MockTask()]
695         nested_group = [MockTask(), recursively_nested_group, MockTask()]
696         sequence_tasks = [MockTask(), nested_group, MockTask()]
697
698         graph.sequence(*sequence_tasks)
699         graph_tasks = [t for t in graph.tasks]
700         assert set(graph_tasks) == set([sequence_tasks[0], nested_group[0],
701                                         recursively_nested_group[0], recursively_nested_group[1],
702                                         nested_group[2], sequence_tasks[2]])
703
704         assert list(graph.get_dependencies(nested_group[0])) == [sequence_tasks[0]]
705         assert list(graph.get_dependencies(recursively_nested_group[0])) == [sequence_tasks[0]]
706         assert list(graph.get_dependencies(recursively_nested_group[1])) == [sequence_tasks[0]]
707         assert list(graph.get_dependencies(nested_group[2])) == [sequence_tasks[0]]
708
709         assert list(graph.get_dependents(nested_group[0])) == [sequence_tasks[2]]
710         assert list(graph.get_dependents(recursively_nested_group[0])) == [sequence_tasks[2]]
711         assert list(graph.get_dependents(recursively_nested_group[1])) == [sequence_tasks[2]]
712         assert list(graph.get_dependents(nested_group[2])) == [sequence_tasks[2]]
713
714     def test_sequence_with_empty_group(self, graph):
715         tasks = [MockTask(), [], MockTask()]
716         graph.sequence(*tasks)
717         graph_tasks = set([t for t in graph.tasks])
718         assert graph_tasks == set([tasks[0], tasks[2]])
719         assert list(graph.get_dependents(tasks[0])) == [tasks[2]]
720         assert list(graph.get_dependencies(tasks[2])) == [tasks[0]]
721
722     def test_sequence_with_recursively_nested_sequence_and_interdependencies(self, graph):
723         recursively_nested_tasks = list(graph.sequence(MockTask(), MockTask(), MockTask()))
724         nested_tasks = list(graph.sequence(MockTask(),
725                                            MockTask(),
726                                            MockTask(),
727                                            recursively_nested_tasks))
728         tasks = [MockTask(), MockTask(), MockTask(), nested_tasks]
729         graph.sequence(*tasks)
730
731         assert set(graph.tasks) == set(tasks[:3] + nested_tasks[:3] + recursively_nested_tasks)
732         assert set(graph.get_dependencies(tasks[0])) == set()
733         for i in xrange(1, len(tasks[:-1])):
734             assert set(graph.get_dependencies(tasks[i])) == set([tasks[i - 1]])
735
736         assert set(graph.get_dependencies(nested_tasks[0])) == set([tasks[2]])
737         for i in xrange(1, len(nested_tasks[:-1])):
738             assert set(graph.get_dependencies(nested_tasks[i])) == \
739                    set([tasks[2], nested_tasks[i-1]])
740
741         assert set(graph.get_dependencies(recursively_nested_tasks[0])) == \
742                set([tasks[2], nested_tasks[2]])
743         for i in xrange(1, len(recursively_nested_tasks[:-1])):
744             assert set(graph.get_dependencies(recursively_nested_tasks[i])) == \
745                    set([tasks[2], nested_tasks[2], recursively_nested_tasks[i-1]])