Coverage for tests/test_packages.py: 12%

97 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-20 10:50 +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 environment. 

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

127 

128 new_package = "smtpd" 

129 else: 

130 import wave # noqa: F401 

131 

132 new_package = "wave" 

133 self.assertNotIn(new_package, packages) 

134 

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) 

141 

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) 

149 

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

157 

158 # Loop over keys to check iterator. 

159 keys = set(new) 

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

161 

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

165 

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) 

171 

172 with self.assertRaises(ValueError): 

173 new.toBytes("unknown_format") 

174 

175 with self.assertRaises(ValueError): 

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

177 

178 with self.assertRaises(TypeError): 

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

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

181 

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) 

187 

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

191 

192 self.assertEqual(packages_p, packages_y) 

193 

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) 

200 

201 

202if __name__ == "__main__": 

203 unittest.main()