Coverage for tests/test_generic_workflow.py: 15%
173 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-14 02:22 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-14 02:22 -0700
1# This file is part of ctrl_bps.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21import io
22import unittest
23from collections import Counter
25import lsst.ctrl.bps.generic_workflow as gw
26import networkx
27import networkx.algorithms.isomorphism as iso
30class TestGenericWorkflowJob(unittest.TestCase):
31 def testEquality(self):
32 job1 = gw.GenericWorkflowJob("job1")
33 job2 = gw.GenericWorkflowJob("job1")
34 self.assertEqual(job1, job2)
37class TestGenericWorkflow(unittest.TestCase):
38 def setUp(self):
39 self.exec1 = gw.GenericWorkflowExec(
40 name="test1.py", src_uri="${CTRL_BPS_DIR}/bin/test1.py", transfer_executable=False
41 )
42 self.job1 = gw.GenericWorkflowJob("job1", label="label1")
43 self.job1.quanta_counts = Counter({"pt1": 1, "pt2": 2})
44 self.job1.executable = self.exec1
46 self.job2 = gw.GenericWorkflowJob("job2", label="label2")
47 self.job2.quanta_counts = Counter({"pt1": 1, "pt2": 2})
48 self.job2.executable = self.exec1
50 def testAddJobDuplicate(self):
51 gwf = gw.GenericWorkflow("mytest")
52 gwf.add_job(self.job1)
53 with self.assertRaises(RuntimeError):
54 gwf.add_job(self.job1)
56 def testAddJobValid(self):
57 gwf = gw.GenericWorkflow("mytest")
58 gwf.add_job(self.job1)
59 self.assertEqual(1, gwf.number_of_nodes())
60 self.assertListEqual(["job1"], list(gwf))
61 getjob = gwf.get_job("job1")
62 self.assertEqual(self.job1, getjob)
64 def testAddJobRelationshipsSingle(self):
65 gwf = gw.GenericWorkflow("mytest")
66 gwf.add_job(self.job1)
67 gwf.add_job(self.job2)
68 gwf.add_job_relationships("job1", "job2")
69 self.assertListEqual([("job1", "job2")], list(gwf.edges()))
71 def testAddJobRelationshipsMultiChild(self):
72 job3 = gw.GenericWorkflowJob("job3")
73 job3.label = "label2"
74 job3.quanta_counts = Counter({"pt1": 1, "pt2": 2})
75 job3.executable = self.exec1
77 gwf = gw.GenericWorkflow("mytest")
78 gwf.add_job(self.job1)
79 gwf.add_job(self.job2)
80 gwf.add_job(job3)
81 gwf.add_job_relationships("job1", ["job2", "job3"])
82 self.assertListEqual([("job1", "job2"), ("job1", "job3")], list(gwf.edges()))
84 def testAddJobRelationshipsMultiParents(self):
85 job3 = gw.GenericWorkflowJob("job3")
86 job3.label = "label2"
87 job3.quanta_counts = Counter({"pt1": 1, "pt2": 2})
88 job3.executable = self.exec1
89 gwf = gw.GenericWorkflow("mytest")
90 gwf.add_job(self.job1)
91 gwf.add_job(self.job2)
92 gwf.add_job(job3)
93 gwf.add_job_relationships(["job1", "job2"], "job3")
94 self.assertListEqual([("job1", "job3"), ("job2", "job3")], list(gwf.edges()))
96 def testAddJobRelationshipsNone(self):
97 gwf = gw.GenericWorkflow("mytest")
98 gwf.add_job(self.job1)
99 gwf.add_job_relationships(None, "job1")
100 self.assertListEqual([], list(gwf.edges()))
101 gwf.add_job_relationships("job1", None)
102 self.assertListEqual([], list(gwf.edges()))
104 def testGetJobExists(self):
105 gwf = gw.GenericWorkflow("mytest")
106 gwf.add_job(self.job1)
107 get_job = gwf.get_job("job1")
108 self.assertIs(self.job1, get_job)
110 def testGetJobError(self):
111 gwf = gw.GenericWorkflow("mytest")
112 gwf.add_job(self.job1)
113 with self.assertRaises(KeyError):
114 _ = gwf.get_job("job_not_there")
116 def testSaveInvalidFormat(self):
117 gwf = gw.GenericWorkflow("mytest")
118 stream = io.BytesIO()
119 with self.assertRaises(RuntimeError):
120 gwf.save(stream, "badformat")
122 def testSavePickle(self):
123 gwf = gw.GenericWorkflow("mytest")
124 gwf.add_job(self.job1)
125 gwf.add_job(self.job2)
126 gwf.add_job_relationships("job1", "job2")
127 stream = io.BytesIO()
128 gwf.save(stream, "pickle")
129 stream.seek(0)
130 gwf2 = gw.GenericWorkflow.load(stream, "pickle")
131 self.assertTrue(
132 networkx.is_isomorphic(gwf, gwf2, node_match=iso.categorical_node_match("data", None))
133 )
135 def testLabels(self):
136 job3 = gw.GenericWorkflowJob("job3")
137 job3.label = "label2"
138 gwf = gw.GenericWorkflow("mytest")
139 gwf.add_job(self.job1)
140 gwf.add_job(self.job2)
141 gwf.add_job(job3)
142 gwf.add_job_relationships(["job1", "job2"], "job3")
143 self.assertListEqual(["label1", "label2"], gwf.labels)
145 def testRegenerateLabels(self):
146 job3 = gw.GenericWorkflowJob("job3", label="label2")
147 gwf = gw.GenericWorkflow("mytest")
148 gwf.add_job(self.job1)
149 gwf.add_job(self.job2)
150 gwf.add_job(job3)
151 gwf.add_job_relationships(["job1", "job2"], "job3")
152 self.job1.label = "label1b"
153 self.job2.label = "label1b"
154 job3.label = "label2b"
155 gwf.regenerate_labels()
156 self.assertListEqual(["label1b", "label2b"], gwf.labels)
158 def testJobCounts(self):
159 job3 = gw.GenericWorkflowJob("job3", label="label2")
160 gwf = gw.GenericWorkflow("mytest")
161 gwf.add_job(self.job1)
162 gwf.add_job(self.job2)
163 gwf.add_job(job3)
164 gwf.add_job_relationships(["job1", "job2"], "job3")
165 self.assertEqual(Counter({"label1": 1, "label2": 2}), gwf.job_counts)
167 def testDelJob(self):
168 job3 = gw.GenericWorkflowJob("job3", label="label2")
169 gwf = gw.GenericWorkflow("mytest")
170 gwf.add_job(self.job1)
171 gwf.add_job(self.job2)
172 gwf.add_job(job3)
173 gwf.add_job_relationships(["job1", "job2"], "job3")
175 gwf.del_job("job2")
177 self.assertListEqual([("job1", "job3")], list(gwf.edges()))
178 self.assertEqual(Counter({"label1": 1, "label2": 1}), gwf.job_counts)
180 def testAddWorkflowSource(self):
181 job3 = gw.GenericWorkflowJob("job3")
182 job3.label = "label2"
183 gwf = gw.GenericWorkflow("mytest")
184 gwf.add_job(self.job1)
185 gwf.add_job(self.job2)
186 gwf.add_job(job3)
187 gwf.add_job_relationships(["job1", "job2"], "job3")
189 srcjob1 = gw.GenericWorkflowJob("srcjob1")
190 srcjob1.label = "srclabel1"
191 srcjob1.executable = self.exec1
192 srcjob2 = gw.GenericWorkflowJob("srcjob2")
193 srcjob2.label = "srclabel1"
194 srcjob2.executable = self.exec1
195 srcjob3 = gw.GenericWorkflowJob("srcjob3")
196 srcjob3.label = "srclabel2"
197 srcjob3.executable = self.exec1
198 srcjob4 = gw.GenericWorkflowJob("srcjob4")
199 srcjob4.label = "srclabel2"
200 srcjob4.executable = self.exec1
201 gwf2 = gw.GenericWorkflow("mytest2")
202 gwf2.add_job(srcjob1)
203 gwf2.add_job(srcjob2)
204 gwf2.add_job(srcjob3)
205 gwf2.add_job(srcjob4)
206 gwf2.add_job_relationships("srcjob1", "srcjob3")
207 gwf2.add_job_relationships("srcjob2", "srcjob4")
209 gwf.add_workflow_source(gwf2)
211 self.assertEqual(Counter({"srclabel1": 2, "srclabel2": 2, "label1": 1, "label2": 2}), gwf.job_counts)
212 self.assertListEqual(["srclabel1", "srclabel2", "label1", "label2"], gwf.labels)
213 self.assertListEqual(
214 sorted(
215 [
216 ("srcjob1", "srcjob3"),
217 ("srcjob2", "srcjob4"),
218 ("srcjob3", "job1"),
219 ("srcjob3", "job2"),
220 ("srcjob4", "job1"),
221 ("srcjob4", "job2"),
222 ("job1", "job3"),
223 ("job2", "job3"),
224 ]
225 ),
226 sorted(list(gwf.edges())),
227 )
229 def testGetJobsByLabel(self):
230 job3 = gw.GenericWorkflowJob("job3")
231 job3.label = "label3"
232 gwf = gw.GenericWorkflow("mytest")
233 gwf.add_job(self.job1)
234 gwf.add_job(self.job2)
235 gwf.add_job(job3)
236 gwf.add_job_relationships(["job1", "job2"], "job3")
238 self.assertListEqual([job3], gwf.get_jobs_by_label("label3"))
241if __name__ == "__main__": 241 ↛ 242line 241 didn't jump to line 242, because the condition on line 241 was never true
242 unittest.main()