Coverage for tests/test_configurableActions.py: 9%
144 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-11 02:59 -0700
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-11 02:59 -0700
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
26from lsst.pex.config import FieldValidationError
27from lsst.pex.config.configurableActions.tests import (
28 ActionTest1,
29 ActionTest2,
30 ActionTest3,
31 TestConfig,
32 TestDivideAction,
33 TestSingleColumnAction,
34)
37class ConfigurableActionsTestCase(unittest.TestCase):
38 def _createConfig(self, default=None, singleDefault=None):
39 class NewTestConfig(TestConfig):
40 def setDefaults(self):
41 super().setDefaults()
42 if default is not None:
43 for k, v in default.items():
44 setattr(self.actions, k, v)
45 if singleDefault is not None:
46 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(
115 default={"test1": ActionTest1, "test2": ActionTest2, "test3": ActionTest3}
116 )
117 config = configClass()
118 config.actions.remove = ("test1", "test2")
119 self.assertEqual(tuple(config.actions.fieldNames), ("test3",))
121 configClass = self._createConfig(
122 default={"test1": ActionTest1, "test2": ActionTest2, "test3": ActionTest3}
123 )
124 config = configClass()
125 config.actions.remove = "test1"
126 self.assertEqual(tuple(config.actions.fieldNames), ("test2", "test3"))
128 # singleAction
129 # Test that an action can be reassigned
130 configClass = self._createConfig(singleDefault=ActionTest1)
131 config = configClass()
132 self.assertEqual(config.singleAction(), 0)
134 config.singleAction = ActionTest2
135 self.assertEqual(config.singleAction(), 1)
137 config.singleAction = ActionTest3()
138 self.assertEqual(config.singleAction(), 3)
140 # Verify that ConfigurableActionStructField can be assigned to with
141 # a ConfigurableActionStruct, SimpleNamespace
142 otherConfigClass = self._createConfig(
143 default={"test1": ActionTest1(var=1), "test2": ActionTest2(var=2)}
144 )
145 assignSource1 = otherConfigClass().actions
146 assignSource2 = SimpleNamespace(test1=ActionTest1(var=1), test2=ActionTest2(var=2))
148 for source in (assignSource1, assignSource2):
149 configClass = self._createConfig()
150 config = configClass()
151 config.actions = source
153 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2"))
154 self.assertEqual((config.actions.test1.var, config.actions.test2.var), (1, 2))
156 # Fail if assigment is ConfigurableActionStructField
157 with self.assertRaises(ValueError):
158 configClass = self._createConfig()
159 config = configClass()
160 config.actions = otherConfigClass.actions
162 # Fail if assignment is some other type
163 with self.assertRaises(ValueError):
164 configClass = self._createConfig()
165 config = configClass()
166 config.actions = {}
168 def testValidate(self):
169 configClass = self._createConfig(
170 default={"test1": ActionTest1, "test2": ActionTest2, "test3": ActionTest3},
171 singleDefault=ActionTest1,
172 )
173 config = configClass()
174 config.validate()
176 def testFreeze(self):
177 configClass = self._createConfig(
178 default={"test1": ActionTest1, "test2": ActionTest2}, singleDefault=ActionTest1
179 )
180 config = configClass()
181 config.freeze()
183 with self.assertRaises(FieldValidationError):
184 config.actions.test3 = ActionTest3
186 with self.assertRaises(FieldValidationError):
187 config.actions.test1.var = 2
189 with self.assertRaises(FieldValidationError):
190 config.actions.test2.var = 0
192 with self.assertRaises(FieldValidationError):
193 config.singleAction = ActionTest2
195 with self.assertRaises(FieldValidationError):
196 config.singleAction.var = 3
198 def testCompare(self):
199 configClass = self._createConfig(
200 default={"test1": ActionTest1, "test2": ActionTest2}, singleDefault=ActionTest1
201 )
202 config = configClass()
203 config2 = configClass()
205 self.assertTrue(config.compare(config2))
207 # Test equality fail for ConfigurableActionsStructField
208 config3 = configClass()
209 config3.actions.test1.var = 99
210 self.assertFalse(config.compare(config3))
212 # Test equality fail for ConfigurableActionsField
213 config4 = configClass()
214 config4.singleAction.var = 99
215 self.assertFalse(config.compare(config4))
217 def testSave(self):
218 # This method will also test rename, as it is part of the
219 # implementation in pex_config
220 ioObject = StringIO()
221 config = TestConfig()
222 config.actions.test1 = ActionTest1
223 config.actions.test2 = ActionTest2
224 config.singleAction = TestDivideAction(
225 colA=TestSingleColumnAction(column="a"),
226 colB=TestSingleColumnAction(column="b"),
227 )
229 config.saveToStream(ioObject)
230 string1 = ioObject.getvalue()
231 loadedConfig = TestConfig()
232 loadedConfig.loadFromStream(string1)
233 self.assertTrue(config.compare(loadedConfig), msg=f"{config} != {loadedConfig}")
234 # Be sure that the fields are actually there
235 self.assertEqual(loadedConfig.actions.test1.var, 0)
236 self.assertEqual(loadedConfig.singleAction.colA.column, "a")
237 self.assertEqual(loadedConfig.singleAction.colB.column, "b")
238 # Save an equivalent struct with fields originally ordered differently,
239 # check that the saved form is the same (via deterministic sorting).
240 config2 = TestConfig()
241 config2.actions.test2 = ActionTest2
242 config2.actions.test1 = ActionTest1
243 config2.singleAction = TestDivideAction(
244 colB=TestSingleColumnAction(column="b"),
245 colA=TestSingleColumnAction(column="a"),
246 )
247 ioObject2 = StringIO()
248 config2.saveToStream(ioObject2)
249 self.maxDiff = None
250 self.assertEqual(string1, ioObject2.getvalue())
252 def testToDict(self):
253 """Test the toDict interface"""
254 configClass = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1)
255 config = configClass()
256 self.assertEqual(config.toDict(), {"actions": {"test1": {"var": 0}}, "singleAction": {"var": 0}})
259if __name__ == "__main__": 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true
260 unittest.main()