Coverage for tests / test_eups.py: 29%

155 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-30 08:38 +0000

1# This file is part of lsst-resources. 

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 

12import os 

13import re 

14import sys 

15import unittest 

16import unittest.mock 

17 

18from lsst.resources import ResourceInfo, ResourcePath 

19from lsst.resources.eups import EupsResourcePath 

20from lsst.resources.tests import GenericTestCase 

21 

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

23PKG_DIR = os.path.join(TESTDIR, "packages", "eups") 

24PACKAGE_PATH = os.path.join(TESTDIR, "packages") 

25 

26 

27class EupsTestCase(GenericTestCase, unittest.TestCase): 

28 """Generic test of resource URIs.""" 

29 

30 scheme = "eups" 

31 netloc = "pkg" 

32 

33 @classmethod 

34 def setUpClass(cls) -> None: 

35 # The actual value does not matter for these tests. 

36 os.environ["PKG_DIR"] = PKG_DIR 

37 super().setUpClass() 

38 

39 @classmethod 

40 def tearDownClass(cls) -> None: 

41 del os.environ["PKG_DIR"] 

42 super().tearDownClass() 

43 

44 def test_relative(self) -> None: 

45 # This test uses two additional netlocs which need corresponding 

46 # environment variables to function. The values do not matter. 

47 with unittest.mock.patch.dict(os.environ, {"OTHER_DIR": "x", "MY.HOST_DIR": "y"}): 

48 super().test_relative() 

49 

50 

51class EupsReadTestCase(unittest.TestCase): 

52 """Test that EUPS information can be read. 

53 

54 EUPS resources can be thought of as being read only even if the 

55 underlying URI is a ``file`` URI. 

56 """ 

57 

58 scheme = "eups" 

59 netloc = "pkg" 

60 

61 @classmethod 

62 def setUpClass(cls) -> None: 

63 # The actual value does not matter for these tests. 

64 os.environ["PKG_DIR"] = PKG_DIR 

65 super().setUpClass() 

66 

67 @classmethod 

68 def tearDownClass(cls) -> None: 

69 del os.environ["PKG_DIR"] 

70 super().tearDownClass() 

71 

72 def setUp(self): 

73 self.root = f"{self.scheme}://{self.netloc}" 

74 self.root_uri = ResourcePath(self.root) 

75 

76 def test_read(self): 

77 uri = self.root_uri.join("config/test.txt") 

78 self.assertTrue(uri.exists(), f"Check {uri} exists") 

79 

80 content = uri.read().decode() 

81 self.assertIn("A test config.", content) 

82 

83 with uri.as_local() as local_uri: 

84 self.assertEqual(local_uri.scheme, "file") 

85 self.assertTrue(local_uri.exists()) 

86 

87 truncated = uri.read(size=9).decode() 

88 self.assertEqual(truncated, content[:9]) 

89 

90 # Check that directory determination can work directly without the 

91 # trailing slash. 

92 d = self.root_uri.join("config") 

93 self.assertTrue(d.isdir()) 

94 self.assertTrue(d.dirLike) 

95 

96 d = self.root_uri.join("config/", forceDirectory=True) 

97 self.assertTrue(d._proxy.dirLike) # Ensure that dir-ness propagates to proxy. 

98 self.assertTrue(d.exists(), f"Check directory {d} exists") 

99 self.assertTrue(d.isdir()) 

100 

101 with self.assertRaises(IsADirectoryError): 

102 with d.as_local() as local_uri: 

103 pass 

104 

105 j = d.join("test.txt") 

106 self.assertEqual(uri, j) 

107 self.assertFalse(j.dirLike) 

108 self.assertFalse(j.isdir()) 

109 not_there = d.join("not-there.yaml") 

110 self.assertFalse(not_there.exists()) 

111 

112 bad = ResourcePath(f"{self.scheme}://bad.module/not.yaml") 

113 multi = ResourcePath.mexists([uri, bad, not_there]) 

114 self.assertTrue(multi[uri]) 

115 self.assertFalse(multi[bad]) 

116 self.assertFalse(multi[not_there]) 

117 

118 # Check that the bad URI works as expected. 

119 self.assertFalse(bad.exists()) 

120 self.assertFalse(bad.isdir()) 

121 with self.assertRaises(FileNotFoundError): 

122 bad.read() 

123 with self.assertRaises(FileNotFoundError): 

124 with bad.as_local(): 

125 pass 

126 with self.assertRaises(FileNotFoundError): 

127 with bad.open("r"): 

128 pass 

129 

130 # fsspec is always not implemented. 

131 with self.assertRaises(NotImplementedError): 

132 bad.to_fsspec() 

133 

134 def test_open(self): 

135 uri = self.root_uri.join("config/test.txt") 

136 with uri.open("rb") as buffer: 

137 content = buffer.read() 

138 self.assertEqual(uri.read(), content) 

139 

140 with uri.open("r") as buffer: 

141 content = buffer.read() 

