Coverage for tests/test_versioning.py: 30%
108 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-21 09:55 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-21 09:55 +0000
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/>.
22import os
23import os.path
24import tempfile
25import unittest
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
38TESTDIR = os.path.abspath(os.path.dirname(__file__))
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)
48class Manager0(VersionedExtension):
49 """Versioned extension implementation for tests."""
51 @classmethod
52 def currentVersions(cls) -> list[VersionTuple]:
53 return []
56class Manager1(VersionedExtension):
57 """Versioned extension implementation for tests."""
59 @classmethod
60 def currentVersions(cls) -> list[VersionTuple]:
61 return [V_1_0_0]
64class Manager1_1(VersionedExtension): # noqa: N801
65 """Versioned extension implementation for tests."""
67 @classmethod
68 def currentVersions(cls) -> list[VersionTuple]:
69 return [V_1_1_0]
72class Manager2(VersionedExtension):
73 """Versioned extension implementation for tests.
75 This extension supports two schema versions.
76 """
78 @classmethod
79 def currentVersions(cls) -> list[VersionTuple]:
80 return [V_1_0_0, V_2_0_0]
82 @classmethod
83 def _newDefaultSchemaVersion(cls) -> VersionTuple:
84 return V_1_0_0
87class SchemaVersioningTestCase(unittest.TestCase):
88 """Tests for schema versioning classes."""
90 def setUp(self):
91 self.root = makeTestTempDir(TESTDIR)
93 def tearDown(self):
94 removeTestTempDir(self.root)
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)
101 def test_new_schema(self) -> None:
102 """Test for creating new database schema."""
103 # Check that managers know what schema versions they can make.
104 Manager1.checkNewSchemaVersion(V_1_0_0)
105 Manager2.checkNewSchemaVersion(V_1_0_0)
106 Manager2.checkNewSchemaVersion(V_2_0_0)
107 with self.assertRaises(IncompatibleVersionError):
108 Manager1.checkNewSchemaVersion(V_1_0_1)
109 with self.assertRaises(IncompatibleVersionError):
110 Manager1.checkNewSchemaVersion(V_1_1_0)
111 with self.assertRaises(IncompatibleVersionError):
112 Manager2.checkNewSchemaVersion(V_1_0_1)
114 manager_versions = (
115 ((None, V_1_0_0), (None, V_1_0_0)),
116 ((V_1_0_0, V_1_0_0), (V_1_0_0, V_1_0_0)),
117 ((None, V_1_0_0), (V_2_0_0, V_2_0_0)),
118 )
120 for (v1, result1), (v2, result2) in manager_versions:
121 # This is roughly what RegistryManagerTypes.makeRepo does.
122 if v1 is not None:
123 Manager1.checkNewSchemaVersion(v1)
124 if v2 is not None:
125 Manager2.checkNewSchemaVersion(v2)
126 manager0 = Manager0()
127 manager1 = Manager1(registry_schema_version=v1)
128 manager2 = Manager2(registry_schema_version=v2)
129 self.assertEqual(manager1.newSchemaVersion(), result1)
130 self.assertEqual(manager2.newSchemaVersion(), result2)
132 database = self.makeEmptyDatabase()
133 with database.declareStaticTables(create=True) as context:
134 attributes = DefaultButlerAttributeManager.initialize(database, context)
136 vmgr = ButlerVersionsManager(attributes)
137 vmgr.storeManagersConfig({"manager0": manager0, "manager1": manager1, "manager2": manager2})
139 attr_dict = {key: value for key, value in attributes.items()}
140 expected = {
141 "config:registry.managers.manager0": Manager0.extensionName(),
142 "config:registry.managers.manager1": Manager1.extensionName(),
143 "config:registry.managers.manager2": Manager2.extensionName(),
144 f"version:{Manager1.extensionName()}": str(result1),
145 f"version:{Manager2.extensionName()}": str(result2),
146 }
147 self.assertEqual(attr_dict, expected)
149 def test_existing_schema(self) -> None:
150 """Test for reading manager versions from existing database."""
151 manager_versions = (
152 ((None, V_1_0_0), (None, V_1_0_0)),
153 ((V_1_0_0, V_1_0_0), (V_1_0_0, V_1_0_0)),
154 )
156 for (v1, result1), (v2, result2) in manager_versions:
157 # This is roughly what RegistryManagerTypes.loadRepo does.
158 if v1 is not None:
159 Manager1.checkNewSchemaVersion(v1)
160 if v2 is not None:
161 Manager2.checkNewSchemaVersion(v2)
162 manager0 = Manager0()
163 manager1 = Manager1(registry_schema_version=v1)
164 manager2 = Manager2(registry_schema_version=v2)
166 # Create new schema first.
167 database = self.makeEmptyDatabase()
168 with database.declareStaticTables(create=True) as context:
169 attributes = DefaultButlerAttributeManager.initialize(database, context)
171 vmgr = ButlerVersionsManager(attributes)
172 vmgr.storeManagersConfig({"manager0": manager0, "manager1": manager1, "manager2": manager2})
174 # Switch to reading existing manager configs/versions.
175 with database.declareStaticTables(create=False) as context:
176 attributes = DefaultButlerAttributeManager.initialize(database, context)
178 vmgr = ButlerVersionsManager(attributes)
179 vmgr.checkManagersConfig({"manager0": Manager0, "manager1": Manager1, "manager2": Manager2})
180 versions = vmgr.managerVersions()
182 Manager1.checkCompatibility(result1, database.isWriteable())
183 Manager2.checkCompatibility(result2, database.isWriteable())
185 # Make manager instances using versions from registry.
186 manager0 = Manager0(registry_schema_version=versions.get("manager0"))
187 manager1 = Manager1(registry_schema_version=versions.get("manager1"))
188 manager2 = Manager2(registry_schema_version=versions.get("manager2"))
189 self.assertIsNone(manager0._registry_schema_version)
190 self.assertEqual(manager1._registry_schema_version, result1)
191 self.assertEqual(manager2._registry_schema_version, result2)
193 def test_compatibility(self) -> None:
194 """Test for version compatibility rules."""
195 # Manager, version, update, compatible
196 compat_matrix = (
197 (Manager0, V_1_0_0, False, True),
198 (Manager0, V_1_0_0, True, True),
199 (Manager1, V_1_0_0, False, True),
200 (Manager1, V_1_0_0, True, True),
201 (Manager1, V_1_0_1, False, True),
202 (Manager1, V_1_0_1, True, True),
203 (Manager1, V_1_1_0, False, False),
204 (Manager1, V_1_1_0, True, False),
205 (Manager1, V_2_0_0, False, False),
206 (Manager1, V_2_0_0, True, False),
207 (Manager1_1, V_1_0_0, False, True),
208 (Manager1_1, V_1_0_0, True, False),
209 (Manager1_1, V_1_0_1, False, True),
210 (Manager1_1, V_1_0_1, True, False),
211 (Manager1_1, V_1_1_0, False, True),
212 (Manager1_1, V_1_1_0, True, True),
213 (Manager2, V_1_0_0, False, True),
214 (Manager2, V_1_0_0, True, True),
215 (Manager2, V_1_0_1, False, True),
216 (Manager2, V_1_0_1, True, True),
217 (Manager2, V_1_1_0, False, False),
218 (Manager2, V_1_1_0, True, False),
219 (Manager2, V_2_0_0, False, True),
220 (Manager2, V_2_0_0, True, True),
221 )
223 for Manager, version, update, compatible in compat_matrix:
224 with self.subTest(test=(Manager, version, update, compatible)):
225 if compatible:
226 Manager.checkCompatibility(version, update)
227 else:
228 with self.assertRaises(IncompatibleVersionError):
229 Manager.checkCompatibility(version, update)
232if __name__ == "__main__":
233 unittest.main()