toil 6.1.0a1__py3-none-any.whl → 8.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +122 -315
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +173 -89
- toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
- toil/batchSystems/awsBatch.py +244 -135
- toil/batchSystems/cleanup_support.py +26 -16
- toil/batchSystems/contained_executor.py +31 -28
- toil/batchSystems/gridengine.py +86 -50
- toil/batchSystems/htcondor.py +166 -89
- toil/batchSystems/kubernetes.py +632 -382
- toil/batchSystems/local_support.py +20 -15
- toil/batchSystems/lsf.py +134 -81
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +290 -151
- toil/batchSystems/mesos/executor.py +79 -50
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +46 -28
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +296 -125
- toil/batchSystems/slurm.py +603 -138
- toil/batchSystems/torque.py +47 -33
- toil/bus.py +186 -76
- toil/common.py +664 -368
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1136 -483
- toil/cwl/utils.py +17 -22
- toil/deferred.py +63 -42
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +140 -60
- toil/fileStores/cachingFileStore.py +717 -269
- toil/fileStores/nonCachingFileStore.py +116 -87
- toil/job.py +1225 -368
- toil/jobStores/abstractJobStore.py +416 -266
- toil/jobStores/aws/jobStore.py +863 -477
- toil/jobStores/aws/utils.py +201 -120
- toil/jobStores/conftest.py +3 -2
- toil/jobStores/fileJobStore.py +292 -154
- toil/jobStores/googleJobStore.py +140 -74
- toil/jobStores/utils.py +36 -15
- toil/leader.py +668 -272
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +74 -31
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +214 -39
- toil/lib/aws/utils.py +287 -231
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +104 -47
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +361 -199
- toil/lib/ec2nodes.py +174 -106
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +5 -3
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +141 -15
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +66 -21
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +68 -15
- toil/lib/retry.py +126 -81
- toil/lib/threading.py +299 -82
- toil/lib/throttle.py +16 -15
- toil/options/common.py +843 -409
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +73 -17
- toil/provisioners/__init__.py +117 -46
- toil/provisioners/abstractProvisioner.py +332 -157
- toil/provisioners/aws/__init__.py +70 -33
- toil/provisioners/aws/awsProvisioner.py +1145 -715
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +155 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +128 -62
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +224 -70
- toil/test/__init__.py +282 -183
- toil/test/batchSystems/batchSystemTest.py +460 -210
- toil/test/batchSystems/batch_system_plugin_test.py +90 -0
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +110 -49
- toil/test/cactus/__init__.py +0 -0
- toil/test/cactus/test_cactus_integration.py +56 -0
- toil/test/cwl/cwlTest.py +496 -287
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/cwl/seqtk_seq.cwl +1 -1
- toil/test/docs/scriptsTest.py +69 -46
- toil/test/jobStores/jobStoreTest.py +427 -264
- toil/test/lib/aws/test_iam.py +118 -50
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +58 -50
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/__init__.py +13 -0
- toil/test/options/options.py +42 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +166 -44
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +141 -101
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +32 -24
- toil/test/src/environmentTest.py +135 -0
- toil/test/src/fileStoreTest.py +539 -272
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +46 -21
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +121 -71
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +10 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +73 -23
- toil/test/utils/toilDebugTest.py +103 -33
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +245 -106
- toil/test/wdl/wdltoil_test.py +818 -149
- toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
- toil/toilState.py +120 -35
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +214 -27
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +256 -140
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +32 -14
- toil/utils/toilSshCluster.py +49 -22
- toil/utils/toilStats.py +356 -273
- toil/utils/toilStatus.py +292 -139
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +12 -12
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3913 -1033
- toil/worker.py +367 -184
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-6.1.0a1.dist-info/METADATA +0 -125
- toil-6.1.0a1.dist-info/RECORD +0 -237
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
toil/test/cwl/cwlTest.py
CHANGED
|
@@ -26,10 +26,13 @@ import zipfile
|
|
|
26
26
|
from functools import partial
|
|
27
27
|
from io import StringIO
|
|
28
28
|
from pathlib import Path
|
|
29
|
-
from typing import
|
|
29
|
+
from typing import TYPE_CHECKING, Callable, Optional, cast
|
|
30
30
|
from unittest.mock import Mock, call
|
|
31
31
|
from urllib.request import urlretrieve
|
|
32
32
|
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from cwltool.utils import CWLObjectType
|
|
35
|
+
|
|
33
36
|
import pytest
|
|
34
37
|
|
|
35
38
|
pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) # noqa
|
|
@@ -37,33 +40,33 @@ sys.path.insert(0, pkg_root) # noqa
|
|
|
37
40
|
|
|
38
41
|
from schema_salad.exceptions import ValidationException
|
|
39
42
|
|
|
40
|
-
from toil.cwl.utils import (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
from toil.cwl.utils import (
|
|
44
|
+
DirectoryStructure,
|
|
45
|
+
download_structure,
|
|
46
|
+
visit_cwl_class_and_reduce,
|
|
47
|
+
visit_top_cwl_class,
|
|
48
|
+
)
|
|
44
49
|
from toil.fileStores import FileID
|
|
45
50
|
from toil.fileStores.abstractFileStore import AbstractFileStore
|
|
46
51
|
from toil.lib.threading import cpu_count
|
|
47
|
-
from toil.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
slow)
|
|
66
|
-
from toil.test.provisioners.clusterTest import AbstractClusterTest
|
|
52
|
+
from toil.test import (
|
|
53
|
+
ToilTest,
|
|
54
|
+
needs_aws_s3,
|
|
55
|
+
needs_cwl,
|
|
56
|
+
needs_docker,
|
|
57
|
+
needs_docker_cuda,
|
|
58
|
+
needs_gridengine,
|
|
59
|
+
needs_kubernetes,
|
|
60
|
+
needs_local_cuda,
|
|
61
|
+
needs_lsf,
|
|
62
|
+
needs_mesos,
|
|
63
|
+
needs_online,
|
|
64
|
+
needs_singularity_or_docker,
|
|
65
|
+
needs_slurm,
|
|
66
|
+
needs_torque,
|
|
67
|
+
needs_wes_server,
|
|
68
|
+
slow,
|
|
69
|
+
)
|
|
67
70
|
|
|
68
71
|
log = logging.getLogger(__name__)
|
|
69
72
|
CONFORMANCE_TEST_TIMEOUT = 10000
|
|
@@ -74,14 +77,14 @@ def run_conformance_tests(
|
|
|
74
77
|
yml: str,
|
|
75
78
|
runner: Optional[str] = None,
|
|
76
79
|
caching: bool = False,
|
|
77
|
-
batchSystem: str = None,
|
|
78
|
-
selected_tests: str = None,
|
|
79
|
-
selected_tags: str = None,
|
|
80
|
-
skipped_tests: str = None,
|
|
81
|
-
extra_args: Optional[
|
|
80
|
+
batchSystem: Optional[str] = None,
|
|
81
|
+
selected_tests: Optional[str] = None,
|
|
82
|
+
selected_tags: Optional[str] = None,
|
|
83
|
+
skipped_tests: Optional[str] = None,
|
|
84
|
+
extra_args: Optional[list[str]] = None,
|
|
82
85
|
must_support_all_features: bool = False,
|
|
83
86
|
junit_file: Optional[str] = None,
|
|
84
|
-
):
|
|
87
|
+
) -> None:
|
|
85
88
|
"""
|
|
86
89
|
Run the CWL conformance tests.
|
|
87
90
|
|
|
@@ -139,7 +142,7 @@ def run_conformance_tests(
|
|
|
139
142
|
"--relax-path-checks",
|
|
140
143
|
# Defaults to 20s but we can't start hundreds of nodejs processes that fast on our CI potatoes
|
|
141
144
|
"--eval-timeout=600",
|
|
142
|
-
f"--caching={caching}"
|
|
145
|
+
f"--caching={caching}",
|
|
143
146
|
]
|
|
144
147
|
|
|
145
148
|
if extra_args:
|
|
@@ -173,30 +176,53 @@ def run_conformance_tests(
|
|
|
173
176
|
cmd.extend(["--"] + args_passed_directly_to_runner)
|
|
174
177
|
|
|
175
178
|
log.info("Running: '%s'", "' '".join(cmd))
|
|
179
|
+
output_lines: list[str] = []
|
|
176
180
|
try:
|
|
177
|
-
|
|
181
|
+
child = subprocess.Popen(
|
|
182
|
+
cmd, cwd=workDir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if child.stdout is not None:
|
|
186
|
+
for line_bytes in child.stdout:
|
|
187
|
+
# Pass through all the logs
|
|
188
|
+
line_text = line_bytes.decode("utf-8", errors="replace").rstrip()
|
|
189
|
+
output_lines.append(line_text)
|
|
190
|
+
log.info(line_text)
|
|
191
|
+
|
|
192
|
+
# Once it's done writing, amke sure it succeeded.
|
|
193
|
+
child.wait()
|
|
194
|
+
log.info("CWL tests finished with exit code %s", child.returncode)
|
|
195
|
+
if child.returncode != 0:
|
|
196
|
+
# Act like check_output and raise an error.
|
|
197
|
+
raise subprocess.CalledProcessError(child.returncode, " ".join(cmd))
|
|
178
198
|
finally:
|
|
179
199
|
if job_store_override:
|
|
180
200
|
# Clean up the job store we used for all the tests, if it is still there.
|
|
181
201
|
subprocess.run(["toil", "clean", job_store_override])
|
|
182
202
|
|
|
183
203
|
except subprocess.CalledProcessError as e:
|
|
204
|
+
log.info("CWL test runner return code was unsuccessful")
|
|
184
205
|
only_unsupported = False
|
|
185
206
|
# check output -- if we failed but only have unsupported features, we're okay
|
|
186
207
|
p = re.compile(
|
|
187
208
|
r"(?P<failures>\d+) failures, (?P<unsupported>\d+) unsupported features"
|
|
188
209
|
)
|
|
189
210
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
m = p.search(line)
|
|
211
|
+
for line_text in output_lines:
|
|
212
|
+
m = p.search(line_text)
|
|
193
213
|
if m:
|
|
194
214
|
if int(m.group("failures")) == 0 and int(m.group("unsupported")) > 0:
|
|
195
215
|
only_unsupported = True
|
|
196
216
|
break
|
|
197
217
|
if (not only_unsupported) or must_support_all_features:
|
|
198
|
-
|
|
218
|
+
log.error(
|
|
219
|
+
"CWL tests gave unacceptable output:\n%s", "\n".join(output_lines)
|
|
220
|
+
)
|
|
199
221
|
raise e
|
|
222
|
+
log.info("Unsuccessful return code is OK")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
TesterFuncType = Callable[[str, str, "CWLObjectType"], None]
|
|
200
226
|
|
|
201
227
|
|
|
202
228
|
@needs_cwl
|
|
@@ -207,43 +233,49 @@ class CWLWorkflowTest(ToilTest):
|
|
|
207
233
|
inputs.
|
|
208
234
|
"""
|
|
209
235
|
|
|
210
|
-
def setUp(self):
|
|
236
|
+
def setUp(self) -> None:
|
|
211
237
|
"""Runs anew before each test to create farm fresh temp dirs."""
|
|
212
238
|
self.outDir = f"/tmp/toil-cwl-test-{str(uuid.uuid4())}"
|
|
213
239
|
os.makedirs(self.outDir)
|
|
214
240
|
self.rootDir = self._projectRootPath()
|
|
241
|
+
self.jobStoreDir = f"./jobstore-{str(uuid.uuid4())}"
|
|
215
242
|
|
|
216
|
-
def tearDown(self):
|
|
243
|
+
def tearDown(self) -> None:
|
|
217
244
|
"""Clean up outputs."""
|
|
218
245
|
if os.path.exists(self.outDir):
|
|
219
246
|
shutil.rmtree(self.outDir)
|
|
247
|
+
if os.path.exists(self.jobStoreDir):
|
|
248
|
+
shutil.rmtree(self.jobStoreDir)
|
|
220
249
|
unittest.TestCase.tearDown(self)
|
|
221
250
|
|
|
222
|
-
def test_cwl_cmdline_input(self):
|
|
251
|
+
def test_cwl_cmdline_input(self) -> None:
|
|
223
252
|
"""
|
|
224
253
|
Test that running a CWL workflow with inputs specified on the command line passes.
|
|
225
254
|
"""
|
|
226
255
|
from toil.cwl import cwltoil
|
|
256
|
+
|
|
227
257
|
cwlfile = "src/toil/test/cwl/conditional_wf.cwl"
|
|
228
258
|
args = [cwlfile, "--message", "str", "--sleep", "2"]
|
|
229
259
|
st = StringIO()
|
|
230
260
|
# If the workflow runs, it must have had options
|
|
231
261
|
cwltoil.main(args, stdout=st)
|
|
232
262
|
|
|
233
|
-
def _tester(
|
|
263
|
+
def _tester(
|
|
264
|
+
self,
|
|
265
|
+
cwlfile: str,
|
|
266
|
+
jobfile: str,
|
|
267
|
+
expect: "CWLObjectType",
|
|
268
|
+
main_args: list[str] = [],
|
|
269
|
+
out_name: str = "output",
|
|
270
|
+
output_here: bool = False,
|
|
271
|
+
) -> None:
|
|
234
272
|
from toil.cwl import cwltoil
|
|
235
273
|
|
|
236
274
|
st = StringIO()
|
|
237
275
|
main_args = main_args[:]
|
|
238
276
|
if not output_here:
|
|
239
277
|
# Don't just dump output in the working directory.
|
|
240
|
-
main_args.extend(
|
|
241
|
-
[
|
|
242
|
-
"--logDebug",
|
|
243
|
-
"--outdir",
|
|
244
|
-
self.outDir
|
|
245
|
-
]
|
|
246
|
-
)
|
|
278
|
+
main_args.extend(["--logDebug", "--outdir", self.outDir])
|
|
247
279
|
main_args.extend(
|
|
248
280
|
[
|
|
249
281
|
os.path.join(self.rootDir, cwlfile),
|
|
@@ -258,13 +290,20 @@ class CWLWorkflowTest(ToilTest):
|
|
|
258
290
|
self.assertEqual(out, expect)
|
|
259
291
|
|
|
260
292
|
for k, v in expect.items():
|
|
261
|
-
if
|
|
293
|
+
if (
|
|
294
|
+
isinstance(v, dict)
|
|
295
|
+
and "class" in v
|
|
296
|
+
and v["class"] == "File"
|
|
297
|
+
and "path" in v
|
|
298
|
+
):
|
|
262
299
|
# This is a top-level output file.
|
|
263
300
|
# None of our output files should be executable.
|
|
264
301
|
self.assertTrue(os.path.exists(v["path"]))
|
|
265
302
|
self.assertFalse(os.stat(v["path"]).st_mode & stat.S_IXUSR)
|
|
266
303
|
|
|
267
|
-
def _debug_worker_tester(
|
|
304
|
+
def _debug_worker_tester(
|
|
305
|
+
self, cwlfile: str, jobfile: str, expect: "CWLObjectType"
|
|
306
|
+
) -> None:
|
|
268
307
|
from toil.cwl import cwltoil
|
|
269
308
|
|
|
270
309
|
st = StringIO()
|
|
@@ -284,21 +323,21 @@ class CWLWorkflowTest(ToilTest):
|
|
|
284
323
|
out["output"].pop("nameroot", None)
|
|
285
324
|
self.assertEqual(out, expect)
|
|
286
325
|
|
|
287
|
-
def revsort(self, cwl_filename, tester_fn):
|
|
326
|
+
def revsort(self, cwl_filename: str, tester_fn: TesterFuncType) -> None:
|
|
288
327
|
tester_fn(
|
|
289
328
|
"src/toil/test/cwl/" + cwl_filename,
|
|
290
329
|
"src/toil/test/cwl/revsort-job.json",
|
|
291
330
|
self._expected_revsort_output(self.outDir),
|
|
292
331
|
)
|
|
293
332
|
|
|
294
|
-
def revsort_no_checksum(self, cwl_filename, tester_fn):
|
|
333
|
+
def revsort_no_checksum(self, cwl_filename: str, tester_fn: TesterFuncType) -> None:
|
|
295
334
|
tester_fn(
|
|
296
335
|
"src/toil/test/cwl/" + cwl_filename,
|
|
297
336
|
"src/toil/test/cwl/revsort-job.json",
|
|
298
337
|
self._expected_revsort_nochecksum_output(self.outDir),
|
|
299
338
|
)
|
|
300
339
|
|
|
301
|
-
def download(self, inputs, tester_fn):
|
|
340
|
+
def download(self, inputs: str, tester_fn: TesterFuncType) -> None:
|
|
302
341
|
input_location = os.path.join("src/toil/test/cwl", inputs)
|
|
303
342
|
tester_fn(
|
|
304
343
|
"src/toil/test/cwl/download.cwl",
|
|
@@ -306,7 +345,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
306
345
|
self._expected_download_output(self.outDir),
|
|
307
346
|
)
|
|
308
347
|
|
|
309
|
-
def load_contents(self, inputs, tester_fn):
|
|
348
|
+
def load_contents(self, inputs: str, tester_fn: TesterFuncType) -> None:
|
|
310
349
|
input_location = os.path.join("src/toil/test/cwl", inputs)
|
|
311
350
|
tester_fn(
|
|
312
351
|
"src/toil/test/cwl/load_contents.cwl",
|
|
@@ -314,7 +353,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
314
353
|
self._expected_load_contents_output(self.outDir),
|
|
315
354
|
)
|
|
316
355
|
|
|
317
|
-
def download_directory(self, inputs, tester_fn):
|
|
356
|
+
def download_directory(self, inputs: str, tester_fn: TesterFuncType) -> None:
|
|
318
357
|
input_location = os.path.join("src/toil/test/cwl", inputs)
|
|
319
358
|
tester_fn(
|
|
320
359
|
"src/toil/test/cwl/download_directory.cwl",
|
|
@@ -322,7 +361,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
322
361
|
self._expected_download_output(self.outDir),
|
|
323
362
|
)
|
|
324
363
|
|
|
325
|
-
def download_subdirectory(self, inputs, tester_fn):
|
|
364
|
+
def download_subdirectory(self, inputs: str, tester_fn: TesterFuncType) -> None:
|
|
326
365
|
input_location = os.path.join("src/toil/test/cwl", inputs)
|
|
327
366
|
tester_fn(
|
|
328
367
|
"src/toil/test/cwl/download_subdirectory.cwl",
|
|
@@ -330,7 +369,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
330
369
|
self._expected_download_output(self.outDir),
|
|
331
370
|
)
|
|
332
371
|
|
|
333
|
-
def test_mpi(self):
|
|
372
|
+
def test_mpi(self) -> None:
|
|
334
373
|
from toil.cwl import cwltoil
|
|
335
374
|
|
|
336
375
|
stdout = StringIO()
|
|
@@ -355,7 +394,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
355
394
|
self.assertTrue(isinstance(two_pids[1], int))
|
|
356
395
|
|
|
357
396
|
@needs_aws_s3
|
|
358
|
-
def test_s3_as_secondary_file(self):
|
|
397
|
+
def test_s3_as_secondary_file(self) -> None:
|
|
359
398
|
from toil.cwl import cwltoil
|
|
360
399
|
|
|
361
400
|
stdout = StringIO()
|
|
@@ -374,29 +413,52 @@ class CWLWorkflowTest(ToilTest):
|
|
|
374
413
|
with open(out["output"]["location"][len("file://") :]) as f:
|
|
375
414
|
self.assertEqual(f.read().strip(), "When is s4 coming out?")
|
|
376
415
|
|
|
377
|
-
def test_run_revsort(self):
|
|
416
|
+
def test_run_revsort(self) -> None:
|
|
378
417
|
self.revsort("revsort.cwl", self._tester)
|
|
379
418
|
|
|
380
|
-
def test_run_revsort_nochecksum(self):
|
|
419
|
+
def test_run_revsort_nochecksum(self) -> None:
|
|
381
420
|
self.revsort_no_checksum(
|
|
382
421
|
"revsort.cwl", partial(self._tester, main_args=["--no-compute-checksum"])
|
|
383
422
|
)
|
|
384
423
|
|
|
385
|
-
def
|
|
424
|
+
def test_run_revsort_no_container(self) -> None:
|
|
425
|
+
self.revsort(
|
|
426
|
+
"revsort.cwl", partial(self._tester, main_args=["--no-container"])
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
def test_run_revsort2(self) -> None:
|
|
386
430
|
self.revsort("revsort2.cwl", self._tester)
|
|
387
431
|
|
|
388
|
-
def test_run_revsort_debug_worker(self):
|
|
432
|
+
def test_run_revsort_debug_worker(self) -> None:
|
|
389
433
|
self.revsort("revsort.cwl", self._debug_worker_tester)
|
|
390
434
|
|
|
391
|
-
def test_run_colon_output(self):
|
|
435
|
+
def test_run_colon_output(self) -> None:
|
|
392
436
|
self._tester(
|
|
393
437
|
"src/toil/test/cwl/colon_test_output.cwl",
|
|
394
438
|
"src/toil/test/cwl/colon_test_output_job.yaml",
|
|
395
439
|
self._expected_colon_output(self.outDir),
|
|
396
440
|
out_name="result",
|
|
397
441
|
)
|
|
442
|
+
|
|
443
|
+
@pytest.mark.integrative
|
|
444
|
+
@needs_singularity_or_docker
|
|
445
|
+
def test_run_dockstore_trs(self) -> None:
|
|
446
|
+
from toil.cwl import cwltoil
|
|
447
|
+
|
|
448
|
+
stdout = StringIO()
|
|
449
|
+
main_args = [
|
|
450
|
+
"--outdir",
|
|
451
|
+
self.outDir,
|
|
452
|
+
"#workflow/github.com/dockstore-testing/md5sum-checker",
|
|
453
|
+
"https://raw.githubusercontent.com/dockstore-testing/md5sum-checker/refs/heads/master/md5sum/md5sum-input-cwl.json"
|
|
454
|
+
]
|
|
455
|
+
cwltoil.main(main_args, stdout=stdout)
|
|
456
|
+
out = json.loads(stdout.getvalue())
|
|
457
|
+
with open(out.get("output_file", {}).get("location")[len("file://") :]) as f:
|
|
458
|
+
computed_hash = f.read().strip()
|
|
459
|
+
self.assertEqual(computed_hash, "00579a00e3e7fa0674428ac7049423e2")
|
|
398
460
|
|
|
399
|
-
def test_glob_dir_bypass_file_store(self):
|
|
461
|
+
def test_glob_dir_bypass_file_store(self) -> None:
|
|
400
462
|
self.maxDiff = 1000
|
|
401
463
|
try:
|
|
402
464
|
# We need to output to the current directory to make sure that
|
|
@@ -406,7 +468,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
406
468
|
"src/toil/test/cwl/empty.json",
|
|
407
469
|
self._expected_glob_dir_output(os.getcwd()),
|
|
408
470
|
main_args=["--bypass-file-store"],
|
|
409
|
-
output_here=True
|
|
471
|
+
output_here=True,
|
|
410
472
|
)
|
|
411
473
|
finally:
|
|
412
474
|
# Clean up anything we made in the current directory.
|
|
@@ -415,59 +477,128 @@ class CWLWorkflowTest(ToilTest):
|
|
|
415
477
|
except FileNotFoundError:
|
|
416
478
|
pass
|
|
417
479
|
|
|
480
|
+
def test_required_input_condition_protection(self) -> None:
|
|
481
|
+
# This doesn't run containerized
|
|
482
|
+
self._tester(
|
|
483
|
+
"src/toil/test/cwl/not_run_required_input.cwl",
|
|
484
|
+
"src/toil/test/cwl/empty.json",
|
|
485
|
+
{},
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
@needs_slurm
|
|
489
|
+
def test_slurm_node_memory(self) -> None:
|
|
490
|
+
pass
|
|
491
|
+
|
|
492
|
+
# Run the workflow. This will either finish quickly and tell us the
|
|
493
|
+
# memory we got, or take a long time because it requested a whole
|
|
494
|
+
# node's worth of memory and no nodes are free right now. We need to
|
|
495
|
+
# support both.
|
|
496
|
+
|
|
497
|
+
# And if we run out of time we need to stop the workflow gracefully and
|
|
498
|
+
# cancel the Slurm jobs.
|
|
499
|
+
|
|
500
|
+
main_args = [
|
|
501
|
+
f"--jobStore={self.jobStoreDir}",
|
|
502
|
+
# Avoid racing to toil kill before the jobstore is removed
|
|
503
|
+
"--clean=never",
|
|
504
|
+
"--batchSystem=slurm",
|
|
505
|
+
"--no-cwl-default-ram",
|
|
506
|
+
"--slurmDefaultAllMem=True",
|
|
507
|
+
"--outdir",
|
|
508
|
+
self.outDir,
|
|
509
|
+
os.path.join(self.rootDir, "src/toil/test/cwl/measure_default_memory.cwl"),
|
|
510
|
+
]
|
|
511
|
+
try:
|
|
512
|
+
log.debug("Start test workflow")
|
|
513
|
+
child = subprocess.Popen(
|
|
514
|
+
["toil-cwl-runner"] + main_args, stdout=subprocess.PIPE
|
|
515
|
+
)
|
|
516
|
+
output, _ = child.communicate(timeout=60)
|
|
517
|
+
except subprocess.TimeoutExpired:
|
|
518
|
+
# The job didn't finish quickly; presumably waiting for a full node.
|
|
519
|
+
# Stop the workflow
|
|
520
|
+
log.debug("Workflow might be waiting for a full node. Stop it.")
|
|
521
|
+
subprocess.check_call(["toil", "kill", self.jobStoreDir])
|
|
522
|
+
# Wait another little bit for it to clean up, making sure to collect output in case it is blocked on writing
|
|
523
|
+
child.communicate(timeout=20)
|
|
524
|
+
# Kill it off in case it is still running
|
|
525
|
+
child.kill()
|
|
526
|
+
# Reap it
|
|
527
|
+
child.wait()
|
|
528
|
+
# The test passes
|
|
529
|
+
else:
|
|
530
|
+
out = json.loads(output)
|
|
531
|
+
log.debug("Workflow output: %s", out)
|
|
532
|
+
memory_string = out["memory"]
|
|
533
|
+
log.debug("Observed memory: %s", memory_string)
|
|
534
|
+
# If there's no memory limit enforced, Slurm will return "unlimited".
|
|
535
|
+
# Set result to something sensible.
|
|
536
|
+
if memory_string.strip() == "unlimited":
|
|
537
|
+
result = 4 * 1024 * 1024
|
|
538
|
+
else:
|
|
539
|
+
result = int(memory_string)
|
|
540
|
+
# We should see more than the CWL default or the Toil default, assuming Slurm nodes of reasonable size (3 GiB).
|
|
541
|
+
self.assertGreater(result, 3 * 1024 * 1024)
|
|
542
|
+
|
|
418
543
|
@needs_aws_s3
|
|
419
|
-
def test_download_s3(self):
|
|
544
|
+
def test_download_s3(self) -> None:
|
|
420
545
|
self.download("download_s3.json", self._tester)
|
|
421
546
|
|
|
422
|
-
def test_download_http(self):
|
|
547
|
+
def test_download_http(self) -> None:
|
|
423
548
|
self.download("download_http.json", self._tester)
|
|
424
549
|
|
|
425
|
-
def test_download_https(self):
|
|
550
|
+
def test_download_https(self) -> None:
|
|
426
551
|
self.download("download_https.json", self._tester)
|
|
427
552
|
|
|
428
|
-
def test_download_https_reference(self):
|
|
429
|
-
self.download(
|
|
553
|
+
def test_download_https_reference(self) -> None:
|
|
554
|
+
self.download(
|
|
555
|
+
"download_https.json",
|
|
556
|
+
partial(self._tester, main_args=["--reference-inputs"]),
|
|
557
|
+
)
|
|
430
558
|
|
|
431
|
-
def test_download_file(self):
|
|
559
|
+
def test_download_file(self) -> None:
|
|
432
560
|
self.download("download_file.json", self._tester)
|
|
433
561
|
|
|
434
562
|
@needs_aws_s3
|
|
435
|
-
def test_download_directory_s3(self):
|
|
563
|
+
def test_download_directory_s3(self) -> None:
|
|
436
564
|
self.download_directory("download_directory_s3.json", self._tester)
|
|
437
565
|
|
|
438
566
|
@needs_aws_s3
|
|
439
|
-
def test_download_directory_s3_reference(self):
|
|
440
|
-
self.download_directory(
|
|
567
|
+
def test_download_directory_s3_reference(self) -> None:
|
|
568
|
+
self.download_directory(
|
|
569
|
+
"download_directory_s3.json",
|
|
570
|
+
partial(self._tester, main_args=["--reference-inputs"]),
|
|
571
|
+
)
|
|
441
572
|
|
|
442
|
-
def test_download_directory_file(self):
|
|
573
|
+
def test_download_directory_file(self) -> None:
|
|
443
574
|
self.download_directory("download_directory_file.json", self._tester)
|
|
444
575
|
|
|
445
576
|
@needs_aws_s3
|
|
446
|
-
def test_download_subdirectory_s3(self):
|
|
577
|
+
def test_download_subdirectory_s3(self) -> None:
|
|
447
578
|
self.download_subdirectory("download_subdirectory_s3.json", self._tester)
|
|
448
579
|
|
|
449
|
-
def test_download_subdirectory_file(self):
|
|
580
|
+
def test_download_subdirectory_file(self) -> None:
|
|
450
581
|
self.download_subdirectory("download_subdirectory_file.json", self._tester)
|
|
451
582
|
|
|
452
583
|
# We also want to make sure we can run a bare tool with loadContents on the inputs, which requires accessing the input data early in the leader.
|
|
453
584
|
|
|
454
585
|
@needs_aws_s3
|
|
455
|
-
def test_load_contents_s3(self):
|
|
586
|
+
def test_load_contents_s3(self) -> None:
|
|
456
587
|
self.load_contents("download_s3.json", self._tester)
|
|
457
588
|
|
|
458
|
-
def test_load_contents_http(self):
|
|
589
|
+
def test_load_contents_http(self) -> None:
|
|
459
590
|
self.load_contents("download_http.json", self._tester)
|
|
460
591
|
|
|
461
|
-
def test_load_contents_https(self):
|
|
592
|
+
def test_load_contents_https(self) -> None:
|
|
462
593
|
self.load_contents("download_https.json", self._tester)
|
|
463
594
|
|
|
464
|
-
def test_load_contents_file(self):
|
|
595
|
+
def test_load_contents_file(self) -> None:
|
|
465
596
|
self.load_contents("download_file.json", self._tester)
|
|
466
597
|
|
|
467
598
|
@slow
|
|
468
599
|
@pytest.mark.integrative
|
|
469
|
-
@unittest.skip
|
|
470
|
-
def test_bioconda(self):
|
|
600
|
+
@unittest.skip("Fails too often due to remote service")
|
|
601
|
+
def test_bioconda(self) -> None:
|
|
471
602
|
self._tester(
|
|
472
603
|
"src/toil/test/cwl/seqtk_seq.cwl",
|
|
473
604
|
"src/toil/test/cwl/seqtk_seq_job.json",
|
|
@@ -476,10 +607,23 @@ class CWLWorkflowTest(ToilTest):
|
|
|
476
607
|
out_name="output1",
|
|
477
608
|
)
|
|
478
609
|
|
|
610
|
+
@needs_docker
|
|
611
|
+
def test_default_args(self) -> None:
|
|
612
|
+
self._tester(
|
|
613
|
+
"src/toil/test/cwl/seqtk_seq.cwl",
|
|
614
|
+
"src/toil/test/cwl/seqtk_seq_job.json",
|
|
615
|
+
self._expected_seqtk_output(self.outDir),
|
|
616
|
+
main_args=[
|
|
617
|
+
"--default-container",
|
|
618
|
+
"quay.io/biocontainers/seqtk:1.4--he4a0461_1",
|
|
619
|
+
],
|
|
620
|
+
out_name="output1",
|
|
621
|
+
)
|
|
622
|
+
|
|
479
623
|
@needs_docker
|
|
480
624
|
@pytest.mark.integrative
|
|
481
|
-
@unittest.skip
|
|
482
|
-
def test_biocontainers(self):
|
|
625
|
+
@unittest.skip("Fails too often due to remote service")
|
|
626
|
+
def test_biocontainers(self) -> None:
|
|
483
627
|
self._tester(
|
|
484
628
|
"src/toil/test/cwl/seqtk_seq.cwl",
|
|
485
629
|
"src/toil/test/cwl/seqtk_seq_job.json",
|
|
@@ -491,7 +635,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
491
635
|
@needs_docker
|
|
492
636
|
@needs_docker_cuda
|
|
493
637
|
@needs_local_cuda
|
|
494
|
-
def test_cuda(self):
|
|
638
|
+
def test_cuda(self) -> None:
|
|
495
639
|
self._tester(
|
|
496
640
|
"src/toil/test/cwl/nvidia_smi.cwl",
|
|
497
641
|
"src/toil/test/cwl/empty.json",
|
|
@@ -500,14 +644,13 @@ class CWLWorkflowTest(ToilTest):
|
|
|
500
644
|
)
|
|
501
645
|
|
|
502
646
|
@slow
|
|
503
|
-
def test_restart(self):
|
|
647
|
+
def test_restart(self) -> None:
|
|
504
648
|
"""
|
|
505
649
|
Enable restarts with toil-cwl-runner -- run failing test, re-run correct test.
|
|
506
650
|
Only implemented for single machine.
|
|
507
651
|
"""
|
|
508
652
|
log.info("Running CWL Test Restart. Expecting failure, then success.")
|
|
509
653
|
from toil.cwl import cwltoil
|
|
510
|
-
from toil.jobStores.abstractJobStore import NoSuchJobStoreException
|
|
511
654
|
|
|
512
655
|
outDir = self._createTempDir()
|
|
513
656
|
cwlDir = os.path.join(self._projectRootPath(), "src", "toil", "test", "cwl")
|
|
@@ -529,7 +672,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
529
672
|
][-1]
|
|
530
673
|
os.symlink(os.path.join(cal_path, "date"), f'{os.path.join(outDir, "rev")}')
|
|
531
674
|
|
|
532
|
-
def path_with_bogus_rev():
|
|
675
|
+
def path_with_bogus_rev() -> str:
|
|
533
676
|
# append to the front of the PATH so that we check there first
|
|
534
677
|
return f"{outDir}:" + os.environ["PATH"]
|
|
535
678
|
|
|
@@ -537,22 +680,31 @@ class CWLWorkflowTest(ToilTest):
|
|
|
537
680
|
# Force a failure by trying to use an incorrect version of `rev` from the PATH
|
|
538
681
|
os.environ["PATH"] = path_with_bogus_rev()
|
|
539
682
|
try:
|
|
540
|
-
|
|
683
|
+
subprocess.check_output(
|
|
684
|
+
["toil-cwl-runner"] + cmd,
|
|
685
|
+
env=os.environ.copy(),
|
|
686
|
+
stderr=subprocess.STDOUT,
|
|
687
|
+
)
|
|
541
688
|
self.fail("Expected problem job with incorrect PATH did not fail")
|
|
542
|
-
except
|
|
689
|
+
except subprocess.CalledProcessError:
|
|
543
690
|
pass
|
|
544
691
|
# Finish the job with a correct PATH
|
|
545
692
|
os.environ["PATH"] = orig_path
|
|
546
|
-
|
|
693
|
+
cmd.insert(0, "--restart")
|
|
694
|
+
cwltoil.main(cmd)
|
|
547
695
|
# Should fail because previous job completed successfully
|
|
548
696
|
try:
|
|
549
|
-
|
|
697
|
+
subprocess.check_output(
|
|
698
|
+
["toil-cwl-runner"] + cmd,
|
|
699
|
+
env=os.environ.copy(),
|
|
700
|
+
stderr=subprocess.STDOUT,
|
|
701
|
+
)
|
|
550
702
|
self.fail("Restart with missing directory did not fail")
|
|
551
|
-
except
|
|
703
|
+
except subprocess.CalledProcessError:
|
|
552
704
|
pass
|
|
553
705
|
|
|
554
706
|
@needs_aws_s3
|
|
555
|
-
def test_streamable(self, extra_args:
|
|
707
|
+
def test_streamable(self, extra_args: Optional[list[str]] = None) -> None:
|
|
556
708
|
"""
|
|
557
709
|
Test that a file with 'streamable'=True is a named pipe.
|
|
558
710
|
This is a CWL1.2 feature.
|
|
@@ -585,13 +737,13 @@ class CWLWorkflowTest(ToilTest):
|
|
|
585
737
|
self.assertEqual(f.read().strip(), "When is s4 coming out?")
|
|
586
738
|
|
|
587
739
|
@needs_aws_s3
|
|
588
|
-
def test_streamable_reference(self):
|
|
740
|
+
def test_streamable_reference(self) -> None:
|
|
589
741
|
"""
|
|
590
742
|
Test that a streamable file is a stream even when passed around by URI.
|
|
591
743
|
"""
|
|
592
744
|
self.test_streamable(extra_args=["--reference-inputs"])
|
|
593
745
|
|
|
594
|
-
def test_preemptible(self):
|
|
746
|
+
def test_preemptible(self) -> None:
|
|
595
747
|
"""
|
|
596
748
|
Tests that the http://arvados.org/cwl#UsePreemptible extension is supported.
|
|
597
749
|
"""
|
|
@@ -615,7 +767,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
615
767
|
with open(out[out_name]["location"][len("file://") :]) as f:
|
|
616
768
|
self.assertEqual(f.read().strip(), "hello")
|
|
617
769
|
|
|
618
|
-
def test_preemptible_expression(self):
|
|
770
|
+
def test_preemptible_expression(self) -> None:
|
|
619
771
|
"""
|
|
620
772
|
Tests that the http://arvados.org/cwl#UsePreemptible extension is validated.
|
|
621
773
|
"""
|
|
@@ -636,10 +788,9 @@ class CWLWorkflowTest(ToilTest):
|
|
|
636
788
|
except ValidationException as e:
|
|
637
789
|
# Make sure we chastise the user appropriately.
|
|
638
790
|
assert "expressions are not allowed" in str(e)
|
|
639
|
-
|
|
640
791
|
|
|
641
792
|
@staticmethod
|
|
642
|
-
def _expected_seqtk_output(outDir):
|
|
793
|
+
def _expected_seqtk_output(outDir: str) -> "CWLObjectType":
|
|
643
794
|
path = os.path.join(outDir, "out")
|
|
644
795
|
loc = "file://" + path
|
|
645
796
|
return {
|
|
@@ -654,7 +805,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
654
805
|
}
|
|
655
806
|
|
|
656
807
|
@staticmethod
|
|
657
|
-
def _expected_revsort_output(outDir):
|
|
808
|
+
def _expected_revsort_output(outDir: str) -> "CWLObjectType":
|
|
658
809
|
path = os.path.join(outDir, "output.txt")
|
|
659
810
|
loc = "file://" + path
|
|
660
811
|
return {
|
|
@@ -669,7 +820,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
669
820
|
}
|
|
670
821
|
|
|
671
822
|
@staticmethod
|
|
672
|
-
def _expected_revsort_nochecksum_output(outDir):
|
|
823
|
+
def _expected_revsort_nochecksum_output(outDir: str) -> "CWLObjectType":
|
|
673
824
|
path = os.path.join(outDir, "output.txt")
|
|
674
825
|
loc = "file://" + path
|
|
675
826
|
return {
|
|
@@ -683,7 +834,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
683
834
|
}
|
|
684
835
|
|
|
685
836
|
@staticmethod
|
|
686
|
-
def _expected_download_output(outDir):
|
|
837
|
+
def _expected_download_output(outDir: str) -> "CWLObjectType":
|
|
687
838
|
path = os.path.join(outDir, "output.txt")
|
|
688
839
|
loc = "file://" + path
|
|
689
840
|
return {
|
|
@@ -693,12 +844,12 @@ class CWLWorkflowTest(ToilTest):
|
|
|
693
844
|
"size": 0,
|
|
694
845
|
"class": "File",
|
|
695
846
|
"checksum": "sha1$da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
|
696
|
-
"path": path
|
|
847
|
+
"path": path,
|
|
697
848
|
}
|
|
698
849
|
}
|
|
699
850
|
|
|
700
851
|
@staticmethod
|
|
701
|
-
def _expected_glob_dir_output(out_dir):
|
|
852
|
+
def _expected_glob_dir_output(out_dir: str) -> "CWLObjectType":
|
|
702
853
|
dir_path = os.path.join(out_dir, "shouldmake")
|
|
703
854
|
dir_loc = "file://" + dir_path
|
|
704
855
|
file_path = os.path.join(dir_path, "test.txt")
|
|
@@ -720,14 +871,14 @@ class CWLWorkflowTest(ToilTest):
|
|
|
720
871
|
"checksum": "sha1$da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
|
721
872
|
"size": 0,
|
|
722
873
|
"nameroot": "test",
|
|
723
|
-
"nameext": ".txt"
|
|
874
|
+
"nameext": ".txt",
|
|
724
875
|
}
|
|
725
|
-
]
|
|
876
|
+
],
|
|
726
877
|
}
|
|
727
878
|
}
|
|
728
879
|
|
|
729
880
|
@classmethod
|
|
730
|
-
def _expected_load_contents_output(cls, out_dir):
|
|
881
|
+
def _expected_load_contents_output(cls, out_dir: str) -> "CWLObjectType":
|
|
731
882
|
"""
|
|
732
883
|
Generate the putput we expect from load_contents.cwl, when sending
|
|
733
884
|
output files to the given directory.
|
|
@@ -737,7 +888,7 @@ class CWLWorkflowTest(ToilTest):
|
|
|
737
888
|
return expected
|
|
738
889
|
|
|
739
890
|
@staticmethod
|
|
740
|
-
def _expected_colon_output(outDir):
|
|
891
|
+
def _expected_colon_output(outDir: str) -> "CWLObjectType":
|
|
741
892
|
path = os.path.join(outDir, "A:Gln2Cys_result")
|
|
742
893
|
loc = "file://" + os.path.join(outDir, "A%3AGln2Cys_result")
|
|
743
894
|
return {
|
|
@@ -755,13 +906,13 @@ class CWLWorkflowTest(ToilTest):
|
|
|
755
906
|
"size": 1111,
|
|
756
907
|
"nameroot": "whale",
|
|
757
908
|
"nameext": ".txt",
|
|
758
|
-
"path": f"{path}/whale.txt"
|
|
909
|
+
"path": f"{path}/whale.txt",
|
|
759
910
|
}
|
|
760
911
|
],
|
|
761
912
|
}
|
|
762
913
|
}
|
|
763
914
|
|
|
764
|
-
def _expected_streaming_output(self, outDir):
|
|
915
|
+
def _expected_streaming_output(self, outDir: str) -> "CWLObjectType":
|
|
765
916
|
path = os.path.join(outDir, "output.txt")
|
|
766
917
|
loc = "file://" + path
|
|
767
918
|
return {
|
|
@@ -783,7 +934,7 @@ class CWLv10Test(ToilTest):
|
|
|
783
934
|
Run the CWL 1.0 conformance tests in various environments.
|
|
784
935
|
"""
|
|
785
936
|
|
|
786
|
-
def setUp(self):
|
|
937
|
+
def setUp(self) -> None:
|
|
787
938
|
"""Runs anew before each test to create farm fresh temp dirs."""
|
|
788
939
|
self.outDir = f"/tmp/toil-cwl-test-{str(uuid.uuid4())}"
|
|
789
940
|
os.makedirs(self.outDir)
|
|
@@ -804,7 +955,7 @@ class CWLv10Test(ToilTest):
|
|
|
804
955
|
shutil.move("common-workflow-language-%s" % testhash, self.cwlSpec)
|
|
805
956
|
os.remove("spec.zip")
|
|
806
957
|
|
|
807
|
-
def tearDown(self):
|
|
958
|
+
def tearDown(self) -> None:
|
|
808
959
|
"""Clean up outputs."""
|
|
809
960
|
if os.path.exists(self.outDir):
|
|
810
961
|
shutil.rmtree(self.outDir)
|
|
@@ -812,99 +963,106 @@ class CWLv10Test(ToilTest):
|
|
|
812
963
|
|
|
813
964
|
@slow
|
|
814
965
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
815
|
-
def test_run_conformance_with_caching(self):
|
|
966
|
+
def test_run_conformance_with_caching(self) -> None:
|
|
816
967
|
self.test_run_conformance(caching=True)
|
|
817
968
|
|
|
818
969
|
@slow
|
|
819
970
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
820
971
|
def test_run_conformance(
|
|
821
|
-
self,
|
|
822
|
-
|
|
972
|
+
self,
|
|
973
|
+
batchSystem: Optional[str] = None,
|
|
974
|
+
caching: bool = False,
|
|
975
|
+
selected_tests: Optional[str] = None,
|
|
976
|
+
skipped_tests: Optional[str] = None,
|
|
977
|
+
extra_args: Optional[list[str]] = None,
|
|
978
|
+
) -> None:
|
|
823
979
|
run_conformance_tests(
|
|
824
980
|
workDir=self.workDir,
|
|
825
981
|
yml="conformance_test_v1.0.yaml",
|
|
826
982
|
caching=caching,
|
|
827
983
|
batchSystem=batchSystem,
|
|
828
984
|
selected_tests=selected_tests,
|
|
985
|
+
skipped_tests=skipped_tests,
|
|
986
|
+
extra_args=extra_args,
|
|
829
987
|
)
|
|
830
988
|
|
|
831
989
|
@slow
|
|
832
990
|
@needs_lsf
|
|
833
|
-
@unittest.skip
|
|
834
|
-
def test_lsf_cwl_conformance(self,
|
|
835
|
-
|
|
991
|
+
@unittest.skip("Not run")
|
|
992
|
+
def test_lsf_cwl_conformance(self, caching: bool = False) -> None:
|
|
993
|
+
self.test_run_conformance(batchSystem="lsf", caching=caching)
|
|
836
994
|
|
|
837
995
|
@slow
|
|
838
996
|
@needs_slurm
|
|
839
|
-
@unittest.skip
|
|
840
|
-
def test_slurm_cwl_conformance(self,
|
|
841
|
-
|
|
997
|
+
@unittest.skip("Not run")
|
|
998
|
+
def test_slurm_cwl_conformance(self, caching: bool = False) -> None:
|
|
999
|
+
self.test_run_conformance(batchSystem="slurm", caching=caching)
|
|
842
1000
|
|
|
843
1001
|
@slow
|
|
844
1002
|
@needs_torque
|
|
845
|
-
@unittest.skip
|
|
846
|
-
def test_torque_cwl_conformance(self,
|
|
847
|
-
|
|
1003
|
+
@unittest.skip("Not run")
|
|
1004
|
+
def test_torque_cwl_conformance(self, caching: bool = False) -> None:
|
|
1005
|
+
self.test_run_conformance(batchSystem="torque", caching=caching)
|
|
848
1006
|
|
|
849
1007
|
@slow
|
|
850
1008
|
@needs_gridengine
|
|
851
|
-
@unittest.skip
|
|
852
|
-
def test_gridengine_cwl_conformance(self,
|
|
853
|
-
|
|
1009
|
+
@unittest.skip("Not run")
|
|
1010
|
+
def test_gridengine_cwl_conformance(self, caching: bool = False) -> None:
|
|
1011
|
+
self.test_run_conformance(batchSystem="grid_engine", caching=caching)
|
|
854
1012
|
|
|
855
1013
|
@slow
|
|
856
1014
|
@needs_mesos
|
|
857
|
-
@unittest.skip
|
|
858
|
-
def test_mesos_cwl_conformance(self,
|
|
859
|
-
|
|
1015
|
+
@unittest.skip("Not run")
|
|
1016
|
+
def test_mesos_cwl_conformance(self, caching: bool = False) -> None:
|
|
1017
|
+
self.test_run_conformance(batchSystem="mesos", caching=caching)
|
|
860
1018
|
|
|
861
1019
|
@slow
|
|
862
1020
|
@needs_kubernetes
|
|
863
|
-
def test_kubernetes_cwl_conformance(self,
|
|
864
|
-
|
|
1021
|
+
def test_kubernetes_cwl_conformance(self, caching: bool = False) -> None:
|
|
1022
|
+
self.test_run_conformance(
|
|
1023
|
+
caching=caching,
|
|
865
1024
|
batchSystem="kubernetes",
|
|
866
1025
|
extra_args=["--retryCount=3"],
|
|
867
1026
|
# This test doesn't work with
|
|
868
1027
|
# Singularity; see
|
|
869
1028
|
# https://github.com/common-workflow-language/cwltool/blob/7094ede917c2d5b16d11f9231fe0c05260b51be6/conformance-test.sh#L99-L117
|
|
870
1029
|
skipped_tests="docker_entrypoint",
|
|
871
|
-
**kwargs,
|
|
872
1030
|
)
|
|
873
1031
|
|
|
874
1032
|
@slow
|
|
875
1033
|
@needs_lsf
|
|
876
|
-
@unittest.skip
|
|
877
|
-
def test_lsf_cwl_conformance_with_caching(self):
|
|
878
|
-
|
|
1034
|
+
@unittest.skip("Not run")
|
|
1035
|
+
def test_lsf_cwl_conformance_with_caching(self) -> None:
|
|
1036
|
+
self.test_lsf_cwl_conformance(caching=True)
|
|
879
1037
|
|
|
880
1038
|
@slow
|
|
881
1039
|
@needs_slurm
|
|
882
|
-
@unittest.skip
|
|
883
|
-
def test_slurm_cwl_conformance_with_caching(self):
|
|
884
|
-
|
|
1040
|
+
@unittest.skip("Not run")
|
|
1041
|
+
def test_slurm_cwl_conformance_with_caching(self) -> None:
|
|
1042
|
+
self.test_slurm_cwl_conformance(caching=True)
|
|
885
1043
|
|
|
886
1044
|
@slow
|
|
887
1045
|
@needs_torque
|
|
888
|
-
@unittest.skip
|
|
889
|
-
def test_torque_cwl_conformance_with_caching(self):
|
|
890
|
-
|
|
1046
|
+
@unittest.skip("Not run")
|
|
1047
|
+
def test_torque_cwl_conformance_with_caching(self) -> None:
|
|
1048
|
+
self.test_torque_cwl_conformance(caching=True)
|
|
891
1049
|
|
|
892
1050
|
@slow
|
|
893
1051
|
@needs_gridengine
|
|
894
|
-
@unittest.skip
|
|
895
|
-
def test_gridengine_cwl_conformance_with_caching(self):
|
|
896
|
-
|
|
1052
|
+
@unittest.skip("Not run")
|
|
1053
|
+
def test_gridengine_cwl_conformance_with_caching(self) -> None:
|
|
1054
|
+
self.test_gridengine_cwl_conformance(caching=True)
|
|
897
1055
|
|
|
898
1056
|
@slow
|
|
899
1057
|
@needs_mesos
|
|
900
|
-
@unittest.skip
|
|
901
|
-
def test_mesos_cwl_conformance_with_caching(self):
|
|
902
|
-
|
|
1058
|
+
@unittest.skip("Not run")
|
|
1059
|
+
def test_mesos_cwl_conformance_with_caching(self) -> None:
|
|
1060
|
+
self.test_mesos_cwl_conformance(caching=True)
|
|
903
1061
|
|
|
904
1062
|
@slow
|
|
905
1063
|
@needs_kubernetes
|
|
906
|
-
def test_kubernetes_cwl_conformance_with_caching(self):
|
|
907
|
-
|
|
1064
|
+
def test_kubernetes_cwl_conformance_with_caching(self) -> None:
|
|
1065
|
+
self.test_kubernetes_cwl_conformance(caching=True)
|
|
908
1066
|
|
|
909
1067
|
|
|
910
1068
|
@needs_cwl
|
|
@@ -914,8 +1072,12 @@ class CWLv11Test(ToilTest):
|
|
|
914
1072
|
Run the CWL 1.1 conformance tests in various environments.
|
|
915
1073
|
"""
|
|
916
1074
|
|
|
1075
|
+
rootDir: str
|
|
1076
|
+
cwlSpec: str
|
|
1077
|
+
test_yaml: str
|
|
1078
|
+
|
|
917
1079
|
@classmethod
|
|
918
|
-
def setUpClass(cls):
|
|
1080
|
+
def setUpClass(cls) -> None:
|
|
919
1081
|
"""Runs anew before each test."""
|
|
920
1082
|
cls.rootDir = cls._projectRootPath()
|
|
921
1083
|
cls.cwlSpec = os.path.join(cls.rootDir, "src/toil/test/cwl/spec_v11")
|
|
@@ -929,37 +1091,50 @@ class CWLv11Test(ToilTest):
|
|
|
929
1091
|
)
|
|
930
1092
|
p.communicate()
|
|
931
1093
|
|
|
932
|
-
def tearDown(self):
|
|
1094
|
+
def tearDown(self) -> None:
|
|
933
1095
|
"""Clean up outputs."""
|
|
934
1096
|
unittest.TestCase.tearDown(self)
|
|
935
1097
|
|
|
936
1098
|
@slow
|
|
937
1099
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
938
|
-
def test_run_conformance(
|
|
939
|
-
|
|
1100
|
+
def test_run_conformance(
|
|
1101
|
+
self,
|
|
1102
|
+
caching: bool = False,
|
|
1103
|
+
batchSystem: Optional[str] = None,
|
|
1104
|
+
skipped_tests: Optional[str] = None,
|
|
1105
|
+
extra_args: Optional[list[str]] = None,
|
|
1106
|
+
) -> None:
|
|
1107
|
+
run_conformance_tests(
|
|
1108
|
+
workDir=self.cwlSpec,
|
|
1109
|
+
yml=self.test_yaml,
|
|
1110
|
+
caching=caching,
|
|
1111
|
+
batchSystem=batchSystem,
|
|
1112
|
+
skipped_tests=skipped_tests,
|
|
1113
|
+
extra_args=extra_args,
|
|
1114
|
+
)
|
|
940
1115
|
|
|
941
1116
|
@slow
|
|
942
1117
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
943
|
-
def test_run_conformance_with_caching(self):
|
|
1118
|
+
def test_run_conformance_with_caching(self) -> None:
|
|
944
1119
|
self.test_run_conformance(caching=True)
|
|
945
1120
|
|
|
946
1121
|
@slow
|
|
947
1122
|
@needs_kubernetes
|
|
948
|
-
def test_kubernetes_cwl_conformance(self,
|
|
949
|
-
|
|
1123
|
+
def test_kubernetes_cwl_conformance(self, caching: bool = False) -> None:
|
|
1124
|
+
self.test_run_conformance(
|
|
950
1125
|
batchSystem="kubernetes",
|
|
951
1126
|
extra_args=["--retryCount=3"],
|
|
952
1127
|
# These tests don't work with
|
|
953
1128
|
# Singularity; see
|
|
954
1129
|
# https://github.com/common-workflow-language/cwltool/blob/7094ede917c2d5b16d11f9231fe0c05260b51be6/conformance-test.sh#L99-L117
|
|
955
1130
|
skipped_tests="docker_entrypoint,stdin_shorcut",
|
|
956
|
-
|
|
1131
|
+
caching=caching,
|
|
957
1132
|
)
|
|
958
1133
|
|
|
959
1134
|
@slow
|
|
960
1135
|
@needs_kubernetes
|
|
961
|
-
def test_kubernetes_cwl_conformance_with_caching(self):
|
|
962
|
-
|
|
1136
|
+
def test_kubernetes_cwl_conformance_with_caching(self) -> None:
|
|
1137
|
+
self.test_kubernetes_cwl_conformance(caching=True)
|
|
963
1138
|
|
|
964
1139
|
|
|
965
1140
|
@needs_cwl
|
|
@@ -969,8 +1144,12 @@ class CWLv12Test(ToilTest):
|
|
|
969
1144
|
Run the CWL 1.2 conformance tests in various environments.
|
|
970
1145
|
"""
|
|
971
1146
|
|
|
1147
|
+
rootDir: str
|
|
1148
|
+
cwlSpec: str
|
|
1149
|
+
test_yaml: str
|
|
1150
|
+
|
|
972
1151
|
@classmethod
|
|
973
|
-
def setUpClass(cls):
|
|
1152
|
+
def setUpClass(cls) -> None:
|
|
974
1153
|
"""Runs anew before each test."""
|
|
975
1154
|
cls.rootDir = cls._projectRootPath()
|
|
976
1155
|
cls.cwlSpec = os.path.join(cls.rootDir, "src/toil/test/cwl/spec_v12")
|
|
@@ -984,52 +1163,73 @@ class CWLv12Test(ToilTest):
|
|
|
984
1163
|
)
|
|
985
1164
|
p.communicate()
|
|
986
1165
|
|
|
987
|
-
def tearDown(self):
|
|
1166
|
+
def tearDown(self) -> None:
|
|
988
1167
|
"""Clean up outputs."""
|
|
989
1168
|
unittest.TestCase.tearDown(self)
|
|
990
1169
|
|
|
991
1170
|
@slow
|
|
992
1171
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
993
|
-
def test_run_conformance(
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1172
|
+
def test_run_conformance(
|
|
1173
|
+
self,
|
|
1174
|
+
runner: Optional[str] = None,
|
|
1175
|
+
caching: bool = False,
|
|
1176
|
+
batchSystem: Optional[str] = None,
|
|
1177
|
+
selected_tests: Optional[str] = None,
|
|
1178
|
+
skipped_tests: Optional[str] = None,
|
|
1179
|
+
extra_args: Optional[list[str]] = None,
|
|
1180
|
+
must_support_all_features: bool = False,
|
|
1181
|
+
junit_file: Optional[str] = None,
|
|
1182
|
+
) -> None:
|
|
1183
|
+
if junit_file is None:
|
|
1184
|
+
junit_file = os.path.join(self.rootDir, "conformance-1.2.junit.xml")
|
|
1185
|
+
run_conformance_tests(
|
|
1186
|
+
workDir=self.cwlSpec,
|
|
1187
|
+
yml=self.test_yaml,
|
|
1188
|
+
runner=runner,
|
|
1189
|
+
caching=caching,
|
|
1190
|
+
batchSystem=batchSystem,
|
|
1191
|
+
selected_tests=selected_tests,
|
|
1192
|
+
skipped_tests=skipped_tests,
|
|
1193
|
+
extra_args=extra_args,
|
|
1194
|
+
must_support_all_features=must_support_all_features,
|
|
1195
|
+
junit_file=junit_file,
|
|
1196
|
+
)
|
|
999
1197
|
|
|
1000
1198
|
@slow
|
|
1001
1199
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
1002
|
-
def test_run_conformance_with_caching(self):
|
|
1200
|
+
def test_run_conformance_with_caching(self) -> None:
|
|
1003
1201
|
self.test_run_conformance(
|
|
1004
1202
|
caching=True,
|
|
1005
|
-
junit_file
|
|
1006
|
-
self.rootDir, "caching-conformance-1.2.junit.xml"
|
|
1007
|
-
)
|
|
1203
|
+
junit_file=os.path.join(self.rootDir, "caching-conformance-1.2.junit.xml"),
|
|
1008
1204
|
)
|
|
1009
1205
|
|
|
1010
1206
|
@slow
|
|
1011
1207
|
@pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
|
|
1012
|
-
def test_run_conformance_with_in_place_update(self):
|
|
1208
|
+
def test_run_conformance_with_in_place_update(self) -> None:
|
|
1013
1209
|
"""
|
|
1014
1210
|
Make sure that with --bypass-file-store we properly support in place
|
|
1015
1211
|
update on a single node, and that this doesn't break any other
|
|
1016
1212
|
features.
|
|
1017
1213
|
"""
|
|
1018
1214
|
self.test_run_conformance(
|
|
1019
|
-
extra_args=["--bypass-file-store"],
|
|
1020
|
-
|
|
1215
|
+
extra_args=["--bypass-file-store"],
|
|
1216
|
+
must_support_all_features=True,
|
|
1217
|
+
junit_file=os.path.join(
|
|
1021
1218
|
self.rootDir, "in-place-update-conformance-1.2.junit.xml"
|
|
1022
|
-
)
|
|
1219
|
+
),
|
|
1023
1220
|
)
|
|
1024
1221
|
|
|
1025
1222
|
@slow
|
|
1026
1223
|
@needs_kubernetes
|
|
1027
|
-
def test_kubernetes_cwl_conformance(
|
|
1028
|
-
|
|
1029
|
-
|
|
1224
|
+
def test_kubernetes_cwl_conformance(
|
|
1225
|
+
self, caching: bool = False, junit_file: Optional[str] = None
|
|
1226
|
+
) -> None:
|
|
1227
|
+
if junit_file is None:
|
|
1228
|
+
junit_file = os.path.join(
|
|
1030
1229
|
self.rootDir, "kubernetes-conformance-1.2.junit.xml"
|
|
1031
1230
|
)
|
|
1032
|
-
|
|
1231
|
+
self.test_run_conformance(
|
|
1232
|
+
caching=caching,
|
|
1033
1233
|
batchSystem="kubernetes",
|
|
1034
1234
|
extra_args=["--retryCount=3"],
|
|
1035
1235
|
# This test doesn't work with
|
|
@@ -1038,13 +1238,13 @@ class CWLv12Test(ToilTest):
|
|
|
1038
1238
|
# and
|
|
1039
1239
|
# https://github.com/common-workflow-language/cwltool/issues/1441#issuecomment-826747975
|
|
1040
1240
|
skipped_tests="docker_entrypoint",
|
|
1041
|
-
|
|
1241
|
+
junit_file=junit_file,
|
|
1042
1242
|
)
|
|
1043
1243
|
|
|
1044
1244
|
@slow
|
|
1045
1245
|
@needs_kubernetes
|
|
1046
|
-
def test_kubernetes_cwl_conformance_with_caching(self):
|
|
1047
|
-
|
|
1246
|
+
def test_kubernetes_cwl_conformance_with_caching(self) -> None:
|
|
1247
|
+
self.test_kubernetes_cwl_conformance(
|
|
1048
1248
|
caching=True,
|
|
1049
1249
|
junit_file=os.path.join(
|
|
1050
1250
|
self.rootDir, "kubernetes-caching-conformance-1.2.junit.xml"
|
|
@@ -1053,7 +1253,7 @@ class CWLv12Test(ToilTest):
|
|
|
1053
1253
|
|
|
1054
1254
|
@slow
|
|
1055
1255
|
@needs_wes_server
|
|
1056
|
-
def test_wes_server_cwl_conformance(self):
|
|
1256
|
+
def test_wes_server_cwl_conformance(self) -> None:
|
|
1057
1257
|
"""
|
|
1058
1258
|
Run the CWL conformance tests via WES. TOIL_WES_ENDPOINT must be
|
|
1059
1259
|
specified. If the WES server requires authentication, set TOIL_WES_USER
|
|
@@ -1078,96 +1278,16 @@ class CWLv12Test(ToilTest):
|
|
|
1078
1278
|
# 1. `cwltool --print-deps` doesn't seem to include secondary files from the default
|
|
1079
1279
|
# e.g.: https://github.com/common-workflow-language/cwl-v1.2/blob/1.2.1_proposed/tests/mixed-versions/wf-v10.cwl#L4-L10
|
|
1080
1280
|
|
|
1081
|
-
|
|
1281
|
+
self.test_run_conformance(
|
|
1082
1282
|
runner="toil-wes-cwl-runner",
|
|
1083
1283
|
selected_tests="1-309,313-337",
|
|
1084
1284
|
extra_args=extra_args,
|
|
1085
1285
|
)
|
|
1086
1286
|
|
|
1087
1287
|
|
|
1088
|
-
@needs_aws_ec2
|
|
1089
|
-
@needs_fetchable_appliance
|
|
1090
|
-
@slow
|
|
1091
|
-
class CWLOnARMTest(AbstractClusterTest):
|
|
1092
|
-
"""
|
|
1093
|
-
Run the CWL 1.2 conformance tests on ARM specifically.
|
|
1094
|
-
"""
|
|
1095
|
-
|
|
1096
|
-
def __init__(self, methodName):
|
|
1097
|
-
super().__init__(methodName=methodName)
|
|
1098
|
-
self.clusterName = "cwl-test-" + str(uuid.uuid4())
|
|
1099
|
-
self.leaderNodeType = "t4g.2xlarge"
|
|
1100
|
-
self.clusterType = "kubernetes"
|
|
1101
|
-
# We need to be running in a directory which Flatcar and the Toil Appliance both have
|
|
1102
|
-
self.cwl_test_dir = "/tmp/toil/cwlTests"
|
|
1103
|
-
|
|
1104
|
-
def setUp(self):
|
|
1105
|
-
super().setUp()
|
|
1106
|
-
self.jobStore = f"aws:{self.awsRegion()}:cluster-{uuid.uuid4()}"
|
|
1107
|
-
|
|
1108
|
-
@needs_env_var("CI_COMMIT_SHA", "a git commit sha")
|
|
1109
|
-
def test_cwl_on_arm(self):
|
|
1110
|
-
# Make a cluster
|
|
1111
|
-
self.launchCluster()
|
|
1112
|
-
# get the leader so we know the IP address - we don't need to wait since create cluster
|
|
1113
|
-
# already ensures the leader is running
|
|
1114
|
-
self.cluster = cluster_factory(
|
|
1115
|
-
provisioner="aws", zone=self.zone, clusterName=self.clusterName
|
|
1116
|
-
)
|
|
1117
|
-
self.leader = self.cluster.getLeader()
|
|
1118
|
-
|
|
1119
|
-
commit = os.environ["CI_COMMIT_SHA"]
|
|
1120
|
-
self.sshUtil(
|
|
1121
|
-
[
|
|
1122
|
-
"bash",
|
|
1123
|
-
"-c",
|
|
1124
|
-
f"mkdir -p {self.cwl_test_dir} && cd {self.cwl_test_dir} && git clone https://github.com/DataBiosphere/toil.git",
|
|
1125
|
-
]
|
|
1126
|
-
)
|
|
1127
|
-
|
|
1128
|
-
# We use CI_COMMIT_SHA to retrieve the Toil version needed to run the CWL tests
|
|
1129
|
-
self.sshUtil(
|
|
1130
|
-
["bash", "-c", f"cd {self.cwl_test_dir}/toil && git checkout {commit}"]
|
|
1131
|
-
)
|
|
1132
|
-
|
|
1133
|
-
# --never-download prevents silent upgrades to pip, wheel and setuptools
|
|
1134
|
-
self.sshUtil(
|
|
1135
|
-
[
|
|
1136
|
-
"bash",
|
|
1137
|
-
"-c",
|
|
1138
|
-
f"virtualenv --system-site-packages --never-download {self.venvDir}",
|
|
1139
|
-
]
|
|
1140
|
-
)
|
|
1141
|
-
self.sshUtil(
|
|
1142
|
-
[
|
|
1143
|
-
"bash",
|
|
1144
|
-
"-c",
|
|
1145
|
-
f". .{self.venvDir}/bin/activate && cd {self.cwl_test_dir}/toil && make prepare && make develop extras=[all]",
|
|
1146
|
-
]
|
|
1147
|
-
)
|
|
1148
|
-
|
|
1149
|
-
# Runs the CWLv12Test on an ARM instance
|
|
1150
|
-
self.sshUtil(
|
|
1151
|
-
[
|
|
1152
|
-
"bash",
|
|
1153
|
-
"-c",
|
|
1154
|
-
f". .{self.venvDir}/bin/activate && cd {self.cwl_test_dir}/toil && pytest --log-cli-level DEBUG -r s src/toil/test/cwl/cwlTest.py::CWLv12Test::test_run_conformance",
|
|
1155
|
-
]
|
|
1156
|
-
)
|
|
1157
|
-
|
|
1158
|
-
# We know if it succeeds it should save a junit XML for us to read.
|
|
1159
|
-
# Bring it back to be an artifact.
|
|
1160
|
-
self.rsync_util(
|
|
1161
|
-
f":{self.cwl_test_dir}/toil/conformance-1.2.junit.xml",
|
|
1162
|
-
os.path.join(
|
|
1163
|
-
self._projectRootPath(),
|
|
1164
|
-
"arm-conformance-1.2.junit.xml"
|
|
1165
|
-
)
|
|
1166
|
-
)
|
|
1167
|
-
|
|
1168
1288
|
@needs_cwl
|
|
1169
1289
|
@pytest.mark.cwl_small_log_dir
|
|
1170
|
-
def test_workflow_echo_string_scatter_stderr_log_dir(tmp_path: Path):
|
|
1290
|
+
def test_workflow_echo_string_scatter_stderr_log_dir(tmp_path: Path) -> None:
|
|
1171
1291
|
log_dir = tmp_path / "cwl-logs"
|
|
1172
1292
|
job_store = "test_workflow_echo_string_scatter_stderr_log_dir"
|
|
1173
1293
|
toil = "toil-cwl-runner"
|
|
@@ -1272,9 +1392,12 @@ def test_log_dir_echo_stderr(tmp_path: Path) -> None:
|
|
|
1272
1392
|
assert output == "hello\n"
|
|
1273
1393
|
|
|
1274
1394
|
|
|
1395
|
+
# TODO: It's not clear how this test tests filename conflict resolution; it
|
|
1396
|
+
# seems like it runs a python script to copy some files and makes sure the
|
|
1397
|
+
# workflow doesn't fail.
|
|
1275
1398
|
@needs_cwl
|
|
1276
1399
|
@pytest.mark.cwl_small_log_dir
|
|
1277
|
-
def test_filename_conflict_resolution(tmp_path: Path):
|
|
1400
|
+
def test_filename_conflict_resolution(tmp_path: Path) -> None:
|
|
1278
1401
|
out_dir = tmp_path / "cwl-out-dir"
|
|
1279
1402
|
toil = "toil-cwl-runner"
|
|
1280
1403
|
options = [
|
|
@@ -1294,10 +1417,31 @@ def test_filename_conflict_resolution(tmp_path: Path):
|
|
|
1294
1417
|
assert b"Finished toil run successfully" in stderr
|
|
1295
1418
|
assert p.returncode == 0
|
|
1296
1419
|
|
|
1420
|
+
|
|
1421
|
+
@needs_cwl
|
|
1422
|
+
@pytest.mark.cwl_small_log_dir
|
|
1423
|
+
def test_filename_conflict_resolution_3_or_more(tmp_path: Path) -> None:
|
|
1424
|
+
out_dir = tmp_path / "cwl-out-dir"
|
|
1425
|
+
toil = "toil-cwl-runner"
|
|
1426
|
+
options = [
|
|
1427
|
+
f"--outdir={out_dir}",
|
|
1428
|
+
"--clean=always",
|
|
1429
|
+
]
|
|
1430
|
+
cwl = os.path.join(os.path.dirname(__file__), "scatter_duplicate_outputs.cwl")
|
|
1431
|
+
cmd = [toil] + options + [cwl]
|
|
1432
|
+
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
1433
|
+
stdout, stderr = p.communicate()
|
|
1434
|
+
assert b"Finished toil run successfully" in stderr
|
|
1435
|
+
assert p.returncode == 0
|
|
1436
|
+
assert (
|
|
1437
|
+
len(os.listdir(out_dir)) == 9
|
|
1438
|
+
), "All 9 files made by the scatter should be in the directory"
|
|
1439
|
+
|
|
1440
|
+
|
|
1297
1441
|
@needs_cwl
|
|
1298
1442
|
@needs_docker
|
|
1299
1443
|
@pytest.mark.cwl_small_log_dir
|
|
1300
|
-
def test_filename_conflict_detection(tmp_path: Path):
|
|
1444
|
+
def test_filename_conflict_detection(tmp_path: Path) -> None:
|
|
1301
1445
|
"""
|
|
1302
1446
|
Make sure we don't just stage files over each other when using a container.
|
|
1303
1447
|
"""
|
|
@@ -1316,10 +1460,11 @@ def test_filename_conflict_detection(tmp_path: Path):
|
|
|
1316
1460
|
assert b"File staging conflict" in stderr
|
|
1317
1461
|
assert p.returncode != 0
|
|
1318
1462
|
|
|
1463
|
+
|
|
1319
1464
|
@needs_cwl
|
|
1320
1465
|
@needs_docker
|
|
1321
1466
|
@pytest.mark.cwl_small_log_dir
|
|
1322
|
-
def test_filename_conflict_detection_at_root(tmp_path: Path):
|
|
1467
|
+
def test_filename_conflict_detection_at_root(tmp_path: Path) -> None:
|
|
1323
1468
|
"""
|
|
1324
1469
|
Make sure we don't just stage files over each other.
|
|
1325
1470
|
|
|
@@ -1343,7 +1488,7 @@ def test_filename_conflict_detection_at_root(tmp_path: Path):
|
|
|
1343
1488
|
|
|
1344
1489
|
@needs_cwl
|
|
1345
1490
|
@pytest.mark.cwl_small
|
|
1346
|
-
def test_pick_value_with_one_null_value(caplog):
|
|
1491
|
+
def test_pick_value_with_one_null_value(caplog: pytest.LogCaptureFixture) -> None:
|
|
1347
1492
|
"""
|
|
1348
1493
|
Make sure toil-cwl-runner does not false log a warning when pickValue is
|
|
1349
1494
|
used but outputSource only contains one null value. See: #3991.
|
|
@@ -1357,12 +1502,15 @@ def test_pick_value_with_one_null_value(caplog):
|
|
|
1357
1502
|
with caplog.at_level(logging.WARNING, logger="toil.cwl.cwltoil"):
|
|
1358
1503
|
cwltoil.main(args)
|
|
1359
1504
|
for line in caplog.messages:
|
|
1360
|
-
assert
|
|
1505
|
+
assert (
|
|
1506
|
+
"You had a conditional step that did not run, but you did not use pickValue to handle the skipped input."
|
|
1507
|
+
not in line
|
|
1508
|
+
)
|
|
1361
1509
|
|
|
1362
1510
|
|
|
1363
1511
|
@needs_cwl
|
|
1364
1512
|
@pytest.mark.cwl_small
|
|
1365
|
-
def test_workflow_echo_string():
|
|
1513
|
+
def test_workflow_echo_string() -> None:
|
|
1366
1514
|
toil = "toil-cwl-runner"
|
|
1367
1515
|
jobstore = f"--jobStore=file:explicit-local-jobstore-{uuid.uuid4()}"
|
|
1368
1516
|
option_1 = "--strict-memory-limit"
|
|
@@ -1372,14 +1520,18 @@ def test_workflow_echo_string():
|
|
|
1372
1520
|
cmd = [toil, jobstore, option_1, option_2, option_3, cwl]
|
|
1373
1521
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
1374
1522
|
stdout, stderr = p.communicate()
|
|
1375
|
-
|
|
1376
|
-
|
|
1523
|
+
stdout2 = stdout.decode("utf-8")
|
|
1524
|
+
stderr2 = stderr.decode("utf-8")
|
|
1525
|
+
assert (
|
|
1526
|
+
stdout2.strip() == "{}"
|
|
1527
|
+
), f"Got wrong output: {stdout2}\nWith error: {stderr2}"
|
|
1528
|
+
assert "Finished toil run successfully" in stderr2
|
|
1377
1529
|
assert p.returncode == 0
|
|
1378
1530
|
|
|
1379
1531
|
|
|
1380
1532
|
@needs_cwl
|
|
1381
1533
|
@pytest.mark.cwl_small
|
|
1382
|
-
def test_workflow_echo_string_scatter_capture_stdout():
|
|
1534
|
+
def test_workflow_echo_string_scatter_capture_stdout() -> None:
|
|
1383
1535
|
toil = "toil-cwl-runner"
|
|
1384
1536
|
jobstore = f"--jobStore=file:explicit-local-jobstore-{uuid.uuid4()}"
|
|
1385
1537
|
option_1 = "--strict-memory-limit"
|
|
@@ -1391,6 +1543,8 @@ def test_workflow_echo_string_scatter_capture_stdout():
|
|
|
1391
1543
|
cmd = [toil, jobstore, option_1, option_2, option_3, cwl]
|
|
1392
1544
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
1393
1545
|
stdout, stderr = p.communicate()
|
|
1546
|
+
log.debug("Workflow standard output: %s", stdout)
|
|
1547
|
+
assert len(stdout) > 0
|
|
1394
1548
|
outputs = json.loads(stdout)
|
|
1395
1549
|
out_list = outputs["list_out"]
|
|
1396
1550
|
assert len(out_list) == 2, f"outList shoud have two file elements {out_list}"
|
|
@@ -1412,7 +1566,7 @@ def test_workflow_echo_string_scatter_capture_stdout():
|
|
|
1412
1566
|
|
|
1413
1567
|
@needs_cwl
|
|
1414
1568
|
@pytest.mark.cwl_small
|
|
1415
|
-
def test_visit_top_cwl_class():
|
|
1569
|
+
def test_visit_top_cwl_class() -> None:
|
|
1416
1570
|
structure = {
|
|
1417
1571
|
"class": "Directory",
|
|
1418
1572
|
"listing": [
|
|
@@ -1438,7 +1592,7 @@ def test_visit_top_cwl_class():
|
|
|
1438
1592
|
|
|
1439
1593
|
counter = 0
|
|
1440
1594
|
|
|
1441
|
-
def increment(thing:
|
|
1595
|
+
def increment(thing: "CWLObjectType") -> None:
|
|
1442
1596
|
"""
|
|
1443
1597
|
Make sure we are at something CWL object like, and count it.
|
|
1444
1598
|
"""
|
|
@@ -1463,7 +1617,7 @@ def test_visit_top_cwl_class():
|
|
|
1463
1617
|
|
|
1464
1618
|
@needs_cwl
|
|
1465
1619
|
@pytest.mark.cwl_small
|
|
1466
|
-
def test_visit_cwl_class_and_reduce():
|
|
1620
|
+
def test_visit_cwl_class_and_reduce() -> None:
|
|
1467
1621
|
structure = {
|
|
1468
1622
|
"class": "Directory",
|
|
1469
1623
|
"listing": [
|
|
@@ -1489,7 +1643,7 @@ def test_visit_cwl_class_and_reduce():
|
|
|
1489
1643
|
|
|
1490
1644
|
down_count = 0
|
|
1491
1645
|
|
|
1492
|
-
def op_down(thing:
|
|
1646
|
+
def op_down(thing: "CWLObjectType") -> int:
|
|
1493
1647
|
"""
|
|
1494
1648
|
Grab the ID of the thing we are at, and count what we visit going
|
|
1495
1649
|
down.
|
|
@@ -1501,7 +1655,7 @@ def test_visit_cwl_class_and_reduce():
|
|
|
1501
1655
|
up_count = 0
|
|
1502
1656
|
up_child_count = 0
|
|
1503
1657
|
|
|
1504
|
-
def op_up(thing:
|
|
1658
|
+
def op_up(thing: "CWLObjectType", down_value: int, child_results: list[str]) -> str:
|
|
1505
1659
|
"""
|
|
1506
1660
|
Check the down return value and the up return values, and count
|
|
1507
1661
|
what we visit going up and what child relationships we have.
|
|
@@ -1524,7 +1678,7 @@ def test_visit_cwl_class_and_reduce():
|
|
|
1524
1678
|
|
|
1525
1679
|
@needs_cwl
|
|
1526
1680
|
@pytest.mark.cwl_small
|
|
1527
|
-
def test_download_structure(tmp_path) -> None:
|
|
1681
|
+
def test_download_structure(tmp_path: Path) -> None:
|
|
1528
1682
|
"""
|
|
1529
1683
|
Make sure that download_structure makes the right calls to what it thinks is the file store.
|
|
1530
1684
|
"""
|
|
@@ -1534,7 +1688,7 @@ def test_download_structure(tmp_path) -> None:
|
|
|
1534
1688
|
fid2 = FileID("adifferentfile", 1000, True)
|
|
1535
1689
|
|
|
1536
1690
|
# And what directory structure it would be in
|
|
1537
|
-
structure = {
|
|
1691
|
+
structure: DirectoryStructure = {
|
|
1538
1692
|
"dir1": {
|
|
1539
1693
|
"dir2": {
|
|
1540
1694
|
"f1": "toilfile:" + fid1.pack(),
|
|
@@ -1555,9 +1709,9 @@ def test_download_structure(tmp_path) -> None:
|
|
|
1555
1709
|
# These will be populated.
|
|
1556
1710
|
# TODO: This cache seems unused. Remove it?
|
|
1557
1711
|
# This maps filesystem path to CWL URI
|
|
1558
|
-
index = {}
|
|
1712
|
+
index: dict[str, str] = {}
|
|
1559
1713
|
# This maps CWL URI to filesystem path
|
|
1560
|
-
existing = {}
|
|
1714
|
+
existing: dict[str, str] = {}
|
|
1561
1715
|
|
|
1562
1716
|
# Do the download
|
|
1563
1717
|
download_structure(file_store, index, existing, structure, to_dir)
|
|
@@ -1573,11 +1727,16 @@ def test_download_structure(tmp_path) -> None:
|
|
|
1573
1727
|
assert os.path.join(to_dir, "dir1/dir2/f1again") in index
|
|
1574
1728
|
assert os.path.join(to_dir, "anotherfile") in index
|
|
1575
1729
|
assert (
|
|
1576
|
-
index[os.path.join(to_dir, "dir1/dir2/f1")]
|
|
1730
|
+
index[os.path.join(to_dir, "dir1/dir2/f1")]
|
|
1731
|
+
== cast(
|
|
1732
|
+
DirectoryStructure, cast(DirectoryStructure, structure["dir1"])["dir2"]
|
|
1733
|
+
)["f1"]
|
|
1577
1734
|
)
|
|
1578
1735
|
assert (
|
|
1579
1736
|
index[os.path.join(to_dir, "dir1/dir2/f1again")]
|
|
1580
|
-
==
|
|
1737
|
+
== cast(
|
|
1738
|
+
DirectoryStructure, cast(DirectoryStructure, structure["dir1"])["dir2"]
|
|
1739
|
+
)["f1again"]
|
|
1581
1740
|
)
|
|
1582
1741
|
assert index[os.path.join(to_dir, "anotherfile")] == structure["anotherfile"]
|
|
1583
1742
|
|
|
@@ -1605,3 +1764,53 @@ def test_download_structure(tmp_path) -> None:
|
|
|
1605
1764
|
],
|
|
1606
1765
|
any_order=True,
|
|
1607
1766
|
)
|
|
1767
|
+
|
|
1768
|
+
|
|
1769
|
+
@needs_cwl
|
|
1770
|
+
@pytest.mark.timeout(300)
|
|
1771
|
+
def test_import_on_workers() -> None:
|
|
1772
|
+
args = [
|
|
1773
|
+
"src/toil/test/cwl/download.cwl",
|
|
1774
|
+
"src/toil/test/cwl/download_file.json",
|
|
1775
|
+
"--runImportsOnWorkers",
|
|
1776
|
+
"--importWorkersDisk=10MiB",
|
|
1777
|
+
"--realTimeLogging=True",
|
|
1778
|
+
"--logLevel=INFO",
|
|
1779
|
+
"--logColors=False",
|
|
1780
|
+
]
|
|
1781
|
+
from toil.cwl import cwltoil
|
|
1782
|
+
|
|
1783
|
+
detector = ImportWorkersMessageHandler()
|
|
1784
|
+
|
|
1785
|
+
# Set up a log message detector to the root logger
|
|
1786
|
+
logging.getLogger().addHandler(detector)
|
|
1787
|
+
|
|
1788
|
+
cwltoil.main(args)
|
|
1789
|
+
|
|
1790
|
+
assert detector.detected is True
|
|
1791
|
+
|
|
1792
|
+
|
|
1793
|
+
# StreamHandler is generic, _typeshed doesn't exist at runtime, do a bit of typing trickery, see https://github.com/python/typeshed/issues/5680
|
|
1794
|
+
if TYPE_CHECKING:
|
|
1795
|
+
from _typeshed import SupportsWrite
|
|
1796
|
+
|
|
1797
|
+
_stream_handler = logging.StreamHandler[SupportsWrite[str]]
|
|
1798
|
+
else:
|
|
1799
|
+
_stream_handler = logging.StreamHandler
|
|
1800
|
+
|
|
1801
|
+
|
|
1802
|
+
class ImportWorkersMessageHandler(_stream_handler):
|
|
1803
|
+
"""
|
|
1804
|
+
Detect the import workers log message and set a flag.
|
|
1805
|
+
"""
|
|
1806
|
+
|
|
1807
|
+
def __init__(self) -> None:
|
|
1808
|
+
self.detected = False # Have we seen the message we want?
|
|
1809
|
+
|
|
1810
|
+
super().__init__(sys.stderr)
|
|
1811
|
+
|
|
1812
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
1813
|
+
if (record.msg % record.args).startswith(
|
|
1814
|
+
"Issued job 'CWLImportJob' CWLImportJob"
|
|
1815
|
+
):
|
|
1816
|
+
self.detected = True
|