Skip to content

Commit 1ff2488

Browse files
committed
add tests to fix coverage
1 parent a3ac9f8 commit 1ff2488

File tree

5 files changed

+453
-5
lines changed

5 files changed

+453
-5
lines changed

tests/e2e_in_docker.sh

100644100755
File mode changed.

tests/pytest_in_docker.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ docker run -it \
1111
--cov-report=xml \
1212
--cov=babs \
1313
--pdb \
14-
/babs/tests/test_update_input_data.py
14+
/babs/tests/
1515

tests/test_base.py

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
"""Test the check_setup functionality."""
22

3+
import os.path as op
34
import random
45
from pathlib import Path
6+
from unittest.mock import MagicMock
57

8+
import pandas as pd
69
import pytest
710
import yaml
811

912
from babs import BABSCheckSetup
10-
from babs.base import CONFIG_SECTIONS
13+
from babs.base import BABS, CONFIG_SECTIONS
1114
from babs.utils import read_yaml
1215

1316

@@ -50,8 +53,6 @@ def test_missing_directories(tmp_path_factory):
5053

5154
def test_validate_pipeline_config(babs_project_sessionlevel):
5255
"""Test _validate_pipeline_config method."""
53-
from babs.base import BABS
54-
5556
babs_proj = BABS(babs_project_sessionlevel)
5657

5758
# Test valid config
@@ -74,3 +75,130 @@ def test_validate_pipeline_config(babs_project_sessionlevel):
7475
babs_proj.pipeline = [{'missing': 'container_name'}]
7576
with pytest.raises(ValueError, match='Pipeline step 0 missing required field: container_name'):
7677
babs_proj._validate_pipeline_config()
78+
79+
80+
def test_project_root_not_exists(tmp_path):
81+
"""Test FileNotFoundError when project_root doesn't exist."""
82+
non_existent_path = tmp_path / 'does_not_exist'
83+
with pytest.raises(FileNotFoundError, match='`project_root` does not exist!'):
84+
BABS(non_existent_path)
85+
86+
87+
def test_analysis_path_not_exists(tmp_path):
88+
"""Test FileNotFoundError when analysis path doesn't exist."""
89+
project_root = tmp_path / 'project'
90+
project_root.mkdir()
91+
with pytest.raises(FileNotFoundError, match='is not a valid BABS project'):
92+
BABS(project_root)
93+
94+
95+
def test_config_path_not_exists(babs_project_sessionlevel):
96+
"""Test FileNotFoundError when config path doesn't exist."""
97+
babs_proj = BABSCheckSetup(babs_project_sessionlevel)
98+
Path(babs_proj.config_path).unlink()
99+
100+
with pytest.raises(FileNotFoundError, match='is not a valid BABS project'):
101+
BABS(babs_project_sessionlevel)
102+
103+
104+
def test_pipeline_config_details(babs_project_sessionlevel):
105+
"""Test pipeline validation with config details."""
106+
babs_proj = BABS(babs_project_sessionlevel)
107+
108+
# Test with cluster_resources, bids_app_args, singularity_args
109+
babs_proj.pipeline = [
110+
{
111+
'container_name': 'test-app',
112+
'config': {
113+
'cluster_resources': {'memory': '8GB', 'cpus': 4},
114+
'bids_app_args': {'--nthreads': 4},
115+
'singularity_args': ['--bind', '/tmp'],
116+
},
117+
}
118+
]
119+
babs_proj._validate_pipeline_config()
120+
121+
# Test with inter_step_cmds
122+
babs_proj.pipeline = [{'container_name': 'test-app', 'inter_step_cmds': ['echo "test"']}]
123+
babs_proj._validate_pipeline_config()
124+
125+
# Test with both
126+
babs_proj.pipeline = [
127+
{
128+
'container_name': 'test-app',
129+
'config': {'cluster_resources': {'memory': '8GB'}},
130+
'inter_step_cmds': ['echo "test"'],
131+
}
132+
]
133+
babs_proj._validate_pipeline_config()
134+
135+
136+
def test_update_inclusion_empty_combine(babs_project_sessionlevel):
137+
"""Test _update_inclusion_dataframe when combined dataframe is empty."""
138+
babs_proj = BABS(babs_project_sessionlevel)
139+
initial_inclusion_df = pd.DataFrame({'sub_id': ['sub-9999'], 'ses_id': ['ses-9999']})
140+
141+
with pytest.raises(ValueError, match='No subjects/sessions to analyze!'):
142+
babs_proj._update_inclusion_dataframe(initial_inclusion_df=initial_inclusion_df)
143+
144+
145+
def test_update_inclusion_warning(babs_project_sessionlevel, capsys):
146+
"""Test _update_inclusion_dataframe warning when initial df has more subjects."""
147+
babs_proj = BABS(babs_project_sessionlevel)
148+
actual_df = babs_proj.input_datasets.generate_inclusion_dataframe()
149+
150+
if 'ses_id' in actual_df.columns:
151+
initial_inclusion_df = pd.DataFrame(
152+
{
153+
'sub_id': ['sub-0001', 'sub-0002', 'sub-9999'],
154+
'ses_id': ['ses-01', 'ses-01', 'ses-01'],
155+
}
156+
)
157+
else:
158+
initial_inclusion_df = pd.DataFrame({'sub_id': ['sub-0001', 'sub-0002', 'sub-9999']})
159+
160+
babs_proj._update_inclusion_dataframe(initial_inclusion_df=initial_inclusion_df)
161+
captured = capsys.readouterr()
162+
assert 'Warning: The initial inclusion dataframe' in captured.out
163+
164+
165+
def test_datalad_save_filter_files(babs_project_sessionlevel):
166+
"""Test datalad_save with filter_files parameter."""
167+
babs_proj = BABS(babs_project_sessionlevel)
168+
test_file = op.join(babs_proj.analysis_path, 'code', 'test_file.txt')
169+
Path(test_file).parent.mkdir(parents=True, exist_ok=True)
170+
Path(test_file).write_text('test content')
171+
172+
babs_proj.datalad_save(
173+
path=test_file, message='Test save with filter', filter_files=['test_file.txt']
174+
)
175+
assert Path(test_file).exists()
176+
177+
178+
def test_datalad_save_failure(babs_project_sessionlevel, monkeypatch):
179+
"""Test datalad_save when save fails."""
180+
babs_proj = BABS(babs_project_sessionlevel)
181+
mock_save = MagicMock(return_value=[{'status': 'error', 'message': 'Save failed'}])
182+
monkeypatch.setattr(babs_proj.analysis_datalad_handle, 'save', mock_save)
183+
184+
test_file = op.join(babs_proj.analysis_path, 'code', 'test_file.txt')
185+
Path(test_file).parent.mkdir(parents=True, exist_ok=True)
186+
Path(test_file).write_text('test content')
187+
188+
with pytest.raises(Exception, match='`datalad save` failed!'):
189+
babs_proj.datalad_save(path=test_file, message='Test save')
190+
191+
192+
def test_key_info_ria_only(babs_project_sessionlevel):
193+
"""Test wtf_key_info with flag_output_ria_only=True."""
194+
babs_proj = BABS(babs_project_sessionlevel)
195+
babs_proj.wtf_key_info(flag_output_ria_only=True)
196+
assert babs_proj.output_ria_data_dir is not None
197+
198+
199+
def test_key_info_full(babs_project_sessionlevel):
200+
"""Test wtf_key_info with flag_output_ria_only=False."""
201+
babs_proj = BABS(babs_project_sessionlevel)
202+
babs_proj.wtf_key_info(flag_output_ria_only=False)
203+
assert babs_proj.output_ria_data_dir is not None
204+
assert babs_proj.analysis_dataset_id is not None

