Coverage for tests/test_configurableActions.py: 21%
147 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-13 03:31 -0700
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-13 03:31 -0700
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 import (ConfigurableActionStructField, ConfigurableAction,
27 ConfigurableActionField)
28from lsst.pex.config import Config, Field, FieldValidationError
29from lsst.pipe.base import Struct
32class ActionTest1(ConfigurableAction):
33 var = Field(doc="test field", dtype=int, default=0)
35 def __call__(self):
36 return self.var
38 def validate(self):
39 assert(self.var is not None)
42class ActionTest2(ConfigurableAction):
43 var = Field(doc="test field", dtype=int, default=1)
45 def __call__(self):
46 return self.var
48 def validate(self):
49 assert(self.var is not None)
52class ActionTest3(ConfigurableAction):
53 var = Field(doc="test field", dtype=int, default=3)
55 def __call__(self):
56 return self.var
58 def validate(self):
59 assert(self.var is not None)
62class ConfigurableActionsTestCase(unittest.TestCase):
63 def _createConfig(self, default=None, singleDefault=None):
64 class TestConfig(Config):
65 actions = ConfigurableActionStructField(doc="Actions to be tested", default=default)
66 singleAction = ConfigurableActionField(doc="A configurable action", default=singleDefault)
67 return TestConfig
69 def testConfigInstatiation(self):
70 # This will raise if there is an issue instantiating something
71 configClass = self._createConfig()
72 config = configClass()
73 self.assertTrue(hasattr(config, "actions"))
74 self.assertTrue(hasattr(config, "singleAction"))
76 # test again with default values
77 configClass = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1)
78 config = configClass()
80 # verify the defaults were created
81 self.assertTrue(hasattr(config.actions, "test1"))
82 self.assertTrue(hasattr(config.actions.test1, "var"))
83 self.assertEqual(config.actions.test1.var, 0)
85 self.assertTrue(hasattr(config.singleAction, "var"))
86 self.assertEqual(config.singleAction.var, 0)
88 def testAssignment(self):
89 # Struct actions
90 # Test that a new action can be added with assignment
91 configClass = self._createConfig(default={"test1": ActionTest1})
92 config = configClass()
93 config.actions.test2 = ActionTest2
95 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
96 self.assertEqual(config.actions.test2.var, 1)
98 # verify the same as above, but assigning with instances
99 configClass = self._createConfig(default={"test1": ActionTest1})
100 config = configClass()
101 config.actions.test3 = ActionTest3()
103 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test3"))
104 self.assertEqual(config.actions.test3.var, 3)
106 # The following is designed to support pipeline config setting
107 # Test assignment using the update accessor
108 configClass = self._createConfig(default={"test1": ActionTest1})
109 config = configClass()
110 config.actions.update = {"test2": ActionTest2, "test3": ActionTest3}
112 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3"))
114 configClass = self._createConfig(default={"test1": ActionTest1})
115 configClass2 = self._createConfig(default={"test2": ActionTest2, "test3": ActionTest3})
116 config = configClass()
117 config2 = configClass2()
118 config.actions.update = config2.actions
120 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3"))
122 # verify tha the update interface cannot be used to assign invalid
123 # identifiers
124 configClass = self._createConfig()
125 config = configClass()
126 with self.assertRaises(ValueError):
127 config.actions.update = {"name with space": ActionTest2}
129 with self.assertRaises(ValueError):
130 config.actions.update = {"9leading_number": ActionTest2}
132 # Test remove "assignment" using the remove accessor
133 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2,
134 "test3": ActionTest3})
135 config = configClass()
136 config.actions.remove = ("test1", "test2")
137 self.assertEqual(tuple(config.actions.fieldNames), ("test3", ))
139 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2,
140 "test3": ActionTest3})
141 config = configClass()
142 config.actions.remove = "test1"
143 self.assertEqual(tuple(config.actions.fieldNames), ("test2", "test3"))
145 # singleAction
146 # Test that an action can be reassigned
147 configClass = self._createConfig(singleDefault=ActionTest1)
148 config = configClass()
149 self.assertEqual(config.singleAction(), 0)
151 config.singleAction = ActionTest2
152 self.assertEqual(config.singleAction(), 1)
154 config.singleAction = ActionTest3()
155 self.assertEqual(config.singleAction(), 3)
157 # Verify that ConfigurableActionStructField can be assigned to with
158 # a ConfigurableActionStruct, Struct, and SimpleNamespace
159 otherConfigClass = self._createConfig(default={"test1": ActionTest1(var=1),
160 "test2": ActionTest2(var=2)})
161 assignSource1 = otherConfigClass().actions
162 assignSource2 = Struct(test1=ActionTest1(var=1), test2=ActionTest2(var=2))
163 assignSource3 = SimpleNamespace(test1=ActionTest1(var=1), test2=ActionTest2(var=2))
165 for source in (assignSource1, assignSource2, assignSource3):
166 configClass = self._createConfig()
167 config = configClass()
168 config.actions = source
170 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
171 self.assertEqual((config.actions.test1.var, config.actions.test2.var), (1, 2))
173 # Fail if assigment is ConfigurableActionStructField
174 with self.assertRaises(ValueError):
175 configClass = self._createConfig()
176 config = configClass()
177 config.actions = otherConfigClass.actions
179 # Fail if assignment is some other type
180 with self.assertRaises(ValueError):
181 configClass = self._createConfig()
182 config = configClass()
183 config.actions = {}
185 def testValidate(self):
186 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2,
187 "test3": ActionTest3}, singleDefault=ActionTest1)
188 config = configClass()
189 config.validate()
191 def testFreeze(self):
192 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2},
193 singleDefault=ActionTest1)
194 config = configClass()
195 config.freeze()
197 with self.assertRaises(FieldValidationError):
198 config.actions.test3 = ActionTest3
200 with self.assertRaises(FieldValidationError):
201 config.actions.test1.var = 2
203 with self.assertRaises(FieldValidationError):
204 config.actions.test2.var = 0
206 with self.assertRaises(FieldValidationError):
207 config.singleAction = ActionTest2
209 with self.assertRaises(FieldValidationError):
210 config.singleAction.var = 3
212 def testCompare(self):
213 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2},
214 singleDefault=ActionTest1)
215 config = configClass()
216 config2 = configClass()
218 self.assertTrue(config.compare(config2))
220 # Test equality fail for ConfigurableActionsStructField
221 config3 = configClass()
222 config3.actions.test1.var = 99
223 self.assertFalse(config.compare(config3))
225 # Test equality fail for ConfigurableActionsField
226 config4 = configClass()
227 config4.singleAction.var = 99
228 self.assertFalse(config.compare(config4))
230 def testSave(self):
231 # This method will also test rename, as it is part of the
232 # implementation in pex_config
233 ioObject = StringIO()
234 configClass = self._createConfig(default={"test1": ActionTest1},
235 singleDefault=ActionTest1)
236 config = configClass()
238 config.saveToStream(ioObject)
239 loadedConfig = configClass()
240 loadedConfig.loadFromStream(ioObject.read())
241 self.assertTrue(config.compare(loadedConfig))
242 # Be sure that the fields are actually there
243 self.assertEqual(loadedConfig.actions.test1.var, 0)
244 self.assertEqual(loadedConfig.singleAction.var, 0)
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()