Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase Code Coverage #80

Merged
merged 18 commits into from
Jan 27, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nrrd/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ def parse_number_list(x, dtype=None):
if dtype is None:
number_list_trunc = number_list.astype(int)

# If there is no difference between the truncated number list and the number list, then that means that the
# number list was all integers and we can just return that
if np.all((number_list - number_list_trunc) == 0):
number_list = number_list_trunc
elif dtype == int:
Expand Down
10 changes: 6 additions & 4 deletions nrrd/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ def _get_field_type(field, custom_field_map):
return 'double list'
elif field in ['kinds', 'labels', 'units', 'space units', 'centerings']:
return 'string list'
elif field in []:
return 'int vector'
# No int vector fields as of now
# elif field in []:
# return 'int vector'
elif field in ['space origin']:
return 'double vector'
elif field in ['measurement frame']:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the place where Isaiah and I corrected 'double matrix'? Not sure if you are working on a different branch.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is the place. I just checked this PR and it's up to date. The measurement frame field type is 'double matrix' as corrected.

Expand Down Expand Up @@ -185,7 +186,7 @@ def _validate_magic_line(line):
raise NRRDError('Unsupported NRRD file version (version: %i). This library only supports v%i and below.'
% (version, 5))
except ValueError:
raise NRRDError('Invalid NRRD magic line: %s' % (line,))
raise NRRDError('Invalid NRRD magic line: %s' % line)

return len(line)

Expand Down Expand Up @@ -331,7 +332,8 @@ def read_data(header, fh=None, filename=None):
raise NRRDError('Header is missing required field: "%s".' % field)

if header['dimension'] != len(header['sizes']):
raise NRRDError('Number of elements in sizes does not match dimension')
raise NRRDError('Number of elements in sizes does not match dimension. Dimension: %i, len(sizes): %i' % (
header['dimension'], len(header['sizes'])))

# Determine the data type from the header
dtype = _determine_datatype(header)
Expand Down
9 changes: 8 additions & 1 deletion nrrd/tests/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def test_parse_optional_matrix(self):
with self.assertRaisesRegex(nrrd.NRRDError, 'Matrix should have same number of elements in each row'):
nrrd.parse_optional_matrix('none (1,0,0,0) (0,1,0) (0,0,1)')

def test_parse_number_list(self):
def test_parse_number_list_int(self):
self.assert_equal_with_datatype(nrrd.parse_number_list('1 2 3 4'), [1, 2, 3, 4])
self.assert_equal_with_datatype(nrrd.parse_number_list('1 2 3 4', dtype=float), [1., 2., 3., 4.])
self.assert_equal_with_datatype(nrrd.parse_number_list('1 2 3 4', dtype=int), [1, 2, 3, 4])
Expand All @@ -146,6 +146,13 @@ def test_parse_number_list(self):
with self.assertRaisesRegex(nrrd.NRRDError, 'dtype should be None for automatic type detection, float or int'):
nrrd.parse_number_list('1 2 3 4', dtype=np.uint8)

def test_parse_number_list_float(self):
self.assert_equal_with_datatype(nrrd.parse_number_list('1.5 2 3 4'), [1.5, 2., 3., 4.])
self.assert_equal_with_datatype(nrrd.parse_number_list('1 2 3 4.9', dtype=float), [1., 2., 3., 4.9])
self.assert_equal_with_datatype(nrrd.parse_number_list('1 2.9 3 4', dtype=int), [1, 2, 3, 4])

self.assert_equal_with_datatype(nrrd.parse_number_list('1.3'), [1.3])

def test_parse_number_auto_dtype(self):
self.assertEqual(nrrd.parse_number_auto_dtype('25'), 25)
self.assertEqual(nrrd.parse_number_auto_dtype('25.125'), 25.125)
Expand Down
132 changes: 115 additions & 17 deletions nrrd/tests/test_reading.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from nrrd.tests.util import *
import nrrd


