Coverage for tests/test_packages.py: 12%
97 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-02-27 11:49 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-02-27 11:49 +0000
1# This file is part of utils.
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 os
23import sys
24import unittest
25from collections.abc import Mapping
27import lsst.utils.packages
28import lsst.utils.tests
30TESTDIR = os.path.abspath(os.path.dirname(__file__))
33class PackagesTestCase(unittest.TestCase):
34 """Tests for package version collection.
36 Unfortunately, we're somewhat limited in what we can test because
37 we only get the versions of things being used at runtime, and this
38 package sits rather low in the dependency chain so there's not
39 usually a lot of other packages available when this test gets run.
40 Therefore some of the tests are only checking that things don't
41 explode, since they are incapable of testing much more than that.
42 """
44 def testPython(self):
45 """Test that we get the right version for this python package."""
46 versions = lsst.utils.packages.getPythonPackages()
47 expected = lsst.utils.version.__version__
48 self.assertEqual(versions["utils"], expected)
50 def testEnvironment(self):
51 """Test getting versions from the environment.
53 Unfortunately, none of the products that need their versions divined
54 from the environment are dependencies of this package, and so all we
55 can do is test that this doesn't fall over.
56 """
57 lsst.utils.packages.getEnvironmentPackages()
59 def testConda(self):
60 """Test getting versions from conda environment.
62 We do not rely on being run in a conda environment so all we can do is
63 test that this doesn't fall over.
64 """
65 lsst.utils.packages.getCondaPackages()
67 def _writeTempFile(self, packages, suffix):
68 """Write packages to a temp file using the supplied suffix and read
69 back.
70 """
71 with lsst.utils.tests.getTempFilePath(suffix) as tempName:
72 packages.write(tempName)
73 new = lsst.utils.packages.Packages.read(tempName)
74 return new
76 def testPackages(self):
77 """Test the Packages class."""
78 packages = lsst.utils.packages.Packages.fromSystem()
79 self.assertIsInstance(packages, Mapping)
81 # Check that stringification is not crashing.
82 self.assertTrue(str(packages).startswith("Packages({"))
83 self.assertTrue(repr(packages).startswith("Packages({"))
85 # Test pickling and YAML
86 new = self._writeTempFile(packages, ".pickle")
87 new_pkl = self._writeTempFile(packages, ".pkl")
88 new_yaml = self._writeTempFile(packages, ".yaml")
89 new_json = self._writeTempFile(packages, ".json")
91 self.assertIsInstance(new, lsst.utils.packages.Packages, f"Checking type ({type(new)}) from pickle")
92 self.assertIsInstance(
93 new_yaml, lsst.utils.packages.Packages, f"Checking type ({type(new_yaml)}) from YAML"
94 )
95 self.assertEqual(new, packages)
96 self.assertEqual(new_pkl, new)
97 self.assertEqual(new, new_yaml)
98 self.assertEqual(new, new_json)
100 # Dict compatibility.
101 for k, v in new.items():
102 self.assertEqual(new[k], v)
104 with self.assertRaises(ValueError):
105 self._writeTempFile(packages, ".txt")
107 with self.assertRaises(ValueError):
108 # .txt extension is not recognized
109 lsst.utils.packages.Packages.read("something.txt")
111 # 'packages' and 'new' should have identical content
112 self.assertDictEqual(packages.difference(new), {})
113 self.assertDictEqual(packages.missing(new), {})
114 self.assertDictEqual(packages.extra(new), {})
115 self.assertEqual(len(packages), len(new))
117 # Check inverted comparisons
118 self.assertDictEqual(new.difference(packages), {})
119 self.assertDictEqual(new.missing(packages), {})
120 self.assertDictEqual(new.extra(packages), {})
122 # Now load an obscure python package and the list of packages should
123 # change. Shouldn't be used by anything we've previously imported and
124 # preferably should not be a deprecated package.
125 if sys.version_info < (3, 10, 0):
126 import smtpd # noqa: F401
128 new_package = "smtpd"
129 else:
130 import wave # noqa: F401
132 new_package = "wave"
133 self.assertNotIn(new_package, packages)
135 new = lsst.utils.packages.Packages.fromSystem()
136 self.assertDictEqual(packages.difference(new), {}) # No inconsistencies
137 self.assertDictEqual(packages.extra(new), {}) # Nothing in 'packages' that's not in 'new'
138 missing = packages.missing(new)
139 self.assertGreater(len(missing), 0) # 'packages' should be missing some stuff in 'new'
140 self.assertIn(new_package, missing)
142 # Inverted comparisons
143 self.assertDictEqual(new.difference(packages), {})
144 self.assertDictEqual(new.missing(packages), {}) # Nothing in 'new' that's not in 'packages'
145 extra = new.extra(packages)
146 self.assertGreater(len(extra), 0) # 'new' has extra stuff compared to 'packages'
147 self.assertIn(new_package, extra)
148 self.assertIn(new_package, new)
150 # Run with both a Packages and a dict
151 for new_pkg in (new, dict(new)):
152 packages.update(new_pkg) # Should now be identical
153 self.assertDictEqual(packages.difference(new_pkg), {})
154 self.assertDictEqual(packages.missing(new_pkg), {})
155 self.assertDictEqual(packages.extra(new_pkg), {})
156 self.assertEqual(len(packages), len(new_pkg))
158 # Loop over keys to check iterator.
159 keys = set(new)
160 self.assertEqual(keys, set(dict(new).keys()))
162 # Loop over values to check that we do get them all.
163 values = set(new.values())
164 self.assertEqual(values, set(dict(new).values()))
166 # Serialize via bytes
167 for format in ("pickle", "yaml", "json"):
168 asbytes = new.toBytes(format)
169 from_bytes = lsst.utils.packages.Packages.fromBytes(asbytes, format)
170 self.assertEqual(from_bytes, new)
172 with self.assertRaises(ValueError):
173 new.toBytes("unknown_format")
175 with self.assertRaises(ValueError):
176 lsst.utils.packages.Packages.fromBytes(from_bytes, "unknown_format")
178 with self.assertRaises(TypeError):
179 some_yaml = b"list: [1, 2]"
180 lsst.utils.packages.Packages.fromBytes(some_yaml, "yaml")
182 def testBackwardsCompatibility(self):
183 """Test if we can read old data files."""
184 # Pickle contents changed when moving to dict base class.
185 packages_p = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.pickle"))
186 self.assertIsInstance(packages_p, lsst.utils.packages.Packages)
188 # YAML format is unchanged when moving from special class to dict
189 # but test anyway.
190 packages_y = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.yaml"))
192 self.assertEqual(packages_p, packages_y)
194 # Now check that we can read the version 2 files that were
195 # written with Packages inheriting from dict but still in `base`.
196 packages_p2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.pickle"))
197 packages_y2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.yaml"))
198 self.assertEqual(packages_p2, packages_y2)
199 self.assertIsInstance(packages_p2, lsst.utils.packages.Packages)
202if __name__ == "__main__":
203 unittest.main()