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