1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
15 import arvados.collection
17 import arvados_cwl.executor
19 from cwltool.pathmapper import MapperEnt
20 from .mock_discovery import get_rootDesc
22 from arvados_cwl.pathmapper import ArvPathMapper
24 def upload_mock(files, api, dry_run=False, num_retries=0, project=None, fnPattern="$(file %s/%s)", name=None, collection=None, packed=None):
25 pdh = "99999999999999999999999999999991+99"
27 c.keepref = "%s/%s" % (pdh, os.path.basename(c.fn))
28 c.fn = fnPattern % (pdh, os.path.basename(c.fn))
30 class TestPathmap(unittest.TestCase):
32 self.api = mock.MagicMock()
33 self.api._rootDesc = get_rootDesc()
36 root_logger = logging.getLogger('')
38 # Remove existing RuntimeStatusLoggingHandlers if they exist
39 handlers = [h for h in root_logger.handlers if not isinstance(h, arvados_cwl.executor.RuntimeStatusLoggingHandler)]
40 root_logger.handlers = handlers
42 def test_keepref(self):
43 """Test direct keep references."""
45 arvrunner = arvados_cwl.executor.ArvCwlExecutor(self.api)
47 p = ArvPathMapper(arvrunner, [{
49 "location": "keep:99999999999999999999999999999991+99/hw.py"
50 }], "", "/test/%s", "/test/%s/%s")
52 self.assertEqual({'keep:99999999999999999999999999999991+99/hw.py': MapperEnt(resolved='keep:99999999999999999999999999999991+99/hw.py', target='/test/99999999999999999999999999999991+99/hw.py', type='File', staged=True)},
55 @mock.patch("arvados.commands.run.uploadfiles")
56 @mock.patch("arvados.commands.run.statfile")
57 def test_upload(self, statfile, upl):
58 """Test pathmapper uploading files."""
60 arvrunner = arvados_cwl.executor.ArvCwlExecutor(self.api)
62 def statfile_mock(prefix, fn, fnPattern="$(file %s/%s)", dirPattern="$(dir %s/%s/)", raiseOSError=False):
63 st = arvados.commands.run.UploadFile("", "tests/hw.py")
66 upl.side_effect = upload_mock
67 statfile.side_effect = statfile_mock
69 p = ArvPathMapper(arvrunner, [{
71 "location": "file:tests/hw.py"
72 }], "", "/test/%s", "/test/%s/%s")
74 self.assertEqual({'file:tests/hw.py': MapperEnt(resolved='keep:99999999999999999999999999999991+99/hw.py', target='/test/99999999999999999999999999999991+99/hw.py', type='File', staged=True)},
77 @mock.patch("arvados.commands.run.uploadfiles")
78 @mock.patch("arvados.commands.run.statfile")
79 def test_statfile(self, statfile, upl):
80 """Test pathmapper handling ArvFile references."""
81 arvrunner = arvados_cwl.executor.ArvCwlExecutor(self.api)
83 # An ArvFile object returned from arvados.commands.run.statfile means the file is located on a
84 # keep mount, so we can construct a direct reference directly without upload.
85 def statfile_mock(prefix, fn, fnPattern="$(file %s/%s)", dirPattern="$(dir %s/%s/)", raiseOSError=False):
86 st = arvados.commands.run.ArvFile("", fnPattern % ("99999999999999999999999999999991+99", "hw.py"))
89 upl.side_effect = upload_mock
90 statfile.side_effect = statfile_mock
92 p = ArvPathMapper(arvrunner, [{
94 "location": "file:tests/hw.py"
95 }], "", "/test/%s", "/test/%s/%s")
97 self.assertEqual({'file:tests/hw.py': MapperEnt(resolved='keep:99999999999999999999999999999991+99/hw.py', target='/test/99999999999999999999999999999991+99/hw.py', type='File', staged=True)},
100 @mock.patch("os.stat")
101 def test_missing_file(self, stat):
102 """Test pathmapper handling missing references."""
103 arvrunner = arvados_cwl.executor.ArvCwlExecutor(self.api)
105 stat.side_effect = OSError(2, "No such file or directory")
107 with self.assertRaises(OSError):
108 p = ArvPathMapper(arvrunner, [{
110 "location": "file:tests/hw.py"
111 }], "", "/test/%s", "/test/%s/%s")
113 def test_needs_new_collection(self):
114 arvrunner = arvados_cwl.executor.ArvCwlExecutor(self.api)
116 # Plain file. Don't need a new collection.
119 "location": "keep:99999999999999999999999999999991+99/hw.py",
122 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
123 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
124 self.assertFalse(p.needs_new_collection(a))
126 # A file that isn't in the pathmap (for some reason). Need a new collection.
127 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
128 self.assertTrue(p.needs_new_collection(a))
130 # A file with a secondary file in the same collection. Don't need
134 "location": "keep:99999999999999999999999999999991+99/hw.py",
138 "location": "keep:99999999999999999999999999999991+99/hw.pyc",
142 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
143 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
144 p._pathmap["keep:99999999999999999999999999999991+99/hw.pyc"] = True
145 self.assertFalse(p.needs_new_collection(a))
147 # Secondary file is in a different collection from the
148 # a new collectionprimary. Need a new collection.
151 "location": "keep:99999999999999999999999999999991+99/hw.py",
155 "location": "keep:99999999999999999999999999999992+99/hw.pyc",
159 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
160 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
161 p._pathmap["keep:99999999999999999999999999999992+99/hw.pyc"] = True
162 self.assertTrue(p.needs_new_collection(a))
164 # Secondary file should be staged to a different name than
165 # path in location. Need a new collection.
168 "location": "keep:99999999999999999999999999999991+99/hw.py",
172 "location": "keep:99999999999999999999999999999991+99/hw.pyc",
173 "basename": "hw.other"
176 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
177 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
178 p._pathmap["keep:99999999999999999999999999999991+99/hw.pyc"] = True
179 self.assertTrue(p.needs_new_collection(a))
181 # Secondary file is a directory. Do not need a new collection.
184 "location": "keep:99999999999999999999999999999991+99/hw.py",
187 "class": "Directory",
188 "location": "keep:99999999999999999999999999999991+99/hw",
192 "location": "keep:99999999999999999999999999999991+99/hw/h2",
197 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
198 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
199 p._pathmap["keep:99999999999999999999999999999991+99/hw"] = True
200 p._pathmap["keep:99999999999999999999999999999991+99/hw/h2"] = True
201 self.assertFalse(p.needs_new_collection(a))
203 # Secondary file is a renamed directory. Need a new collection.
206 "location": "keep:99999999999999999999999999999991+99/hw.py",
209 "class": "Directory",
210 "location": "keep:99999999999999999999999999999991+99/hw",
214 "location": "keep:99999999999999999999999999999991+99/hw/h2",
219 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
220 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
221 p._pathmap["keep:99999999999999999999999999999991+99/hw"] = True
222 p._pathmap["keep:99999999999999999999999999999991+99/hw/h2"] = True
223 self.assertTrue(p.needs_new_collection(a))
225 # Secondary file is a file literal. Need a new collection.
228 "location": "keep:99999999999999999999999999999991+99/hw.py",
233 "basename": "hw.pyc",
237 p = ArvPathMapper(arvrunner, [], "", "%s", "%s/%s")
238 p._pathmap["keep:99999999999999999999999999999991+99/hw.py"] = True
239 p._pathmap["_:123"] = True
240 self.assertTrue(p.needs_new_collection(a))
242 def test_is_in_collection(self):
243 arvrunner = arvados_cwl.executor.ArvCwlExecutor(self.api)
244 self.maxDiff = 1000000
247 p = ArvPathMapper(arvrunner, [{
249 "location": "file://"+cwd+"/tests/fake-keep-mount/fake_collection_dir/subdir/banana.txt"
250 }], "", "/test/%s", "/test/%s/%s")
252 self.assertEqual({"file://"+cwd+"/tests/fake-keep-mount/fake_collection_dir/subdir/banana.txt": MapperEnt(resolved='keep:99999999999999999999999999999991+99/subdir/banana.txt', target='/test/99999999999999999999999999999991+99/subdir/banana.txt', type='File', staged=True)},