diff --git a/README.md b/README.md index a63ac35a54eebcad5f8603229aaa6fe2197ad6fa..a3ab1ba2fc53104b2e61fb65808ad41e3a2094d4 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,10 @@ guide](doc/contributing.rst). Tests ----- -To run the tests, install `pytest` and `pytest-cov`, and then run: +To run the tests, install `mock`, `pytest`, `pytest-runner`, `pytest-html`, +and `pytest-cov`, and then run: python setup.py test -A code coverage report will be generated in `htmlcov/`. +A test report will be generated at `report.html`, and a code coverage report +will be generated in `htmlcov/`. diff --git a/fsl/data/atlases.py b/fsl/data/atlases.py index db1925fa46e88b69ffe596070cdf765291bc0e18..9f6b9fe8df68f665233dadd9fac579521f8ea80f 100644 --- a/fsl/data/atlases.py +++ b/fsl/data/atlases.py @@ -40,6 +40,8 @@ load an atlas image, which will be one of the following atlas-specific """ +from __future__ import division + import xml.etree.ElementTree as et import os.path as op import glob @@ -521,11 +523,11 @@ class AtlasDescription(object): return self.atlasID != other.atlasID - def __cmp__(self, other): + def __lt__(self, other): """Compares this ``AtlasDescription`` with another by their ``name`` attribute. """ - return cmp(self.name.lower(), other.name.lower()) + return self.name.lower() < other.name.lower() class Atlas(fslimage.Image): @@ -559,8 +561,10 @@ class Atlas(fslimage.Image): res = resolution reses = np.concatenate(atlasDesc.pixdims) - if resolution is None: imageIdx = np.argmin(reses) / 3 - else: imageIdx = np.argmin(np.abs(reses - res)) / 3 + if resolution is None: imageIdx = np.argmin(reses) + else: imageIdx = np.argmin(np.abs(reses - res)) + + imageIdx = imageIdx // 3 if isLabel: imageFile = atlasDesc.summaryImages[imageIdx] else: imageFile = atlasDesc.images[ imageIdx] diff --git a/fsl/data/featdesign.py b/fsl/data/featdesign.py index ee5843064b0d2c9bccc93dd4322be98f5f51f2a4..ed8d905994e98a1c6a240230cab56307f6a6b5b8 100644 --- a/fsl/data/featdesign.py +++ b/fsl/data/featdesign.py @@ -540,7 +540,7 @@ def getFirstLevelEVs(featDir, settings, designMat): # above, about the voxelwise # confound EV locations, holds startIdx = len(evs) + 1 - if voxConfLocs != range(startIdx, startIdx + len(voxConfFiles)): + if voxConfLocs != list(range(startIdx, startIdx + len(voxConfFiles))): raise FSFError('Unsupported voxelwise confound ordering ' '({} -> {})'.format(startIdx, voxConfLocs)) @@ -640,26 +640,11 @@ def loadDesignMat(designmat): :arg designmat: Path to the ``design.mat`` file. """ - matrix = None - log.debug('Loading FEAT design matrix from {}'.format(designmat)) - with open(designmat, 'rt') as f: - - while True: - line = f.readline() - - # readline returns an empty string at EOF - if line == '': - break - - # Matrix data starts after "/Matrix" - if line.strip() == '/Matrix': - break - - matrix = np.loadtxt(f, ndmin=2) + matrix = np.loadtxt(designmat, comments='/', ndmin=2) - if matrix is None or matrix.size == 0: + if matrix is None or matrix.size == 0 or len(matrix.shape) != 2: raise FSFError('{} does not appear to be a ' 'valid design.mat file'.format(designmat)) diff --git a/fsl/data/image.py b/fsl/data/image.py index 1b93982be91d51ba32da528a555d9030faddb9b1..f44c3bec5539e84f24a8ee9285fd713499b687aa 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -470,8 +470,8 @@ class Nifti(notifier.Notifier): elif qform_code != constants.NIFTI_XFORM_UNKNOWN: code = qform_code # Invalid values - if code > 4: code = constants.NIFTI_XFORM_UNKNOWN - elif code < 0: code = constants.NIFTI_XFORM_UNKNOWN + if code not in range(5): + code = constants.NIFTI_XFORM_UNKNOWN return int(code) @@ -836,7 +836,7 @@ class Image(Nifti): self.__nibImage = None self.__imageWrapper = None - if self.__fileobj is not None: + if getattr(self, '__fileobj', None) is not None: self.__fileobj.close() diff --git a/fsl/data/imagewrapper.py b/fsl/data/imagewrapper.py index 810624daaccd695c857f0b1f90ccd989e7207b55..81658f594817ad90b3f4f9c7fd7fecbd9949e0a2 100644 --- a/fsl/data/imagewrapper.py +++ b/fsl/data/imagewrapper.py @@ -415,7 +415,7 @@ class ImageWrapper(notifier.Notifier): """ shape = self.__image.shape - slices = zip([0] * len(shape), shape) + slices = [[0, s] for s in shape] return sliceCovered(slices, self.__coverage) diff --git a/fsl/data/mesh.py b/fsl/data/mesh.py index e34ebf2a7b3bedaee0ef80c629c5b23a794fa577..2a9c47df7bdacc938cbcef57bf459b05529f8db1 100644 --- a/fsl/data/mesh.py +++ b/fsl/data/mesh.py @@ -221,7 +221,6 @@ def loadVTKPolydataFile(infile): for i in range(nVertices): vertLine = lines[i + 5] - vertices[i, :] = map(float, vertLine.split()) vertices[i, :] = [float(w) for w in vertLine.split()] indexOffset = 0 @@ -232,7 +231,6 @@ def loadVTKPolydataFile(infile): start = indexOffset end = indexOffset + polygonLengths[i] - indices[start:end] = map(int, polyLine[1:]) indices[start:end] = [int(w) for w in polyLine[1:]] indexOffset += polygonLengths[i] diff --git a/fsl/utils/async.py b/fsl/utils/async.py index 938a83c384746540cf77bce76dd089e64591d4aa..a8e8efa0e41a39336b4acecb6ab7709c1c14ac04 100644 --- a/fsl/utils/async.py +++ b/fsl/utils/async.py @@ -290,8 +290,11 @@ def _wxIdleLoop(ev): except queue.Empty: # Make sure that we get called periodically, - # if EVT_IDLE decides to stop firing. - _idleTimer.Start(_idleCallRate, wx.TIMER_ONE_SHOT) + # if EVT_IDLE decides to stop firing. If + # _idleTimer is None, then idleReset has + # probably been called. + if _idleTimer is not None: + _idleTimer.Start(_idleCallRate, wx.TIMER_ONE_SHOT) return now = time.time() diff --git a/pytest.ini b/pytest.ini index 599c0c249814ef8da3a0efca547b52e16dcdb48b..4352354c39baad6c64cc31779521515065481c51 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tests -addopts = -v --niters=50 --cov=fsl --cov-report=html +addopts = -s -v --niters=50 --cov=fsl --cov-report=html --html=report.html diff --git a/requirements.txt b/requirements.txt index 6370b5c227e5a96c814bccef79407d3bc5bb98bf..a35583e56da774621c9fee8dfcc693f2ffacd962 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ six>=1.10.0,<2.0 numpy>=1.11.1,<2.0 nibabel>=2.1,<3.0 -indexed_gzip>=0.3.1,<0.4 +indexed_gzip>=0.3.2,<0.4 wxPython>=3.0.2.0,<=4.0 diff --git a/setup.py b/setup.py index a83332d4a2c721261bb158086934d247e3aec18a..c793739fa383707ced42d2984d0953777a1325d7 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ with open(op.join(basedir, "fsl", "version.py")) as f: for line in f: if line.startswith('__version__'): exec(line, version) - break + break with open(op.join(basedir, 'README.md'), 'rt') as f: readme = f.read() @@ -45,9 +45,9 @@ with open(op.join(basedir, 'README.md'), 'rt') as f: class doc(Command): """Build the API documentation. """ - + user_options = [] - + def initialize_options(self): pass @@ -64,12 +64,12 @@ class doc(Command): env = dict(os.environ) ppath = [op.join(pkgutil.get_loader('fsl').filename, '..')] - + env['PYTHONPATH'] = op.pathsep.join(ppath) print('Building documentation [{}]'.format(destdir)) - sp.call(['sphinx-build', docdir, destdir], env=env) + sp.call(['sphinx-build', docdir, destdir], env=env) setup( @@ -102,7 +102,11 @@ setup( install_requires=install_requires, setup_requires=['pytest-runner'], - tests_require=['pytest', 'mock', 'pytest-cov', 'pytest-runner'], + tests_require=['pytest', + 'mock', + 'pytest-cov', + 'pytest-html', + 'pytest-runner'], test_suite='tests', cmdclass={'doc' : doc}, diff --git a/tests/__init__.py b/tests/__init__.py index 106f41767bf137ee895d693d7ad727967dfdef2e..0d5e4affcc5fe572197a5c79c9a6363590e2ef59 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -24,15 +24,14 @@ def testdir(contents=None): if contents is not None: contents = [op.join(*c.split('/')) for c in contents] - print(contents) - + class ctx(object): def __init__(self, contents): self.contents = contents - + def __enter__(self): - + self.testdir = tempfile.mkdtemp() if self.contents is not None: @@ -55,7 +54,7 @@ def make_dummy_files(paths): def make_dummy_file(path, contents=None): """Makes a plain text file. Returns a hash of the file contents. """ dirname = op.dirname(path) - + if not op.exists(dirname): os.makedirs(dirname) @@ -148,8 +147,8 @@ def make_mock_feat_analysis(featdir, src = featdir dest = op.join(testdir, op.basename(featdir)) - featdir = dest - + featdir = dest + shutil.copytree(src, dest) if indata: @@ -184,18 +183,18 @@ def make_mock_feat_analysis(featdir, if copes: files = glob.glob(op.join(featdir, 'stats', 'cope*nii.gz')) otherFiles .extend(files) - otherShapes.extend([shape] * len(files)) + otherShapes.extend([shape] * len(files)) if zstats: files = glob.glob(op.join(featdir, 'stats', 'zstat*nii.gz')) otherFiles .extend(files) - otherShapes.extend([shape] * len(files)) - + otherShapes.extend([shape] * len(files)) + if residuals: files = glob.glob(op.join(featdir, 'stats', 'res4d.nii.gz')) otherFiles .extend(files) - otherShapes.extend([shape4D]) - + otherShapes.extend([shape4D]) + if clustMasks: files = glob.glob(op.join(featdir, 'cluster_mask*nii.gz')) otherFiles .extend(files) diff --git a/tests/test_async.py b/tests/test_async.py index 9a2e03d56b0aaf4db299eaf2d694da1636ab0f14..3e7819d4013e0d91c76d3852b092ab4a621c52f7 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -59,7 +59,7 @@ def _run_with_wx(func, *args, **kwargs): frame.Destroy() _wxapp.ExitMainLoop() wx.CallLater(finishingDelay, finish) - + frame.Show() wx.CallLater(startingDelay, wrap) @@ -69,7 +69,7 @@ def _run_with_wx(func, *args, **kwargs): if raised[0] and propagateRaise: raise raised[0] - + return result[0] @@ -100,8 +100,8 @@ def _test_run(): taskRun[0] = True def errtask(): - taskRun[0] = True - raise Exception('Task crashed!') + taskRun[0] = True + raise Exception('Task which was supposed to crash crashed!') def onFinish(): onFinishCalled[0] = True @@ -127,7 +127,7 @@ def _test_run(): taskRun[ 0] = False onFinishCalled[0] = False - + t = async.run(errtask, onFinish, onError) if t is not None: @@ -157,7 +157,7 @@ def test_idle(): called[0] = arg == 1 and kwarg1 == 2 def errtask(arg, kwarg1=None): - raise Exception('Task crashed!') + raise Exception('Task which was supposed to crash crashed!') assert async.getIdleTimeout() > 0 @@ -173,10 +173,10 @@ def test_idle(): # Run a crashing task directly with pytest.raises(Exception): - async.idle(errtask) + async.idle(errtask, 1, kwarg1=2) # Run a crashing task on idle loop - error should not propagate - _run_with_wx(async.idle, errtask) + _run_with_wx(async.idle, errtask, 1, kwarg1=2) def test_inidle(): @@ -413,7 +413,7 @@ def _test_wait(): if t is not None: t.join() - + _wait_for_idle_loop_to_clear() assert all(threadtaskscalled) @@ -449,7 +449,7 @@ def test_TaskThread_onFinish(): taskCalled[0] = True def onFinish(): - onFinishCalled[0] = True + onFinishCalled[0] = True tt = async.TaskThread() tt.start() @@ -488,7 +488,7 @@ def test_TaskThread_isQueued(): time.sleep(0.3) tt.stop() - tt.join() + tt.join() assert queued assert called[0] @@ -517,7 +517,7 @@ def test_TaskThread_dequeue(): time.sleep(0.3) tt.stop() - tt.join() + tt.join() assert not called[0] @@ -542,7 +542,7 @@ def test_TaskThread_TaskVeto(): time.sleep(0.5) tt.stop() - tt.join() + tt.join() assert taskCalled[0] assert not onFinishCalled[0] @@ -579,7 +579,7 @@ def test_mutex(): t[0].method2start = None t[0].method1end = None t[0].method2end = None - + t1 = threading.Thread(target=thread1) t2 = threading.Thread(target=thread2) diff --git a/tests/test_featdesign.py b/tests/test_featdesign.py index c1f6a21010580b5882d239666d31fd36dd1ae51e..f685ff712364c6338faaade5af3cea151ccc87a5 100644 --- a/tests/test_featdesign.py +++ b/tests/test_featdesign.py @@ -68,7 +68,7 @@ with the following commands: - 2 temporal derivative EVs - 1 voxelwise EV - 6 Standard motion parameters - - 2 contrasts - one on each stimulus EV + - 2 contrasts - one on each stimulus EV `1stlevel_3.feat` - 45 time points @@ -99,7 +99,7 @@ with the following commands: - Shape (10, 10, 10) - Three subjects - Two copes - - One main EV - group average + - One main EV - group average """ @@ -145,7 +145,7 @@ for i, featdir in enumerate(featdirs): def test_FEATFSFDesign(): for featdir in TEST_ANALYSES.keys(): - + nev = TEST_ANALYSES[featdir]['nevs'] shape = TEST_ANALYSES[featdir]['shape'] desshape = TEST_ANALYSES[featdir]['designShape'] @@ -306,16 +306,14 @@ def test_getFirstLevelEVs_3(): (featdesign.MotionParameterEV, {'index' : 29, 'motionIndex' : 23}), (featdesign.ConfoundEV, {'index' : 30, 'confIndex' : 0}), (featdesign.ConfoundEV, {'index' : 31, 'confIndex' : 1})] - + evs = featdesign.getFirstLevelEVs(featdir, settings, matrix) - + assert len(evs) == 32 for i, (evtype, atts) in enumerate(expected): - print(i, evs[i]) - assert isinstance(evs[i], evtype) for k, v in atts.items(): assert getattr(evs[i], k) == v @@ -330,17 +328,15 @@ def test_getFirstLevelEVs_realdata(): (featdesign.TemporalDerivativeEV, {'index' : 3})] evs = featdesign.getFirstLevelEVs(featdir, settings, matrix) - + assert len(evs) == 4 for i, (evtype, atts) in enumerate(expected): - print(i, evs[i]) - assert isinstance(evs[i], evtype) for k, v in atts.items(): assert getattr(evs[i], k) == v - + def test_getHigherLevelEVs_1(): @@ -355,8 +351,8 @@ def test_getHigherLevelEVs_1(): assert isinstance(evs[0], featdesign.NormalEV) assert evs[0].index == 0 assert evs[0].origIndex == 0 - - + + def test_getHigherLevelEVs_2(): @@ -386,9 +382,9 @@ def test_loadDesignMat(): assert mat.shape == shape nonfile = op.join(datadir, 'non-existent-file') - badfile = op.join(datadir, '1stlevel_1.feat', 'design.fsf') + badfile = op.join(datadir, '1stlevel_1.feat', 'design.fsf') with pytest.raises(Exception): featdesign.loadDesignMat(nonfile) with pytest.raises(Exception): - featdesign.loadDesignMat(badfile) + featdesign.loadDesignMat(badfile) diff --git a/tests/test_image_advanced.py b/tests/test_image_advanced.py index 58b60e2751fd4a60ff597629ef4be4e4f514521b..a8401b92d4239f782ebd0959f7b7ef4bcd04f126 100644 --- a/tests/test_image_advanced.py +++ b/tests/test_image_advanced.py @@ -16,18 +16,18 @@ import fsl.data.image as fslimage def test_image_indexed_threaded( ): _test_image_indexed(True) -def test_image_indexed_unthreaded(): _test_image_indexed(False) +def test_image_indexed_unthreaded(): _test_image_indexed(False) def _test_image_indexed(threaded): with tests.testdir() as testdir: - + filename = op.join(testdir, 'image.nii.gz') # Data range grows with volume data = np.zeros((100, 100, 100, 50)) for vol in range(data.shape[-1]): data[..., vol] = vol - + fslimage.Image(data).save(filename) img = fslimage.Image( @@ -45,7 +45,7 @@ def _test_image_indexed(threaded): if threaded: img.getImageWrapper().getTaskThread().waitUntilIdle() - + assert img.dataRange == (0, vol) end1 = time.time() @@ -66,7 +66,7 @@ def test_image_indexed_read4D_unthreaded(seed): def _test_image_indexed_read4D(threaded): with tests.testdir() as testdir: - + filename = op.join(testdir, 'image.nii.gz') # Data range grows with volume @@ -74,7 +74,7 @@ def _test_image_indexed_read4D(threaded): data = np.zeros((50, 50, 50, nvols)) for vol in range(nvols): data[..., vol] = vol - + fslimage.Image(data).save(filename) img = fslimage.Image( @@ -82,15 +82,15 @@ def _test_image_indexed_read4D(threaded): loadData=False, calcRange=False, indexed=True, - threaded=threaded) + threaded=threaded) # Test reading slice through # 4D (e.g. voxel time course) - # + # # n.b. This is SUPER SLOW!! voxels = tests.random_voxels((50, 50, 50), 5) for i, xyz in enumerate(voxels): - + x, y, z = [int(v) for v in xyz] data = img[x, y, z, :] @@ -98,21 +98,21 @@ def _test_image_indexed_read4D(threaded): img.getImageWrapper().getTaskThread().waitUntilIdle() assert np.all(data == np.arange(nvols)) - + def test_image_indexed_save_threaded( ): _test_image_indexed_save(True) -def test_image_indexed_save_unthreaded(): _test_image_indexed_save(False) +def test_image_indexed_save_unthreaded(): _test_image_indexed_save(False) def _test_image_indexed_save(threaded): with tests.testdir() as testdir: - + filename = op.join(testdir, 'image.nii.gz') # Data range grows with volume data = np.zeros((100, 100, 100, 50)) for vol in range(data.shape[-1]): data[..., vol] = vol - + fslimage.Image(data).save(filename) img = fslimage.Image( @@ -149,7 +149,7 @@ def _test_image_indexed_save(threaded): # get rebuilt to this point img[..., 0] img[..., 40] - + if threaded: img.getImageWrapper().getTaskThread().waitUntilIdle() @@ -162,8 +162,8 @@ def _test_image_indexed_save(threaded): img.getImageWrapper().getTaskThread().waitUntilIdle() # make sure we got the modified data - assert img.dataRange == (0, 49) - + assert img.dataRange == (0, 49) + # Finally, reload, and verify the change img = fslimage.Image(filename) @@ -229,8 +229,8 @@ def _test_image_calcRange(threaded): img.calcRange(np.prod(img.shape) * img.dtype.itemsize + 1) if threaded: img.getImageWrapper().getTaskThread().waitUntilIdle() - assert img.dataRange == (0, 10) - + assert img.dataRange == (0, 10) + # Check that calcRange(smallnum) will # calculate the range of the first slice img = fslimage.Image(data, **kwargs) @@ -252,15 +252,15 @@ def _test_image_calcRange(threaded): if threaded: img.getImageWrapper().getTaskThread().waitUntilIdle() assert img.dataRange == (0, 10) - + # Check that calcRange(numBiggerThanImage) # will calculate the full range img = fslimage.Image(data, **kwargs) img.calcRange(np.prod(img.shape) * img.dtype.itemsize + 1) if threaded: img.getImageWrapper().getTaskThread().waitUntilIdle() - assert img.dataRange == (0, 10) - + assert img.dataRange == (0, 10) + # Check that calcRange(smallnum) will # calculate the range of the first volume img = fslimage.Image(data, **kwargs) diff --git a/tests/test_immv_imcp.py b/tests/test_immv_imcp.py index 6b6f030fb322f70ed825db23a6e3790cf9811741..37b8639d36ec8436fc257762efe85aa690d2f4fd 100644 --- a/tests/test_immv_imcp.py +++ b/tests/test_immv_imcp.py @@ -12,7 +12,7 @@ from __future__ import print_function import os import os.path as op import shutil -import subprocess as sp +import subprocess as sp import tempfile import logging @@ -35,6 +35,12 @@ from . import looks_like_image from . import cleardir +real_print = print + +def print(*args, **kwargs): + pass + + def makeImage(filename): return hash(make_random_image(filename).get_data().tobytes()) @@ -106,18 +112,18 @@ def checkFilesToExpect(files, outdir, outputType, datahashes): def test_imcp_script_shouldPass(move=False): - + # The imcp/immv scripts should honour the - # FSLOUTPUTTYPE env var. If it is unset + # FSLOUTPUTTYPE env var. If it is unset # or invalid - '' in this case), they # should produce .nii.gz outputTypes = ['NIFTI', 'NIFTI_PAIR', 'NIFTI_GZ', ''] - + # Test tuples have the following structure (each # item is a string which will be split on spaces): - # + # # (files_to_create, imcp_args, files_to_expect) # # The files_to_expect is a list of @@ -134,26 +140,26 @@ def test_imcp_script_shouldPass(move=False): ('a.nii', 'a b.nii', 'b'), ('a.nii', 'a.nii b.nii', 'b'), ('a.nii', 'a .', 'a'), - ('a.nii', 'a.nii .', 'a'), + ('a.nii', 'a.nii .', 'a'), ('a.nii', 'a b.hdr', 'b'), ('a.nii', 'a b.img', 'b'), ('a.nii', 'a b.nii.gz', 'b'), ('a.nii', 'a.nii b.hdr', 'b'), ('a.nii', 'a.nii b.img', 'b'), - ('a.nii', 'a.nii b.nii.gz', 'b'), - + ('a.nii', 'a.nii b.nii.gz', 'b'), + ('a.nii.gz', 'a b', 'b'), ('a.nii.gz', 'a.nii.gz b', 'b'), ('a.nii.gz', 'a b.nii.gz', 'b'), ('a.nii.gz', 'a.nii.gz b.nii.gz', 'b'), ('a.nii.gz', 'a .', 'a'), - ('a.nii.gz', 'a.nii.gz .', 'a'), + ('a.nii.gz', 'a.nii.gz .', 'a'), ('a.nii.gz', 'a b.hdr', 'b'), ('a.nii.gz', 'a b.img', 'b'), ('a.nii.gz', 'a b.nii', 'b'), ('a.nii.gz', 'a.nii.gz b.hdr', 'b'), ('a.nii.gz', 'a.nii.gz b.img', 'b'), - ('a.nii.gz', 'a.nii.gz b.nii', 'b'), + ('a.nii.gz', 'a.nii.gz b.nii', 'b'), ('a.img', 'a b', 'b'), ('a.img', 'a b.img', 'b'), @@ -166,8 +172,8 @@ def test_imcp_script_shouldPass(move=False): ('a.img', 'a.hdr b', 'b'), ('a.img', 'a.hdr b.img', 'b'), ('a.img', 'a.hdr b.hdr', 'b'), - ('a.img', 'a.hdr .', 'a'), - + ('a.img', 'a.hdr .', 'a'), + ('a.img', 'a b.nii', 'b'), ('a.img', 'a b.nii.gz', 'b'), ('a.img', 'a .', 'a'), @@ -191,7 +197,7 @@ def test_imcp_script_shouldPass(move=False): ('a.img b.img', 'a.img b.hdr .', 'a b'), ('a.img b.img', 'a.hdr b .', 'a b'), ('a.img b.img', 'a.hdr b.img .', 'a b'), - ('a.img b.img', 'a.hdr b.hdr .', 'a b'), + ('a.img b.img', 'a.hdr b.hdr .', 'a b'), ('a.nii.gz b.nii.gz', 'a b .', 'a b'), ('a.nii.gz b.nii.gz', 'a b.nii.gz .', 'a b'), @@ -226,7 +232,7 @@ def test_imcp_script_shouldPass(move=False): ('a.img', 'a.hdr a .', 'a'), ('a.img', 'a.hdr a.img .', 'a'), ('a.img', 'a.hdr a.hdr .', 'a'), - + ('a.img b.img', 'a a b b .', 'a b'), ('a.img b.img', 'a a b b.img .', 'a b'), ('a.img b.img', 'a a b b.hdr .', 'a b'), @@ -262,7 +268,7 @@ def test_imcp_script_shouldPass(move=False): ('a.img b.img', 'a.img a.hdr b b.hdr .', 'a b'), ('a.img b.img', 'a.img a.hdr b.img b .', 'a b'), ('a.img b.img', 'a.img a.hdr b.img b.img .', 'a b'), - ('a.img b.img', 'a.img a.hdr b.img b.hdr .', 'a b'), + ('a.img b.img', 'a.img a.hdr b.img b.hdr .', 'a b'), ('a.img b.img', 'a.hdr a b b .', 'a b'), ('a.img b.img', 'a.hdr a b b.img .', 'a b'), ('a.img b.img', 'a.hdr a b b.hdr .', 'a b'), @@ -297,18 +303,18 @@ def test_imcp_script_shouldPass(move=False): ('a.img a.nii.gz a.nii ', 'a.img a.nii.gz a.nii .', 'a'), ('a.img a.nii a.nii.gz', 'a.img a.nii a.nii.gz .', 'a'), ('a.nii.gz a.img a.nii ', 'a.nii.gz a.img a.nii .', 'a'), - ('a.nii.gz a.nii a.img ', 'a.nii.gz a.nii a.img .', 'a'), + ('a.nii.gz a.nii a.img ', 'a.nii.gz a.nii a.img .', 'a'), ] indir = tempfile.mkdtemp() outdir = tempfile.mkdtemp() - + try: for outputType in outputTypes: - + os.environ['FSLOUTPUTTYPE'] = outputType - + for files_to_create, imcp_args, files_to_expect in tests: imageHashes = [] @@ -333,7 +339,7 @@ def test_imcp_script_shouldPass(move=False): else: imcp_script.main(imcp_args) print('indir after: ', os.listdir(indir)) - print('outdir after: ', os.listdir(outdir)) + print('outdir after: ', os.listdir(outdir)) checkFilesToExpect( files_to_expect, outdir, outputType, imageHashes) @@ -346,11 +352,11 @@ def test_imcp_script_shouldPass(move=False): cleardir(indir) cleardir(outdir) - + finally: shutil.rmtree(indir) shutil.rmtree(outdir) - + def test_imcp_script_shouldFail(move=False): @@ -361,11 +367,11 @@ def test_imcp_script_shouldFail(move=False): # - ambiguous inputs # - input is incomplete pair (e.g. .hdr # without corresponding .img) - + # a.img # a.hdr # a.nii - # + # # FAIL: imcp a dest # (files_to_create, imcp_args[, preproc]) @@ -397,7 +403,7 @@ def test_imcp_script_shouldFail(move=False): ('a.img', 'a.hdr b', 'rm indir/a.hdr'), ('a.img', 'a b', 'rm indir/a.img'), ('a.img', 'a.img b', 'rm indir/a.img'), - ('a.img', 'a.hdr b', 'rm indir/a.img'), + ('a.img', 'a.hdr b', 'rm indir/a.img'), ] if move: @@ -410,13 +416,13 @@ def test_imcp_script_shouldFail(move=False): indir = tempfile.mkdtemp() outdir = tempfile.mkdtemp() - + try: for test in tests: files_to_create = test[0] imcp_args = test[1] - + if len(test) == 3: preproc = test[2] else: preproc = None @@ -455,9 +461,9 @@ def test_imcp_script_shouldFail(move=False): def test_immv_script_shouldPass(): test_imcp_script_shouldPass(move=True) - + def test_immv_script_shouldFail(): - test_imcp_script_shouldFail(move=True) + test_imcp_script_shouldFail(move=True) def test_imcp_shouldPass(move=False): @@ -473,7 +479,7 @@ def test_imcp_shouldPass(move=False): # imgp src.img dest -> dest.nii.gz # imgp src.img dest.nii -> dest.nii.gz - # + # # (files_to_create, # [( imcp_args, files_which_should_exist), # ... @@ -524,9 +530,9 @@ def test_imcp_shouldPass(move=False): ('file.nii file.nii', 'file.nii'), # TODO both img/nii files - ]), - - + ]), + + ('file.nii', [ ('file file', 'file.nii'), ('file file.nii', 'file.nii'), @@ -545,7 +551,7 @@ def test_imcp_shouldPass(move=False): ('file.nii.gz .', 'file.nii.gz'), ]), - + ('file.nii file.blob', [ ('file file', 'file.nii'), ('file file.nii', 'file.nii'), @@ -553,8 +559,8 @@ def test_imcp_shouldPass(move=False): ('file.nii file', 'file.nii'), ('file.nii file.nii', 'file.nii'), ('file.nii .', 'file.nii'), - ]), - + ]), + ('file.nii file.nii.gz', [ ('file.nii file', 'file.nii'), @@ -577,11 +583,11 @@ def test_imcp_shouldPass(move=False): ('001.img 002.img 003.img', [ - + ('001 002 003 .', '001.img 002.img 003.img'), ('001.img 002.img 003.img .', '001.img 002.img 003.img'), ('001.hdr 002.hdr 003.hdr .', '001.img 002.img 003.img'), - + ('001.img 002 003 .', '001.img 002.img 003.img'), ('001.hdr 002 003 .', '001.img 002.img 003.img'), @@ -591,13 +597,13 @@ def test_imcp_shouldPass(move=False): ('001 003 .', '001.img 003.img'), ('001.img 003.img .', '001.img 003.img'), ('001.hdr 003.hdr .', '001.img 003.img'), - + ('001.img 003 .', '001.img 003.img'), ('001.hdr 003 .', '001.img 003.img'), ('001.img 003.img .', '001.img 003.img'), - ('001.hdr 003.hdr .', '001.img 003.img'), - ]), + ('001.hdr 003.hdr .', '001.img 003.img'), + ]), ] @@ -609,7 +615,7 @@ def test_imcp_shouldPass(move=False): for files_to_create, tests in shouldPass: files_to_create = files_to_create.split() - + for imcp_args, should_exist in tests: should_exist = should_exist.split() @@ -636,7 +642,7 @@ def test_imcp_shouldPass(move=False): print(' src: {}'.format(src)) src = op.join(indir, src) - + if move: imcp.immv(src, op.join(outdir, imcp_dest), overwrite=True) else: imcp.imcp(src, op.join(outdir, imcp_dest), overwrite=True) @@ -655,15 +661,15 @@ def test_imcp_shouldPass(move=False): if move: for f in should_exist: assert not op.exists(op.join(indir, f)) - + for f in os.listdir(indir): try: os.remove(op.join(indir, f)) except: pass - + for f in os.listdir(outdir): os.remove(op.join(outdir, f)) - - + + finally: shutil.rmtree(indir) shutil.rmtree(outdir) diff --git a/tests/test_melodicimage.py b/tests/test_melodicimage.py index 7122d6f2dd8482d8a42a13ca366b628b20ddfb62..95853b759dc91cea992e299940a5c9d3e41c550f 100644 --- a/tests/test_melodicimage.py +++ b/tests/test_melodicimage.py @@ -5,6 +5,7 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # +from __future__ import division import os.path as op @@ -52,8 +53,8 @@ def _create_dummy_melodic_analysis(basedir, # rows=frequencies # columns=ICs if timepoints % 2: nfreqs = int(np.ceil(timepoints / 2.0)) - else: nfreqs = timepoints / 2 - + else: nfreqs = timepoints // 2 + ftmixdata = np.zeros((nfreqs, nics)) for ic in range(nics): @@ -63,7 +64,7 @@ def _create_dummy_melodic_analysis(basedir, np.savetxt(mixfile, mixdata) np.savetxt(ftmixfile, ftmixdata) fslimage.Image(icimg).save(icfile) - + if with_data: dataimg = np.zeros(list(shape4D[:3]) + [timepoints]) for t in range(timepoints): @@ -71,7 +72,7 @@ def _create_dummy_melodic_analysis(basedir, hdr = nib.nifti1.Nifti1Header() hdr['pixdim'][4] = tr - + img = fslimage.Image(dataimg, header=hdr, xform=np.eye(4)) img.save(datafile) @@ -105,8 +106,8 @@ def test_MelodicImage_create(): meldir = _create_dummy_melodic_analysis(testdir) icfile = op.join(meldir, 'melodic_IC.nii.gz') icfilenosuf = op.join(meldir, 'melodic_IC') - - # Should be able to specify the + + # Should be able to specify the # melodic dir, or the IC image meli.MelodicImage(meldir) meli.MelodicImage(icfile) @@ -129,7 +130,7 @@ def test_MelodicImage_atts(): assert img.getTopLevelAnalysisDir() == mela.getTopLevelAnalysisDir(meldir) assert img.getDataFile() == mela.getDataFile(meldir) assert img.getMeanFile() == mela.getMeanFile(meldir) - + def test_MelodicImage_componentData(): with tests.testdir() as testdir: @@ -153,7 +154,7 @@ def test_MelodicImage_tr(): img = meli.MelodicImage(meldir) assert img.tr == 1 - + # Otherwise, it should be set to the datafile tr with tests.testdir() as testdir: meldir = _create_dummy_melodic_analysis(testdir, tr=5) @@ -166,7 +167,7 @@ def test_MelodicImage_tr(): cbCalled = [False] def trChanged(*a): cbCalled[0] = True - + meldir = _create_dummy_melodic_analysis(testdir, with_data=False) img = meli.MelodicImage(meldir) diff --git a/tests/test_transform.py b/tests/test_transform.py index ddf6fc6c7a74929accd1ad8977655edb89f0b365..e8b2d342210ce3be368b6b5bdc1af0b114330d2e 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -5,11 +5,16 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # + +from __future__ import division + import glob import os.path as op import itertools as it import numpy as np +import six + import pytest import fsl.utils.transform as transform @@ -22,9 +27,20 @@ datadir = op.join(op.dirname(__file__), 'testdata') def readlines(filename): with open(filename, 'rt') as f: lines = f.readlines() - lines = [l.strip() for l in lines] - lines = [l for l in lines if not l.startswith('#')] - lines = [l for l in lines if l != ''] + lines = [l.strip() for l in lines] + lines = [l for l in lines if not l.startswith('#')] + lines = [l for l in lines if l != ''] + + # numpy.genfromtxt is busted in python 3. + # Pass it [str, str, ...], and it complains: + # + # TypeError: must be str or None, not bytes + # + # Pass it [bytes, bytes, ...], and it works + # fine. + if six.PY3: + lines = [l.encode('ascii') for l in lines] + return lines @@ -33,7 +49,7 @@ def test_invert(): testfile = op.join(datadir, 'test_transform_test_invert.txt') testdata = np.loadtxt(testfile) - nmatrices = testdata.shape[0] / 4 + nmatrices = testdata.shape[0] // 4 for i in range(nmatrices): @@ -45,19 +61,18 @@ def test_invert(): def test_concat(): - + testfile = op.join(datadir, 'test_transform_test_concat.txt') lines = readlines(testfile) - ntests = len(lines) / 4 + ntests = len(lines) // 4 tests = [] for i in range(ntests): ilines = lines[i * 4:i * 4 + 4] - data = np.genfromtxt(ilines) - ninputs = data.shape[1] / 4 - 1 + ninputs = data.shape[1] // 4 - 1 inputs = [] @@ -79,12 +94,12 @@ def test_scaleOffsetXform(): testfile = op.join(datadir, 'test_transform_test_scaleoffsetxform.txt') lines = readlines(testfile) - ntests = len(lines) / 5 + ntests = len(lines) // 5 for i in range(ntests): - + lineoff = i * 5 - scales, offsets = lines[lineoff].split(',') + scales, offsets = lines[lineoff].decode('ascii').split(',') scales = [float(s) for s in scales .split()] offsets = [float(o) for o in offsets.split()] @@ -102,13 +117,13 @@ def test_compose_and_decompose(): testfile = op.join(datadir, 'test_transform_test_compose.txt') lines = readlines(testfile) - ntests = len(lines) / 4 + ntests = len(lines) // 4 for i in range(ntests): xform = lines[i * 4: i * 4 + 4] xform = np.genfromtxt(xform) - + scales, offsets, rotations = transform.decompose(xform) result = transform.compose(scales, offsets, rotations) @@ -127,11 +142,11 @@ def test_compose_and_decompose(): def test_axisBounds(): testfile = op.join(datadir, 'test_transform_test_axisBounds.txt') lines = readlines(testfile) - ntests = len(lines) / 6 + ntests = len(lines) // 6 def readTest(testnum): tlines = lines[testnum * 6: testnum * 6 + 6] - params = [p.strip() for p in tlines[0].split(',')] + params = [p.strip() for p in tlines[0].decode('ascii').split(',')] shape = [int(s) for s in params[0].split()] origin = params[1] boundary = None if params[2] == 'None' else params[2] @@ -140,7 +155,7 @@ def test_axisBounds(): expected = (expected[:3], expected[3:]) return shape, origin, boundary, xform, expected - + allAxes = list(it.chain( range(0, 1, 2), it.permutations((0, 1, 2), 1), @@ -151,11 +166,6 @@ def test_axisBounds(): shape, origin, boundary, xform, expected = readTest(i) - print('Test {}'.format(i)) - print(' shape {}'.format(shape)) - print(' origin {}'.format(origin)) - print(' boundary {}'.format(boundary)) - for axes in allAxes: result = transform.axisBounds(shape, xform, @@ -168,7 +178,7 @@ def test_axisBounds(): assert np.all(np.isclose(exp, result)) - # Do some parameter checks on + # Do some parameter checks on # the first test in the file # which has origin == centre for i in range(ntests): @@ -186,7 +196,7 @@ def test_axisBounds(): with pytest.raises(ValueError): transform.axisBounds(shape, xform, origin='Blag', boundary=boundary) with pytest.raises(ValueError): - transform.axisBounds(shape, xform, origin=origin, boundary='Blufu') + transform.axisBounds(shape, xform, origin=origin, boundary='Blufu') def test_transform(): @@ -214,19 +224,19 @@ def test_transform(): it.permutations((0, 1, 2), 1), it.permutations((0, 1, 2), 2), it.permutations((0, 1, 2), 3))) - + for i, testfile in enumerate(testfiles): - + lines = readlines(testfile) xform = np.genfromtxt(lines[:4]) expected = np.genfromtxt(lines[ 4:]) result = transform.transform(testcoords, xform) - + assert np.all(np.isclose(expected, result)) if not is_orthogonal(xform): continue - + for axes in allAxes: atestcoords = testcoords[:, axes] aexpected = expected[ :, axes] @@ -241,26 +251,26 @@ def test_transform(): coords = badcoords[:, :3] with pytest.raises(IndexError): - transform.transform(coords, badxform) + transform.transform(coords, badxform) with pytest.raises(ValueError): transform.transform(badcoords, xform) - + with pytest.raises(ValueError): transform.transform(badcoords.reshape(5, 2, 4), xform) with pytest.raises(ValueError): - transform.transform(badcoords.reshape(5, 2, 4), xform, axes=1) + transform.transform(badcoords.reshape(5, 2, 4), xform, axes=1) with pytest.raises(ValueError): transform.transform(badcoords[:, (1, 2, 3)], xform, axes=[1, 2]) def test_flirtMatrixToSform(): - + testfile = op.join(datadir, 'test_transform_test_flirtMatrixToSform.txt') lines = readlines(testfile) - ntests = len(lines) / 18 + ntests = len(lines) // 18 for i in range(ntests): tlines = lines[i * 18: i * 18 + 18] @@ -282,7 +292,7 @@ def test_flirtMatrixToSform(): def test_sformToFlirtMatrix(): testfile = op.join(datadir, 'test_transform_test_flirtMatrixToSform.txt') lines = readlines(testfile) - ntests = len(lines) / 18 + ntests = len(lines) // 18 for i in range(ntests): tlines = lines[i * 18: i * 18 + 18] diff --git a/tests/test_vest.py b/tests/test_vest.py index bdd60133ba2fee797bf32bffce3f912a4db9e528..84638a3ce0227fb00b9dbec438cc112cee05e47e 100644 --- a/tests/test_vest.py +++ b/tests/test_vest.py @@ -19,11 +19,11 @@ import fsl.data.vest as vest testfile1 = """%!VEST-LUT %%BeginInstance << -/SavedInstanceClassName /ClassLUT -/PseudoColorMinimum 0.00 -/PseudoColorMaximum 1.00 -/PseudoColorMinControl /Low -/PseudoColorMaxControl /High +/SavedInstanceClassName /ClassLUT +/PseudoColorMinimum 0.00 +/PseudoColorMaximum 1.00 +/PseudoColorMinControl /Low +/PseudoColorMaxControl /High /PseudoColormap [ <-color{0.00,0.00,0.00}-> <-color{0.01,0.01,0.01}-> @@ -47,11 +47,11 @@ testfile1Colours = np.array([ testfile2 = """%!VEST-LUT %%BeginInstance << -/SavedInstanceClassName /ClassLUT -/PseudoColorMinimum 0.00 -/PseudoColorMaximum 1.00 -/PseudoColorMinControl /Low -/PseudoColorMaxControl /High +/SavedInstanceClassName /ClassLUT +/PseudoColorMinimum 0.00 +/PseudoColorMaximum 1.00 +/PseudoColorMinControl /Low +/PseudoColorMaxControl /High /PseudoColormap [ <-color{0.0,0.0,0.0}-> ] @@ -78,16 +78,16 @@ testfile3 = """%!VEST-LUT testfile3Colours = np.array([ [0.0, 0.0, 0.0], [0.5, 0.2, 0.6]]) - + testfile4 = """%!VEST-LUT %%BeginInstance << -/SavedInstanceClassName /ClassLUT -/PseudoColorMinimum 0.00 -/PseudoColorMaximum 1.00 -/PseudoColorMinControl /Low -/PseudoColorMaxControl /High +/SavedInstanceClassName /ClassLUT +/PseudoColorMinimum 0.00 +/PseudoColorMaximum 1.00 +/PseudoColorMinControl /Low +/PseudoColorMaxControl /High /PseudoColormap [ <-color{0.0,1.0,5.0}-> <-color{1.0,2.0,4.0}-> @@ -119,7 +119,7 @@ def _createFiles(testdir): for name, text in zip(names, texts): filename = op.join(testdir, '{}.txt'.format(name)) with open(filename, 'wt') as f: - f.write(text) + f.write(text) @@ -142,7 +142,7 @@ def test_looksLikeVestLutFile(): def test_loadVestLutFile(): - + testdir = tempfile.mkdtemp() testfiles = [ op.join(testdir, 'testfile1.txt'),