Coverage for tests/test_uri.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://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 <http://www.gnu.org/licenses/>.
22import glob
23import os
24import shutil
25import unittest
26import urllib.parse
27import responses
29try:
30 import boto3
31 import botocore
32 from moto import mock_s3
33except ImportError:
34 boto3 = None
36 def mock_s3(cls):
37 """A no-op decorator in case moto mock_s3 can not be imported.
38 """
39 return cls
41from lsst.daf.butler import ButlerURI
42from lsst.daf.butler.core._butlerUri.s3utils import (setAwsEnvCredentials,
43 unsetAwsEnvCredentials)
44from lsst.daf.butler.tests.utils import makeTestTempDir, removeTestTempDir
46TESTDIR = os.path.abspath(os.path.dirname(__file__))
49class FileURITestCase(unittest.TestCase):
50 """Concrete tests for local files"""
52 def setUp(self):
53 # Use a local tempdir because on macOS the temp dirs use symlinks
54 # so relsymlink gets quite confused.
55 self.tmpdir = makeTestTempDir(TESTDIR)
57 def tearDown(self):
58 removeTestTempDir(self.tmpdir)
60 def testFile(self):
61 file = os.path.join(self.tmpdir, "test.txt")
62 uri = ButlerURI(file)
63 self.assertFalse(uri.exists(), f"{uri} should not exist")
64 self.assertEqual(uri.ospath, file)
66 content = "abcdefghijklmnopqrstuv\n"
67 uri.write(content.encode())
68 self.assertTrue(os.path.exists(file), "File should exist locally")
69 self.assertTrue(uri.exists(), f"{uri} should now exist")
70 self.assertEqual(uri.read().decode(), content)
71 self.assertEqual(uri.size(), len(content.encode()))
73 with self.assertRaises(FileNotFoundError):
74 ButlerURI("file/not/there.txt").size()
76 # Check that creating a URI from a URI returns the same thing
77 uri2 = ButlerURI(uri)
78 self.assertEqual(uri, uri2)
79 self.assertEqual(id(uri), id(uri2))
81 def testExtension(self):
82 file = ButlerURI(os.path.join(self.tmpdir, "test.txt"))
83 self.assertEqual(file.updatedExtension(None), file)
84 self.assertEqual(file.updatedExtension(".txt"), file)
85 self.assertEqual(id(file.updatedExtension(".txt")), id(file))
87 fits = file.updatedExtension(".fits.gz")
88 self.assertEqual(fits.basename(), "test.fits.gz")
89 self.assertEqual(fits.updatedExtension(".jpeg").basename(), "test.jpeg")
91 def testRelative(self):
92 """Check that we can get subpaths back from two URIs"""
93 parent = ButlerURI(self.tmpdir, forceDirectory=True, forceAbsolute=True)
94 self.assertTrue(parent.isdir())
95 child = ButlerURI(os.path.join(self.tmpdir, "dir1", "file.txt"), forceAbsolute=True)
97 self.assertEqual(child.relative_to(parent), "dir1/file.txt")
99 not_child = ButlerURI("/a/b/dir1/file.txt")
100 self.assertFalse(not_child.relative_to(parent))
101 self.assertFalse(not_child.isdir())
103 not_directory = ButlerURI(os.path.join(self.tmpdir, "dir1", "file2.txt"))
104 self.assertFalse(child.relative_to(not_directory))
106 # Relative URIs
107 parent = ButlerURI("a/b/", forceAbsolute=False)
108 child = ButlerURI("a/b/c/d.txt", forceAbsolute=False)
109 self.assertFalse(child.scheme)
110 self.assertEqual(child.relative_to(parent), "c/d.txt")
112 # File URI and schemeless URI
113 parent = ButlerURI("file:/a/b/c/")
114 child = ButlerURI("e/f/g.txt", forceAbsolute=False)
116 # If the child is relative and the parent is absolute we assume
117 # that the child is a child of the parent unless it uses ".."
118 self.assertEqual(child.relative_to(parent), "e/f/g.txt")
120 child = ButlerURI("../e/f/g.txt", forceAbsolute=False)
121 self.assertFalse(child.relative_to(parent))
123 child = ButlerURI("../c/e/f/g.txt", forceAbsolute=False)
124 self.assertEqual(child.relative_to(parent), "e/f/g.txt")
126 def testParents(self):
127 """Test of splitting and parent walking."""
128 parent = ButlerURI(self.tmpdir, forceDirectory=True, forceAbsolute=True)
129 child_file = parent.join("subdir/file.txt")
130 self.assertFalse(child_file.isdir())
131 child_subdir, file = child_file.split()
132 self.assertEqual(file, "file.txt")
133 self.assertTrue(child_subdir.isdir())
134 self.assertEqual(child_file.dirname(), child_subdir)
135 self.assertEqual(child_file.basename(), file)
136 self.assertEqual(child_file.parent(), child_subdir)
137 derived_parent = child_subdir.parent()
138 self.assertEqual(derived_parent, parent)
139 self.assertTrue(derived_parent.isdir())
140 self.assertEqual(child_file.parent().parent(), parent)
142 def testEnvVar(self):
143 """Test that environment variables are expanded."""
145 with unittest.mock.patch.dict(os.environ, {"MY_TEST_DIR": "/a/b/c"}):
146 uri = ButlerURI("${MY_TEST_DIR}/d.txt")
147 self.assertEqual(uri.path, "/a/b/c/d.txt")
148 self.assertEqual(uri.scheme, "file")
150 # This will not expand
151 uri = ButlerURI("${MY_TEST_DIR}/d.txt", forceAbsolute=False)
152 self.assertEqual(uri.path, "${MY_TEST_DIR}/d.txt")
153 self.assertFalse(uri.scheme)
155 def testMkdir(self):
156 tmpdir = ButlerURI(self.tmpdir)
157 newdir = tmpdir.join("newdir/seconddir")
158 newdir.mkdir()
159 self.assertTrue(newdir.exists())
160 newfile = newdir.join("temp.txt")
161 newfile.write("Data".encode())
162 self.assertTrue(newfile.exists())
164 def testTransfer(self):
165 src = ButlerURI(os.path.join(self.tmpdir, "test.txt"))
166 content = "Content is some content\nwith something to say\n\n"
167 src.write(content.encode())
169 for mode in ("copy", "link", "hardlink", "symlink", "relsymlink"):
170 dest = ButlerURI(os.path.join(self.tmpdir, f"dest_{mode}.txt"))
171 dest.transfer_from(src, transfer=mode)
172 self.assertTrue(dest.exists(), f"Check that {dest} exists (transfer={mode})")
174 with open(dest.ospath, "r") as fh:
175 new_content = fh.read()
176 self.assertEqual(new_content, content)
178 if mode in ("symlink", "relsymlink"):
179 self.assertTrue(os.path.islink(dest.ospath), f"Check that {dest} is symlink")
181 with self.assertRaises(FileExistsError):
182 dest.transfer_from(src, transfer=mode)
184 dest.transfer_from(src, transfer=mode, overwrite=True)
186 os.remove(dest.ospath)
188 b = src.read()
189 self.assertEqual(b.decode(), new_content)
191 nbytes = 10
192 subset = src.read(size=nbytes)
193 self.assertEqual(len(subset), nbytes)
194 self.assertEqual(subset.decode(), content[:nbytes])
196 with self.assertRaises(ValueError):
197 src.transfer_from(src, transfer="unknown")
199 def testResource(self):
200 u = ButlerURI("resource://lsst.daf.butler/configs/datastore.yaml")
201 self.assertTrue(u.exists(), f"Check {u} exists")
203 content = u.read().decode()
204 self.assertTrue(content.startswith("datastore:"))
206 truncated = u.read(size=9).decode()
207 self.assertEqual(truncated, "datastore")
209 d = ButlerURI("resource://lsst.daf.butler/configs", forceDirectory=True)
210 self.assertTrue(u.exists(), f"Check directory {d} exists")
212 j = d.join("datastore.yaml")
213 self.assertEqual(u, j)
214 self.assertFalse(j.dirLike)
215 self.assertFalse(j.isdir())
216 self.assertFalse(d.join("not-there.yaml").exists())
218 def testEscapes(self):
219 """Special characters in file paths"""
220 src = ButlerURI("bbb/???/test.txt", root=self.tmpdir, forceAbsolute=True)
221 self.assertFalse(src.scheme)
222 src.write(b"Some content")
223 self.assertTrue(src.exists())
225 # abspath always returns a file scheme
226 file = src.abspath()
227 self.assertTrue(file.exists())
228 self.assertIn("???", file.ospath)
229 self.assertNotIn("???", file.path)
231 file = file.updatedFile("tests??.txt")
232 self.assertNotIn("??.txt", file.path)
233 file.write(b"Other content")
234 self.assertEqual(file.read(), b"Other content")
236 src = src.updatedFile("tests??.txt")
237 self.assertIn("??.txt", src.path)
238 self.assertEqual(file.read(), src.read(), f"reading from {file.ospath} and {src.ospath}")
240 # File URI and schemeless URI
241 parent = ButlerURI("file:" + urllib.parse.quote("/a/b/c/de/??/"))
242 child = ButlerURI("e/f/g.txt", forceAbsolute=False)
243 self.assertEqual(child.relative_to(parent), "e/f/g.txt")
245 child = ButlerURI("e/f??#/g.txt", forceAbsolute=False)
246 self.assertEqual(child.relative_to(parent), "e/f??#/g.txt")
248 child = ButlerURI("file:" + urllib.parse.quote("/a/b/c/de/??/e/f??#/g.txt"))
249 self.assertEqual(child.relative_to(parent), "e/f??#/g.txt")
251 self.assertEqual(child.relativeToPathRoot, "a/b/c/de/??/e/f??#/g.txt")
253 # Schemeless so should not quote
254 dir = ButlerURI("bbb/???/", root=self.tmpdir, forceAbsolute=True, forceDirectory=True)
255 self.assertIn("???", dir.ospath)
256 self.assertIn("???", dir.path)
257 self.assertFalse(dir.scheme)
259 # dir.join() morphs into a file scheme
260 new = dir.join("test_j.txt")
261 self.assertIn("???", new.ospath, f"Checking {new}")
262 new.write(b"Content")
264 new2name = "###/test??.txt"
265 new2 = dir.join(new2name)
266 self.assertIn("???", new2.ospath)
267 new2.write(b"Content")
268 self.assertTrue(new2.ospath.endswith(new2name))
269 self.assertEqual(new.read(), new2.read())
271 fdir = dir.abspath()
272 self.assertNotIn("???", fdir.path)
273 self.assertIn("???", fdir.ospath)
274 self.assertEqual(fdir.scheme, "file")
275 fnew = dir.join("test_jf.txt")
276 fnew.write(b"Content")
278 fnew2 = fdir.join(new2name)
279 fnew2.write(b"Content")
280 self.assertTrue(fnew2.ospath.endswith(new2name))
281 self.assertNotIn("###", fnew2.path)
283 self.assertEqual(fnew.read(), fnew2.read())
285 # Test that children relative to schemeless and file schemes
286 # still return the same unquoted name
287 self.assertEqual(fnew2.relative_to(fdir), new2name)
288 self.assertEqual(fnew2.relative_to(dir), new2name)
289 self.assertEqual(new2.relative_to(fdir), new2name, f"{new2} vs {fdir}")
290 self.assertEqual(new2.relative_to(dir), new2name)
292 # Check for double quoting
293 plus_path = "/a/b/c+d/"
294 with self.assertLogs(level="WARNING"):
295 uri = ButlerURI(urllib.parse.quote(plus_path), forceDirectory=True)
296 self.assertEqual(uri.ospath, plus_path)
298 # Check that # is not escaped for schemeless URIs
299 hash_path = "/a/b#/c&d#xyz"
300 hpos = hash_path.rfind("#")
301 uri = ButlerURI(hash_path)
302 self.assertEqual(uri.ospath, hash_path[:hpos])
303 self.assertEqual(uri.fragment, hash_path[hpos + 1:])
305 def testHash(self):
306 """Test that we can store URIs in sets and as keys."""
307 uri1 = ButlerURI(TESTDIR)
308 uri2 = uri1.join("test/")
309 s = {uri1, uri2}
310 self.assertIn(uri1, s)
312 d = {uri1: "1", uri2: "2"}
313 self.assertEqual(d[uri2], "2")
315 def testWalk(self):
316 """Test ButlerURI.walk()."""
317 test_dir_uri = ButlerURI(TESTDIR)
319 file = test_dir_uri.join("config/basic/butler.yaml")
320 found = list(ButlerURI.findFileResources([file]))
321 self.assertEqual(found[0], file)
323 # Compare against the full local paths
324 expected = set(p for p in glob.glob(os.path.join(TESTDIR, "config", "**"), recursive=True)
325 if os.path.isfile(p))
326 found = set(u.ospath for u in ButlerURI.findFileResources([test_dir_uri.join("config")]))
327 self.assertEqual(found, expected)
329 # Now solely the YAML files
330 expected_yaml = set(glob.glob(os.path.join(TESTDIR, "config", "**", "*.yaml"), recursive=True))
331 found = set(u.ospath for u in ButlerURI.findFileResources([test_dir_uri.join("config")],
332 file_filter=r".*\.yaml$"))
333 self.assertEqual(found, expected_yaml)
335 # Now two explicit directories and a file
336 expected = set(glob.glob(os.path.join(TESTDIR, "config", "**", "basic", "*.yaml"), recursive=True))
337 expected.update(set(glob.glob(os.path.join(TESTDIR, "config", "**", "templates", "*.yaml"),
338 recursive=True)))
339 expected.add(file.ospath)
341 found = set(u.ospath for u in ButlerURI.findFileResources([file, test_dir_uri.join("config/basic"),
342 test_dir_uri.join("config/templates")],
343 file_filter=r".*\.yaml$"))
344 self.assertEqual(found, expected)
346 # Group by directory -- find everything and compare it with what
347 # we expected to be there in total. We expect to find 9 directories
348 # containing yaml files so make sure we only iterate 9 times.
349 found_yaml = set()
350 counter = 0
351 for uris in ButlerURI.findFileResources([file, test_dir_uri.join("config/")],
352 file_filter=r".*\.yaml$", grouped=True):
353 found = set(u.ospath for u in uris)
354 if found:
355 counter += 1
357 found_yaml.update(found)
359 self.assertEqual(found_yaml, expected_yaml)
360 self.assertEqual(counter, 9)
362 # Grouping but check that single files are returned in a single group
363 # at the end
364 file2 = test_dir_uri.join("config/templates/templates-bad.yaml")
365 found = list(ButlerURI.findFileResources([file, file2, test_dir_uri.join("config/dbAuth")],
366 grouped=True))
367 self.assertEqual(len(found), 2)
368 self.assertEqual(list(found[1]), [file, file2])
370 with self.assertRaises(ValueError):
371 list(file.walk())
373 def testRootURI(self):
374 """Test ButlerURI.root_uri()."""
375 uri = ButlerURI("https://www.notexist.com:8080/file/test")
376 uri2 = ButlerURI("s3://www.notexist.com/file/test")
377 self.assertEqual(uri.root_uri().geturl(), "https://www.notexist.com:8080/")
378 self.assertEqual(uri2.root_uri().geturl(), "s3://www.notexist.com/")
381@unittest.skipIf(not boto3, "Warning: boto3 AWS SDK not found!")
382@mock_s3
383class S3URITestCase(unittest.TestCase):
384 """Tests involving S3"""
386 bucketName = "any_bucket"
387 """Bucket name to use in tests"""
389 def setUp(self):
390 # Local test directory
391 self.tmpdir = makeTestTempDir(TESTDIR)
393 # set up some fake credentials if they do not exist
394 self.usingDummyCredentials = setAwsEnvCredentials()
396 # MOTO needs to know that we expect Bucket bucketname to exist
397 s3 = boto3.resource("s3")
398 s3.create_bucket(Bucket=self.bucketName)
400 def tearDown(self):
401 s3 = boto3.resource("s3")
402 bucket = s3.Bucket(self.bucketName)
403 try:
404 bucket.objects.all().delete()
405 except botocore.exceptions.ClientError as e:
406 if e.response["Error"]["Code"] == "404":
407 # the key was not reachable - pass
408 pass
409 else:
410 raise
412 bucket = s3.Bucket(self.bucketName)
413 bucket.delete()
415 # unset any potentially set dummy credentials
416 if self.usingDummyCredentials:
417 unsetAwsEnvCredentials()
419 shutil.rmtree(self.tmpdir, ignore_errors=True)
421 def makeS3Uri(self, path):
422 return f"s3://{self.bucketName}/{path}"
424 def testTransfer(self):
425 src = ButlerURI(os.path.join(self.tmpdir, "test.txt"))
426 content = "Content is some content\nwith something to say\n\n"
427 src.write(content.encode())
428 self.assertTrue(src.exists())
429 self.assertEqual(src.size(), len(content.encode()))
431 dest = ButlerURI(self.makeS3Uri("test.txt"))
432 self.assertFalse(dest.exists())
434 with self.assertRaises(FileNotFoundError):
435 dest.size()
437 dest.transfer_from(src, transfer="copy")
438 self.assertTrue(dest.exists())
440 dest2 = ButlerURI(self.makeS3Uri("copied.txt"))
441 dest2.transfer_from(dest, transfer="copy")
442 self.assertTrue(dest2.exists())
444 local = ButlerURI(os.path.join(self.tmpdir, "copied.txt"))
445 local.transfer_from(dest2, transfer="copy")
446 with open(local.ospath, "r") as fd:
447 new_content = fd.read()
448 self.assertEqual(new_content, content)
450 with self.assertRaises(ValueError):
451 dest2.transfer_from(local, transfer="symlink")
453 b = dest.read()
454 self.assertEqual(b.decode(), new_content)
456 nbytes = 10
457 subset = dest.read(size=nbytes)
458 self.assertEqual(len(subset), nbytes) # Extra byte comes back
459 self.assertEqual(subset.decode(), content[:nbytes])
461 with self.assertRaises(FileExistsError):
462 dest.transfer_from(src, transfer="copy")
464 dest.transfer_from(src, transfer="copy", overwrite=True)
466 def testWalk(self):
467 """Test that we can list an S3 bucket"""
468 # Files we want to create
469 expected = ("a/x.txt", "a/y.txt", "a/z.json", "a/b/w.txt", "a/b/c/d/v.json")
470 expected_uris = [ButlerURI(self.makeS3Uri(path)) for path in expected]
471 for uri in expected_uris:
472 # Doesn't matter what we write
473 uri.write("123".encode())
475 # Find all the files in the a/ tree
476 found = set(uri.path for uri in ButlerURI.findFileResources([ButlerURI(self.makeS3Uri("a/"))]))
477 self.assertEqual(found, {uri.path for uri in expected_uris})
479 # Find all the files in the a/ tree but group by folder
480 found = ButlerURI.findFileResources([ButlerURI(self.makeS3Uri("a/"))],
481 grouped=True)
482 expected = (("/a/x.txt", "/a/y.txt", "/a/z.json"), ("/a/b/w.txt",), ("/a/b/c/d/v.json",))
484 for got, expect in zip(found, expected):
485 self.assertEqual(tuple(u.path for u in got), expect)
487 # Find only JSON files
488 found = set(uri.path for uri in ButlerURI.findFileResources([ButlerURI(self.makeS3Uri("a/"))],
489 file_filter=r"\.json$"))
490 self.assertEqual(found, {uri.path for uri in expected_uris if uri.path.endswith(".json")})
492 # JSON files grouped by directory
493 found = ButlerURI.findFileResources([ButlerURI(self.makeS3Uri("a/"))],
494 file_filter=r"\.json$", grouped=True)
495 expected = (("/a/z.json",), ("/a/b/c/d/v.json",))
497 for got, expect in zip(found, expected):
498 self.assertEqual(tuple(u.path for u in got), expect)
500 # Check pagination works with large numbers of files. S3 API limits
501 # us to 1000 response per list_objects call so create lots of files
502 created = set()
503 counter = 1
504 n_dir1 = 1100
505 while counter <= n_dir1:
506 new = ButlerURI(self.makeS3Uri(f"test/file{counter:04d}.txt"))
507 new.write(f"{counter}".encode())
508 created.add(str(new))
509 counter += 1
510 counter = 1
511 # Put some in a subdirectory to make sure we are looking in a
512 # hierarchy.
513 n_dir2 = 100
514 while counter <= n_dir2:
515 new = ButlerURI(self.makeS3Uri(f"test/subdir/file{counter:04d}.txt"))
516 new.write(f"{counter}".encode())
517 created.add(str(new))
518 counter += 1
520 found = ButlerURI.findFileResources([ButlerURI(self.makeS3Uri("test/"))])
521 self.assertEqual({str(u) for u in found}, created)
523 # Again with grouping.
524 found = list(ButlerURI.findFileResources([ButlerURI(self.makeS3Uri("test/"))], grouped=True))
525 self.assertEqual(len(found), 2)
526 dir_1 = list(found[0])
527 dir_2 = list(found[1])
528 self.assertEqual(len(dir_1), n_dir1)
529 self.assertEqual(len(dir_2), n_dir2)
531 def testWrite(self):
532 s3write = ButlerURI(self.makeS3Uri("created.txt"))
533 content = "abcdefghijklmnopqrstuv\n"
534 s3write.write(content.encode())
535 self.assertEqual(s3write.read().decode(), content)
537 def testRelative(self):
538 """Check that we can get subpaths back from two URIs"""
539 parent = ButlerURI(self.makeS3Uri("rootdir"), forceDirectory=True)
540 child = ButlerURI(self.makeS3Uri("rootdir/dir1/file.txt"))
542 self.assertEqual(child.relative_to(parent), "dir1/file.txt")
544 not_child = ButlerURI(self.makeS3Uri("/a/b/dir1/file.txt"))
545 self.assertFalse(not_child.relative_to(parent))
547 not_s3 = ButlerURI(os.path.join(self.tmpdir, "dir1", "file2.txt"))
548 self.assertFalse(child.relative_to(not_s3))
550 def testQuoting(self):
551 """Check that quoting works."""
552 parent = ButlerURI(self.makeS3Uri("rootdir"), forceDirectory=True)
553 subpath = "rootdir/dir1+/file?.txt"
554 child = ButlerURI(self.makeS3Uri(urllib.parse.quote(subpath)))
556 self.assertEqual(child.relative_to(parent), "dir1+/file?.txt")
557 self.assertEqual(child.basename(), "file?.txt")
558 self.assertEqual(child.relativeToPathRoot, subpath)
559 self.assertIn("%", child.path)
560 self.assertEqual(child.unquoted_path, "/" + subpath)
563# Mock required environment variables during tests
564@unittest.mock.patch.dict(os.environ, {"LSST_BUTLER_WEBDAV_AUTH": "TOKEN",
565 "LSST_BUTLER_WEBDAV_TOKEN_FILE": os.path.join(
566 TESTDIR, "config/testConfigs/webdav/token"),
567 "LSST_BUTLER_WEBDAV_CA_BUNDLE": "/path/to/ca/certs"})
568class WebdavURITestCase(unittest.TestCase):
570 def setUp(self):
571 serverRoot = "www.not-exists.orgx"
572 existingFolderName = "existingFolder"
573 existingFileName = "existingFile"
574 notExistingFileName = "notExistingFile"
576 self.baseURL = ButlerURI(
577 f"https://{serverRoot}", forceDirectory=True)
578 self.existingFileButlerURI = ButlerURI(
579 f"https://{serverRoot}/{existingFolderName}/{existingFileName}")
580 self.notExistingFileButlerURI = ButlerURI(
581 f"https://{serverRoot}/{existingFolderName}/{notExistingFileName}")
582 self.existingFolderButlerURI = ButlerURI(
583 f"https://{serverRoot}/{existingFolderName}", forceDirectory=True)
584 self.notExistingFolderButlerURI = ButlerURI(
585 f"https://{serverRoot}/{notExistingFileName}", forceDirectory=True)
587 # Need to declare the options
588 responses.add(responses.OPTIONS,
589 self.baseURL.geturl(),
590 status=200, headers={"DAV": "1,2,3"})
592 # Used by ButlerHttpURI.exists()
593 responses.add(responses.HEAD,
594 self.existingFileButlerURI.geturl(),
595 status=200, headers={'Content-Length': '1024'})
596 responses.add(responses.HEAD,
597 self.notExistingFileButlerURI.geturl(),
598 status=404)
600 # Used by ButlerHttpURI.read()
601 responses.add(responses.GET,
602 self.existingFileButlerURI.geturl(),
603 status=200,
604 body=str.encode("It works!"))
605 responses.add(responses.GET,
606 self.notExistingFileButlerURI.geturl(),
607 status=404)
609 # Used by ButlerHttpURI.write()
610 responses.add(responses.PUT,
611 self.existingFileButlerURI.geturl(),
612 status=201)
614 # Used by ButlerHttpURI.transfer_from()
615 responses.add(responses.Response(url=self.existingFileButlerURI.geturl(),
616 method="COPY",
617 headers={"Destination": self.existingFileButlerURI.geturl()},
618 status=201))
619 responses.add(responses.Response(url=self.existingFileButlerURI.geturl(),
620 method="COPY",
621 headers={"Destination": self.notExistingFileButlerURI.geturl()},
622 status=201))
623 responses.add(responses.Response(url=self.existingFileButlerURI.geturl(),
624 method="MOVE",
625 headers={"Destination": self.notExistingFileButlerURI.geturl()},
626 status=201))
628 # Used by ButlerHttpURI.remove()
629 responses.add(responses.DELETE,
630 self.existingFileButlerURI.geturl(),
631 status=200)
632 responses.add(responses.DELETE,
633 self.notExistingFileButlerURI.geturl(),
634 status=404)
636 # Used by ButlerHttpURI.mkdir()
637 responses.add(responses.HEAD,
638 self.existingFolderButlerURI.geturl(),
639 status=200, headers={'Content-Length': '1024'})
640 responses.add(responses.HEAD,
641 self.baseURL.geturl(),
642 status=200, headers={'Content-Length': '1024'})
643 responses.add(responses.HEAD,
644 self.notExistingFolderButlerURI.geturl(),
645 status=404)
646 responses.add(responses.Response(url=self.notExistingFolderButlerURI.geturl(),
647 method="MKCOL",
648 status=201))
649 responses.add(responses.Response(url=self.existingFolderButlerURI.geturl(),
650 method="MKCOL",
651 status=403))
653 @responses.activate
654 def testExists(self):
656 self.assertTrue(self.existingFileButlerURI.exists())
657 self.assertFalse(self.notExistingFileButlerURI.exists())
659 self.assertEqual(self.existingFileButlerURI.size(), 1024)
660 with self.assertRaises(FileNotFoundError):
661 self.notExistingFileButlerURI.size()
663 @responses.activate
664 def testRemove(self):
666 self.assertIsNone(self.existingFileButlerURI.remove())
667 with self.assertRaises(FileNotFoundError):
668 self.notExistingFileButlerURI.remove()
670 @responses.activate
671 def testMkdir(self):
673 # The mock means that we can't check this now exists
674 self.notExistingFolderButlerURI.mkdir()
676 # This should do nothing
677 self.existingFolderButlerURI.mkdir()
679 with self.assertRaises(ValueError):
680 self.notExistingFileButlerURI.mkdir()
682 @responses.activate
683 def testRead(self):
685 self.assertEqual(self.existingFileButlerURI.read().decode(), "It works!")
686 self.assertNotEqual(self.existingFileButlerURI.read().decode(), "Nope.")
687 with self.assertRaises(FileNotFoundError):
688 self.notExistingFileButlerURI.read()
690 @responses.activate
691 def testWrite(self):
693 self.assertIsNone(self.existingFileButlerURI.write(data=str.encode("Some content.")))
694 with self.assertRaises(FileExistsError):
695 self.existingFileButlerURI.write(data=str.encode("Some content."), overwrite=False)
697 @responses.activate
698 def testTransfer(self):
700 self.assertIsNone(self.notExistingFileButlerURI.transfer_from(
701 src=self.existingFileButlerURI))
702 self.assertIsNone(self.notExistingFileButlerURI.transfer_from(
703 src=self.existingFileButlerURI,
704 transfer="move"))
705 with self.assertRaises(FileExistsError):
706 self.existingFileButlerURI.transfer_from(src=self.existingFileButlerURI)
707 with self.assertRaises(ValueError):
708 self.notExistingFileButlerURI.transfer_from(
709 src=self.existingFileButlerURI,
710 transfer="unsupported")
712 def testParent(self):
714 self.assertEqual(self.existingFolderButlerURI.geturl(),
715 self.notExistingFileButlerURI.parent().geturl())
716 self.assertEqual(self.baseURL.geturl(),
717 self.baseURL.parent().geturl())
718 self.assertEqual(self.existingFileButlerURI.parent().geturl(),
719 self.existingFileButlerURI.dirname().geturl())
722if __name__ == "__main__": 722 ↛ 723line 722 didn't jump to line 723, because the condition on line 722 was never true
723 unittest.main()