Coverage for tests / test_packages.py: 12%
103 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:31 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:31 +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 unittest
24from collections.abc import Mapping
26import lsst.utils.packages
27import lsst.utils.tests
29TESTDIR = os.path.abspath(os.path.dirname(__file__))
32class PackagesTestCase(unittest.TestCase):
33 """Tests for package version collection.
35 Unfortunately, we're somewhat limited in what we can test because
36 we only get the versions of things being used at runtime, and this
37 package sits rather low in the dependency chain so there's not
38 usually a lot of other packages available when this test gets run.
39 Therefore some of the tests are only checking that things don't
40 explode, since they are incapable of testing much more than that.
41 """
43 def testPython(self):
44 """Test that we get the right version for this python package."""
45 versions = lsst.utils.packages.getPythonPackages()
46 expected = lsst.utils.version.__version__
47 self.assertEqual(versions["utils"], expected)
49 # Check that standard python library packages are not included.
50 self.assertNotIn("os", versions)
52 # Should always include the python version information.
53 self.assertIn("python", versions)
55 # Also for all installed distributions.
56 versions2 = lsst.utils.packages.getAllPythonDistributions()
57 self.assertEqual(versions2["utils"], expected)
58 self.assertNotIn("os", versions2)
59 self.assertIn("python", versions2)
61 # Packages import yaml and so it should have a version.
62 # yaml is a package but PyYAML is the distribution.
63 self.assertEqual(versions["yaml"], versions2["PyYAML"])
65 def testEnvironment(self):
66 """Test getting versions from the environment.
68 Unfortunately, none of the products that need their versions divined
69 from the environment are dependencies of this package, and so all we
70 can do is test that this doesn't fall over.
71 """
72 lsst.utils.packages.getEnvironmentPackages()
73 lsst.utils.packages.getEnvironmentPackages(include_all=True)
75 def testConda(self):
76 """Test getting versions from conda environment.
78 We do not rely on being run in a conda environment so all we can do is
79 test that this doesn't fall over.
80 """
81 lsst.utils.packages.getCondaPackages()
83 def _writeTempFile(self, packages, suffix):
84 """Write packages to a temp file using the supplied suffix and read
85 back.
86 """
87 with lsst.utils.tests.getTempFilePath(suffix) as tempName:
88 packages.write(tempName)
89 new = lsst.utils.packages.Packages.read(tempName)
90 return new
92 def testPackages(self):
93 """Test the Packages class."""
94 packages = lsst.utils.packages.Packages.fromSystem()
95 self.assertIsInstance(packages, Mapping)
97 # Check that stringification is not crashing.
98 self.assertTrue(str(packages).startswith("Packages({"))
99 self.assertTrue(repr(packages).startswith("Packages({"))
101 # Test pickling and YAML
102 new = self._writeTempFile(packages, ".pickle")
103 new_pkl = self._writeTempFile(packages, ".pkl")
104 new_yaml = self._writeTempFile(packages, ".yaml")
105 new_json = self._writeTempFile(packages, ".json")
107 self.assertIsInstance(new, lsst.utils.packages.Packages, f"Checking type ({type(new)}) from pickle")
108 self.assertIsInstance(
109 new_yaml, lsst.utils.packages.Packages, f"Checking type ({type(new_yaml)}) from YAML"
110 )
111 self.assertEqual(new, packages)
112 self.assertEqual(new_pkl, new)
113 self.assertEqual(new, new_yaml)
114 self.assertEqual(new, new_json)
116 # Dict compatibility.
117 for k, v in new.items():
118 self.assertEqual(new[k], v)
120 with self.assertRaises(ValueError):
121 self._writeTempFile(packages, ".txt")
123 with self.assertRaises(ValueError):
124 # .txt extension is not recognized
125 lsst.utils.packages.Packages.read("something.txt")
127 # 'packages' and 'new' should have identical content
128 self.assertDictEqual(packages.difference(new), {})
129 self.assertDictEqual(packages.missing(new), {})
130 self.assertDictEqual(packages.extra(new), {})
131 self.assertEqual(len(packages), len(new))
133 # Check inverted comparisons
134 self.assertDictEqual(new.difference(packages), {})
135 self.assertDictEqual(new.missing(packages), {})
136 self.assertDictEqual(new.extra(packages), {})
138 # Check comparison functionality. Can not import a package that we
139 # do not know is present (and standard library packages are not
140 # reported) so instead pretend.
141 new_package = "pretend_package"
142 self.assertNotIn(new_package, packages)
144 new = lsst.utils.packages.Packages(packages.copy())
145 new[new_package] = new["python"]
146 self.assertDictEqual(packages.difference(new), {}) # No inconsistencies
147 self.assertDictEqual(packages.extra(new), {}) # Nothing in 'packages' that's not in 'new'
148 missing = packages.missing(new)
149 self.assertGreater(len(missing), 0) # 'packages' should be missing some stuff in 'new'
150 self.assertIn(new_package, missing)
152 # Inverted comparisons
153 self.assertDictEqual(new.difference(packages), {})
154 self.assertDictEqual(new.missing(packages), {}) # Nothing in 'new' that's not in 'packages'
155 extra = new.extra(packages)
156 self.assertGreater(len(extra), 0) # 'new' has extra stuff compared to 'packages'
157 self.assertIn(new_package, extra)
158 self.assertIn(new_package, new)
160 # Run with both a Packages and a dict
161 for new_pkg in (new, dict(new)):
162 packages.update(new_pkg) # Should now be identical
163 self.assertDictEqual(packages.difference(new_pkg), {})
164 self.assertDictEqual(packages.missing(new_pkg), {})
165 self.assertDictEqual(packages.extra(new_pkg), {})
166 self.assertEqual(len(packages), len(new_pkg))
168 # Loop over keys to check iterator.
169 keys = set(new)
170 self.assertEqual(keys, set(dict(new).keys()))
172 # Loop over values to check that we do get them all.
173 values = set(new.values())
174 self.assertEqual(values, set(dict(new).values()))
176 # Serialize via bytes
177 for format in ("pickle", "yaml", "json"):
178 asbytes = new.toBytes(format)
179 from_bytes = lsst.utils.packages.Packages.fromBytes(asbytes, format)
180 self.assertEqual(from_bytes, new)
182 with self.assertRaises(ValueError):
183 new.toBytes("unknown_format")
185 with self.assertRaises(ValueError):
186 lsst.utils.packages.Packages.fromBytes(from_bytes, "unknown_format")
188 with self.assertRaises(TypeError):
189 some_yaml = b"list: [1, 2]"
190 lsst.utils.packages.Packages.fromBytes(some_yaml, "yaml")
192 # Check that "all" packages runs and does not include stdlib.
193 all = lsst.utils.packages.Packages.fromSystem(include_all=True)
194 self.assertNotIn("os", all)
196 def testBackwardsCompatibility(self):
197 """Test if we can read old data files."""
198 # Pickle contents changed when moving to dict base class.
199 packages_p = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.pickle"))
200 self.assertIsInstance(packages_p, lsst.utils.packages.Packages)
202 # YAML format is unchanged when moving from special class to dict
203 # but test anyway.
204 packages_y = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.yaml"))
206 self.assertEqual(packages_p, packages_y)
208 # Now check that we can read the version 2 files that were
209 # written with Packages inheriting from dict but still in `base`.
210 packages_p2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.pickle"))
211 packages_y2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.yaml"))
212 self.assertEqual(packages_p2, packages_y2)
213 self.assertIsInstance(packages_p2, lsst.utils.packages.Packages)
216if __name__ == "__main__":
217 unittest.main()