1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
5 from future import standard_library
6 standard_library.install_aliases()
15 from unittest import mock
19 import arvados_cwl.executor
20 from .mock_discovery import get_rootDesc
22 class TestMakeOutput(unittest.TestCase):
24 self.api = mock.MagicMock()
25 self.api._rootDesc = get_rootDesc()
28 root_logger = logging.getLogger('')
30 # Remove existing RuntimeStatusLoggingHandlers if they exist
31 handlers = [h for h in root_logger.handlers if not isinstance(h, arvados_cwl.executor.RuntimeStatusLoggingHandler)]
32 root_logger.handlers = handlers
34 @mock.patch("arvados.collection.Collection")
35 @mock.patch("arvados.collection.CollectionReader")
36 def test_make_output_collection(self, reader, col):
37 keep_client = mock.MagicMock()
38 runner = arvados_cwl.executor.ArvCwlExecutor(self.api, keep_client=keep_client)
39 runner.project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
41 final = mock.MagicMock()
42 col.return_value = final
43 readermock = mock.MagicMock()
44 reader.return_value = readermock
46 final_uuid = final.manifest_locator()
47 num_retries = runner.num_retries
49 cwlout = io.StringIO()
50 openmock = mock.MagicMock()
51 final.open.return_value = openmock
52 openmock.__enter__.return_value = cwlout
54 _, runner.final_output_collection = runner.make_output_collection("Test output", ["foo"], "tag0,tag1,tag2", {}, {
57 "location": "keep:99999999999999999999999999999991+99/foo.txt",
63 "location": "keep:99999999999999999999999999999992+99/bar.txt",
64 "basename": "baz.txt",
69 final.copy.assert_has_calls([mock.call('bar.txt', 'baz.txt', overwrite=False, source_collection=readermock)])
70 final.copy.assert_has_calls([mock.call('foo.txt', 'foo.txt', overwrite=False, source_collection=readermock)])
71 final.save_new.assert_has_calls([mock.call(ensure_unique_name=True, name='Test output', owner_uuid='zzzzz-j7d0g-zzzzzzzzzzzzzzz', properties={}, storage_classes=['foo'])])
74 "basename": "baz.txt",
76 "location": "baz.txt",
80 "basename": "foo.txt",
82 "location": "foo.txt",
85 }""", cwlout.getvalue())
87 self.assertIs(final, runner.final_output_collection)
88 self.assertIs(final_uuid, runner.final_output_collection.manifest_locator())
89 self.api.links().create.assert_has_calls([mock.call(body={"head_uuid": final_uuid, "link_class": "tag", "name": "tag0"}), mock.call().execute(num_retries=num_retries)])
90 self.api.links().create.assert_has_calls([mock.call(body={"head_uuid": final_uuid, "link_class": "tag", "name": "tag1"}), mock.call().execute(num_retries=num_retries)])
91 self.api.links().create.assert_has_calls([mock.call(body={"head_uuid": final_uuid, "link_class": "tag", "name": "tag2"}), mock.call().execute(num_retries=num_retries)])
93 @mock.patch("arvados.collection.Collection")
94 @mock.patch("arvados.collection.CollectionReader")
95 def test_make_output_for_multiple_file_targets(self, reader, col):
96 keep_client = mock.MagicMock()
97 runner = arvados_cwl.executor.ArvCwlExecutor(self.api, keep_client=keep_client)
98 runner.project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
100 final = mock.MagicMock()
101 col.return_value = final
102 readermock = mock.MagicMock()
103 reader.return_value = readermock
105 # This output describes a single file listed in 2 different directories
106 _, runner.final_output_collection = runner.make_output_collection("Test output", ["foo"], "", {}, { 'out': [
108 'basename': 'testdir1',
111 'basename': 'test.txt',
114 'location': 'keep:99999999999999999999999999999991+99/test.txt',
119 'location': '_:99999999999999999999999999999992+99',
123 'basename': 'testdir2',
126 'basename': 'test.txt',
129 'location': 'keep:99999999999999999999999999999991+99/test.txt',
135 'location': '_:99999999999999999999999999999993+99',
139 # Check that copy is called on the collection for both locations
140 final.copy.assert_any_call("test.txt", "testdir1/test.txt", source_collection=mock.ANY, overwrite=mock.ANY)
141 final.copy.assert_any_call("test.txt", "testdir2/test.txt", source_collection=mock.ANY, overwrite=mock.ANY)
143 @mock.patch("arvados.collection.Collection")
144 @mock.patch("arvados.collection.CollectionReader")
145 def test_make_output_for_literal_name_conflicts(self, reader, col):
146 keep_client = mock.MagicMock()
147 runner = arvados_cwl.executor.ArvCwlExecutor(self.api, keep_client=keep_client)
148 runner.project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
150 final = mock.MagicMock()
151 col.return_value = final
152 readermock = mock.MagicMock()
153 reader.return_value = readermock
155 # This output describes two literals with the same basename
156 _, runner.final_output_collection = runner.make_output_collection("Test output", ["foo"], "", {}, [
160 'basename': 'a_file',
162 'nameroot': 'a_file',
163 'location': '_:f168fc0c-4291-40aa-a04e-366d57390560',
165 'contents': 'Hello file literal.'
171 'basename': 'a_file',
173 'nameroot': 'a_file',
174 'location': '_:1728da8f-c64e-4a3e-b2e2-1ee356be7bc8',
176 'contents': 'Hello file literal.'
180 # Check that the file name conflict is resolved and open is called for both
181 final.open.assert_any_call("a_file", "wb")
182 final.open.assert_any_call("a_file_2", "wb")