Coverage for tests/test_versioning.py: 25%

108 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-07 00:58 -0700

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://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 <http://www.gnu.org/licenses/>. 

21 

22import os 

23import os.path 

24import tempfile 

25import unittest 

26 

27from lsst.daf.butler.registry.attributes import DefaultButlerAttributeManager 

28from lsst.daf.butler.registry.databases.sqlite import SqliteDatabase 

29from lsst.daf.butler.registry.interfaces import ( 

30 Database, 

31 IncompatibleVersionError, 

32 VersionedExtension, 

33 VersionTuple, 

34) 

35from lsst.daf.butler.registry.versions import ButlerVersionsManager 

36from lsst.daf.butler.tests.utils import makeTestTempDir, removeTestTempDir 

37 

38TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

39 

40# Assorted version numbers used throughout the tests 

41V_1_0_0 = VersionTuple(major=1, minor=0, patch=0) 

42V_1_0_1 = VersionTuple(major=1, minor=0, patch=1) 

43V_1_1_0 = VersionTuple(major=1, minor=1, patch=0) 

44V_2_0_0 = VersionTuple(major=2, minor=0, patch=0) 

45V_2_0_1 = VersionTuple(major=2, minor=0, patch=1) 

46 

47 

48class Manager0(VersionedExtension): 

49 """Versioned extension implementation for tests.""" 

50 

51 @classmethod 

52 def currentVersions(cls) -> list[VersionTuple]: 

53 return [] 

54 

55 

56class Manager1(VersionedExtension): 

57 """Versioned extension implementation for tests.""" 

58 

59 @classmethod 

60 def currentVersions(cls) -> list[VersionTuple]: 

61 return [V_1_0_0] 

62 

63 

64class Manager1_1(VersionedExtension): # noqa: N801 

65 """Versioned extension implementation for tests.""" 

66 

67 @classmethod 

68 def currentVersions(cls) -> list[VersionTuple]: 

69 return [V_1_1_0] 

70 

71 

72class Manager2(VersionedExtension): 

73 """Versioned extension implementation for tests. 

74 

75 This extension supports two schema versions. 

76 """ 

77 

78 @classmethod 

79 def currentVersions(cls) -> list[VersionTuple]: 

80 return [V_1_0_0, V_2_0_0] 

81 

82 @classmethod 

83 def _newDefaultSchemaVersion(cls) -> VersionTuple: 

84 return V_1_0_0 

85 

86 

87class SchemaVersioningTestCase(unittest.TestCase): 

88 """Tests for schema versioning classes.""" 

89 

90 def setUp(self): 

91 self.root = makeTestTempDir(TESTDIR) 

92 

93 def tearDown(self): 

94 removeTestTempDir(self.root) 

95 

96 def makeEmptyDatabase(self, origin: int = 0) -> Database: 

97 _, filename = tempfile.mkstemp(dir=self.root, suffix=".sqlite3") 

98 engine = SqliteDatabase.makeEngine(filename=filename) 

99 return SqliteDatabase.fromEngine(engine=engine, origin=origin) 

100 

101 def test_new_schema(self) -> None: 

102 """Test for creating new database schema.""" 

103 

104 # Check that managers know what schema versions they can make. 

105 Manager1.checkNewSchemaVersion(V_1_0_0) 

106 Manager2.checkNewSchemaVersion(V_1_0_0) 

107 Manager2.checkNewSchemaVersion(V_2_0_0) 

108 with self.assertRaises(IncompatibleVersionError): 

109 Manager1.checkNewSchemaVersion(V_1_0_1) 

110 with self.assertRaises(IncompatibleVersionError): 

111 Manager1.checkNewSchemaVersion(V_1_1_0) 

112 with self.assertRaises(IncompatibleVersionError): 

113 Manager2.checkNewSchemaVersion(V_1_0_1) 

114 

115 manager_versions = ( 

116 ((None, V_1_0_0), (None, V_1_0_0)), 

117 ((V_1_0_0, V_1_0_0), (V_1_0_0, V_1_0_0)), 

118 ((None, V_1_0_0), (V_2_0_0, V_2_0_0)), 

119 ) 

120 

121 for (v1, result1), (v2, result2) in manager_versions: 

122 # This is roughly what RegistryManagerTypes.makeRepo does. 

123 if v1 is not None: 

124 Manager1.checkNewSchemaVersion(v1) 

125 if v2 is not None: 

126 Manager2.checkNewSchemaVersion(v2) 

127 manager0 = Manager0() 

128 manager1 = Manager1(registry_schema_version=v1) 

129 manager2 = Manager2(registry_schema_version=v2) 

130 self.assertEqual(manager1.newSchemaVersion(), result1) 

131 self.assertEqual(manager2.newSchemaVersion(), result2) 

132 

133 database = self.makeEmptyDatabase() 

134 with database.declareStaticTables(create=True) as context: 

135 attributes = DefaultButlerAttributeManager.initialize(database, context) 

