Coverage for tests/test_configurableActions.py: 10%

147 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-21 02:32 -0800

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/>. 

21 

22import unittest 

23from io import StringIO 

24from types import SimpleNamespace 

25 

26from lsst.pipe.tasks.configurableActions.tests import ( 

27 ActionTest1, 

28 ActionTest2, 

29 ActionTest3, 

30 TestConfig, 

31) 

32from lsst.pipe.tasks.dataFrameActions import DivideColumns, SingleColumnAction 

33 

34from lsst.pex.config import FieldValidationError 

35from lsst.pipe.base import Struct 

36 

37 

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 

49 

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")) 

56 

57 # test again with default values 

58 configClass = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1) 

59 config = configClass() 

60 

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) 

65 

66 self.assertTrue(hasattr(config.singleAction, "var")) 

67 self.assertEqual(config.singleAction.var, 0) 

68 

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 

75 

76 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2")) 

77 self.assertEqual(config.actions.test2.var, 1) 

78 

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() 

83 

84 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test3")) 

85 self.assertEqual(config.actions.test3.var, 3) 

86 

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} 

92 

93 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3")) 

94 

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 

100 

101 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2", "test3")) 

102 

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} 

109 

110 with self.assertRaises(ValueError): 

111 config.actions.update = {"9leading_number": ActionTest2} 

112 

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", )) 

119 

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")) 

125 

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) 

131 

132 config.singleAction = ActionTest2 

133 self.assertEqual(config.singleAction(), 1) 

134 

135 config.singleAction = ActionTest3() 

136 self.assertEqual(config.singleAction(), 3) 

137 

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)) 

145 

146 for source in (assignSource1, assignSource2, assignSource3): 

147 configClass = self._createConfig() 

148 config = configClass() 

149 config.actions = source 

150 

151 self.assertEqual(tuple(config.actions.fieldNames), ("test1", "test2")) 

152 self.assertEqual((config.actions.test1.var, config.actions.test2.var), (1, 2)) 

153 

154 # Fail if assigment is ConfigurableActionStructField 

155 with self.assertRaises(ValueError): 

156 configClass = self._createConfig() 

157 config = configClass() 

158 config.actions = otherConfigClass.actions 

159 

160 # Fail if assignment is some other type 

161 with self.assertRaises(ValueError): 

162 configClass = self._createConfig() 

163 config = configClass() 

164 config.actions = {} 

165 

166 def testValidate(self): 

167 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2, 

168 "test3": ActionTest3}, singleDefault=ActionTest1) 

169 config = configClass() 

170 config.validate() 

171 

172 def testFreeze(self): 

173 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2}, 

174 singleDefault=ActionTest1) 

175 config = configClass() 

176 config.freeze() 

177 

178 with self.assertRaises(FieldValidationError): 

179 config.actions.test3 = ActionTest3 

180 

181 with self.assertRaises(FieldValidationError): 

182 config.actions.test1.var = 2 

183 

184 with self.assertRaises(FieldValidationError): 

185 config.actions.test2.var = 0 

186 

187 with self.assertRaises(FieldValidationError): 

188 config.singleAction = ActionTest2 

189 

190 with self.assertRaises(FieldValidationError): 

191 config.singleAction.var = 3 

192 

193 def testCompare(self): 

194 configClass = self._createConfig(default={"test1": ActionTest1, "test2": ActionTest2}, 

195 singleDefault=ActionTest1) 

196 config = configClass() 

197 config2 = configClass() 

198 

199 self.assertTrue(config.compare(config2)) 

200 

201 # Test equality fail for ConfigurableActionsStructField 

202 config3 = configClass() 

203 config3.actions.test1.var = 99 

204 self.assertFalse(config.compare(config3)) 

205 

206 # Test equality fail for ConfigurableActionsField 

207 config4 = configClass() 

208 config4.singleAction.var = 99 

209 self.assertFalse(config.compare(config4)) 

210 

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 ) 

222 

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()) 

245 

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}}) 

252 

253 

254if __name__ == "__main__": 254 ↛ 255line 254 didn't jump to line 255, because the condition on line 254 was never true

255 unittest.main()