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

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/>. 

21 

22import os 

23import sys 

24import unittest 

25from collections.abc import Mapping 

26 

27import lsst.utils.packages 

28import lsst.utils.tests 

29 

30TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

31 

32 

33class PackagesTestCase(unittest.TestCase): 

34 """Tests for package version collection 

35 

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 """ 

43 

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) 

49 

50 def testEnvironment(self): 

51 """Test getting versions from the environment 

52 

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() 

58 

59 def testConda(self): 

60 """Test getting versions from conda environement 

61 

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() 

66 

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 

75 

76 def testPackages(self): 

77 """Test the Packages class""" 

78 packages = lsst.utils.packages.Packages.fromSystem() 

79 self.assertIsInstance(packages, Mapping) 

80 

81 # Check that stringification is not crashing. 

82 self.assertTrue(str(packages).startswith("Packages({")) 

83 self.assertTrue(repr(packages).startswith("Packages({")) 

84 

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") 

90 

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) 

99 

100 # Dict compatibility. 

101 for k, v in new.items(): 

102 self.assertEqual(new[k], v) 

103 

104 with self.assertRaises(ValueError): 

105 self._writeTempFile(packages, ".txt") 

106 

107 with self.assertRaises(ValueError): 

108 # .txt extension is not recognized 

109 lsst.utils.packages.Packages.read("something.txt") 

110 

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)) 

116 

117 # Check inverted comparisons 

118 self.assertDictEqual(new.difference(packages), {}) 

119 self.assertDictEqual(new.missing(packages), {}) 

120 self.assertDictEqual(new.extra(packages), {}) 

121 

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 

132 

133 new_package = "smtpd" 

134 else: 

135 import chunk # noqa: F401 

136 

137 new_package = "chunk" 

138 

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) 

145 

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) 

153 

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)) 

161 

162 # Loop over keys to check iterator. 

163 keys = set(new) 

164 self.assertEqual(keys, set(dict(new).keys())) 

165 

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())) 

169 

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) 

175 

176 with self.assertRaises(ValueError): 

177 new.toBytes("unknown_format") 

178 

179 with self.assertRaises(ValueError): 

180 lsst.utils.packages.Packages.fromBytes(from_bytes, "unknown_format") 

181 

182 with self.assertRaises(TypeError): 

183 some_yaml = b"list: [1, 2]" 

184 lsst.utils.packages.Packages.fromBytes(some_yaml, "yaml") 

185 

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) 

191 

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")) 

195 

196 self.assertEqual(packages_p, packages_y) 

197 

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) 

204 

205 

206if __name__ == "__main__": 

207 unittest.main()