Coverage for tests/test_sqlite.py : 26%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# This file is part of daf_butler. # # Developed for the LSST Data Management System. # This product includes software developed by the LSST Project # (http://www.lsst.org). # See the COPYRIGHT file at the top-level directory of this distribution # for details of code ownership. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>.
def removeWritePermission(filename): mode = os.stat(filename).st_mode try: os.chmod(filename, stat.S_IREAD) yield finally: os.chmod(filename, mode)
"""Check whether we really can modify a database.
This intentionally allows any exception to be raised (not just `ReadOnlyDatabaseError`) to deal with cases where the file is read-only but the Database was initialized (incorrectly) with writeable=True. """ try: with database.declareStaticTables(create=True) as context: context.addTable( "a", ddl.TableSpec(fields=[ddl.FieldSpec("b", dtype=sqlalchemy.Integer, primaryKey=True)]) ) return True except Exception: return False
"""Tests for `SqliteDatabase` using a standard file-based database. """
self.root = tempfile.mkdtemp(dir=TESTDIR)
if self.root is not None and os.path.exists(self.root): shutil.rmtree(self.root, ignore_errors=True)
_, filename = tempfile.mkstemp(dir=self.root, suffix=".sqlite3") connection = SqliteDatabase.connect(filename=filename) return SqliteDatabase.fromConnection(connection=connection, origin=origin)
connection = SqliteDatabase.connect(filename=database.filename, writeable=writeable) return SqliteDatabase.fromConnection(origin=database.origin, connection=connection, writeable=writeable)
with removeWritePermission(database.filename): yield self.getNewConnection(database, writeable=False)
"""Test that different ways of connecting to a SQLite database are equivalent. """ _, filename = tempfile.mkstemp(dir=self.root, suffix=".sqlite3") # Create a read-write database by passing in the filename. rwFromFilename = SqliteDatabase.fromConnection(SqliteDatabase.connect(filename=filename), origin=0) self.assertEqual(rwFromFilename.filename, filename) self.assertEqual(rwFromFilename.origin, 0) self.assertTrue(rwFromFilename.isWriteable()) self.assertTrue(isEmptyDatabaseActuallyWriteable(rwFromFilename)) # Create a read-write database via a URI. rwFromUri = SqliteDatabase.fromUri(f"sqlite:///{filename}", origin=0) self.assertEqual(rwFromUri.filename, filename) self.assertEqual(rwFromUri.origin, 0) self.assertTrue(rwFromUri.isWriteable()) self.assertTrue(isEmptyDatabaseActuallyWriteable(rwFromUri)) # We don't support SQLite URIs inside SQLAlchemy URIs. with self.assertRaises(NotImplementedError): SqliteDatabase.connect(uri=f"sqlite:///file:{filename}?uri=true")
# Test read-only connections against a read-only file. with removeWritePermission(filename): # Create a read-only database by passing in the filename. roFromFilename = SqliteDatabase.fromConnection(SqliteDatabase.connect(filename=filename), origin=0, writeable=False) self.assertEqual(roFromFilename.filename, filename) self.assertEqual(roFromFilename.origin, 0) self.assertFalse(roFromFilename.isWriteable()) self.assertFalse(isEmptyDatabaseActuallyWriteable(roFromFilename)) # Create a read-write database via a URI. roFromUri = SqliteDatabase.fromUri(f"sqlite:///{filename}", origin=0, writeable=False) self.assertEqual(roFromUri.filename, filename) self.assertEqual(roFromUri.origin, 0) self.assertFalse(roFromUri.isWriteable()) self.assertFalse(isEmptyDatabaseActuallyWriteable(roFromUri))
"""Tests for `SqliteDatabase` using an in-memory database. """
connection = SqliteDatabase.connect(filename=None) return SqliteDatabase.fromConnection(connection=connection, origin=origin)
return SqliteDatabase.fromConnection(origin=database.origin, connection=database._connection, writeable=writeable)
yield self.getNewConnection(database, writeable=False)
"""Test that different ways of connecting to a SQLite database are equivalent. """ # Create an in-memory database by passing filename=None. memFromFilename = SqliteDatabase.fromConnection(SqliteDatabase.connect(filename=None), origin=0) self.assertIsNone(memFromFilename.filename) self.assertEqual(memFromFilename.origin, 0) self.assertTrue(memFromFilename.isWriteable()) self.assertTrue(isEmptyDatabaseActuallyWriteable(memFromFilename)) # Create an in-memory database via a URI. memFromUri = SqliteDatabase.fromUri("sqlite://", origin=0) self.assertIsNone(memFromUri.filename) self.assertEqual(memFromUri.origin, 0) self.assertTrue(memFromUri.isWriteable()) self.assertTrue(isEmptyDatabaseActuallyWriteable(memFromUri)) # We don't support SQLite URIs inside SQLAlchemy URIs. with self.assertRaises(NotImplementedError): SqliteDatabase.connect(uri="sqlite:///:memory:?uri=true") # We don't support read-only in-memory databases. with self.assertRaises(NotImplementedError): SqliteDatabase.connect(filename=None, writeable=False)
"""Tests for `Registry` backed by a SQLite file-based database. """
self.root = tempfile.mkdtemp(dir=TESTDIR)
if self.root is not None and os.path.exists(self.root): shutil.rmtree(self.root, ignore_errors=True)
_, filename = tempfile.mkstemp(dir=self.root, suffix=".sqlite3") config = RegistryConfig() config["db"] = f"sqlite:///{filename}" return Registry.fromConfig(config, create=True, butlerRoot=self.root)
"""Tests for `Registry` backed by a SQLite in-memory database. """
config = RegistryConfig() config["db"] = f"sqlite://" return Registry.fromConfig(config, create=True)
"""Tests for using region fields in `Registry` dimensions. """ # TODO: the test regions used here are enormous (significant fractions # of the sphere), and that makes this test prohibitively slow on # most real databases. These should be made more realistic, and the # test moved to daf/butler/registry/tests/registry.py. registry = self.makeRegistry() regionTract = ConvexPolygon((UnitVector3d(1, 0, 0), UnitVector3d(0, 1, 0), UnitVector3d(0, 0, 1))) regionPatch = ConvexPolygon((UnitVector3d(1, 1, 0), UnitVector3d(0, 1, 0), UnitVector3d(0, 0, 1))) regionVisit = ConvexPolygon((UnitVector3d(1, 0, 0), UnitVector3d(0, 1, 1), UnitVector3d(0, 0, 1))) regionVisitDetector = ConvexPolygon((UnitVector3d(1, 0, 0), UnitVector3d(0, 1, 0), UnitVector3d(0, 1, 1))) for a, b in itertools.combinations((regionTract, regionPatch, regionVisit, regionVisitDetector), 2): self.assertNotEqual(a, b)
# This depends on current dimensions.yaml definitions self.assertEqual(len(list(registry.queryDimensions(["patch", "htm7"]))), 0)
# Add some dimension entries registry.insertDimensionData("instrument", {"name": "DummyCam"}) registry.insertDimensionData("physical_filter", {"instrument": "DummyCam", "name": "dummy_r", "abstract_filter": "r"}, {"instrument": "DummyCam", "name": "dummy_i", "abstract_filter": "i"}) for detector in (1, 2, 3, 4, 5): registry.insertDimensionData("detector", {"instrument": "DummyCam", "id": detector, "full_name": str(detector)}) registry.insertDimensionData("visit", {"instrument": "DummyCam", "id": 0, "name": "zero", "physical_filter": "dummy_r", "region": regionVisit}, {"instrument": "DummyCam", "id": 1, "name": "one", "physical_filter": "dummy_i"}) registry.insertDimensionData("skymap", {"skymap": "DummySkyMap", "hash": bytes()}) registry.insertDimensionData("tract", {"skymap": "DummySkyMap", "tract": 0, "region": regionTract}) registry.insertDimensionData("patch", {"skymap": "DummySkyMap", "tract": 0, "patch": 0, "cell_x": 0, "cell_y": 0, "region": regionPatch}) registry.insertDimensionData("visit_detector_region", {"instrument": "DummyCam", "visit": 0, "detector": 2, "region": regionVisitDetector})
def getRegion(dataId): return registry.expandDataId(dataId).region
# Get region for a tract self.assertEqual(regionTract, getRegion({"skymap": "DummySkyMap", "tract": 0})) # Attempt to get region for a non-existent tract with self.assertRaises(LookupError): getRegion({"skymap": "DummySkyMap", "tract": 1}) # Get region for a (tract, patch) combination self.assertEqual(regionPatch, getRegion({"skymap": "DummySkyMap", "tract": 0, "patch": 0})) # Get region for a non-existent (tract, patch) combination with self.assertRaises(LookupError): getRegion({"skymap": "DummySkyMap", "tract": 0, "patch": 1}) # Get region for a visit self.assertEqual(regionVisit, getRegion({"instrument": "DummyCam", "visit": 0})) # Attempt to get region for a non-existent visit with self.assertRaises(LookupError): getRegion({"instrument": "DummyCam", "visit": 10}) # Get region for a (visit, detector) combination self.assertEqual(regionVisitDetector, getRegion({"instrument": "DummyCam", "visit": 0, "detector": 2})) # Attempt to get region for a non-existent (visit, detector) # combination. This returns None rather than raising because we don't # want to require the region record to be present. self.assertIsNone(getRegion({"instrument": "DummyCam", "visit": 0, "detector": 3})) # getRegion for a dataId containing no spatial dimensions should # return None self.assertIsNone(getRegion({"instrument": "DummyCam"})) # getRegion for a mix of spatial dimensions should return # NotImplemented, at least until we get it implemented. self.assertIs(getRegion({"instrument": "DummyCam", "visit": 0, "detector": 2, "skymap": "DummySkyMap", "tract": 0}), NotImplemented) # Check if we can get the region for a skypix self.assertIsInstance(getRegion({"htm9": 1000}), ConvexPolygon) # patch_htm7_overlap should not be empty self.assertNotEqual(len(list(registry.queryDimensions(["patch", "htm7"]))), 0)
unittest.main() |