Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/cwltool/tests/test_examples.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:18:57 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d30785e31577 |
---|---|
1 import json | |
2 import logging | |
3 import os | |
4 import stat | |
5 import sys | |
6 from io import BytesIO, StringIO | |
7 import pytest | |
8 from typing_extensions import Text | |
9 | |
10 import schema_salad.validate | |
11 | |
12 import cwltool.checker | |
13 import cwltool.expression as expr | |
14 import cwltool.factory | |
15 import cwltool.pathmapper | |
16 import cwltool.process | |
17 import cwltool.workflow | |
18 from cwltool.context import RuntimeContext | |
19 from cwltool.errors import WorkflowException | |
20 from cwltool.main import main | |
21 from cwltool.utils import onWindows | |
22 from cwltool.resolver import Path | |
23 from cwltool.process import CWL_IANA | |
24 from cwltool.sandboxjs import JavascriptException | |
25 from .util import (get_data, get_main_output, get_windows_safe_factory, subprocess, | |
26 needs_docker, working_directory, needs_singularity, temp_dir, windows_needs_docker) | |
27 | |
28 import six | |
29 | |
30 try: | |
31 reload | |
32 except: # pylint: disable=bare-except | |
33 try: | |
34 from imp import reload | |
35 except: | |
36 from importlib import reload | |
37 | |
38 sys.argv = [''] | |
39 | |
40 expression_match = [ | |
41 ("(foo)", True), | |
42 ("(foo.bar)", True), | |
43 ("(foo['bar'])", True), | |
44 ("(foo[\"bar\"])", True), | |
45 ("(foo.bar.baz)", True), | |
46 ("(foo['bar'].baz)", True), | |
47 ("(foo['bar']['baz'])", True), | |
48 ("(foo['b\\'ar']['baz'])", True), | |
49 ("(foo['b ar']['baz'])", True), | |
50 ("(foo_bar)", True), | |
51 | |
52 ("(foo.[\"bar\"])", False), | |
53 ("(.foo[\"bar\"])", False), | |
54 ("(foo [\"bar\"])", False), | |
55 ("( foo[\"bar\"])", False), | |
56 ("(foo[bar].baz)", False), | |
57 ("(foo['bar\"].baz)", False), | |
58 ("(foo['bar].baz)", False), | |
59 ("{foo}", False), | |
60 ("(foo.bar", False), | |
61 ("foo.bar)", False), | |
62 ("foo.b ar)", False), | |
63 ("foo.b\'ar)", False), | |
64 ("(foo+bar", False), | |
65 ("(foo bar", False) | |
66 ] | |
67 | |
68 | |
69 @pytest.mark.parametrize('expression,expected', expression_match) | |
70 def test_expression_match(expression, expected): | |
71 match = expr.param_re.match(expression) | |
72 assert (match is not None) == expected | |
73 | |
74 | |
75 interpolate_input = { | |
76 "foo": { | |
77 "bar": { | |
78 "baz": "zab1" | |
79 }, | |
80 "b ar": { | |
81 "baz": 2 | |
82 }, | |
83 "b'ar": { | |
84 "baz": True | |
85 }, | |
86 'b"ar': { | |
87 "baz": None | |
88 } | |
89 }, | |
90 "lst": ["A", "B"] | |
91 } | |
92 | |
93 interpolate_parameters = [ | |
94 ("$(foo)", interpolate_input["foo"]), | |
95 | |
96 ("$(foo.bar)", interpolate_input["foo"]["bar"]), | |
97 ("$(foo['bar'])", interpolate_input["foo"]["bar"]), | |
98 ("$(foo[\"bar\"])", interpolate_input["foo"]["bar"]), | |
99 | |
100 ("$(foo.bar.baz)", interpolate_input['foo']['bar']['baz']), | |
101 ("$(foo['bar'].baz)", interpolate_input['foo']['bar']['baz']), | |
102 ("$(foo['bar'][\"baz\"])", interpolate_input['foo']['bar']['baz']), | |
103 ("$(foo.bar['baz'])", interpolate_input['foo']['bar']['baz']), | |
104 | |
105 ("$(foo['b\\'ar'].baz)", True), | |
106 ("$(foo[\"b'ar\"].baz)", True), | |
107 ("$(foo['b\\\"ar'].baz)", None), | |
108 | |
109 ("$(lst[0])", "A"), | |
110 ("$(lst[1])", "B"), | |
111 ("$(lst.length)", 2), | |
112 ("$(lst['length'])", 2), | |
113 | |
114 ("-$(foo.bar)", """-{"baz": "zab1"}"""), | |
115 ("-$(foo['bar'])", """-{"baz": "zab1"}"""), | |
116 ("-$(foo[\"bar\"])", """-{"baz": "zab1"}"""), | |
117 | |
118 ("-$(foo.bar.baz)", "-zab1"), | |
119 ("-$(foo['bar'].baz)", "-zab1"), | |
120 ("-$(foo['bar'][\"baz\"])", "-zab1"), | |
121 ("-$(foo.bar['baz'])", "-zab1"), | |
122 | |
123 ("-$(foo['b ar'].baz)", "-2"), | |
124 ("-$(foo['b\\'ar'].baz)", "-true"), | |
125 ("-$(foo[\"b\\'ar\"].baz)", "-true"), | |
126 ("-$(foo['b\\\"ar'].baz)", "-null"), | |
127 | |
128 ("$(foo.bar) $(foo.bar)", """{"baz": "zab1"} {"baz": "zab1"}"""), | |
129 ("$(foo['bar']) $(foo['bar'])", """{"baz": "zab1"} {"baz": "zab1"}"""), | |
130 ("$(foo[\"bar\"]) $(foo[\"bar\"])", """{"baz": "zab1"} {"baz": "zab1"}"""), | |
131 | |
132 ("$(foo.bar.baz) $(foo.bar.baz)", "zab1 zab1"), | |
133 ("$(foo['bar'].baz) $(foo['bar'].baz)", "zab1 zab1"), | |
134 ("$(foo['bar'][\"baz\"]) $(foo['bar'][\"baz\"])", "zab1 zab1"), | |
135 ("$(foo.bar['baz']) $(foo.bar['baz'])", "zab1 zab1"), | |
136 | |
137 ("$(foo['b ar'].baz) $(foo['b ar'].baz)", "2 2"), | |
138 ("$(foo['b\\'ar'].baz) $(foo['b\\'ar'].baz)", "true true"), | |
139 ("$(foo[\"b\\'ar\"].baz) $(foo[\"b\\'ar\"].baz)", "true true"), | |
140 ("$(foo['b\\\"ar'].baz) $(foo['b\\\"ar'].baz)", "null null") | |
141 ] | |
142 | |
143 | |
144 @pytest.mark.parametrize('pattern,expected', interpolate_parameters) | |
145 def test_expression_interpolate(pattern, expected): | |
146 assert expr.interpolate(pattern, interpolate_input) == expected | |
147 | |
148 interpolate_bad_parameters = [ | |
149 ("$(fooz)"), | |
150 ("$(foo.barz)"), | |
151 ("$(foo['barz'])"), | |
152 ("$(foo[\"barz\"])"), | |
153 | |
154 ("$(foo.bar.bazz)"), | |
155 ("$(foo['bar'].bazz)"), | |
156 ("$(foo['bar'][\"bazz\"])"), | |
157 ("$(foo.bar['bazz'])"), | |
158 | |
159 ("$(foo['b\\'ar'].bazz)"), | |
160 ("$(foo[\"b'ar\"].bazz)"), | |
161 ("$(foo['b\\\"ar'].bazz)"), | |
162 | |
163 ("$(lst[O])"), # not "0" the number, but the letter O | |
164 ("$(lst[2])"), | |
165 ("$(lst.lengthz)"), | |
166 ("$(lst['lengthz'])"), | |
167 | |
168 ("-$(foo.barz)"), | |
169 ("-$(foo['barz'])"), | |
170 ("-$(foo[\"barz\"])"), | |
171 | |
172 ("-$(foo.bar.bazz)"), | |
173 ("-$(foo['bar'].bazz)"), | |
174 ("-$(foo['bar'][\"bazz\"])"), | |
175 ("-$(foo.bar['bazz'])"), | |
176 | |
177 ("-$(foo['b ar'].bazz)"), | |
178 ("-$(foo['b\\'ar'].bazz)"), | |
179 ("-$(foo[\"b\\'ar\"].bazz)"), | |
180 ("-$(foo['b\\\"ar'].bazz)"), | |
181 ] | |
182 | |
183 @pytest.mark.parametrize('pattern', interpolate_bad_parameters) | |
184 def test_expression_interpolate_failures(pattern): | |
185 result = None | |
186 try: | |
187 result = expr.interpolate(pattern, interpolate_input) | |
188 except JavascriptException: | |
189 return | |
190 assert false, 'Should have produced a JavascriptException, got "{}".'.format(result) | |
191 | |
192 | |
193 @windows_needs_docker | |
194 def test_factory(): | |
195 factory = get_windows_safe_factory() | |
196 echo = factory.make(get_data("tests/echo.cwl")) | |
197 | |
198 assert echo(inp="foo") == {"out": "foo\n"} | |
199 | |
200 | |
201 def test_factory_bad_outputs(): | |
202 factory = cwltool.factory.Factory() | |
203 | |
204 with pytest.raises(schema_salad.validate.ValidationException): | |
205 factory.make(get_data("tests/echo_broken_outputs.cwl")) | |
206 | |
207 | |
208 def test_factory_default_args(): | |
209 factory = cwltool.factory.Factory() | |
210 | |
211 assert factory.runtime_context.use_container is True | |
212 assert factory.runtime_context.on_error == "stop" | |
213 | |
214 | |
215 def test_factory_redefined_args(): | |
216 runtime_context = RuntimeContext() | |
217 runtime_context.use_container = False | |
218 runtime_context.on_error = "continue" | |
219 factory = cwltool.factory.Factory(runtime_context=runtime_context) | |
220 | |
221 assert factory.runtime_context.use_container is False | |
222 assert factory.runtime_context.on_error == "continue" | |
223 | |
224 | |
225 def test_factory_partial_scatter(): | |
226 runtime_context = RuntimeContext() | |
227 runtime_context.on_error = "continue" | |
228 factory = cwltool.factory.Factory(runtime_context=runtime_context) | |
229 | |
230 with pytest.raises(cwltool.factory.WorkflowStatus) as err_info: | |
231 factory.make(get_data("tests/wf/scatterfail.cwl"))() | |
232 | |
233 err = err_info.value | |
234 assert err.out["out"][0]["checksum"] == 'sha1$e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e' | |
235 assert err.out["out"][1] is None | |
236 assert err.out["out"][2]["checksum"] == 'sha1$a3db5c13ff90a36963278c6a39e4ee3c22e2a436' | |
237 | |
238 | |
239 def test_factory_partial_output(): | |
240 runtime_context = RuntimeContext() | |
241 runtime_context.on_error = "continue" | |
242 factory = cwltool.factory.Factory(runtime_context=runtime_context) | |
243 | |
244 with pytest.raises(cwltool.factory.WorkflowStatus) as err_info: | |
245 factory.make(get_data("tests/wf/wffail.cwl"))() | |
246 | |
247 err = err_info.value | |
248 assert err.out["out1"]["checksum"] == 'sha1$e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e' | |
249 assert err.out["out2"] is None | |
250 | |
251 | |
252 def test_scandeps(): | |
253 obj = { | |
254 "id": "file:///example/foo.cwl", | |
255 "steps": [ | |
256 { | |
257 "id": "file:///example/foo.cwl#step1", | |
258 "inputs": [{ | |
259 "id": "file:///example/foo.cwl#input1", | |
260 "default": { | |
261 "class": "File", | |
262 "location": "file:///example/data.txt" | |
263 } | |
264 }], | |
265 "run": { | |
266 "id": "file:///example/bar.cwl", | |
267 "inputs": [{ | |
268 "id": "file:///example/bar.cwl#input2", | |
269 "default": { | |
270 "class": "Directory", | |
271 "location": "file:///example/data2", | |
272 "listing": [{ | |
273 "class": "File", | |
274 "location": "file:///example/data3.txt", | |
275 "secondaryFiles": [{ | |
276 "class": "File", | |
277 "location": "file:///example/data5.txt" | |
278 }] | |
279 }] | |
280 }, | |
281 }, { | |
282 "id": "file:///example/bar.cwl#input3", | |
283 "default": { | |
284 "class": "Directory", | |
285 "listing": [{ | |
286 "class": "File", | |
287 "location": "file:///example/data4.txt" | |
288 }] | |
289 } | |
290 }, { | |
291 "id": "file:///example/bar.cwl#input4", | |
292 "default": { | |
293 "class": "File", | |
294 "contents": "file literal" | |
295 } | |
296 }] | |
297 } | |
298 } | |
299 ] | |
300 } | |
301 | |
302 def loadref(base, p): | |
303 if isinstance(p, dict): | |
304 return p | |
305 raise Exception("test case can't load things") | |
306 | |
307 scanned_deps = cwltool.process.scandeps( | |
308 obj["id"], obj, | |
309 {"$import", "run"}, | |
310 {"$include", "$schemas", "location"}, | |
311 loadref) | |
312 | |
313 scanned_deps.sort(key=lambda k: k["basename"]) | |
314 | |
315 expected_deps = [ | |
316 {"basename": "bar.cwl", | |
317 "nameroot": "bar", | |
318 "class": "File", | |
319 "format": CWL_IANA, | |
320 "nameext": ".cwl", | |
321 "location": "file:///example/bar.cwl"}, | |
322 {"basename": "data.txt", | |
323 "nameroot": "data", | |
324 "class": "File", | |
325 "nameext": ".txt", | |
326 "location": "file:///example/data.txt"}, | |
327 {"basename": "data2", | |
328 "class": "Directory", | |
329 "location": "file:///example/data2", | |
330 "listing": [ | |
331 {"basename": "data3.txt", | |
332 "nameroot": "data3", | |
333 "class": "File", | |
334 "nameext": ".txt", | |
335 "location": "file:///example/data3.txt", | |
336 "secondaryFiles": [ | |
337 {"class": "File", | |
338 "basename": "data5.txt", | |
339 "location": "file:///example/data5.txt", | |
340 "nameext": ".txt", | |
341 "nameroot": "data5" | |
342 }] | |
343 }] | |
344 }, { | |
345 "basename": "data4.txt", | |
346 "nameroot": "data4", | |
347 "class": "File", | |
348 "nameext": ".txt", | |
349 "location": "file:///example/data4.txt" | |
350 }] | |
351 | |
352 assert scanned_deps == expected_deps | |
353 | |
354 scanned_deps = cwltool.process.scandeps( | |
355 obj["id"], obj, | |
356 set(("run"), ), | |
357 set(), loadref) | |
358 | |
359 scanned_deps.sort(key=lambda k: k["basename"]) | |
360 | |
361 expected_deps = [{ | |
362 "basename": "bar.cwl", | |
363 "nameroot": "bar", | |
364 "format": CWL_IANA, | |
365 "class": "File", | |
366 "nameext": ".cwl", | |
367 "location": "file:///example/bar.cwl" | |
368 }] | |
369 | |
370 assert scanned_deps == expected_deps | |
371 | |
372 def test_trick_scandeps(): | |
373 if sys.version_info[0] < 3: | |
374 stream = BytesIO() | |
375 else: | |
376 stream = StringIO() | |
377 | |
378 main(["--print-deps", "--debug", get_data("tests/wf/trick_defaults.cwl")], stdout=stream) | |
379 assert json.loads(stream.getvalue())["secondaryFiles"][0]["location"][:2] != "_:" | |
380 | |
381 | |
382 def test_input_deps(): | |
383 if sys.version_info[0] < 3: | |
384 stream = BytesIO() | |
385 else: | |
386 stream = StringIO() | |
387 | |
388 main(["--print-input-deps", get_data("tests/wf/count-lines1-wf.cwl"), | |
389 get_data("tests/wf/wc-job.json")], stdout=stream) | |
390 | |
391 expected = {"class": "File", | |
392 "location": "wc-job.json", | |
393 "format": CWL_IANA, | |
394 "secondaryFiles": [{"class": "File", | |
395 "location": "whale.txt", | |
396 "basename": "whale.txt", | |
397 "nameroot": "whale", | |
398 "nameext": ".txt"}]} | |
399 assert json.loads(stream.getvalue()) == expected | |
400 | |
401 | |
402 def test_input_deps_cmdline_opts(): | |
403 if sys.version_info[0] < 3: | |
404 stream = BytesIO() | |
405 else: | |
406 stream = StringIO() | |
407 | |
408 main(["--print-input-deps", | |
409 get_data("tests/wf/count-lines1-wf.cwl"), | |
410 "--file1", get_data("tests/wf/whale.txt")], stdout=stream) | |
411 expected = {"class": "File", | |
412 "location": "", | |
413 "format": CWL_IANA, | |
414 "secondaryFiles": [{"class": "File", | |
415 "location": "whale.txt", | |
416 "basename": "whale.txt", | |
417 "nameroot": "whale", | |
418 "nameext": ".txt"}]} | |
419 assert json.loads(stream.getvalue()) == expected | |
420 | |
421 | |
422 def test_input_deps_cmdline_opts_relative_deps_cwd(): | |
423 if sys.version_info[0] < 3: | |
424 stream = BytesIO() | |
425 else: | |
426 stream = StringIO() | |
427 | |
428 data_path = get_data("tests/wf/whale.txt") | |
429 main(["--print-input-deps", "--relative-deps", "cwd", | |
430 get_data("tests/wf/count-lines1-wf.cwl"), | |
431 "--file1", data_path], stdout=stream) | |
432 | |
433 goal = {"class": "File", | |
434 "location": "", | |
435 "format": CWL_IANA, | |
436 "secondaryFiles": [{"class": "File", | |
437 "location": str( | |
438 Path(os.path.relpath( | |
439 data_path, os.path.curdir))), | |
440 "basename": "whale.txt", | |
441 "nameroot": "whale", | |
442 "nameext": ".txt"}]} | |
443 assert json.loads(stream.getvalue()) == goal | |
444 | |
445 | |
446 def test_dedupe(): | |
447 not_deduped = [ | |
448 {"class": "File", | |
449 "location": "file:///example/a"}, | |
450 {"class": "File", | |
451 "location": "file:///example/a"}, | |
452 {"class": "File", | |
453 "location": "file:///example/d"}, | |
454 {"class": "Directory", | |
455 "location": "file:///example/c", | |
456 "listing": [ | |
457 {"class": "File", | |
458 "location": "file:///example/d"} | |
459 ]} | |
460 ] | |
461 | |
462 expected = [ | |
463 {"class": "File", | |
464 "location": "file:///example/a"}, | |
465 {"class": "Directory", | |
466 "location": "file:///example/c", | |
467 "listing": [ | |
468 {"class": "File", | |
469 "location": "file:///example/d"} | |
470 ]} | |
471 ] | |
472 | |
473 assert cwltool.pathmapper.dedup(not_deduped) == expected | |
474 | |
475 | |
476 record = { | |
477 'fields': [ | |
478 {'type': {'items': 'string', 'type': 'array'}, | |
479 'name': u'file:///home/chapmanb/drive/work/cwl/test_bcbio_cwl/run_info-cwl-workflow/wf-variantcall.cwl#vc_rec/vc_rec/description' | |
480 }, | |
481 {'type': {'items': 'File', 'type': 'array'}, | |
482 'name': u'file:///home/chapmanb/drive/work/cwl/test_bcbio_cwl/run_info-cwl-workflow/wf-variantcall.cwl#vc_rec/vc_rec/vrn_file' | |
483 }], | |
484 'type': 'record', | |
485 'name': u'file:///home/chapmanb/drive/work/cwl/test_bcbio_cwl/run_info-cwl-workflow/wf-variantcall.cwl#vc_rec/vc_rec' | |
486 } | |
487 | |
488 source_to_sink = [ | |
489 ('0', | |
490 {'items': ['string', 'null'], 'type': 'array'}, | |
491 {'items': ['string', 'null'], 'type': 'array'}, | |
492 True | |
493 ), | |
494 ('1', | |
495 {'items': ['string'], 'type': 'array'}, | |
496 {'items': ['string', 'null'], 'type': 'array'}, | |
497 True | |
498 ), | |
499 ('2', | |
500 {'items': ['string', 'null'], 'type': 'array'}, | |
501 {'items': ['string'], 'type': 'array'}, | |
502 True | |
503 ), | |
504 ('3', | |
505 {'items': ['string'], 'type': 'array'}, | |
506 {'items': ['int'], 'type': 'array'}, | |
507 False | |
508 ), | |
509 ('record 0', | |
510 record, record, | |
511 True | |
512 ), | |
513 ('record 1', | |
514 record, {'items': 'string', 'type': 'array'}, | |
515 False | |
516 ) | |
517 ] | |
518 | |
519 | |
520 @pytest.mark.parametrize('name, source, sink, expected', source_to_sink) | |
521 def test_compare_types(name, source, sink, expected): | |
522 assert cwltool.workflow.can_assign_src_to_sink(source, sink) == expected, name | |
523 | |
524 | |
525 source_to_sink_strict = [ | |
526 ('0', | |
527 ['string', 'null'], ['string', 'null'], | |
528 True | |
529 ), | |
530 ('1', | |
531 ['string'], ['string', 'null'], | |
532 True | |
533 ), | |
534 ('2', | |
535 ['string', 'int'], ['string', 'null'], | |
536 False | |
537 ), | |
538 ('3', | |
539 {'items': ['string'], 'type': 'array'}, | |
540 {'items': ['string', 'null'], 'type': 'array'}, | |
541 True | |
542 ), | |
543 ('4', | |
544 {'items': ['string', 'int'], 'type': 'array'}, | |
545 {'items': ['string', 'null'], 'type': 'array'}, | |
546 False | |
547 ) | |
548 ] | |
549 | |
550 | |
551 @pytest.mark.parametrize('name, source, sink, expected', source_to_sink_strict) | |
552 def test_compare_types_strict(name, source, sink, expected): | |
553 assert cwltool.workflow.can_assign_src_to_sink(source, sink, strict=True) == expected, name | |
554 | |
555 | |
556 typechecks = [ | |
557 (['string', 'int'], ['string', 'int', 'null'], | |
558 None, None, | |
559 "pass" | |
560 ), | |
561 (['string', 'int'], ['string', 'null'], | |
562 None, None, | |
563 "warning" | |
564 ), | |
565 (['File', 'int'], ['string', 'null'], | |
566 None, None, | |
567 "exception" | |
568 ), | |
569 ({'items': ['string', 'int'], 'type': 'array'}, | |
570 {'items': ['string', 'int', 'null'], 'type': 'array'}, | |
571 None, None, | |
572 "pass" | |
573 ), | |
574 ({'items': ['string', 'int'], 'type': 'array'}, | |
575 {'items': ['string', 'null'], 'type': 'array'}, | |
576 None, None, | |
577 "warning" | |
578 ), | |
579 ({'items': ['File', 'int'], 'type': 'array'}, | |
580 {'items': ['string', 'null'], 'type': 'array'}, | |
581 None, None, | |
582 "exception" | |
583 ), | |
584 # check linkMerge when sinktype is not an array | |
585 (['string', 'int'], ['string', 'int', 'null'], | |
586 "merge_nested", None, | |
587 "exception" | |
588 ), | |
589 # check linkMerge: merge_nested | |
590 (['string', 'int'], | |
591 {'items': ['string', 'int', 'null'], 'type': 'array'}, | |
592 "merge_nested", None, | |
593 "pass" | |
594 ), | |
595 (['string', 'int'], | |
596 {'items': ['string', 'null'], 'type': 'array'}, | |
597 "merge_nested", None, | |
598 "warning" | |
599 ), | |
600 (['File', 'int'], | |
601 {'items': ['string', 'null'], 'type': 'array'}, | |
602 "merge_nested", None, | |
603 "exception" | |
604 ), | |
605 # check linkMerge: merge_nested and sinktype is "Any" | |
606 (['string', 'int'], "Any", | |
607 "merge_nested", None, | |
608 "pass" | |
609 ), | |
610 # check linkMerge: merge_flattened | |
611 (['string', 'int'], | |
612 {'items': ['string', 'int', 'null'], 'type': 'array'}, | |
613 "merge_flattened", None, | |
614 "pass" | |
615 ), | |
616 (['string', 'int'], | |
617 {'items': ['string', 'null'], 'type': 'array'}, | |
618 "merge_flattened", None, | |
619 "warning" | |
620 ), | |
621 (['File', 'int'], | |
622 {'items': ['string', 'null'], 'type': 'array'}, | |
623 "merge_flattened", None, | |
624 "exception" | |
625 ), | |
626 ({'items': ['string', 'int'], 'type': 'array'}, | |
627 {'items': ['string', 'int', 'null'], 'type': 'array'}, | |
628 "merge_flattened", None, | |
629 "pass" | |
630 ), | |
631 ({'items': ['string', 'int'], 'type': 'array'}, | |
632 {'items': ['string', 'null'], 'type': 'array'}, | |
633 "merge_flattened", None, | |
634 "warning" | |
635 ), | |
636 ({'items': ['File', 'int'], 'type': 'array'}, | |
637 {'items': ['string', 'null'], 'type': 'array'}, | |
638 "merge_flattened", None, | |
639 "exception"), | |
640 # check linkMerge: merge_flattened and sinktype is "Any" | |
641 (['string', 'int'], "Any", | |
642 "merge_flattened", None, | |
643 "pass" | |
644 ), | |
645 ({'items': ['string', 'int'], 'type': 'array'}, "Any", | |
646 "merge_flattened", None, | |
647 "pass" | |
648 ), | |
649 # check linkMerge: merge_flattened when srctype is a list | |
650 ([{'items': 'string', 'type': 'array'}], | |
651 {'items': 'string', 'type': 'array'}, | |
652 "merge_flattened", None, | |
653 "pass" | |
654 ), | |
655 # check valueFrom | |
656 ({'items': ['File', 'int'], 'type': 'array'}, | |
657 {'items': ['string', 'null'], 'type': 'array'}, | |
658 "merge_flattened", "special value", | |
659 "pass" | |
660 ) | |
661 ] | |
662 | |
663 | |
664 @pytest.mark.parametrize('src_type,sink_type,link_merge,value_from,expected_type', typechecks) | |
665 def test_typechecking(src_type, sink_type, link_merge, value_from, expected_type): | |
666 assert cwltool.checker.check_types( | |
667 src_type, sink_type, linkMerge=link_merge, valueFrom=value_from | |
668 ) == expected_type | |
669 | |
670 | |
671 def test_lifting(): | |
672 # check that lifting the types of the process outputs to the workflow step | |
673 # fails if the step 'out' doesn't match. | |
674 factory = cwltool.factory.Factory() | |
675 with pytest.raises(schema_salad.validate.ValidationException): | |
676 echo = factory.make(get_data("tests/test_bad_outputs_wf.cwl")) | |
677 assert echo(inp="foo") == {"out": "foo\n"} | |
678 | |
679 | |
680 def test_malformed_outputs(): | |
681 # check that tool validation fails if one of the outputs is not a valid CWL type | |
682 factory = cwltool.factory.Factory() | |
683 with pytest.raises(schema_salad.validate.ValidationException): | |
684 factory.make(get_data("tests/wf/malformed_outputs.cwl"))() | |
685 | |
686 | |
687 def test_separate_without_prefix(): | |
688 # check that setting 'separate = false' on an inputBinding without prefix fails the workflow | |
689 factory = cwltool.factory.Factory() | |
690 with pytest.raises(WorkflowException): | |
691 factory.make(get_data("tests/wf/separate_without_prefix.cwl"))() | |
692 | |
693 | |
694 def test_static_checker(): | |
695 # check that the static checker raises exception when a source type | |
696 # mismatches its sink type. | |
697 factory = cwltool.factory.Factory() | |
698 | |
699 with pytest.raises(schema_salad.validate.ValidationException): | |
700 factory.make(get_data("tests/checker_wf/broken-wf.cwl")) | |
701 | |
702 with pytest.raises(schema_salad.validate.ValidationException): | |
703 factory.make(get_data("tests/checker_wf/broken-wf2.cwl")) | |
704 | |
705 with pytest.raises(schema_salad.validate.ValidationException): | |
706 factory.make(get_data("tests/checker_wf/broken-wf3.cwl")) | |
707 | |
708 | |
709 def test_var_spool_cwl_checker1(): | |
710 """Confirm that references to /var/spool/cwl are caught.""" | |
711 stream = StringIO() | |
712 streamhandler = logging.StreamHandler(stream) | |
713 _logger = logging.getLogger("cwltool") | |
714 _logger.addHandler(streamhandler) | |
715 | |
716 factory = cwltool.factory.Factory() | |
717 try: | |
718 factory.make(get_data("tests/non_portable.cwl")) | |
719 assert "non_portable.cwl:18:4: Non-portable reference to /var/spool/cwl detected" in stream.getvalue() | |
720 finally: | |
721 _logger.removeHandler(streamhandler) | |
722 | |
723 | |
724 def test_var_spool_cwl_checker2(): | |
725 """Confirm that references to /var/spool/cwl are caught.""" | |
726 stream = StringIO() | |
727 streamhandler = logging.StreamHandler(stream) | |
728 _logger = logging.getLogger("cwltool") | |
729 _logger.addHandler(streamhandler) | |
730 | |
731 factory = cwltool.factory.Factory() | |
732 try: | |
733 factory.make(get_data("tests/non_portable2.cwl")) | |
734 assert "non_portable2.cwl:19:4: Non-portable reference to /var/spool/cwl detected" in stream.getvalue() | |
735 finally: | |
736 _logger.removeHandler(streamhandler) | |
737 | |
738 | |
739 def test_var_spool_cwl_checker3(): | |
740 """Confirm that references to /var/spool/cwl are caught.""" | |
741 stream = StringIO() | |
742 streamhandler = logging.StreamHandler(stream) | |
743 _logger = logging.getLogger("cwltool") | |
744 _logger.addHandler(streamhandler) | |
745 | |
746 factory = cwltool.factory.Factory() | |
747 try: | |
748 factory.make(get_data("tests/portable.cwl")) | |
749 assert "Non-portable reference to /var/spool/cwl detected" not in stream.getvalue() | |
750 finally: | |
751 _logger.removeHandler(streamhandler) | |
752 | |
753 | |
754 def test_print_dot(): | |
755 assert main(["--print-dot", get_data('tests/wf/revsort.cwl')]) == 0 | |
756 | |
757 test_factors = [(""), ("--parallel"), ("--debug"), ("--parallel --debug")] | |
758 | |
759 @pytest.mark.parametrize("factor", test_factors) | |
760 def test_js_console_cmd_line_tool(factor): | |
761 for test_file in ("js_output.cwl", "js_output_workflow.cwl"): | |
762 commands = factor.split() | |
763 commands.extend(["--js-console", "--no-container", get_data("tests/wf/" + test_file)]) | |
764 error_code, _, stderr = get_main_output(commands) | |
765 | |
766 assert "[log] Log message" in stderr | |
767 assert "[err] Error message" in stderr | |
768 | |
769 assert error_code == 0, stderr | |
770 | |
771 @pytest.mark.parametrize("factor", test_factors) | |
772 def test_no_js_console(factor): | |
773 for test_file in ("js_output.cwl", "js_output_workflow.cwl"): | |
774 commands = factor.split() | |
775 commands.extend(["--no-container", get_data("tests/wf/" + test_file)]) | |
776 _, _, stderr = get_main_output(commands) | |
777 | |
778 assert "[log] Log message" not in stderr | |
779 assert "[err] Error message" not in stderr | |
780 | |
781 | |
782 @needs_docker | |
783 @pytest.mark.parametrize("factor", test_factors) | |
784 def test_cid_file_dir(tmpdir, factor): | |
785 test_file = "cache_test_workflow.cwl" | |
786 cwd = tmpdir.chdir() | |
787 commands = factor.split() | |
788 commands.extend(["--cidfile-dir", str(tmpdir), get_data("tests/wf/" + test_file)]) | |
789 error_code, stdout, stderr = get_main_output(commands) | |
790 assert "completed success" in stderr | |
791 assert error_code == 0 | |
792 cidfiles_count = sum(1 for _ in tmpdir.visit(fil="*")) | |
793 assert cidfiles_count == 2 | |
794 cwd.chdir() | |
795 tmpdir.remove(ignore_errors=True) | |
796 | |
797 | |
798 @needs_docker | |
799 @pytest.mark.parametrize("factor", test_factors) | |
800 def test_cid_file_dir_arg_is_file_instead_of_dir(tmpdir, factor): | |
801 test_file = "cache_test_workflow.cwl" | |
802 bad_cidfile_dir = Text(tmpdir.ensure("cidfile-dir-actually-a-file")) | |
803 commands = factor.split() | |
804 commands.extend(["--cidfile-dir", bad_cidfile_dir, | |
805 get_data("tests/wf/" + test_file)]) | |
806 error_code, _, stderr = get_main_output(commands) | |
807 assert "is not a directory, please check it first" in stderr, stderr | |
808 assert error_code == 2 or error_code == 1, stderr | |
809 tmpdir.remove(ignore_errors=True) | |
810 | |
811 | |
812 @needs_docker | |
813 @pytest.mark.parametrize("factor", test_factors) | |
814 def test_cid_file_non_existing_dir(tmpdir, factor): | |
815 test_file = "cache_test_workflow.cwl" | |
816 bad_cidfile_dir = Text(tmpdir.join("cidfile-dir-badpath")) | |
817 commands = factor.split() | |
818 commands.extend(['--record-container-id',"--cidfile-dir", bad_cidfile_dir, | |
819 get_data("tests/wf/" + test_file)]) | |
820 error_code, _, stderr = get_main_output(commands) | |
821 assert "directory doesn't exist, please create it first" in stderr, stderr | |
822 assert error_code == 2 or error_code == 1, stderr | |
823 tmpdir.remove(ignore_errors=True) | |
824 | |
825 | |
826 @needs_docker | |
827 @pytest.mark.parametrize("factor", test_factors) | |
828 def test_cid_file_w_prefix(tmpdir, factor): | |
829 test_file = "cache_test_workflow.cwl" | |
830 cwd = tmpdir.chdir() | |
831 try: | |
832 commands = factor.split() | |
833 commands.extend(['--record-container-id', '--cidfile-prefix=pytestcid', | |
834 get_data("tests/wf/" + test_file)]) | |
835 error_code, stdout, stderr = get_main_output(commands) | |
836 finally: | |
837 listing = tmpdir.listdir() | |
838 cwd.chdir() | |
839 cidfiles_count = sum(1 for _ in tmpdir.visit(fil="pytestcid*")) | |
840 tmpdir.remove(ignore_errors=True) | |
841 assert "completed success" in stderr | |
842 assert error_code == 0 | |
843 assert cidfiles_count == 2, '{}/n{}'.format(listing, stderr) | |
844 | |
845 | |
846 @needs_docker | |
847 @pytest.mark.parametrize("factor", test_factors) | |
848 def test_secondary_files_v1_1(factor): | |
849 test_file = "secondary-files.cwl" | |
850 test_job_file = "secondary-files-job.yml" | |
851 try: | |
852 old_umask = os.umask(stat.S_IWOTH) # test run with umask 002 | |
853 commands = factor.split() | |
854 commands.extend(["--enable-dev", | |
855 get_data(os.path.join("tests", test_file)), | |
856 get_data(os.path.join("tests", test_job_file))]) | |
857 error_code, _, stderr = get_main_output(commands) | |
858 finally: | |
859 assert stat.S_IMODE(os.stat('lsout').st_mode) == 436 # 664 in octal, '-rw-rw-r--' | |
860 os.umask(old_umask) # revert back to original umask | |
861 assert "completed success" in stderr | |
862 assert error_code == 0 | |
863 | |
864 @needs_docker | |
865 @pytest.mark.parametrize("factor", test_factors) | |
866 def test_secondary_files_v1_0(factor): | |
867 test_file = "secondary-files-string-v1.cwl" | |
868 test_job_file = "secondary-files-job.yml" | |
869 try: | |
870 old_umask = os.umask(stat.S_IWOTH) # test run with umask 002 | |
871 commands = factor.split() | |
872 commands.extend([ | |
873 get_data(os.path.join("tests", test_file)), | |
874 get_data(os.path.join("tests", test_job_file)) | |
875 ]) | |
876 error_code, _, stderr = get_main_output(commands) | |
877 finally: | |
878 assert stat.S_IMODE(os.stat('lsout').st_mode) == 436 # 664 in octal, '-rw-rw-r--' | |
879 os.umask(old_umask) # revert back to original umask | |
880 assert "completed success" in stderr | |
881 assert error_code == 0 | |
882 | |
883 | |
884 @needs_docker | |
885 @pytest.mark.parametrize("factor", test_factors) | |
886 def test_wf_without_container(tmpdir, factor): | |
887 test_file = "hello-workflow.cwl" | |
888 with temp_dir("cwltool_cache") as cache_dir: | |
889 commands = factor.split() | |
890 commands.extend(["--cachedir", cache_dir, "--outdir", str(tmpdir), | |
891 get_data("tests/wf/" + test_file), | |
892 "--usermessage", | |
893 "hello"]) | |
894 error_code, _, stderr = get_main_output(commands) | |
895 | |
896 assert "completed success" in stderr | |
897 assert error_code == 0 | |
898 | |
899 | |
900 @needs_docker | |
901 @pytest.mark.parametrize("factor", test_factors) | |
902 def test_issue_740_fixed(factor): | |
903 test_file = "cache_test_workflow.cwl" | |
904 with temp_dir("cwltool_cache") as cache_dir: | |
905 commands = factor.split() | |
906 commands.extend(["--cachedir", cache_dir, get_data("tests/wf/" + test_file)]) | |
907 error_code, _, stderr = get_main_output(commands) | |
908 | |
909 assert "completed success" in stderr | |
910 assert error_code == 0 | |
911 | |
912 commands = factor.split() | |
913 commands.extend(["--cachedir", cache_dir, get_data("tests/wf/" + test_file)]) | |
914 error_code, _, stderr = get_main_output(commands) | |
915 | |
916 assert "Output of job will be cached in" not in stderr | |
917 assert error_code == 0, stderr | |
918 | |
919 | |
920 @needs_docker | |
921 def test_compute_checksum(): | |
922 runtime_context = RuntimeContext() | |
923 runtime_context.compute_checksum = True | |
924 runtime_context.use_container = onWindows() | |
925 factory = cwltool.factory.Factory(runtime_context=runtime_context) | |
926 echo = factory.make(get_data("tests/wf/cat-tool.cwl")) | |
927 output = echo( | |
928 file1={"class": "File", | |
929 "location": get_data("tests/wf/whale.txt")}, | |
930 reverse=False) | |
931 assert output['output']["checksum"] == "sha1$327fc7aedf4f6b69a42a7c8b808dc5a7aff61376" | |
932 | |
933 | |
934 @needs_docker | |
935 @pytest.mark.parametrize("factor", test_factors) | |
936 def test_no_compute_chcksum(tmpdir, factor): | |
937 test_file = "tests/wf/wc-tool.cwl" | |
938 job_file = "tests/wf/wc-job.json" | |
939 commands = factor.split() | |
940 commands.extend(["--no-compute-checksum", "--outdir", str(tmpdir), | |
941 get_data(test_file), get_data(job_file)]) | |
942 error_code, stdout, stderr = get_main_output(commands) | |
943 assert "completed success" in stderr | |
944 assert error_code == 0 | |
945 assert "checksum" not in stdout | |
946 | |
947 | |
948 @pytest.mark.parametrize("factor", test_factors) | |
949 def test_bad_userspace_runtime(factor): | |
950 test_file = "tests/wf/wc-tool.cwl" | |
951 job_file = "tests/wf/wc-job.json" | |
952 commands = factor.split() | |
953 commands.extend([ | |
954 "--user-space-docker-cmd=quaquioN", "--default-container=debian", | |
955 get_data(test_file), get_data(job_file)]) | |
956 error_code, stdout, stderr = get_main_output(commands) | |
957 assert "or quaquioN is missing or broken" in stderr, stderr | |
958 assert error_code == 1 | |
959 | |
960 @windows_needs_docker | |
961 @pytest.mark.parametrize("factor", test_factors) | |
962 def test_bad_basecommand(factor): | |
963 test_file = "tests/wf/missing-tool.cwl" | |
964 commands = factor.split() | |
965 commands.extend([get_data(test_file)]) | |
966 error_code, stdout, stderr = get_main_output(commands) | |
967 assert "'neenooGo' not found" in stderr, stderr | |
968 assert error_code == 1 | |
969 | |
970 | |
971 @needs_docker | |
972 @pytest.mark.parametrize("factor", test_factors) | |
973 def test_bad_basecommand_docker(factor): | |
974 test_file = "tests/wf/missing-tool.cwl" | |
975 commands = factor.split() | |
976 commands.extend( | |
977 ["--debug", "--default-container", "debian", get_data(test_file)]) | |
978 error_code, stdout, stderr = get_main_output(commands) | |
979 assert "permanentFail" in stderr, stderr | |
980 assert error_code == 1 | |
981 | |
982 @pytest.mark.parametrize("factor", test_factors) | |
983 def test_v1_0_position_expression(factor): | |
984 test_file = "tests/echo-position-expr.cwl" | |
985 test_job = "tests/echo-position-expr-job.yml" | |
986 commands = factor.split() | |
987 commands.extend( | |
988 ['--debug', get_data(test_file), get_data(test_job)]) | |
989 error_code, stdout, stderr = get_main_output(commands) | |
990 assert "is not int" in stderr, stderr | |
991 assert error_code == 1 | |
992 | |
993 | |
994 @windows_needs_docker | |
995 @pytest.mark.parametrize("factor", test_factors) | |
996 def test_optional_numeric_output_0(factor): | |
997 test_file = "tests/wf/optional-numerical-output-0.cwl" | |
998 commands = factor.split() | |
999 commands.extend([get_data(test_file)]) | |
1000 error_code, stdout, stderr = get_main_output(commands) | |
1001 | |
1002 assert "completed success" in stderr | |
1003 assert error_code == 0 | |
1004 assert json.loads(stdout)['out'] == 0 | |
1005 | |
1006 @pytest.mark.parametrize("factor", test_factors) | |
1007 @windows_needs_docker | |
1008 def test_env_filtering(factor): | |
1009 test_file = "tests/env.cwl" | |
1010 commands = factor.split() | |
1011 commands.extend([get_data(test_file)]) | |
1012 error_code, stdout, stderr = get_main_output(commands) | |
1013 | |
1014 process = subprocess.Popen(["sh", "-c", r"""getTrueShellExeName() { | |
1015 local trueExe nextTarget 2>/dev/null | |
1016 trueExe=$(ps -o comm= $$) || return 1 | |
1017 [ "${trueExe#-}" = "$trueExe" ] || trueExe=${trueExe#-} | |
1018 [ "${trueExe#/}" != "$trueExe" ] || trueExe=$([ -n "$ZSH_VERSION" ] && which -p "$trueExe" || which "$trueExe") | |
1019 while nextTarget=$(readlink "$trueExe"); do trueExe=$nextTarget; done | |
1020 printf '%s\n' "$(basename "$trueExe")" | |
1021 } ; getTrueShellExeName"""], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=None) | |
1022 sh_name, sh_name_err = process.communicate() | |
1023 sh_name = sh_name.decode('utf-8').strip() | |
1024 | |
1025 assert "completed success" in stderr, (error_code, stdout, stderr) | |
1026 assert error_code == 0, (error_code, stdout, stderr) | |
1027 if onWindows(): | |
1028 target = 5 | |
1029 elif sh_name == "dash": | |
1030 target = 4 | |
1031 else: # bash adds "SHLVL" and "_" environment variables | |
1032 target = 6 | |
1033 result = json.loads(stdout)['env_count'] | |
1034 details = '' | |
1035 if result != target: | |
1036 _, details, _ = get_main_output(["--quiet", get_data("tests/env2.cwl")]) | |
1037 print(sh_name) | |
1038 print(sh_name_err) | |
1039 print(details) | |
1040 assert result == target, (error_code, sh_name, sh_name_err, details, stdout, stderr) | |
1041 | |
1042 @windows_needs_docker | |
1043 def test_v1_0_arg_empty_prefix_separate_false(): | |
1044 test_file = "tests/arg-empty-prefix-separate-false.cwl" | |
1045 error_code, stdout, stderr = get_main_output( | |
1046 ['--debug', get_data(test_file), "--echo"]) | |
1047 assert "completed success" in stderr | |
1048 assert error_code == 0 |