Coverage for tests/test_configurableActions.py: 11%
147 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-16 11:48 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-16 11:48 +0000
1# This file is part of pipe_tasks.
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/>.
22import unittest
23from io import StringIO
24from types import SimpleNamespace
26from lsst.pipe.tasks.configurableActions.tests import (
27 ActionTest1,
28 ActionTest2,
29 ActionTest3,
30 TestConfig,
31)
32from lsst.pipe.tasks.dataFrameActions import DivideColumns, SingleColumnAction
34from lsst.pex.config import FieldValidationError
35from lsst.pipe.base import Struct
38class ConfigurableActionsTestCase(unittest.TestCase):
39 def _createConfig(self, default=None, singleDefault=None):
40 class NewTestConfig(TestConfig):
41 def setDefaults(self):
42 super().setDefaults()
43 if default is not None:
44 for k, v in default.items():
45 setattr(self.actions, k, v)
46 if singleDefault is not None:
47 self.singleAction = singleDefault
48 return NewTestConfig
50 def testConfigInstantiation(self):
51 # This will raise if there is an issue instantiating something
52 configClass = self._createConfig()
53 config = configClass()
54 self.assertTrue(hasattr(config, "actions"))
55 self.assertTrue(hasattr(config, "singleAction"))
57 # test again with default values
58 configClass = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1)
59 config = configClass()
61 # verify the defaults were created
62 self.assertTrue(hasattr(config.actions, "test1"))
63 self.assertTrue(hasattr(config.actions.test1, "var"))
64 self.assertEqual(config.actions.test1.var, 0)
66 self.assertTrue(hasattr(config.singleAction, "var"))
67 self.assertEqual(config.singleAction.var, 0)
69 def testAssignment(self):
70 # Struct actions
71 # Test that a new action can be added with assignment
72 configClass = self._createConfig(default={"test1": ActionTest1})
73 config = configClass()
74 config.actions.test2 = ActionTest2
76 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
77 self.assertEqual(config.actions.test2.var, 1)
79 # verify the same as above, but assigning with instances
80 configClass = self._createConfig(default={"test1": ActionTest1})
81 config = configClass()
82 config.actions.test3 = ActionTest3()
84 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test3"))
85 self.assertEqual(config.actions.test3.var, 3)
87 # The following is designed to support pipeline config setting
88 # Test assignment using the update accessor
89 configClass = self._createConfig(default={"test1": ActionTest1})
90 config = configClass()
91 config.actions.update = {"test2": ActionTest2, "test3": ActionTest3}
93 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3"))
95 configClass = self._createConfig(default={"test1": ActionTest1})
96 configClass2 = self._createConfig(default={"test2": ActionTest2, "test3": ActionTest3})
97 config = configClass()
98 config2 = configClass2()
99 config.actions.update = config2.actions
101 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3"))
103 # verify tha the update interface cannot be used to assign invalid
104 # identifiers
105 configClass = self._createConfig()
106 config = configClass()
107 with self.assertRaises(ValueError):
108 config.actions.update = {"name with space": ActionTest2}
110 with self.assertRaises(ValueError):
111 config.actions.update = {"9leading_number": ActionTest2}
113 # Test remove "assignment" using the remove accessor
114 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2,
115 "test3": ActionTest3})
116 config = configClass()
117 config.actions.remove = ("test1", "test2")
118 self.assertEqual(tuple(config.actions.fieldNames), ("test3", ))
120 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2,
121 "test3": ActionTest3})
122 config = configClass()
123 config.actions.remove = "test1"
124 self.assertEqual(tuple(config.actions.fieldNames), ("test2", "test3"))
126 # singleAction
127 # Test that an action can be reassigned
128 configClass = self._createConfig(singleDefault=ActionTest1)
129 config = configClass()
130 self.assertEqual(config.singleAction(), 0)
132 config.singleAction = ActionTest2
133 self.assertEqual(config.singleAction(), 1)
135 config.singleAction = ActionTest3()
136 self.assertEqual(config.singleAction(), 3)
138 # Verify that ConfigurableActionStructField can be assigned to with
139 # a ConfigurableActionStruct, Struct, and SimpleNamespace
140 otherConfigClass = self._createConfig(default={"test1": ActionTest1(var=1),
141 "test2": ActionTest2(var=2)})
142 assignSource1 = otherConfigClass().actions
143 assignSource2 = Struct(test1=ActionTest1(var=1), test2=ActionTest2(var=2))
144 assignSource3 = SimpleNamespace(test1=ActionTest1(var=1), test2=ActionTest2(var=2))
146 for source in (assignSource1, assignSource2, assignSource3):
147 configClass = self._createConfig()
148 config = configClass()
149 config.actions = source
151 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
152 self.assertEqual((config.actions.test1.var, config.actions.test2.var), (1, 2))
154 # Fail if assigment is ConfigurableActionStructField
155 with self.assertRaises(ValueError):
156 configClass = self._createConfig()
157 config = configClass()
158 config.actions = otherConfigClass.actions
160 # Fail if assignment is some other type
161 with self.assertRaises(ValueError):
162 configClass = self._createConfig()
163 config = configClass()
164 config.actions = {}
166 def testValidate(self):
167 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2,
168 "test3": ActionTest3}, singleDefault=ActionTest1)
169 config = configClass()
170 config.validate()
172 def testFreeze(self):
173 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2},
174 singleDefault=ActionTest1)
175 config = configClass()
176 config.freeze()
178 with self.assertRaises(FieldValidationError):
179 config.actions.test3 = ActionTest3
181 with self.assertRaises(FieldValidationError):
182 config.actions.test1.var = 2
184 with self.assertRaises(FieldValidationError):
185 config.actions.test2.var = 0
187 with self.assertRaises(FieldValidationError):
188 config.singleAction = ActionTest2
190 with self.assertRaises(FieldValidationError):
191 config.singleAction.var = 3
193 def testCompare(self):
194 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2},
195 singleDefault=ActionTest1)
196 config = configClass()
197 config2 = configClass()
199 self.assertTrue(config.compare(config2))
201 # Test equality fail for ConfigurableActionsStructField
202 config3 = configClass()
203 config3.actions.test1.var = 99
204 self.assertFalse(config.compare(config3))
206 # Test equality fail for ConfigurableActionsField
207 config4 = configClass()
208 config4.singleAction.var = 99
209 self.assertFalse(config.compare(config4))
211 def testSave(self):
212 # This method will also test rename, as it is part of the
213 # implementation in pex_config
214 ioObject = StringIO()
215 config = TestConfig()
216 config.actions.test1 = ActionTest1
217 config.actions.test2 = ActionTest2
218 config.singleAction = DivideColumns(
219 colA=SingleColumnAction(column="a"),
220 colB=SingleColumnAction(column="b"),
221 )
223 config.saveToStream(ioObject)
224 string1 = ioObject.getvalue()
225 loadedConfig = TestConfig()
226 loadedConfig.loadFromStream(string1)
227 self.assertTrue(config.compare(loadedConfig), msg=f"{config} != {loadedConfig}")
228 # Be sure that the fields are actually there
229 self.assertEqual(loadedConfig.actions.test1.var, 0)
230 self.assertEqual(loadedConfig.singleAction.colA.column, "a")
231 self.assertEqual(loadedConfig.singleAction.colB.column, "b")
232 # Save an equivalent struct with fields originally ordered differently,
233 # check that the saved form is the same (via deterministic sorting).
234 config2 = TestConfig()
235 config2.actions.test2 = ActionTest2
236 config2.actions.test1 = ActionTest1
237 config2.singleAction = DivideColumns(
238 colB=SingleColumnAction(column="b"),
239 colA=SingleColumnAction(column="a"),
240 )
241 ioObject2 = StringIO()
242 config2.saveToStream(ioObject2)
243 self.maxDiff = None
244 self.assertEqual(string1, ioObject2.getvalue())
246 def testToDict(self):
247 """Test the toDict interface"""
248 configClass = self._createConfig(default={"test1": ActionTest1},
249 singleDefault=ActionTest1)
250 config = configClass()
251 self.assertEqual(config.toDict(), {'actions': {'test1': {'var': 0}}, 'singleAction': {'var': 0}})
254if __name__ == "__main__": 254 ↛ 255line 254 didn't jump to line 255, because the condition on line 254 was never true
255 unittest.main()