From d79174564cfb4099ef6c801b77f5a05ed90e1e10 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 29 Mar 2019 10:28:25 +0000 Subject: [PATCH] TEST: unit tests for filetree.query --- tests/test_filetree/__init__.py | 0 tests/test_filetree/test_query.py | 326 ++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 tests/test_filetree/__init__.py create mode 100644 tests/test_filetree/test_query.py diff --git a/tests/test_filetree/__init__.py b/tests/test_filetree/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_filetree/test_query.py b/tests/test_filetree/test_query.py new file mode 100644 index 000000000..094686d9e --- /dev/null +++ b/tests/test_filetree/test_query.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python +# +# test_query.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# + +import os +import glob +import shutil +import os.path as op +import contextlib +import textwrap as tw +import itertools as it + +from .. import testdir + +import fsl.utils.filetree as filetree +import fsl.utils.filetree.query as ftquery + + +_test_tree = """ +subj-{participant} + [ses-{session}] + T1w.nii.gz (T1w) + T2w.nii.gz (T2w) + {hemi}.{surf}.gii (surface) +""".strip() + +_subjs = ['01', '02', '03'] +_sess = ['1', '2'] +_hemis = ['L', 'R'] +_surfs = ['midthickness', 'pial', 'white'] + + +@contextlib.contextmanager +def _test_data(): + + files = [] + + for subj, ses in it.product(_subjs, _sess): + sesdir = op.join('subj-{}'.format(subj), 'ses-{}'.format(ses)) + files.append(op.join(sesdir, 'T1w.nii.gz')) + files.append(op.join(sesdir, 'T2w.nii.gz')) + + for hemi, surf in it.product(_hemis, _surfs): + files.append(op.join(sesdir, '{}.{}.gii'.format(hemi, surf))) + + with testdir(files): + with open('_test_tree.tree', 'wt') as f: + f.write(_test_tree) + yield + + +def _expected_matches(short_name, **kwargs): + + matches = [] + subjs = kwargs.get('participant', _subjs) + sess = kwargs.get('session', _sess) + surfs = kwargs.get('surf', _surfs) + hemis = kwargs.get('hemi', _hemis) + + for subj, ses in it.product(subjs, sess): + + sesdir = op.join('subj-{}'.format(subj), 'ses-{}'.format(ses)) + + if short_name in ('T1w', 'T2w'): + f = op.join(sesdir, '{}.nii.gz'.format(short_name)) + matches.append(ftquery.Match(f, + short_name, + {'participant' : subj, + 'session' : ses})) + + elif short_name == 'surface': + for hemi, surf in it.product(hemis, surfs): + f = op.join(sesdir, '{}.{}.gii'.format(hemi, surf)) + matches.append(ftquery.Match(f, + short_name, + {'participant' : subj, + 'session' : ses, + 'hemi' : hemi, + 'surf' : surf})) + + return matches + + +def _run_and_check_query(query, short_name, **vars): + + gotmatches = query.query( short_name, **vars) + expmatches = _expected_matches(short_name, **{k : [v] + for k, v + in vars.items()}) + snvars = query.variables(short_name) + + assert len(snvars) == len(gotmatches.shape) + + for i, var in enumerate(sorted(snvars.keys())): + if var not in vars or vars[var] == '*': + assert len(snvars[var]) == gotmatches.shape[i] + else: + assert gotmatches.shape[i] == 1 + + for expmatch in expmatches: + slc = [] + for var in query.axes(short_name): + if var not in vars or vars[var] == '*': + vidx = snvars[var].index(expmatch.variables[var]) + slc.append(vidx) + else: + slc.append(0) + + assert expmatch == gotmatches[tuple(slc)] + + +def test_query_properties(): + with _test_data(): + + tree = filetree.FileTree.read('_test_tree.tree', '.') + query = filetree.FileTreeQuery(tree) + + assert sorted(query.axes('T1w')) == ['participant', 'session'] + assert sorted(query.axes('T2w')) == ['participant', 'session'] + assert sorted(query.axes('surface')) == ['hemi', + 'participant', + 'session', + 'surf'] + assert sorted(query.short_names) == ['T1w', 'T2w', 'surface'] + + assert query.variables('T1w') == {'participant' : ['01', '02', '03'], + 'session' : ['1', '2']} + assert query.variables('T2w') == {'participant' : ['01', '02', '03'], + 'session' : ['1', '2']} + assert query.variables('surface') == {'participant' : ['01', '02', '03'], + 'session' : ['1', '2'], + 'surf' : ['midthickness', + 'pial', + 'white'], + 'hemi' : ['L', 'R']} + assert query.variables() == {'participant' : ['01', '02', '03'], + 'session' : ['1', '2'], + 'surf' : ['midthickness', + 'pial', + 'white'], + 'hemi' : ['L', 'R']} + + +def test_query(): + with _test_data(): + tree = filetree.FileTree.read('_test_tree.tree', '.') + query = filetree.FileTreeQuery(tree) + + _run_and_check_query(query, 'T1w') + _run_and_check_query(query, 'T1w', participant='01') + _run_and_check_query(query, 'T1w', session='2') + _run_and_check_query(query, 'T1w', participant='02', session='1') + _run_and_check_query(query, 'T2w') + _run_and_check_query(query, 'T2w', participant='01') + _run_and_check_query(query, 'T2w', session='2') + _run_and_check_query(query, 'T2w', participant='02', session='1') + _run_and_check_query(query, 'surface') + _run_and_check_query(query, 'surface', hemi='L') + _run_and_check_query(query, 'surface', surf='midthickness') + _run_and_check_query(query, 'surface', hemi='R', surf='pial') + _run_and_check_query(query, 'surface', participant='03', surf='pial') + _run_and_check_query(query, 'surface', participant='03', sssion='2') + + +def test_query_optional_var_folder(): + with _test_data(): + + # make subj-01 have no session sub-directories + for f in glob.glob(op.join('subj-01', 'ses-1', '*')): + shutil.move(f, 'subj-01') + shutil.rmtree(op.join('subj-01', 'ses-1')) + shutil.rmtree(op.join('subj-01', 'ses-2')) + + tree = filetree.FileTree.read('_test_tree.tree', '.') + query = filetree.FileTreeQuery(tree) + + assert query.variables()['session'] == [None, '1', '2'] + + m = query.query('T1w', participant='01') + m = [ma for ma in m.flatten() if isinstance(ma, ftquery.Match)] + assert len(m) == 1 + assert m[0].filename == op.join('subj-01', 'T1w.nii.gz') + + +def test_query_optional_var_filename(): + + treefile = tw.dedent(""" + sub-{subject} + img[-{modality}].nii.gz (image) + """).strip() + + files = [ + op.join('sub-01', 'img.nii.gz'), + op.join('sub-02', 'img-t1.nii.gz'), + op.join('sub-02', 'img-t2.nii.gz'), + op.join('sub-03', 'img-t1.nii.gz'), + op.join('sub-04', 'img.nii.gz')] + + with testdir(files): + with open('_test_tree.tree', 'wt') as f: + f.write(treefile) + + tree = filetree.FileTree.read('_test_tree.tree', '.') + query = filetree.FileTreeQuery(tree) + + qvars = query.variables() + + assert sorted(qvars.keys()) == ['modality', 'subject'] + assert qvars['subject'] == ['01', '02', '03', '04'] + assert qvars['modality'] == [None, 't1', 't2'] + + m = query.query('image', modality=None) + m = [ma.filename for ma in m.flatten() + if isinstance(ma, ftquery.Match)] + assert m == [op.join('sub-01', 'img.nii.gz'), + op.join('sub-04', 'img.nii.gz')] + + m = query.query('image', modality='t1') + m = [ma.filename for ma in m.flatten() + if isinstance(ma, ftquery.Match)] + assert m == [op.join('sub-02', 'img-t1.nii.gz'), + op.join('sub-03', 'img-t1.nii.gz')] + + m = query.query('image', modality='t2') + m = [ma.filename for ma in m.flatten() + if isinstance(ma, ftquery.Match)] + assert m == [op.join('sub-02', 'img-t2.nii.gz')] + + +def test_query_missing_files(): + with _test_data(): + + os.remove(op.join('subj-01', 'ses-1', 'T1w.nii.gz')) + os.remove(op.join('subj-02', 'ses-2', 'T2w.nii.gz')) + os.remove(op.join('subj-03', 'ses-1', 'L.white.gii')) + os.remove(op.join('subj-03', 'ses-1', 'L.midthickness.gii')) + os.remove(op.join('subj-03', 'ses-1', 'L.pial.gii')) + + tree = filetree.FileTree.read('_test_tree.tree', '.') + query = filetree.FileTreeQuery(tree) + + m = query.query('T1w', session='1') + m = [ma.filename for ma in m.flatten() if isinstance(ma, ftquery.Match)] + assert sorted(m) == [ + op.join('subj-02', 'ses-1', 'T1w.nii.gz'), + op.join('subj-03', 'ses-1', 'T1w.nii.gz')] + + m = query.query('T2w', session='2') + m = [ma.filename for ma in m.flatten() if isinstance(ma, ftquery.Match)] + assert sorted(m) == [ + op.join('subj-01', 'ses-2', 'T2w.nii.gz'), + op.join('subj-03', 'ses-2', 'T2w.nii.gz')] + + m = query.query('surface', session='1', hemi='L') + m = [ma.filename for ma in m.flatten() if isinstance(ma, ftquery.Match)] + assert sorted(m) == [ + op.join('subj-01', 'ses-1', 'L.midthickness.gii'), + op.join('subj-01', 'ses-1', 'L.pial.gii'), + op.join('subj-01', 'ses-1', 'L.white.gii'), + op.join('subj-02', 'ses-1', 'L.midthickness.gii'), + op.join('subj-02', 'ses-1', 'L.pial.gii'), + op.join('subj-02', 'ses-1', 'L.white.gii')] + + + pass + + +def test_scan(): + + with _test_data(): + tree = filetree.FileTree.read('_test_tree.tree', '.') + gotmatches = ftquery.scan(tree) + + expmatches = [] + + for subj, ses in it.product(_subjs, _sess): + + sesdir = op.join('subj-{}'.format(subj), 'ses-{}'.format(ses)) + t1wf = op.join(sesdir, 'T1w.nii.gz') + t2wf = op.join(sesdir, 'T2w.nii.gz') + + expmatches.append(ftquery.Match(t1wf, 'T1w', {'participant' : subj, + 'session' : ses})) + expmatches.append(ftquery.Match(t2wf, 'T2w', {'participant' : subj, + 'session' : ses})) + + for hemi, surf in it.product(_hemis, _surfs): + surff = op.join(sesdir, '{}.{}.gii'.format(hemi, surf)) + + expmatches.append(ftquery.Match(surff, + 'surface', + {'participant' : subj, + 'session' : ses, + 'surf' : surf, + 'hemi' : hemi})) + + + assert len(gotmatches) == len(expmatches) + + for got, exp in zip(sorted(gotmatches), sorted(expmatches)): + assert got.filename == exp.filename + assert got.short_name == exp.short_name + assert got.variables == exp.variables + + +def test_allVariables(): + with _test_data(): + tree = filetree.FileTree.read('_test_tree.tree', '.') + matches = ftquery.scan(tree) + qvars, snames = ftquery.allVariables(tree, matches) + + expqvars = { + 'participant' : _subjs, + 'session' : _sess, + 'surf' : _surfs, + 'hemi' : _hemis} + expsnames = { + 'T1w' : ['participant', 'session'], + 'T2w' : ['participant', 'session'], + 'surface' : ['hemi', 'participant', 'session', 'surf']} + + assert qvars == expqvars + assert snames == expsnames -- GitLab