class TestReadingFunctions(unittest.TestCase):
def setUp(self):
self.expected_header = {u'dimension': 3,
Expand Down Expand Up @@ -67,17 +68,17 @@ def test_read_detached_header_and_data(self):

# Test that the data read is able to be edited
self.assertTrue(data.flags['WRITEABLE'])

def test_read_detached_header_and_data_with_byteskip_minus1(self):
expected_header = self.expected_header
expected_header[u'data file'] = os.path.basename(RAW_DATA_FILE_PATH)
expected_header[u'byte skip'] = -1

data, header = nrrd.read(RAW_BYTESKIP_NHDR_FILE_PATH)

np.testing.assert_equal(self.expected_header, header)
np.testing.assert_equal(data, self.expected_data)

# Test that the data read is able to be edited
self.assertTrue(data.flags['WRITEABLE'])

Expand All @@ -89,23 +90,21 @@ def test_read_detached_header_and_nifti_data_with_byteskip_minus1(self):
expected_header[u'data file'] = 'BallBinary30x30x30.nii.gz'

data, header = nrrd.read(GZ_BYTESKIP_NIFTI_NHDR_FILE_PATH)

np.testing.assert_equal(self.expected_header, header)
np.testing.assert_equal(data, self.expected_data)

# Test that the data read is able to be edited
self.assertTrue(data.flags['WRITEABLE'])

def test_read_detached_header_and_nifti_data(self):

with self.assertRaisesRegex(nrrd.NRRDError, 'Size of the data does not equal '
+ 'the product of all the dimensions: 27000-27176=-176'):
+ 'the product of all the dimensions: 27000-27176=-176'):
nrrd.read(GZ_NIFTI_NHDR_FILE_PATH)

def test_read_detached_header_and_data_with_byteskip_minus5(self):

with self.assertRaisesRegex(nrrd.NRRDError, 'Invalid byteskip, allowed values '
+'are greater than or equal to -1'):
+ 'are greater than or equal to -1'):
nrrd.read(RAW_INVALID_BYTESKIP_NHDR_FILE_PATH)

def test_read_header_and_gz_compressed_data(self):
Expand All @@ -119,7 +118,7 @@ def test_read_header_and_gz_compressed_data(self):

# Test that the data read is able to be edited
self.assertTrue(data.flags['WRITEABLE'])

def test_read_header_and_gz_compressed_data_with_byteskip_minus1(self):
expected_header = self.expected_header
expected_header[u'encoding'] = 'gzip'
Expand All @@ -130,7 +129,7 @@ def test_read_header_and_gz_compressed_data_with_byteskip_minus1(self):

np.testing.assert_equal(self.expected_header, header)
np.testing.assert_equal(data, self.expected_data)

# Test that the data read is able to be edited
self.assertTrue(data.flags['WRITEABLE'])

Expand Down Expand Up @@ -160,8 +159,8 @@ def test_read_header_and_gz_compressed_data_with_lineskip3(self):
self.assertTrue(data.flags['WRITEABLE'])

def test_read_raw_header(self):
expected_header = {u'type': 'float', u'dimension': 3}
header = nrrd.read_header(('NRRD0005', 'type: float', 'dimension: 3'))
expected_header = {u'type': 'float', u'dimension': 3, u'min': 0, u'max': 35.4}
header = nrrd.read_header(('NRRD0005', 'type: float', 'dimension: 3', 'min: 0', 'max: 35.4'))
self.assertEqual(expected_header, header)

