Coverage for tests/test_packages.py: 12%

96 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-08 09: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# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11# 

12 

13import os 

14import sys 

15import unittest 

16from collections.abc import Mapping 

17 

18import lsst.utils.packages 

19import lsst.utils.tests 

20 

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

22 

23 

24class PackagesTestCase(unittest.TestCase): 

25 """Tests for package version collection 

26 

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

34 

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) 

40 

41 def testEnvironment(self): 

42 """Test getting versions from the environment 

43 

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

49 

50 def testConda(self): 

51 """Test getting versions from conda environement 

52 

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

57 

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 

66 

67 def testPackages(self): 

68 """Test the Packages class""" 

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

70 self.assertIsInstance(packages, Mapping) 

71 

72 # Check that stringification is not crashing. 

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

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

75 

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

81 

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) 

90 

91 # Dict compatibility. 

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

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

94 

95 with self.assertRaises(ValueError): 

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

97 

98 with self.assertRaises(ValueError): 

99 # .txt extension is not recognized 

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

101 

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

107 

108 # Check inverted comparisons 

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

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

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

112 

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 

123 

124 new_package = "smtpd" 

125 else: 

126 import chunk # noqa: F401 

127 

128 new_package = "chunk" 

129 

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) 

136 

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) 

144 

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

152 

153 # Loop over keys to check iterator. 

154 keys = set(new) 

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

156 

157 # Loop over values to check that we do get them all. 

158 values = set(new.values()) 

159 self.assertEqual(values, set(dict(new).values())) 

160 

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) 

166 

167 with self.assertRaises(ValueError): 

168 new.toBytes("unknown_format") 

169 

170 with self.assertRaises(ValueError): 

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

172 

173 with self.assertRaises(TypeError): 

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

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

176 

177 def testBackwardsCompatibility(self): 

178 """Test if we can read old data files.""" 

179 # Pickle contents changed when moving to dict base class. 

180 packages_p = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.pickle")) 

181 self.assertIsInstance(packages_p, lsst.utils.packages.Packages) 

182 

183 # YAML format is unchanged when moving from special class to dict 

184 # but test anyway. 

185 packages_y = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v1.yaml")) 

186 

187 self.assertEqual(packages_p, packages_y) 

188 

189 # Now check that we can read the version 2 files that were 

190 # written with Packages inheriting from dict but still in `base`. 

191 packages_p2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.pickle")) 

192 packages_y2 = lsst.utils.packages.Packages.read(os.path.join(TESTDIR, "data", "v2.yaml")) 

193 self.assertEqual(packages_p2, packages_y2) 

194 self.assertIsInstance(packages_p2, lsst.utils.packages.Packages) 

195 

196 

197if __name__ == "__main__": 

198 unittest.main()