1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 from __future__ import absolute_import
6 from future.utils import viewitems
7 from builtins import str
8 from builtins import object
9 from pathlib import Path
10 from six import assertRegex
24 import arvados_fuse as fuse
25 from arvados_fuse import fusedir
26 from . import run_test_server
28 from .integration_test import IntegrationTest
29 from .mount_test_base import MountTestBase
30 from .test_tmp_collection import storage_classes_desired
32 logger = logging.getLogger('arvados.arv-mount')
35 class AssertWithTimeout(object):
36 """Allow some time for an assertion to pass."""
38 def __init__(self, timeout=0):
39 self.timeout = timeout
42 self.deadline = time.time() + self.timeout
51 def attempt(self, fn, *args, **kwargs):
54 except AssertionError:
55 if time.time() > self.deadline:
61 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
62 class FuseMountTest(MountTestBase):
64 super(FuseMountTest, self).setUp()
66 cw = arvados.CollectionWriter()
68 cw.start_new_file('thing1.txt')
70 cw.start_new_file('thing2.txt')
73 cw.start_new_stream('dir1')
74 cw.start_new_file('thing3.txt')
76 cw.start_new_file('thing4.txt')
79 cw.start_new_stream('dir2')
80 cw.start_new_file('thing5.txt')
82 cw.start_new_file('thing6.txt')
85 cw.start_new_stream('dir2/dir3')
86 cw.start_new_file('thing7.txt')
89 cw.start_new_file('thing8.txt')
92 cw.start_new_stream('edgecases')
93 for f in ":/.../-/*/ ".split("/"):
97 for f in ":/.../-/*/ ".split("/"):
98 cw.start_new_stream('edgecases/dirs/' + f)
99 cw.start_new_file('x/x')
102 self.testcollection = cw.finish()
103 self.api.collections().create(body={"manifest_text":cw.manifest_text()}).execute()
106 self.make_mount(fuse.CollectionDirectory, collection_record=self.testcollection)
108 self.assertDirContents(None, ['thing1.txt', 'thing2.txt',
109 'edgecases', 'dir1', 'dir2'])
110 self.assertDirContents('dir1', ['thing3.txt', 'thing4.txt'])
111 self.assertDirContents('dir2', ['thing5.txt', 'thing6.txt', 'dir3'])
112 self.assertDirContents('dir2/dir3', ['thing7.txt', 'thing8.txt'])
113 self.assertDirContents('edgecases',
114 "dirs/:/.../-/*/ ".split("/"))
115 self.assertDirContents('edgecases/dirs',
116 ":/.../-/*/ ".split("/"))
118 files = {'thing1.txt': 'data 1',
119 'thing2.txt': 'data 2',
120 'dir1/thing3.txt': 'data 3',
121 'dir1/thing4.txt': 'data 4',
122 'dir2/thing5.txt': 'data 5',
123 'dir2/thing6.txt': 'data 6',
124 'dir2/dir3/thing7.txt': 'data 7',
125 'dir2/dir3/thing8.txt': 'data 8'}
127 for k, v in viewitems(files):
128 with open(os.path.join(self.mounttmp, k), 'rb') as f:
129 self.assertEqual(v, f.read().decode())
132 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
133 class FuseMagicTest(MountTestBase):
134 def setUp(self, api=None):
135 super(FuseMagicTest, self).setUp(api=api)
137 self.test_project = run_test_server.fixture('groups')['aproject']['uuid']
138 self.non_project_group = run_test_server.fixture('groups')['public_role']['uuid']
139 self.filter_group = run_test_server.fixture('groups')['afiltergroup']['uuid']
140 self.collection_in_test_project = run_test_server.fixture('collections')['foo_collection_in_aproject']['name']
141 self.collection_in_filter_group = run_test_server.fixture('collections')['baz_file']['name']
143 cw = arvados.CollectionWriter()
145 cw.start_new_file('thing1.txt')
148 self.testcollection = cw.finish()
149 self.test_manifest = cw.manifest_text()
150 coll = self.api.collections().create(body={"manifest_text":self.test_manifest}).execute()
151 self.test_manifest_pdh = coll['portable_data_hash']
154 self.make_mount(fuse.MagicDirectory)
156 mount_ls = llfuse.listdir(self.mounttmp)
157 self.assertIn('README', mount_ls)
158 self.assertFalse(any(arvados.util.keep_locator_pattern.match(fn) or
159 arvados.util.uuid_pattern.match(fn)
161 "new FUSE MagicDirectory has no collections or projects")
162 self.assertDirContents(self.testcollection, ['thing1.txt'])
163 self.assertDirContents(os.path.join('by_id', self.testcollection),
165 self.assertIn(self.collection_in_test_project,
166 llfuse.listdir(os.path.join(self.mounttmp, self.test_project)))
167 self.assertIn(self.collection_in_test_project,
168 llfuse.listdir(os.path.join(self.mounttmp, 'by_id', self.test_project)))
169 self.assertIn(self.collection_in_filter_group,
170 llfuse.listdir(os.path.join(self.mounttmp, self.filter_group)))
171 self.assertIn(self.collection_in_filter_group,
172 llfuse.listdir(os.path.join(self.mounttmp, 'by_id', self.filter_group)))
175 mount_ls = llfuse.listdir(self.mounttmp)
176 self.assertIn('README', mount_ls)
177 self.assertIn(self.testcollection, mount_ls)
178 self.assertIn(self.testcollection,
179 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
180 self.assertIn(self.test_project, mount_ls)
181 self.assertIn(self.test_project,
182 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
183 self.assertIn(self.filter_group,
184 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
186 with self.assertRaises(OSError):
187 llfuse.listdir(os.path.join(self.mounttmp, 'by_id', self.non_project_group))
190 files[os.path.join(self.mounttmp, self.testcollection, 'thing1.txt')] = 'data 1'
192 for k, v in viewitems(files):
193 with open(os.path.join(self.mounttmp, k), 'rb') as f:
194 self.assertEqual(v, f.read().decode())
197 class FuseTagsTest(MountTestBase):
199 self.make_mount(fuse.TagsDirectory)
201 d1 = llfuse.listdir(self.mounttmp)
203 self.assertEqual(['foo_tag'], d1)
205 d2 = llfuse.listdir(os.path.join(self.mounttmp, 'foo_tag'))
207 self.assertEqual(['zzzzz-4zz18-fy296fx3hot09f7'], d2)
209 d3 = llfuse.listdir(os.path.join(self.mounttmp, 'foo_tag', 'zzzzz-4zz18-fy296fx3hot09f7'))
211 self.assertEqual(['foo'], d3)
214 class FuseTagsUpdateTest(MountTestBase):
215 def tag_collection(self, coll_uuid, tag_name):
216 return self.api.links().create(
217 body={'link': {'head_uuid': coll_uuid,
223 self.make_mount(fuse.TagsDirectory, poll_time=1)
225 self.assertIn('foo_tag', llfuse.listdir(self.mounttmp))
227 bar_uuid = run_test_server.fixture('collections')['bar_file']['uuid']
228 self.tag_collection(bar_uuid, 'fuse_test_tag')
229 for attempt in AssertWithTimeout(10):
230 attempt(self.assertIn, 'fuse_test_tag', llfuse.listdir(self.mounttmp))
231 self.assertDirContents('fuse_test_tag', [bar_uuid])
233 baz_uuid = run_test_server.fixture('collections')['baz_file']['uuid']
234 l = self.tag_collection(baz_uuid, 'fuse_test_tag')
235 for attempt in AssertWithTimeout(10):
236 attempt(self.assertDirContents, 'fuse_test_tag', [bar_uuid, baz_uuid])
238 self.api.links().delete(uuid=l['uuid']).execute()
239 for attempt in AssertWithTimeout(10):
240 attempt(self.assertDirContents, 'fuse_test_tag', [bar_uuid])
243 def fuseSharedTestHelper(mounttmp):
244 class Test(unittest.TestCase):
246 # Double check that we can open and read objects in this folder as a file,
247 # and that its contents are what we expect.
248 baz_path = os.path.join(
252 'collection in FUSE project',
254 with open(baz_path) as f:
255 self.assertEqual("baz", f.read())
257 # check mtime on collection
258 st = os.stat(baz_path)
260 mtime = st.st_mtime_ns // 1000000000
261 except AttributeError:
263 self.assertEqual(mtime, 1391448174)
265 # shared_dirs is a list of the directories exposed
266 # by fuse.SharedDirectory (i.e. any object visible
267 # to the current user)
268 shared_dirs = llfuse.listdir(mounttmp)
270 self.assertIn('FUSE User', shared_dirs)
272 # fuse_user_objs is a list of the objects owned by the FUSE
273 # test user (which present as files in the 'FUSE User'
275 fuse_user_objs = llfuse.listdir(os.path.join(mounttmp, 'FUSE User'))
276 fuse_user_objs.sort()
277 self.assertEqual(['FUSE Test Project', # project owned by user
278 'collection #1 owned by FUSE', # collection owned by user
279 'collection #2 owned by FUSE' # collection owned by user
282 # test_proj_files is a list of the files in the FUSE Test Project.
283 test_proj_files = llfuse.listdir(os.path.join(mounttmp, 'FUSE User', 'FUSE Test Project'))
284 test_proj_files.sort()
285 self.assertEqual(['collection in FUSE project'
291 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
292 class FuseSharedTest(MountTestBase):
294 self.make_mount(fuse.SharedDirectory,
295 exclude=self.api.users().current().execute()['uuid'])
296 keep = arvados.keep.KeepClient()
297 keep.put("baz".encode())
299 self.pool.apply(fuseSharedTestHelper, (self.mounttmp,))
302 class FuseHomeTest(MountTestBase):
304 self.make_mount(fuse.ProjectDirectory,
305 project_object=self.api.users().current().execute())
307 d1 = llfuse.listdir(self.mounttmp)
308 self.assertIn('Unrestricted public data', d1)
310 d2 = llfuse.listdir(os.path.join(self.mounttmp, 'Unrestricted public data'))
311 public_project = run_test_server.fixture('groups')[
312 'anonymously_accessible_project']
315 for name, item in viewitems(run_test_server.fixture('collections')):
316 if 'name' not in item:
318 elif item['owner_uuid'] == public_project['uuid']:
319 self.assertIn(item['name'], d2)
322 # Artificial assumption here: there is no public
323 # collection fixture with the same name as a
324 # non-public collection.
325 self.assertNotIn(item['name'], d2)
327 self.assertNotEqual(0, found_in)
328 self.assertNotEqual(0, found_not_in)
330 d3 = llfuse.listdir(os.path.join(self.mounttmp, 'Unrestricted public data', 'GNU General Public License, version 3'))
331 self.assertEqual(["GNU_General_Public_License,_version_3.pdf"], d3)
334 def fuseModifyFileTestHelperReadStartContents(mounttmp):
335 class Test(unittest.TestCase):
337 d1 = llfuse.listdir(mounttmp)
338 self.assertEqual(["file1.txt"], d1)
339 with open(os.path.join(mounttmp, "file1.txt")) as f:
340 self.assertEqual("blub", f.read())
343 def fuseModifyFileTestHelperReadEndContents(mounttmp):
344 class Test(unittest.TestCase):
346 d1 = llfuse.listdir(mounttmp)
347 self.assertEqual(["file1.txt"], d1)
348 with open(os.path.join(mounttmp, "file1.txt")) as f:
349 self.assertEqual("plnp", f.read())
352 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
353 class FuseModifyFileTest(MountTestBase):
355 collection = arvados.collection.Collection(api_client=self.api)
356 with collection.open("file1.txt", "w") as f:
359 collection.save_new()
361 m = self.make_mount(fuse.CollectionDirectory)
363 m.new_collection(collection.api_response(), collection)
365 self.pool.apply(fuseModifyFileTestHelperReadStartContents, (self.mounttmp,))
367 with collection.open("file1.txt", "w") as f:
370 self.pool.apply(fuseModifyFileTestHelperReadEndContents, (self.mounttmp,))
373 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
374 class FuseAddFileToCollectionTest(MountTestBase):
376 collection = arvados.collection.Collection(api_client=self.api)
377 with collection.open("file1.txt", "w") as f:
380 collection.save_new()
382 m = self.make_mount(fuse.CollectionDirectory)
384 m.new_collection(collection.api_response(), collection)
386 d1 = llfuse.listdir(self.mounttmp)
387 self.assertEqual(["file1.txt"], d1)
389 with collection.open("file2.txt", "w") as f:
392 d1 = llfuse.listdir(self.mounttmp)
393 self.assertEqual(["file1.txt", "file2.txt"], sorted(d1))
396 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
397 class FuseRemoveFileFromCollectionTest(MountTestBase):
399 collection = arvados.collection.Collection(api_client=self.api)
400 with collection.open("file1.txt", "w") as f:
403 with collection.open("file2.txt", "w") as f:
406 collection.save_new()
408 m = self.make_mount(fuse.CollectionDirectory)
410 m.new_collection(collection.api_response(), collection)
412 d1 = llfuse.listdir(self.mounttmp)
413 self.assertEqual(["file1.txt", "file2.txt"], sorted(d1))
415 collection.remove("file2.txt")
417 d1 = llfuse.listdir(self.mounttmp)
418 self.assertEqual(["file1.txt"], d1)
421 def fuseCreateFileTestHelper(mounttmp):
422 class Test(unittest.TestCase):
424 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
428 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
429 class FuseCreateFileTest(MountTestBase):
431 collection = arvados.collection.Collection(api_client=self.api)
432 collection.save_new()
434 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
435 self.assertEqual(collection2["manifest_text"], "")
437 collection.save_new()
439 m = self.make_mount(fuse.CollectionDirectory)
441 m.new_collection(collection.api_response(), collection)
442 self.assertTrue(m.writable())
444 self.assertNotIn("file1.txt", collection)
446 self.pool.apply(fuseCreateFileTestHelper, (self.mounttmp,))
448 self.assertIn("file1.txt", collection)
450 d1 = llfuse.listdir(self.mounttmp)
451 self.assertEqual(["file1.txt"], d1)
453 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
454 assertRegex(self, collection2["manifest_text"],
455 r'\. d41d8cd98f00b204e9800998ecf8427e\+0\+A\S+ 0:0:file1\.txt$')
458 def fuseWriteFileTestHelperWriteFile(mounttmp):
459 class Test(unittest.TestCase):
461 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
462 f.write("Hello world!")
465 def fuseWriteFileTestHelperReadFile(mounttmp):
466 class Test(unittest.TestCase):
468 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
469 self.assertEqual(f.read(), "Hello world!")
472 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
473 class FuseWriteFileTest(MountTestBase):
475 collection = arvados.collection.Collection(api_client=self.api)
476 collection.save_new()
478 m = self.make_mount(fuse.CollectionDirectory)
480 m.new_collection(collection.api_response(), collection)
481 self.assertTrue(m.writable())
483 self.assertNotIn("file1.txt", collection)
485 self.assertEqual(0, self.operations.write_counter.get())
486 self.pool.apply(fuseWriteFileTestHelperWriteFile, (self.mounttmp,))
487 self.assertEqual(12, self.operations.write_counter.get())
489 with collection.open("file1.txt") as f:
490 self.assertEqual(f.read(), "Hello world!")
492 self.assertEqual(0, self.operations.read_counter.get())
493 self.pool.apply(fuseWriteFileTestHelperReadFile, (self.mounttmp,))
494 self.assertEqual(12, self.operations.read_counter.get())
496 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
497 assertRegex(self, collection2["manifest_text"],
498 r'\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
501 def fuseUpdateFileTestHelper(mounttmp):
502 class Test(unittest.TestCase):
504 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
505 f.write("Hello world!")
507 with open(os.path.join(mounttmp, "file1.txt"), "r+") as f:
509 self.assertEqual(fr, "Hello world!")
511 f.write("Hola mundo!")
514 self.assertEqual(fr, "Hola mundo!!")
516 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
517 self.assertEqual(f.read(), "Hola mundo!!")
521 @parameterized.parameterized_class([{"disk_cache": True}, {"disk_cache": False}])
522 class FuseUpdateFileTest(MountTestBase):
524 collection = arvados.collection.Collection(api_client=self.api)
525 collection.save_new()
527 m = self.make_mount(fuse.CollectionDirectory)
529 m.new_collection(collection.api_response(), collection)
530 self.assertTrue(m.writable())
532 # See note in MountTestBase.setUp
533 self.pool.apply(fuseUpdateFileTestHelper, (self.mounttmp,))
535 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
536 assertRegex(self, collection2["manifest_text"],
537 r'\. daaef200ebb921e011e3ae922dd3266b\+11\+A\S+ 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:11:file1\.txt 22:1:file1\.txt$')
540 def fuseMkdirTestHelper(mounttmp):
541 class Test(unittest.TestCase):
543 with self.assertRaises(IOError):
544 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
545 f.write("Hello world!")
547 os.mkdir(os.path.join(mounttmp, "testdir"))
549 with self.assertRaises(OSError):
550 os.mkdir(os.path.join(mounttmp, "testdir"))
552 d1 = llfuse.listdir(mounttmp)
553 self.assertEqual(["testdir"], d1)
555 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
556 f.write("Hello world!")
558 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
559 self.assertEqual(["file1.txt"], d1)
563 class FuseMkdirTest(MountTestBase):
565 collection = arvados.collection.Collection(api_client=self.api)
566 collection.save_new()
568 m = self.make_mount(fuse.CollectionDirectory)
570 m.new_collection(collection.api_response(), collection)
571 self.assertTrue(m.writable())
573 self.pool.apply(fuseMkdirTestHelper, (self.mounttmp,))
575 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
576 assertRegex(self, collection2["manifest_text"],
577 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
580 def fuseRmTestHelperWriteFile(mounttmp):
581 class Test(unittest.TestCase):
583 os.mkdir(os.path.join(mounttmp, "testdir"))
585 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
586 f.write("Hello world!")
590 def fuseRmTestHelperDeleteFile(mounttmp):
591 class Test(unittest.TestCase):
593 # Can't delete because it's not empty
594 with self.assertRaises(OSError):
595 os.rmdir(os.path.join(mounttmp, "testdir"))
597 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
598 self.assertEqual(["file1.txt"], d1)
601 os.remove(os.path.join(mounttmp, "testdir", "file1.txt"))
603 # Make sure it's empty
604 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
605 self.assertEqual([], d1)
607 # Try to delete it again
608 with self.assertRaises(OSError):
609 os.remove(os.path.join(mounttmp, "testdir", "file1.txt"))
613 def fuseRmTestHelperRmdir(mounttmp):
614 class Test(unittest.TestCase):
616 # Should be able to delete now that it is empty
617 os.rmdir(os.path.join(mounttmp, "testdir"))
619 # Make sure it's empty
620 d1 = llfuse.listdir(os.path.join(mounttmp))
621 self.assertEqual([], d1)
623 # Try to delete it again
624 with self.assertRaises(OSError):
625 os.rmdir(os.path.join(mounttmp, "testdir"))
629 class FuseRmTest(MountTestBase):
631 collection = arvados.collection.Collection(api_client=self.api)
632 collection.save_new()
634 m = self.make_mount(fuse.CollectionDirectory)
636 m.new_collection(collection.api_response(), collection)
637 self.assertTrue(m.writable())
639 self.pool.apply(fuseRmTestHelperWriteFile, (self.mounttmp,))
642 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
643 assertRegex(self, collection2["manifest_text"],
644 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
645 self.pool.apply(fuseRmTestHelperDeleteFile, (self.mounttmp,))
647 # Empty directories are represented by an empty file named "."
648 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
649 assertRegex(self, collection2["manifest_text"],
650 r'./testdir d41d8cd98f00b204e9800998ecf8427e\+0\+A\S+ 0:0:\\056\n')
652 self.pool.apply(fuseRmTestHelperRmdir, (self.mounttmp,))
654 # manifest should be empty now.
655 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
656 self.assertEqual(collection2["manifest_text"], "")
659 def fuseMvFileTestHelperWriteFile(mounttmp):
660 class Test(unittest.TestCase):
662 os.mkdir(os.path.join(mounttmp, "testdir"))
664 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
665 f.write("Hello world!")
669 def fuseMvFileTestHelperMoveFile(mounttmp):
670 class Test(unittest.TestCase):
672 d1 = llfuse.listdir(os.path.join(mounttmp))
673 self.assertEqual(["testdir"], d1)
674 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
675 self.assertEqual(["file1.txt"], d1)
677 os.rename(os.path.join(mounttmp, "testdir", "file1.txt"), os.path.join(mounttmp, "file1.txt"))
679 d1 = llfuse.listdir(os.path.join(mounttmp))
680 self.assertEqual(["file1.txt", "testdir"], sorted(d1))
681 d1 = llfuse.listdir(os.path.join(mounttmp, "testdir"))
682 self.assertEqual([], d1)
686 class FuseMvFileTest(MountTestBase):
688 collection = arvados.collection.Collection(api_client=self.api)
689 collection.save_new()
691 m = self.make_mount(fuse.CollectionDirectory)
693 m.new_collection(collection.api_response(), collection)
694 self.assertTrue(m.writable())
696 self.pool.apply(fuseMvFileTestHelperWriteFile, (self.mounttmp,))
699 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
700 assertRegex(self, collection2["manifest_text"],
701 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
703 self.pool.apply(fuseMvFileTestHelperMoveFile, (self.mounttmp,))
705 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
706 assertRegex(self, collection2["manifest_text"],
707 r'\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt\n\./testdir d41d8cd98f00b204e9800998ecf8427e\+0\+A\S+ 0:0:\\056\n')
710 def fuseRenameTestHelper(mounttmp):
711 class Test(unittest.TestCase):
713 os.mkdir(os.path.join(mounttmp, "testdir"))
715 with open(os.path.join(mounttmp, "testdir", "file1.txt"), "w") as f:
716 f.write("Hello world!")
720 class FuseRenameTest(MountTestBase):
722 collection = arvados.collection.Collection(api_client=self.api)
723 collection.save_new()
725 m = self.make_mount(fuse.CollectionDirectory)
727 m.new_collection(collection.api_response(), collection)
728 self.assertTrue(m.writable())
730 self.pool.apply(fuseRenameTestHelper, (self.mounttmp,))
733 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
734 assertRegex(self, collection2["manifest_text"],
735 r'\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
737 d1 = llfuse.listdir(os.path.join(self.mounttmp))
738 self.assertEqual(["testdir"], d1)
739 d1 = llfuse.listdir(os.path.join(self.mounttmp, "testdir"))
740 self.assertEqual(["file1.txt"], d1)
742 os.rename(os.path.join(self.mounttmp, "testdir"), os.path.join(self.mounttmp, "testdir2"))
744 d1 = llfuse.listdir(os.path.join(self.mounttmp))
745 self.assertEqual(["testdir2"], sorted(d1))
746 d1 = llfuse.listdir(os.path.join(self.mounttmp, "testdir2"))
747 self.assertEqual(["file1.txt"], d1)
749 collection2 = self.api.collections().get(uuid=collection.manifest_locator()).execute()
750 assertRegex(self, collection2["manifest_text"],
751 r'\./testdir2 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$')
754 class FuseUpdateFromEventTest(MountTestBase):
756 collection = arvados.collection.Collection(api_client=self.api)
757 collection.save_new()
759 m = self.make_mount(fuse.CollectionDirectory)
761 m.new_collection(collection.api_response(), collection)
763 self.operations.listen_for_events()
765 d1 = llfuse.listdir(os.path.join(self.mounttmp))
766 self.assertEqual([], sorted(d1))
768 with arvados.collection.Collection(collection.manifest_locator(), api_client=self.api) as collection2:
769 with collection2.open("file1.txt", "w") as f:
772 for attempt in AssertWithTimeout(10):
773 attempt(self.assertEqual, ["file1.txt"], llfuse.listdir(os.path.join(self.mounttmp)))
776 class FuseDeleteProjectEventTest(MountTestBase):
779 aproject = self.api.groups().create(body={
781 "group_class": "project"
784 bproject = self.api.groups().create(body={
786 "group_class": "project",
787 "owner_uuid": aproject["uuid"]
790 self.make_mount(fuse.ProjectDirectory,
791 project_object=self.api.users().current().execute())
793 self.operations.listen_for_events()
795 d1 = llfuse.listdir(os.path.join(self.mounttmp, "aproject"))
796 self.assertEqual(["bproject"], sorted(d1))
798 self.api.groups().delete(uuid=bproject["uuid"]).execute()
800 for attempt in AssertWithTimeout(10):
801 attempt(self.assertEqual, [], llfuse.listdir(os.path.join(self.mounttmp, "aproject")))
804 def fuseFileConflictTestHelper(mounttmp, uuid, keeptmp, settings):
805 class Test(unittest.TestCase):
807 os.environ['KEEP_LOCAL_STORE'] = keeptmp
809 with open(os.path.join(mounttmp, "file1.txt"), "w") as f:
810 with arvados.collection.Collection(uuid, api_client=arvados.api_from_config('v1', apiconfig=settings)) as collection2:
811 with collection2.open("file1.txt", "w") as f2:
815 d1 = sorted(llfuse.listdir(os.path.join(mounttmp)))
816 self.assertEqual(len(d1), 2)
818 with open(os.path.join(mounttmp, "file1.txt"), "r") as f:
819 self.assertEqual(f.read(), "bar")
821 assertRegex(self, d1[1],
822 r'file1\.txt~\d\d\d\d\d\d\d\d-\d\d\d\d\d\d~conflict~')
824 with open(os.path.join(mounttmp, d1[1]), "r") as f:
825 self.assertEqual(f.read(), "foo")
829 class FuseFileConflictTest(MountTestBase):
831 collection = arvados.collection.Collection(api_client=self.api)
832 collection.save_new()
834 m = self.make_mount(fuse.CollectionDirectory)
836 m.new_collection(collection.api_response(), collection)
838 d1 = llfuse.listdir(os.path.join(self.mounttmp))
839 self.assertEqual([], sorted(d1))
841 # See note in MountTestBase.setUp
842 self.pool.apply(fuseFileConflictTestHelper, (self.mounttmp, collection.manifest_locator(), self.keeptmp, arvados.config.settings()))
845 def fuseUnlinkOpenFileTest(mounttmp):
846 class Test(unittest.TestCase):
848 with open(os.path.join(mounttmp, "file1.txt"), "w+") as f:
851 d1 = llfuse.listdir(os.path.join(mounttmp))
852 self.assertEqual(["file1.txt"], sorted(d1))
854 os.remove(os.path.join(mounttmp, "file1.txt"))
856 d1 = llfuse.listdir(os.path.join(mounttmp))
857 self.assertEqual([], sorted(d1))
860 self.assertEqual(f.read(), "foo")
864 self.assertEqual(f.read(), "foobar")
868 class FuseUnlinkOpenFileTest(MountTestBase):
870 collection = arvados.collection.Collection(api_client=self.api)
871 collection.save_new()
873 m = self.make_mount(fuse.CollectionDirectory)
875 m.new_collection(collection.api_response(), collection)
877 # See note in MountTestBase.setUp
878 self.pool.apply(fuseUnlinkOpenFileTest, (self.mounttmp,))
880 self.assertEqual(collection.manifest_text(), "")
883 def fuseMvFileBetweenCollectionsTest1(mounttmp, uuid1, uuid2):
884 class Test(unittest.TestCase):
886 with open(os.path.join(mounttmp, uuid1, "file1.txt"), "w") as f:
887 f.write("Hello world!")
889 d1 = os.listdir(os.path.join(mounttmp, uuid1))
890 self.assertEqual(["file1.txt"], sorted(d1))
891 d1 = os.listdir(os.path.join(mounttmp, uuid2))
892 self.assertEqual([], sorted(d1))
896 def fuseMvFileBetweenCollectionsTest2(mounttmp, uuid1, uuid2):
897 class Test(unittest.TestCase):
899 os.rename(os.path.join(mounttmp, uuid1, "file1.txt"), os.path.join(mounttmp, uuid2, "file2.txt"))
901 d1 = os.listdir(os.path.join(mounttmp, uuid1))
902 self.assertEqual([], sorted(d1))
903 d1 = os.listdir(os.path.join(mounttmp, uuid2))
904 self.assertEqual(["file2.txt"], sorted(d1))
908 class FuseMvFileBetweenCollectionsTest(MountTestBase):
910 collection1 = arvados.collection.Collection(api_client=self.api)
911 collection1.save_new()
913 collection2 = arvados.collection.Collection(api_client=self.api)
914 collection2.save_new()
916 m = self.make_mount(fuse.MagicDirectory)
918 # See note in MountTestBase.setUp
919 self.pool.apply(fuseMvFileBetweenCollectionsTest1, (self.mounttmp,
920 collection1.manifest_locator(),
921 collection2.manifest_locator()))
926 assertRegex(self, collection1.manifest_text(), r"\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$")
927 self.assertEqual(collection2.manifest_text(), "")
929 self.pool.apply(fuseMvFileBetweenCollectionsTest2, (self.mounttmp,
930 collection1.manifest_locator(),
931 collection2.manifest_locator()))
936 self.assertEqual(collection1.manifest_text(), "")
937 assertRegex(self, collection2.manifest_text(), r"\. 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file2\.txt$")
939 collection1.stop_threads()
940 collection2.stop_threads()
943 def fuseMvDirBetweenCollectionsTest1(mounttmp, uuid1, uuid2):
944 class Test(unittest.TestCase):
946 os.mkdir(os.path.join(mounttmp, uuid1, "testdir"))
947 with open(os.path.join(mounttmp, uuid1, "testdir", "file1.txt"), "w") as f:
948 f.write("Hello world!")
950 d1 = os.listdir(os.path.join(mounttmp, uuid1))
951 self.assertEqual(["testdir"], sorted(d1))
952 d1 = os.listdir(os.path.join(mounttmp, uuid1, "testdir"))
953 self.assertEqual(["file1.txt"], sorted(d1))
955 d1 = os.listdir(os.path.join(mounttmp, uuid2))
956 self.assertEqual([], sorted(d1))
961 def fuseMvDirBetweenCollectionsTest2(mounttmp, uuid1, uuid2):
962 class Test(unittest.TestCase):
964 os.rename(os.path.join(mounttmp, uuid1, "testdir"), os.path.join(mounttmp, uuid2, "testdir2"))
966 d1 = os.listdir(os.path.join(mounttmp, uuid1))
967 self.assertEqual([], sorted(d1))
969 d1 = os.listdir(os.path.join(mounttmp, uuid2))
970 self.assertEqual(["testdir2"], sorted(d1))
971 d1 = os.listdir(os.path.join(mounttmp, uuid2, "testdir2"))
972 self.assertEqual(["file1.txt"], sorted(d1))
974 with open(os.path.join(mounttmp, uuid2, "testdir2", "file1.txt"), "r") as f:
975 self.assertEqual(f.read(), "Hello world!")
979 class FuseMvDirBetweenCollectionsTest(MountTestBase):
981 collection1 = arvados.collection.Collection(api_client=self.api)
982 collection1.save_new()
984 collection2 = arvados.collection.Collection(api_client=self.api)
985 collection2.save_new()
987 m = self.make_mount(fuse.MagicDirectory)
989 # See note in MountTestBase.setUp
990 self.pool.apply(fuseMvDirBetweenCollectionsTest1, (self.mounttmp,
991 collection1.manifest_locator(),
992 collection2.manifest_locator()))
997 assertRegex(self, collection1.manifest_text(), r"\./testdir 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$")
998 self.assertEqual(collection2.manifest_text(), "")
1000 self.pool.apply(fuseMvDirBetweenCollectionsTest2, (self.mounttmp,
1001 collection1.manifest_locator(),
1002 collection2.manifest_locator()))
1004 collection1.update()
1005 collection2.update()
1007 self.assertEqual(collection1.manifest_text(), "")
1008 assertRegex(self, collection2.manifest_text(), r"\./testdir2 86fb269d190d2c85f6e0468ceca42a20\+12\+A\S+ 0:12:file1\.txt$")
1010 collection1.stop_threads()
1011 collection2.stop_threads()
1013 def fuseProjectMkdirTestHelper1(mounttmp):
1014 class Test(unittest.TestCase):
1016 os.mkdir(os.path.join(mounttmp, "testcollection"))
1017 with self.assertRaises(OSError):
1018 os.mkdir(os.path.join(mounttmp, "testcollection"))
1021 def fuseProjectMkdirTestHelper2(mounttmp):
1022 class Test(unittest.TestCase):
1024 with open(os.path.join(mounttmp, "testcollection", "file1.txt"), "w") as f:
1025 f.write("Hello world!")
1026 with self.assertRaises(OSError):
1027 os.rmdir(os.path.join(mounttmp, "testcollection"))
1028 os.remove(os.path.join(mounttmp, "testcollection", "file1.txt"))
1029 with self.assertRaises(OSError):
1030 os.remove(os.path.join(mounttmp, "testcollection"))
1031 os.rmdir(os.path.join(mounttmp, "testcollection"))
1034 class FuseProjectMkdirRmdirTest(MountTestBase):
1036 self.make_mount(fuse.ProjectDirectory,
1037 project_object=self.api.users().current().execute())
1039 d1 = llfuse.listdir(self.mounttmp)
1040 self.assertNotIn('testcollection', d1)
1042 self.pool.apply(fuseProjectMkdirTestHelper1, (self.mounttmp,))
1044 d1 = llfuse.listdir(self.mounttmp)
1045 self.assertIn('testcollection', d1)
1047 self.pool.apply(fuseProjectMkdirTestHelper2, (self.mounttmp,))
1049 d1 = llfuse.listdir(self.mounttmp)
1050 self.assertNotIn('testcollection', d1)
1053 def fuseProjectMvTestHelper1(mounttmp):
1054 class Test(unittest.TestCase):
1056 d1 = llfuse.listdir(mounttmp)
1057 self.assertNotIn('testcollection', d1)
1059 os.mkdir(os.path.join(mounttmp, "testcollection"))
1061 d1 = llfuse.listdir(mounttmp)
1062 self.assertIn('testcollection', d1)
1064 with self.assertRaises(OSError):
1065 os.rename(os.path.join(mounttmp, "testcollection"), os.path.join(mounttmp, 'Unrestricted public data'))
1067 os.rename(os.path.join(mounttmp, "testcollection"), os.path.join(mounttmp, 'Unrestricted public data', 'testcollection'))
1069 d1 = llfuse.listdir(mounttmp)
1070 self.assertNotIn('testcollection', d1)
1072 d1 = llfuse.listdir(os.path.join(mounttmp, 'Unrestricted public data'))
1073 self.assertIn('testcollection', d1)
1077 class FuseProjectMvTest(MountTestBase):
1079 self.make_mount(fuse.ProjectDirectory,
1080 project_object=self.api.users().current().execute())
1082 self.pool.apply(fuseProjectMvTestHelper1, (self.mounttmp,))
1085 def fuseFsyncTestHelper(mounttmp, k):
1086 class Test(unittest.TestCase):
1088 fd = os.open(os.path.join(mounttmp, k), os.O_RDONLY)
1094 class FuseFsyncTest(FuseMagicTest):
1096 self.make_mount(fuse.MagicDirectory)
1097 self.pool.apply(fuseFsyncTestHelper, (self.mounttmp, self.testcollection))
1100 class MagicDirApiError(FuseMagicTest):
1102 api = mock.MagicMock()
1103 api.keep.block_cache = mock.MagicMock(cache_max=1)
1104 super(MagicDirApiError, self).setUp(api=api)
1105 api.collections().get().execute.side_effect = iter([
1106 Exception('API fail'),
1108 "manifest_text": self.test_manifest,
1109 "portable_data_hash": self.test_manifest_pdh,
1112 api.keep.get.side_effect = Exception('Keep fail')
1115 with mock.patch('arvados_fuse.fresh.FreshBase._poll_time', new_callable=mock.PropertyMock, return_value=60) as mock_poll_time:
1116 self.make_mount(fuse.MagicDirectory)
1118 self.operations.inodes.inode_cache.cap = 1
1119 self.operations.inodes.inode_cache.min_entries = 2
1121 with self.assertRaises(OSError):
1122 llfuse.listdir(os.path.join(self.mounttmp, self.testcollection))
1124 llfuse.listdir(os.path.join(self.mounttmp, self.testcollection))
1127 class SanitizeFilenameTest(MountTestBase):
1128 def test_sanitize_filename(self):
1129 pdir = fuse.ProjectDirectory(1, {}, self.api, 0, False, project_object=self.api.users().current().execute())
1148 for f in acceptable:
1149 self.assertEqual(f, pdir.sanitize_filename(f))
1150 for f in unacceptable:
1151 self.assertNotEqual(f, pdir.sanitize_filename(f))
1152 # The sanitized filename should be the same length, though.
1153 self.assertEqual(len(f), len(pdir.sanitize_filename(f)))
1155 self.assertEqual("_", pdir.sanitize_filename(""))
1156 self.assertEqual("_", pdir.sanitize_filename("."))
1157 self.assertEqual("__", pdir.sanitize_filename(".."))
1160 class FuseMagicTestPDHOnly(MountTestBase):
1161 def setUp(self, api=None):
1162 super(FuseMagicTestPDHOnly, self).setUp(api=api)
1164 cw = arvados.CollectionWriter()
1166 cw.start_new_file('thing1.txt')
1169 self.testcollection = cw.finish()
1170 self.test_manifest = cw.manifest_text()
1171 created = self.api.collections().create(body={"manifest_text":self.test_manifest}).execute()
1172 self.testcollectionuuid = str(created['uuid'])
1174 def verify_pdh_only(self, pdh_only=False, skip_pdh_only=False):
1175 if skip_pdh_only is True:
1176 self.make_mount(fuse.MagicDirectory) # in this case, the default by_id applies
1178 self.make_mount(fuse.MagicDirectory, pdh_only=pdh_only)
1180 mount_ls = llfuse.listdir(self.mounttmp)
1181 self.assertIn('README', mount_ls)
1182 self.assertFalse(any(arvados.util.keep_locator_pattern.match(fn) or
1183 arvados.util.uuid_pattern.match(fn)
1184 for fn in mount_ls),
1185 "new FUSE MagicDirectory lists Collection")
1187 # look up using pdh should succeed in all cases
1188 self.assertDirContents(self.testcollection, ['thing1.txt'])
1189 self.assertDirContents(os.path.join('by_id', self.testcollection),
1191 mount_ls = llfuse.listdir(self.mounttmp)
1192 self.assertIn('README', mount_ls)
1193 self.assertIn(self.testcollection, mount_ls)
1194 self.assertIn(self.testcollection,
1195 llfuse.listdir(os.path.join(self.mounttmp, 'by_id')))
1198 files[os.path.join(self.mounttmp, self.testcollection, 'thing1.txt')] = 'data 1'
1200 for k, v in viewitems(files):
1201 with open(os.path.join(self.mounttmp, k), 'rb') as f:
1202 self.assertEqual(v, f.read().decode())
1204 # look up using uuid should fail when pdh_only is set
1205 if pdh_only is True:
1206 with self.assertRaises(OSError):
1207 self.assertDirContents(os.path.join('by_id', self.testcollectionuuid),
1210 self.assertDirContents(os.path.join('by_id', self.testcollectionuuid),
1213 def test_with_pdh_only_true(self):
1214 self.verify_pdh_only(pdh_only=True)
1216 def test_with_pdh_only_false(self):
1217 self.verify_pdh_only(pdh_only=False)
1219 def test_with_default_by_id(self):
1220 self.verify_pdh_only(skip_pdh_only=True)
1223 class SlashSubstitutionTest(IntegrationTest):
1226 '--mount-home', 'zzz',
1230 super(SlashSubstitutionTest, self).setUp()
1231 self.api = arvados.safeapi.ThreadSafeApiCache(
1232 arvados.config.settings(),
1235 self.api.config = lambda: {"Collections": {"ForwardSlashNameSubstitution": "[SLASH]"}}
1236 self.testcoll = self.api.collections().create(body={"name": "foo/bar/baz"}).execute()
1237 self.testcolleasy = self.api.collections().create(body={"name": "foo-bar-baz"}).execute()
1238 self.fusename = 'foo[SLASH]bar[SLASH]baz'
1240 @IntegrationTest.mount(argv=mnt_args)
1241 @mock.patch('arvados.util.get_config_once')
1242 def test_slash_substitution_before_listing(self, get_config_once):
1243 get_config_once.return_value = {"Collections": {"ForwardSlashNameSubstitution": "[SLASH]"}}
1244 self.pool_test(os.path.join(self.mnt, 'zzz'), self.fusename)
1245 self.checkContents()
1247 def _test_slash_substitution_before_listing(self, tmpdir, fusename):
1248 with open(os.path.join(tmpdir, 'foo-bar-baz', 'waz'), 'w') as f:
1250 with open(os.path.join(tmpdir, fusename, 'waz'), 'w') as f:
1253 @IntegrationTest.mount(argv=mnt_args)
1254 @mock.patch('arvados.util.get_config_once')
1255 def test_slash_substitution_after_listing(self, get_config_once):
1256 get_config_once.return_value = {"Collections": {"ForwardSlashNameSubstitution": "[SLASH]"}}
1257 self.pool_test(os.path.join(self.mnt, 'zzz'), self.fusename)
1258 self.checkContents()
1260 def _test_slash_substitution_after_listing(self, tmpdir, fusename):
1261 with open(os.path.join(tmpdir, 'foo-bar-baz', 'waz'), 'w') as f:
1264 with open(os.path.join(tmpdir, fusename, 'waz'), 'w') as f:
1267 def checkContents(self):
1268 self.assertRegexpMatches(self.api.collections().get(uuid=self.testcoll['uuid']).execute()['manifest_text'], ' acbd18db') # md5(foo)
1269 self.assertRegexpMatches(self.api.collections().get(uuid=self.testcolleasy['uuid']).execute()['manifest_text'], ' f561aaf6') # md5(xxx)
1271 @IntegrationTest.mount(argv=mnt_args)
1272 @mock.patch('arvados.util.get_config_once')
1273 def test_slash_substitution_conflict(self, get_config_once):
1274 self.testcollconflict = self.api.collections().create(body={"name": self.fusename}).execute()
1275 get_config_once.return_value = {"Collections": {"ForwardSlashNameSubstitution": "[SLASH]"}}
1276 self.pool_test(os.path.join(self.mnt, 'zzz'), self.fusename)
1277 self.assertRegexpMatches(self.api.collections().get(uuid=self.testcollconflict['uuid']).execute()['manifest_text'], ' acbd18db') # md5(foo)
1278 # foo/bar/baz collection unchanged, because it is masked by foo[SLASH]bar[SLASH]baz
1279 self.assertEqual(self.api.collections().get(uuid=self.testcoll['uuid']).execute()['manifest_text'], '')
1281 def _test_slash_substitution_conflict(self, tmpdir, fusename):
1282 with open(os.path.join(tmpdir, fusename, 'waz'), 'w') as f:
1285 class StorageClassesTest(IntegrationTest):
1288 '--mount-home', 'homedir',
1292 super(StorageClassesTest, self).setUp()
1293 self.api = arvados.safeapi.ThreadSafeApiCache(
1294 arvados.config.settings(),
1298 @IntegrationTest.mount(argv=mnt_args)
1299 def test_collection_default_storage_classes(self):
1300 coll_path = os.path.join(self.mnt, 'homedir', 'a_collection')
1301 self.api.collections().create(body={'name':'a_collection'}).execute()
1302 self.pool_test(coll_path)
1304 def _test_collection_default_storage_classes(self, coll):
1305 self.assertEqual(storage_classes_desired(coll), ['default'])
1307 @IntegrationTest.mount(argv=mnt_args+['--storage-classes', 'foo'])
1308 def test_collection_custom_storage_classes(self):
1309 coll_path = os.path.join(self.mnt, 'homedir', 'new_coll')
1311 self.pool_test(coll_path)
1313 def _test_collection_custom_storage_classes(self, coll):
1314 self.assertEqual(storage_classes_desired(coll), ['foo'])
1316 def _readonlyCollectionTestHelper(mounttmp):
1317 f = open(os.path.join(mounttmp, 'thing1.txt'), 'rt')
1318 # Testing that close() doesn't raise an error.
1321 class ReadonlyCollectionTest(MountTestBase):
1323 super(ReadonlyCollectionTest, self).setUp()
1324 cw = arvados.collection.Collection()
1325 with cw.open('thing1.txt', 'wt') as f:
1327 cw.save_new(owner_uuid=run_test_server.fixture("groups")["aproject"]["uuid"])
1328 self.testcollection = cw.api_response()
1331 settings = arvados.config.settings().copy()
1332 settings["ARVADOS_API_TOKEN"] = run_test_server.fixture("api_client_authorizations")["project_viewer"]["api_token"]
1333 self.api = arvados.safeapi.ThreadSafeApiCache(settings, version='v1')
1334 self.make_mount(fuse.CollectionDirectory, collection_record=self.testcollection, enable_write=False)
1336 self.pool.apply(_readonlyCollectionTestHelper, (self.mounttmp,))
1339 @parameterized.parameterized_class([
1340 {'root_class': fusedir.ProjectDirectory, 'root_kwargs': {
1341 'project_object': run_test_server.fixture('users')['admin'],
1343 {'root_class': fusedir.ProjectDirectory, 'root_kwargs': {
1344 'project_object': run_test_server.fixture('groups')['public'],
1347 class UnsupportedCreateTest(MountTestBase):
1353 if 'prefs' in self.root_kwargs.get('project_object', ()):
1354 self.root_kwargs['project_object']['prefs'] = {}
1355 self.make_mount(self.root_class, **self.root_kwargs)
1356 # Make sure the directory knows about its top-level ents.
1357 os.listdir(self.mounttmp)
1359 def test_create(self):
1360 test_path = Path(self.mounttmp, 'test_create')
1361 with self.assertRaises(OSError) as exc_check:
1362 with test_path.open('w'):
1364 self.assertEqual(exc_check.exception.errno, errno.ENOTSUP)
1367 # FIXME: IMO, for consistency with the "create inside a project" case,
1368 # these operations should also return ENOTSUP instead of EPERM.
1369 # Right now they're returning EPERM because the clasess' writable() method
1370 # usually returns False, and the Operations class transforms that accordingly.
1371 # However, for cases where the mount will never be writable, I think ENOTSUP
1372 # is a clearer error: it lets the user know they can't fix the problem by
1373 # adding permissions in Arvados, etc.
1374 @parameterized.parameterized_class([
1375 {'root_class': fusedir.MagicDirectory,
1376 'preset_dir': 'by_id',
1377 'preset_file': 'README',
1380 {'root_class': fusedir.SharedDirectory,
1382 'exclude': run_test_server.fixture('users')['admin']['uuid'],
1384 'preset_dir': 'Active User',
1387 {'root_class': fusedir.TagDirectory,
1389 'tag': run_test_server.fixture('links')['foo_collection_tag']['name'],
1391 'preset_dir': run_test_server.fixture('collections')['foo_collection_in_aproject']['uuid'],
1394 {'root_class': fusedir.TagsDirectory,
1395 'preset_dir': run_test_server.fixture('links')['foo_collection_tag']['name'],
1398 class UnsupportedOperationsTest(UnsupportedCreateTest):
1402 def test_create(self):
1403 test_path = Path(self.mounttmp, 'test_create')
1404 with self.assertRaises(OSError) as exc_check:
1405 with test_path.open('w'):
1407 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1409 def test_mkdir(self):
1410 test_path = Path(self.mounttmp, 'test_mkdir')
1411 with self.assertRaises(OSError) as exc_check:
1413 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1415 def test_rename(self):
1416 src_name = self.preset_dir or self.preset_file
1417 if src_name is None:
1419 test_src = Path(self.mounttmp, src_name)
1420 test_dst = test_src.with_name('test_dst')
1421 with self.assertRaises(OSError) as exc_check:
1422 test_src.rename(test_dst)
1423 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1425 def test_rmdir(self):
1426 if self.preset_dir is None:
1428 test_path = Path(self.mounttmp, self.preset_dir)
1429 with self.assertRaises(OSError) as exc_check:
1431 self.assertEqual(exc_check.exception.errno, errno.EPERM)
1433 def test_unlink(self):
1434 if self.preset_file is None:
1436 test_path = Path(self.mounttmp, self.preset_file)
1437 with self.assertRaises(OSError) as exc_check:
1439 self.assertEqual(exc_check.exception.errno, errno.EPERM)