expected_header = {u'my extra info': u'my : colon-separated : values'}
Expand Down Expand Up @@ -230,11 +229,10 @@ def test_read_simple_4d_nrrd(self):
[np.NaN, np.NaN, np.NaN]]),
'endian': 'little',
'encoding': 'raw',
'measurement frame': np.array([[1.0001, 0., 0.],
[0., 1.0000000006, 0.],
'measurement frame': np.array([[1.0001, 0., 0.],
[0., 1.0000000006, 0.],
[0., 0., 1.000000000000009]])}


data, header = nrrd.read(RAW_4D_NRRD_FILE_PATH)

np.testing.assert_equal(header, expected_header)
Expand Down Expand Up @@ -299,6 +297,106 @@ def test_custom_fields_with_field_map(self):

np.testing.assert_equal(header, expected_header)

def test_invalid_custom_field(self):
custom_field_map = {'int': 'fake'}

with self.assertRaisesRegex(nrrd.NRRDError, 'Invalid field type given: fake'):
nrrd.read_header(ASCII_1D_CUSTOM_FIELDS_FILE_PATH, custom_field_map)

def test_invalid_magic_line(self):
with self.assertRaisesRegex(nrrd.NRRDError, 'Invalid NRRD magic line. Is this an NRRD file?'):
nrrd.read_header(('invalid magic line', 'my extra info:=my : colon-separated : values'))

def test_invalid_magic_line2(self):
with self.assertRaisesRegex(nrrd.NRRDError, 'Unsupported NRRD file version \\(version: 2000\\). This library '
'only supports v5 and below.'):
nrrd.read_header(('NRRD2000', 'my extra info:=my : colon-separated : values'))

def test_invalid_magic_line3(self):
with self.assertRaisesRegex(nrrd.NRRDError, 'Invalid NRRD magic line: NRRDnono'):
nrrd.read_header(('NRRDnono', 'my extra info:=my : colon-separated : values'))

def test_missing_required_field(self):
with open(RAW_NRRD_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# Delete required field
del header['type']

with self.assertRaisesRegex(nrrd.NRRDError, 'Header is missing required field: "type".'):
nrrd.read_data(header, fh, RAW_NRRD_FILE_PATH)

def test_wrong_sizes(self):
with open(RAW_NRRD_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# Make the number of dimensions wrong
header['dimension'] = 2

with self.assertRaisesRegex(nrrd.NRRDError, 'Number of elements in sizes does not match dimension. '
'Dimension: 2, len\\(sizes\\): 3'):
nrrd.read_data(header, fh, RAW_NRRD_FILE_PATH)

def test_invalid_encoding(self):
with open(RAW_NRRD_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# Set the encoding to be incorrect
header['encoding'] = 'fake'

with self.assertRaisesRegex(nrrd.NRRDError, 'Unsupported encoding: "fake"'):
nrrd.read_data(header, fh, RAW_NRRD_FILE_PATH)

def test_detached_header_no_filename(self):
self.expected_header[u'data file'] = os.path.basename(RAW_DATA_FILE_PATH)

with open(RAW_NHDR_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# No filename is specified for read_data
with self.assertRaisesRegex(nrrd.NRRDError, 'Filename parameter must be specified when a relative data file'
' path is given'):
nrrd.read_data(header, fh)

def test_invalid_lineskip(self):
with open(RAW_NRRD_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# Set the line skip to be incorrect
header['line skip'] = -1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I added this one before, but thanks if I haven't.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you tested the byte skip and line skip for all encoding except raw. But that's one benefit of code coverage is for, telling us when something isn't tested.

with self.assertRaisesRegex(nrrd.NRRDError, 'Invalid lineskip, allowed values are greater than or equal to'
' 0'):
nrrd.read_data(header, fh, RAW_NRRD_FILE_PATH)

def test_missing_endianness(self):
with open(RAW_NRRD_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# Delete the endian field from header
# Since our data is short (itemsize = 2), we should receive an error
del header['endian']

with self.assertRaisesRegex(nrrd.NRRDError, 'Header is missing required field: "endian".'):
nrrd.read_data(header, fh, RAW_NRRD_FILE_PATH)

def test_big_endian(self):
with open(RAW_NRRD_FILE_PATH, 'rb') as fh:
header = nrrd.read_header(fh)
np.testing.assert_equal(self.expected_header, header)

# Set endianness to big to verify it is doing correctly
header['endian'] = 'big'

data = nrrd.read_data(header, fh, RAW_NRRD_FILE_PATH)
np.testing.assert_equal(data, self.expected_data.byteswap())


if __name__ == '__main__':
unittest.main()