|
| 1 | +import logging |
| 2 | +from types import SimpleNamespace |
| 3 | + |
| 4 | +import pytest |
| 5 | +from nipype.pipeline import engine as pe |
| 6 | +from nipype.interfaces import utility as niu |
| 7 | + |
| 8 | +from ..plugin import MultiProcPlugin |
| 9 | + |
| 10 | + |
| 11 | +def add(x, y): |
| 12 | + return x + y |
| 13 | + |
| 14 | + |
| 15 | +def addall(inlist): |
| 16 | + import time |
| 17 | + |
| 18 | + time.sleep(0.2) # Simulate some work |
| 19 | + return sum(inlist) |
| 20 | + |
| 21 | + |
| 22 | +@pytest.fixture |
| 23 | +def workflow(tmp_path): |
| 24 | + workflow = pe.Workflow(name="test_wf", base_dir=tmp_path) |
| 25 | + |
| 26 | + inputnode = pe.Node(niu.IdentityInterface(fields=["x", "y"]), name="inputnode") |
| 27 | + outputnode = pe.Node(niu.IdentityInterface(fields=["z"]), name="outputnode") |
| 28 | + |
| 29 | + # Generate many nodes and claim a lot of memory |
| 30 | + add_nd = pe.MapNode( |
| 31 | + niu.Function(function=add, input_names=["x", "y"], output_names=["z"]), |
| 32 | + name="add", |
| 33 | + iterfield=["x"], |
| 34 | + mem_gb=0.8, |
| 35 | + ) |
| 36 | + |
| 37 | + # Regular node |
| 38 | + sum_nd = pe.Node(niu.Function(function=addall, input_names=["inlist"]), name="sum") |
| 39 | + |
| 40 | + # Run without submitting is another code path |
| 41 | + add_more_nd = pe.Node( |
| 42 | + niu.Function(function=add, input_names=["x", "y"], output_names=["z"]), |
| 43 | + name="add_more", |
| 44 | + run_without_submitting=True, |
| 45 | + ) |
| 46 | + |
| 47 | + workflow.connect( |
| 48 | + [ |
| 49 | + (inputnode, add_nd, [("x", "x"), ("y", "y")]), |
| 50 | + (add_nd, sum_nd, [("z", "inlist")]), |
| 51 | + (sum_nd, add_more_nd, [("out", "x")]), |
| 52 | + (inputnode, add_more_nd, [("y", "y")]), |
| 53 | + (add_more_nd, outputnode, [("z", "z")]), |
| 54 | + ] |
| 55 | + ) |
| 56 | + |
| 57 | + inputnode.inputs.x = list(range(30)) |
| 58 | + inputnode.inputs.y = 4 |
| 59 | + |
| 60 | + # Avoid unnecessary sleeps |
| 61 | + workflow.config["execution"]["poll_sleep_duration"] = 0 |
| 62 | + |
| 63 | + return workflow |
| 64 | + |
| 65 | + |
| 66 | +def test_plugin_defaults(workflow, caplog): |
| 67 | + """Test the plugin works without any arguments.""" |
| 68 | + caplog.set_level(logging.CRITICAL, logger="nipype.workflow") |
| 69 | + workflow.run(plugin=MultiProcPlugin()) |
| 70 | + |
| 71 | + |
| 72 | +def test_plugin_args_noconfig(workflow, caplog): |
| 73 | + """Test the plugin works with typical nipype arguments.""" |
| 74 | + caplog.set_level(logging.CRITICAL, logger="nipype.workflow") |
| 75 | + workflow.run( |
| 76 | + plugin=MultiProcPlugin(), |
| 77 | + plugin_args={"n_procs": 2, "memory_gb": 0.1}, |
| 78 | + ) |
| 79 | + |
| 80 | + |
| 81 | +def test_plugin_app_config(workflow, caplog, capsys): |
| 82 | + """Test the plugin works with a nipreps-style configuration.""" |
| 83 | + |
| 84 | + def init_print(): |
| 85 | + print("Custom init") |
| 86 | + |
| 87 | + app_config = SimpleNamespace( |
| 88 | + environment=SimpleNamespace(total_memory_gb=1), |
| 89 | + _process_initializer=init_print(), |
| 90 | + file_path='/does/not/need/to/exist/for/testing', |
| 91 | + ) |
| 92 | + caplog.set_level(logging.CRITICAL, logger="nipype.workflow") |
| 93 | + workflow.run( |
| 94 | + plugin=MultiProcPlugin(), |
| 95 | + plugin_args={"n_procs": 2, "app_config": app_config}, |
| 96 | + ) |
| 97 | + |
| 98 | + captured = capsys.readouterr() |
| 99 | + assert "Custom init" in captured.out |
0 commit comments