Coverage for tests/test_configurableActions.py: 10%
145 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-01 12:22 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-01 12:22 +0000
1# This file is part of pex_config.
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
26import lsst.pex.config.configurableActions.tests as actionTests
27from lsst.pex.config import FieldValidationError
28from lsst.pex.config.configurableActions.tests import ActionTest1, ActionTest2, ActionTest3
31class ConfigurableActionsTestCase(unittest.TestCase):
32 """Test fo ConfigurableActions."""
34 def _createConfig(self, default=None, singleDefault=None):
35 class NewTestConfig(actionTests.TestConfig):
36 def setDefaults(self):
37 super().setDefaults()
38 if default is not None:
39 for k, v in default.items():
40 setattr(self.actions, k, v)
41 if singleDefault is not None:
42 self.singleAction = singleDefault
44 return NewTestConfig
46 def testConfigInstantiation(self):
47 # This will raise if there is an issue instantiating something
48 configClass = self._createConfig()
49 config = configClass()
50 self.assertTrue(hasattr(config, "actions"))
51 self.assertTrue(hasattr(config, "singleAction"))
53 # test again with default values
54 configClass = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1)
55 config = configClass()
57 # verify the defaults were created
58 self.assertTrue(hasattr(config.actions, "test1"))
59 self.assertTrue(hasattr(config.actions.test1, "var"))
60 self.assertEqual(config.actions.test1.var, 0)
62 self.assertTrue(hasattr(config.singleAction, "var"))
63 self.assertEqual(config.singleAction.var, 0)
65 def testAssignment(self):
66 # Struct actions
67 # Test that a new action can be added with assignment
68 configClass = self._createConfig(default={"test1": ActionTest1})
69 config = configClass()
70 config.actions.test2 = ActionTest2
72 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
73 self.assertEqual(config.actions.test2.var, 1)
75 # verify the same as above, but assigning with instances
76 configClass = self._createConfig(default={"test1": ActionTest1})
77 config = configClass()
78 config.actions.test3 = ActionTest3()
80 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test3"))
81 self.assertEqual(config.actions.test3.var, 3)
83 # The following is designed to support pipeline config setting
84 # Test assignment using the update accessor
85 configClass = self._createConfig(default={"test1": ActionTest1})
86 config = configClass()
87 config.actions.update = {"test2": ActionTest2, "test3": ActionTest3}
89 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3"))
91 configClass = self._createConfig(default={"test1": ActionTest1})
92 configClass2 = self._createConfig(default={"test2": ActionTest2, "test3": ActionTest3})
93 config = configClass()
94 config2 = configClass2()
95 config.actions.update = config2.actions
97 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3"))
99 # verify tha the update interface cannot be used to assign invalid
100 # identifiers
101 configClass = self._createConfig()
102 config = configClass()
103 with self.assertRaises(ValueError):
104 config.actions.update = {"name with space": ActionTest2}
106 with self.assertRaises(ValueError):
107 config.actions.update = {"9leading_number": ActionTest2}
109 # Test remove "assignment" using the remove accessor
110 configClass = self._createConfig(
111 default={"test1": ActionTest1, "test2": ActionTest2, "test3": ActionTest3}
112 )
113 config = configClass()
114 config.actions.remove = ("test1", "test2")
115 self.assertEqual(tuple(config.actions.fieldNames), ("test3",))
117 configClass = self._createConfig(
118 default={"test1": ActionTest1, "test2": ActionTest2, "test3": ActionTest3}
119 )
120 config = configClass()
121 config.actions.remove = "test1"
122 self.assertEqual(tuple(config.actions.fieldNames), ("test2", "test3"))
124 # singleAction
125 # Test that an action can be reassigned
126 configClass = self._createConfig(singleDefault=ActionTest1)
127 config = configClass()
128 self.assertEqual(config.singleAction(), 0)
130 config.singleAction = ActionTest2
131 self.assertEqual(config.singleAction(), 1)
133 config.singleAction = ActionTest3()
134 self.assertEqual(config.singleAction(), 3)
136 # Verify that ConfigurableActionStructField can be assigned to with
137 # a ConfigurableActionStruct, SimpleNamespace
138 otherConfigClass = self._createConfig(
139 default={"test1": ActionTest1(var=1), "test2": ActionTest2(var=2)}
140 )
141 assignSource1 = otherConfigClass().actions
142 assignSource2 = SimpleNamespace(test1=ActionTest1(var=1), test2=ActionTest2(var=2))
144 for source in (assignSource1, assignSource2):
145 configClass = self._createConfig()
146 config = configClass()
147 config.actions = source
149 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
150 self.assertEqual((config.actions.test1.var, config.actions.test2.var), (1, 2))
152 # Fail if assigment is ConfigurableActionStructField
153 with self.assertRaises(ValueError):
154 configClass = self._createConfig()
155 config = configClass()
156 config.actions = otherConfigClass.actions
158 # Fail if assignment is some other type
159 with self.assertRaises(ValueError):
160 configClass = self._createConfig()
161 config = configClass()
162 config.actions = {}
164 def testValidate(self):
165 configClass = self._createConfig(
166 default={"test1": ActionTest1, "test2": ActionTest2, "test3": ActionTest3},
167 singleDefault=ActionTest1,
168 )
169 config = configClass()
170 config.validate()
172 def testFreeze(self):
173 configClass = self._createConfig(
174 default={"test1": ActionTest1, "test2": ActionTest2}, singleDefault=ActionTest1
175 )
176 config = configClass()
177 config.freeze()
179 with self.assertRaises(FieldValidationError):
180 config.actions.test3 = ActionTest3
182 with self.assertRaises(FieldValidationError):
183 config.actions.test1.var = 2
185 with self.assertRaises(FieldValidationError):
186 config.actions.test2.var = 0
188 with self.assertRaises(FieldValidationError):
189 config.singleAction = ActionTest2
191 with self.assertRaises(FieldValidationError):
192 config.singleAction.var = 3
194 def testCompare(self):
195 configClass = self._createConfig(
196 default={"test1": ActionTest1, "test2": ActionTest2}, singleDefault=ActionTest1
197 )
198 config = configClass()
199 config2 = configClass()
201 self.assertTrue(config.compare(config2))
203 # Test equality fail for ConfigurableActionsStructField
204 config3 = configClass()
205 config3.actions.test1.var = 99
206 self.assertFalse(config.compare(config3))
208 # Test equality fail for ConfigurableActionsField
209 config4 = configClass()
210 config4.singleAction.var = 99
211 self.assertFalse(config.compare(config4))
213 def testSave(self):
214 # This method will also test rename, as it is part of the
215 # implementation in pex_config
216 ioObject = StringIO()
217 config = actionTests.TestConfig()
218 config.actions.test1 = ActionTest1
219 config.actions.test2 = ActionTest2
220 config.singleAction = actionTests.TestDivideAction(
221 colA=actionTests.TestSingleColumnAction(column="a"),
222 colB=actionTests.TestSingleColumnAction(column="b"),
223 )
225 config.saveToStream(ioObject)
226 string1 = ioObject.getvalue()
227 loadedConfig = actionTests.TestConfig()
228 loadedConfig.loadFromStream(string1)
229 self.assertTrue(config.compare(loadedConfig), msg=f"{config} != {loadedConfig}")
230 # Be sure that the fields are actually there
231 self.assertEqual(loadedConfig.actions.test1.var, 0)
232 self.assertEqual(loadedConfig.singleAction.colA.column, "a")
233 self.assertEqual(loadedConfig.singleAction.colB.column, "b")
234 # Save an equivalent struct with fields originally ordered differently,
235 # check that the saved form is the same (via deterministic sorting).
236 config2 = actionTests.TestConfig()
237 config2.actions.test2 = ActionTest2
238 config2.actions.test1 = ActionTest1
239 config2.singleAction = actionTests.TestDivideAction(
240 colB=actionTests.TestSingleColumnAction(column="b"),
241 colA=actionTests.TestSingleColumnAction(column="a"),
242 )
243 ioObject2 = StringIO()
244 config2.saveToStream(ioObject2)
245 self.maxDiff = None
246 self.assertEqual(string1, ioObject2.getvalue())
248 def testToDict(self):
249 """Test the toDict interface."""
250 configClass = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1)
251 config = configClass()
252 self.assertEqual(config.toDict(), {"actions": {"test1": {"var": 0}}, "singleAction": {"var": 0}})
255if __name__ == "__main__": 255 ↛ 256line 255 didn't jump to line 256, because the condition on line 255 was never true
256 unittest.main()