Coverage for tests/test_packages.py: 12%
96 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-17 07:53 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-17 07:53 +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 environement
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
124 # Shouldn't be used by anything we've previously imported
125 # smtpd can be used on 3.8 since it does have a version string but
126 # it is also a deprecated package so should not be used in tests
127 # for python 3.10 and newer.
128 # chunk does not have a version string but is handled as a stdlib
129 # package on 3.10 and newer.
130 if sys.version_info < (3, 10, 0):
131 import smtpd # noqa: F401
133 new_package = "smtpd"
134 else:
135 import chunk # noqa: F401
137 new_package = "chunk"
139 new = lsst.utils.packages.Packages.fromSystem()
140 self.assertDictEqual(packages.difference(new), {}) # No inconsistencies
141 self.assertDictEqual(packages.extra(new), {}) # Nothing in 'packages' that's not in 'new'
142 missing = packages.missing(new)
143 self.assertGreater(len(missing), 0) # 'packages' should be missing some stuff in 'new'
144 self.assertIn(new_package, missing)
146 # Inverted comparisons
147 self.assertDictEqual(new.difference(packages), {})
148 self.assertDictEqual(new.missing(packages), {}) # Nothing in 'new' that's not in 'packages'
149 extra = new.extra(packages)
150 self.assertGreater(len(extra), 0) # 'new' has extra stuff compared to 'packages'
151 self.assertIn(new_package, extra)
152 self.assertIn(new_package, new)
154 # Run with both a Packages and a dict
155 for new_pkg in (new, dict(new)):
156 packages.update(new_pkg) # Should now be identical
157 self.assertDictEqual(packages.difference(new_pkg), {})
158 self.assertDictEqual(packages.missing(new_pkg), {})
159 self.assertDictEqual(packages.extra(new_pkg), {})
160 self.assertEqual(len(packages), len(new_pkg))
162 # Loop over keys to check iterator.
163 keys = set(new)
164 self.assertEqual(keys, set(dict(new).keys()))
166 # Loop over values to check that we do get them all.
167 values = set(new.values())
168 self.assertEqual(values, set(dict(new).values()))
170 # Serialize via bytes
171 for format in ("pickle", "yaml", "json"):
172 asbytes = new.toBytes(format)
173 from_bytes = lsst.utils.packages.Packages.fromBytes(asbytes, format)
174 self.assertEqual(from_bytes, new)
176 with self.assertRaises(ValueError):
177 new.toBytes("unknown_format")
179 with self.assertRaises(ValueError):
180 lsst.utils.packages.Packages.fromBytes(from_bytes, "unknown_format")
182 with self.assertRaises(TypeError):
183 some_yaml = b"list: [1, 2]"
184 lsst.utils.packages.Packages.fromBytes(some_yaml, "yaml")
186 def testBackwardsCompatibility(self):
187 """Test if we can read old data files."""
188 # Pickle contents changed when moving to dict base class.
189 packages_p = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.pickle"))
190 self.assertIsInstance(packages_p, lsst.utils.packages.Packages)
192 # YAML format is unchanged when moving from special class to dict
193 # but test anyway.
194 packages_y = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.yaml"))
196 self.assertEqual(packages_p, packages_y)
198 # Now check that we can read the version 2 files that were
199 # written with Packages inheriting from dict but still in `base`.
200 packages_p2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.pickle"))
201 packages_y2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.yaml"))
202 self.assertEqual(packages_p2, packages_y2)
203 self.assertIsInstance(packages_p2, lsst.utils.packages.Packages)
206if __name__ == "__main__":
207 unittest.main()