tests/test_merge.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Test merge.py error handling and edge cases."""
2+
3+
import subprocess
4+
from unittest.mock import MagicMock
5+
6+
import pytest
7+
8+
from babs.merge import BABSMerge
9+
from babs.utils import get_git_show_ref_shasum
10+
11+
12+
def test_merge_no_branches(babs_project_sessionlevel, monkeypatch):
13+
"""Test babs_merge when no branches have results."""
14+
babs_proj = BABSMerge(babs_project_sessionlevel)
15+
monkeypatch.setattr(babs_proj, '_get_results_branches', lambda: [])
16+
17+
with pytest.raises(ValueError, match='There is no successfully finished job yet'):
18+
babs_proj.babs_merge()
19+
20+
21+
def test_merge_all_branches_no_results(babs_project_sessionlevel, tmp_path, monkeypatch):
22+
"""Test babs_merge when all branches have no results."""
23+
babs_proj = BABSMerge(babs_project_sessionlevel)
24+
25+
merge_ds_path = tmp_path / 'merge_ds'
26+
merge_ds_path.mkdir()
27+
subprocess.run(['git', 'init'], cwd=merge_ds_path, capture_output=True)
28+
subprocess.run(['git', 'config', 'user.name', 'Test'], cwd=merge_ds_path, capture_output=True)
29+
subprocess.run(
30+
['git', 'config', 'user.email', 'test@test.com'],
31+
cwd=merge_ds_path,
32+
capture_output=True,
33+
)
34+
(merge_ds_path / 'test.txt').write_text('test')
35+
subprocess.run(['git', 'add', 'test.txt'], cwd=merge_ds_path, capture_output=True)
36+
subprocess.run(['git', 'commit', '-m', 'Initial'], cwd=merge_ds_path, capture_output=True)
37+
38+
default_branch = 'main'
39+
try:
40+
subprocess.run(
41+
['git', 'checkout', '-b', default_branch],
42+
cwd=merge_ds_path,
43+
capture_output=True,
44+
)
45+
except Exception:
46+
default_branch = 'master'
47+
48+
git_ref, _ = get_git_show_ref_shasum(default_branch, merge_ds_path)
49+
50+
def mock_branches():
51+
return ['job-123-1-sub-0001']
52+
53+
def mock_key_info(flag_output_ria_only=False):
54+
babs_proj.analysis_dataset_id = 'test-id'
55+
56+
def mock_git_ref(branch, path):
57+
return git_ref, f'{git_ref} refs/remotes/origin/{branch}'
58+
59+
monkeypatch.setattr(babs_proj, '_get_results_branches', mock_branches)
60+
monkeypatch.setattr(babs_proj, 'wtf_key_info', mock_key_info)
61+
monkeypatch.setattr('babs.merge.get_git_show_ref_shasum', mock_git_ref)
62+
from babs.merge import dlapi
63+
64+
monkeypatch.setattr(dlapi, 'clone', lambda source, path: None)
65+
66+
def mock_remote_show(cmd, **kwargs):
67+
if 'remote' in cmd and 'show' in cmd:
68+
result = MagicMock()
69+
result.returncode = 0
70+
result.stdout = f'HEAD branch: {default_branch}\n'.encode()
71+
return result
72+
return subprocess.run(cmd, **kwargs)
73+
74+
monkeypatch.setattr('babs.merge.subprocess.run', mock_remote_show)
75+
76+
with pytest.raises(Exception, match='There is no job branch in output RIA that has results'):
77+
babs_proj.babs_merge()

0 commit comments

Comments
 (0)