136 

137 vmgr = ButlerVersionsManager(attributes) 

138 vmgr.storeManagersConfig({"manager0": manager0, "manager1": manager1, "manager2": manager2}) 

139 

140 attr_dict = {key: value for key, value in attributes.items()} 

141 expected = { 

142 "config:registry.managers.manager0": Manager0.extensionName(), 

143 "config:registry.managers.manager1": Manager1.extensionName(), 

144 "config:registry.managers.manager2": Manager2.extensionName(), 

145 f"version:{Manager1.extensionName()}": str(result1), 

146 f"version:{Manager2.extensionName()}": str(result2), 

147 } 

148 self.assertEqual(attr_dict, expected) 

149 

150 def test_existing_schema(self) -> None: 

151 """Test for reading manager versions from existing database.""" 

152 

153 manager_versions = ( 

154 ((None, V_1_0_0), (None, V_1_0_0)), 

155 ((V_1_0_0, V_1_0_0), (V_1_0_0, V_1_0_0)), 

156 ) 

157 

158 for (v1, result1), (v2, result2) in manager_versions: 

159 # This is roughly what RegistryManagerTypes.loadRepo does. 

160 if v1 is not None: 

161 Manager1.checkNewSchemaVersion(v1) 

162 if v2 is not None: 

163 Manager2.checkNewSchemaVersion(v2) 

164 manager0 = Manager0() 

165 manager1 = Manager1(registry_schema_version=v1) 

166 manager2 = Manager2(registry_schema_version=v2) 

167 

168 # Create new schema first. 

169 database = self.makeEmptyDatabase() 

170 with database.declareStaticTables(create=True) as context: 

171 attributes = DefaultButlerAttributeManager.initialize(database, context) 

172 

173 vmgr = ButlerVersionsManager(attributes) 

174 vmgr.storeManagersConfig({"manager0": manager0, "manager1": manager1, "manager2": manager2}) 

175 

176 # Switch to reading existing manager configs/versions. 

177 with database.declareStaticTables(create=False) as context: 

178 attributes = DefaultButlerAttributeManager.initialize(database, context) 

179 

180 vmgr = ButlerVersionsManager(attributes) 

181 vmgr.checkManagersConfig({"manager0": Manager0, "manager1": Manager1, "manager2": Manager2}) 

182 versions = vmgr.managerVersions() 

183 

184 Manager1.checkCompatibility(result1, database.isWriteable()) 

185 Manager2.checkCompatibility(result2, database.isWriteable()) 

186 

187 # Make manager instances using versions from registry. 

188 manager0 = Manager0(registry_schema_version=versions.get("manager0")) 

189 manager1 = Manager1(registry_schema_version=versions.get("manager1")) 

190 manager2 = Manager2(registry_schema_version=versions.get("manager2")) 

191 self.assertIsNone(manager0._registry_schema_version) 

192 self.assertEqual(manager1._registry_schema_version, result1) 

193 self.assertEqual(manager2._registry_schema_version, result2) 

194 

195 def test_compatibility(self) -> None: 

196 """Test for version compatibility rules.""" 

197 

198 # Manager, version, update, compatible 

199 compat_matrix = ( 

200 (Manager0, V_1_0_0, False, True), 

201 (Manager0, V_1_0_0, True, True), 

202 (Manager1, V_1_0_0, False, True), 

203 (Manager1, V_1_0_0, True, True), 

204 (Manager1, V_1_0_1, False, True), 

205 (Manager1, V_1_0_1, True, True), 

206 (Manager1, V_1_1_0, False, False), 

207 (Manager1, V_1_1_0, True, False), 

208 (Manager1, V_2_0_0, False, False), 

209 (Manager1, V_2_0_0, True, False), 

210 (Manager1_1, V_1_0_0, False, True), 

211 (Manager1_1, V_1_0_0, True, False), 

212 (Manager1_1, V_1_0_1, False, True), 

213 (Manager1_1, V_1_0_1, True, False), 

214 (Manager1_1, V_1_1_0, False, True), 

215 (Manager1_1, V_1_1_0, True, True), 

216 (Manager2, V_1_0_0, False, True), 

217 (Manager2, V_1_0_0, True, True), 

218 (Manager2, V_1_0_1, False, True), 

219 (Manager2, V_1_0_1, True, True), 

220 (Manager2, V_1_1_0, False, False), 

221 (Manager2, V_1_1_0, True, False), 

222 (Manager2, V_2_0_0, False, True), 

223 (Manager2, V_2_0_0, True, True), 

224 ) 

225 

226 for Manager, version, update, compatible in compat_matrix: 

227 with self.subTest(test=(Manager, version, update, compatible)): 

228 if compatible: 

229 Manager.checkCompatibility(version, update) 

230 else: 

231 with self.assertRaises(IncompatibleVersionError): 

232 Manager.checkCompatibility(version, update) 

233 

234 

235if __name__ == "__main__": 

236 unittest.main()