142 self.assertEqual(uri.read().decode(), content) 

143 

144 def test_get_info(self): 

145 file_uri = self.root_uri.join("config/test.txt") 

146 info = file_uri.get_info() 

147 self.assertIsInstance(info, ResourceInfo) 

148 self.assertEqual(info.uri, str(file_uri)) 

149 self.assertTrue(info.is_file) 

150 self.assertGreater(info.size, 0) 

151 

152 dir_uri = self.root_uri.join("config/", forceDirectory=True) 

153 dirinfo = dir_uri.get_info() 

154 self.assertIsInstance(dirinfo, ResourceInfo) 

155 self.assertEqual(dirinfo.uri, str(dir_uri)) 

156 self.assertFalse(dirinfo.is_file) 

157 self.assertEqual(dirinfo.size, 0) 

158 

159 def test_walk(self): 

160 """Test that we can find file resources. 

161 

162 Try to find resources in this package. Python does not care whether 

163 a resource is a Python file or anything else. 

164 """ 

165 resource = ResourcePath(f"{self.scheme}://{self.netloc}/") 

166 resources = set(ResourcePath.findFileResources([resource])) 

167 

168 # Make sure that walk() can run and knows this is a directory. 

169 cfg = resource.join("config/") 

170 _, dirs, files = next(cfg.walk()) 

171 self.assertEqual(dirs, []) 

172 self.assertEqual(set(files), {"test.txt", "test2.yaml"}) 

173 

174 # Do not try to list all possible options. Files can move around 

175 # and cache files can appear. 

176 subset = { 

177 ResourcePath(f"{self.scheme}://{self.netloc}/config/test.txt"), 

178 ResourcePath(f"{self.scheme}://{self.netloc}/config/test2.yaml"), 

179 } 

180 for r in subset: 

181 self.assertIn(r, resources) 

182 

183 resources = set( 

184 ResourcePath.findFileResources( 

185 [ResourcePath(f"{self.scheme}://{self.netloc}/")], file_filter=r".*\.json" 

186 ) 

187 ) 

188 self.assertEqual(resources, set()) 

189 

190 # Compare regex with str. 

191 regex = r".*\.yaml" 

192 y_files_str = list(resource.walk(file_filter=regex)) 

193 y_files_re = list(resource.walk(file_filter=re.compile(regex))) 

194 self.assertGreater(len(y_files_str), 1) 

195 self.assertEqual(y_files_str, y_files_re) 

196 

197 bad_dir = ResourcePath(f"{self.scheme}://bad.module/a/dir/") 

198 self.assertTrue(bad_dir.isdir()) 

199 with self.assertRaises(ValueError): 

200 list(bad_dir.walk()) 

201 

202 def test_env_var(self): 

203 """Test that environment variables are converted.""" 

204 with unittest.mock.patch.dict(os.environ, {"MY_TEST_DIR": TESTDIR}): 

205 for env_string in ("$MY_TEST_DIR", "${MY_TEST_DIR}"): 

206 uri = ResourcePath(f"{env_string}/data/dir1/a.yaml") 

207 self.assertEqual(uri.path, "/data/dir1/a.yaml") 

208 self.assertEqual(uri.scheme, "eups") 

209 self.assertEqual(uri.netloc, "my_test") 

210 self.assertTrue(uri.exists()) 

211 

212 

213class EupsAsResourcesReadTestCase(EupsReadTestCase): 

214 """Test that EUPS information can be read via resources. 

215 

216 EUPS resources can be thought of as being read only even if the 

217 underlying URI is a ``file`` URI. 

218 """ 

219 

220 scheme = "eups" 

221 netloc = "pkg1" 

222 

223 @classmethod 

224 def setUpClass(cls) -> None: 

225 # The actual value does not matter for these tests. 

226 sys.path.append(PACKAGE_PATH) 

227 super().setUpClass() 

228 

229 @classmethod 

230 def tearDownClass(cls) -> None: 

231 sys.path.remove(PACKAGE_PATH) 

232 super().tearDownClass() 

233 

234 

235class EupsAsResourcesReadTestCase2(EupsAsResourcesReadTestCase): 

236 """Test that EUPS information can be read via resources with lsst-style 

237 package. 

238 

239 EUPS resources can be thought of as being read only even if the 

240 underlying URI is a ``file`` URI. 

241 """ 

242 

243 scheme = "eups" 

244 netloc = "pkg2_sub" 

245 

246 @classmethod 

247 def setUpClass(cls) -> None: 

248 # To avoid confusion with other lsst packages, override the default 

249 # prefix that is added to the EUPS name. 

250 cls.prefix = EupsResourcePath._default_namespace 

251 EupsResourcePath._default_namespace = "prefix" 

252 super().setUpClass() 

253 

254 @classmethod 

255 def tearDownClass(cls) -> None: 

256 EupsResourcePath._default_namespace = cls.prefix 

257 super().tearDownClass() 

258 

259 

260if __name__ == "__main__": 

261 unittest.main()