-
Notifications
You must be signed in to change notification settings - Fork 86
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
Enhance unit test coverage #2031
base: dev
Are you sure you want to change the base?
Changes from all commits
7842987
da1cbce
a69307b
cf6ed3c
cfc36c0
8b0ae2a
31e30c0
1cc20d1
b84e8e0
3b89708
40afed3
41178ba
d3798fc
5c75be0
65aac18
2f2dde8
0b813e4
dff1ccd
c8878a0
1fed185
65147be
6c56743
e1231e6
e5e4254
461af3c
e14d1d9
ba90342
22b3025
f09ad42
eba14a8
2feb09f
a06e9d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -85,9 +85,11 @@ def __init__(self, **kwargs): | |||||
|
||||||
@property | ||||||
def data(self): | ||||||
"""The data managed by this object""" | ||||||
return self.__data | ||||||
|
||||||
def __len__(self): | ||||||
"""Size of the data. Same as len(self.data)""" | ||||||
return len(self.__data) | ||||||
|
||||||
def __getitem__(self, args): | ||||||
|
@@ -96,6 +98,14 @@ def __getitem__(self, args): | |||||
return self.data[args] | ||||||
|
||||||
def append(self, arg): | ||||||
""" | ||||||
Append a single element to the data | ||||||
|
||||||
Note: The arg to append should be 1 dimension less than the data. | ||||||
For example, if the data is a 2D array, arg should be a 1D array. | ||||||
Appending to scalar data is not supported. To append multiple | ||||||
elements, use extend. | ||||||
""" | ||||||
if isinstance(self.data, list): | ||||||
self.data.append(arg) | ||||||
elif isinstance(self.data, np.ndarray): | ||||||
|
@@ -105,6 +115,9 @@ def append(self, arg): | |||||
raise ValueError(msg) | ||||||
|
||||||
def extend(self, arg): | ||||||
""" | ||||||
Extend the data with multiple elements. | ||||||
""" | ||||||
if isinstance(self.data, list): | ||||||
self.data.extend(arg) | ||||||
elif isinstance(self.data, np.ndarray): | ||||||
|
@@ -122,15 +135,15 @@ class ScratchData(NWBData): | |||||
@docval({'name': 'name', 'type': str, 'doc': 'the name of this container'}, | ||||||
{'name': 'data', 'type': ('scalar_data', 'array_data', 'data', Data), 'doc': 'the source of the data'}, | ||||||
{'name': 'notes', 'type': str, | ||||||
'doc': 'notes about the data. This argument will be deprecated. Use description instead', 'default': ''}, | ||||||
'doc': 'notes about the data. This argument will be deprecated. Use description instead', 'default': None}, | ||||||
{'name': 'description', 'type': str, 'doc': 'notes about the data', 'default': None}) | ||||||
def __init__(self, **kwargs): | ||||||
notes, description = popargs('notes', 'description', kwargs) | ||||||
super().__init__(**kwargs) | ||||||
if notes != '': | ||||||
if notes is not None: | ||||||
warn('The `notes` argument of ScratchData.__init__ will be deprecated. Use description instead.', | ||||||
PendingDeprecationWarning) | ||||||
if notes != '' and description != '': | ||||||
if notes is not None and description is not None: | ||||||
raise ValueError('Cannot provide both notes and description to ScratchData.__init__. The description ' | ||||||
'argument is recommended.') | ||||||
description = notes | ||||||
|
@@ -141,12 +154,18 @@ def __init__(self, **kwargs): | |||||
|
||||||
@property | ||||||
def notes(self): | ||||||
""" | ||||||
Get the notes attribute. This will be deprecated in the future. | ||||||
""" | ||||||
warn('Use of ScratchData.notes will be deprecated. Use ScratchData.description instead.', | ||||||
PendingDeprecationWarning) | ||||||
return self.description | ||||||
|
||||||
@notes.setter | ||||||
def notes(self, value): | ||||||
""" | ||||||
Set the notes attribute. This will be deprecated in the future. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
""" | ||||||
warn('Use of ScratchData.notes will be deprecated. Use ScratchData.description instead.', | ||||||
PendingDeprecationWarning) | ||||||
self.description = value | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -338,7 +338,7 @@ def add_roi(self, **kwargs): | |||||
"""Add a Region Of Interest (ROI) data to this""" | ||||||
pixel_mask, voxel_mask, image_mask = popargs('pixel_mask', 'voxel_mask', 'image_mask', kwargs) | ||||||
if image_mask is None and pixel_mask is None and voxel_mask is None: | ||||||
raise ValueError("Must provide 'image_mask' and/or 'pixel_mask'") | ||||||
raise ValueError("Must provide at least on of 'image_mask', 'pixel_mask', or 'voxel_mask'") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
rkwargs = dict(kwargs) | ||||||
if image_mask is not None: | ||||||
rkwargs['image_mask'] = image_mask | ||||||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -8,6 +8,8 @@ | |||
from pynwb.base import TimeSeriesReference, TimeSeriesReferenceVectorData | ||||
from pynwb.testing import TestCase | ||||
|
||||
from hdmf.backends.hdf5 import H5DataIO | ||||
|
||||
|
||||
class TimeIntervalsTest(TestCase): | ||||
|
||||
|
@@ -66,6 +68,117 @@ def test_dataframe_roundtrip_drop_ts(self): | |||
self.assertNotIn('timeseries', obtained.columns) | ||||
self.assertEqual(obtained.loc[2, 'foo'], df.loc[2, 'foo']) | ||||
|
||||
def test_add_interval_basic(self): | ||||
"""Test adding interval with just start/stop times""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
epochs.add_interval(start_time=10.0, stop_time=20.0) | ||||
row = epochs[0] | ||||
self.assertEqual(row.loc[0]['start_time'], 10.0) | ||||
self.assertEqual(row.loc[0]['stop_time'], 20.0) | ||||
|
||||
def test_add_interval_tags_string(self): | ||||
"""Test adding interval with tags as comma-separated string""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
epochs.add_interval(start_time=10.0, stop_time=20.0, tags='tag1, tag2, tag3') | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should tags be allowed to be supplied this way? It feels like this approach could be prone to misunderstandings on how the data gets written. Looking at the line of code that this is testing and the tests that fail if you remove it: Line 48 in 5564f82
it seems this line is currently mostly important for converting the tags argument to a list if a single string is provided. (This issue might be also related to hdmf-dev/hdmf#1211, where we discussed deprecating the ability to provide a single string for a ragged column.) |
||||
row = epochs[0] | ||||
self.assertEqual(row.loc[0]['tags'], ['tag1', 'tag2', 'tag3']) | ||||
|
||||
def test_add_interval_tags_list(self): | ||||
"""Test adding interval with tags as list""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
epochs.add_interval(start_time=10.0, stop_time=20.0, tags=['tag1', 'tag2', 'tag3']) | ||||
row = epochs[0] | ||||
self.assertEqual(row.loc[0]['tags'], ['tag1', 'tag2', 'tag3']) | ||||
|
||||
def test_add_interval_single_timeseries_timestamps(self): | ||||
"""Test adding interval with single TimeSeries using timestamps""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
ts = TimeSeries( | ||||
name='test_ts', | ||||
data=list(range(100)), | ||||
unit='units', | ||||
timestamps=np.linspace(0, 10, 100) | ||||
) | ||||
epochs.add_interval(start_time=2.0, stop_time=4.0, timeseries=ts) | ||||
row = epochs[0] | ||||
self.assertEqual(len(row.loc[0]['timeseries']), 1) | ||||
ts_ref = row.loc[0]['timeseries'][0] | ||||
self.assertEqual(ts_ref.idx_start, 20) # at t=2.0 | ||||
self.assertEqual(ts_ref.count, 20) # from t=2.0 to t=4.0 | ||||
|
||||
def test_add_interval_single_timeseries_timestamps_with_dataio(self): | ||||
"""Test adding interval with single TimeSeries using timestamps""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
ts = TimeSeries( | ||||
name='test_ts', | ||||
data=list(range(100)), | ||||
unit='units', | ||||
timestamps=H5DataIO(np.linspace(0, 10, 100)) | ||||
) | ||||
epochs.add_interval(start_time=2.0, stop_time=4.0, timeseries=ts) | ||||
row = epochs[0] | ||||
self.assertEqual(len(row.loc[0]['timeseries']), 1) | ||||
ts_ref = row.loc[0]['timeseries'][0] | ||||
self.assertEqual(ts_ref.idx_start, 20) # at t=2.0 | ||||
self.assertEqual(ts_ref.count, 20) # from t=2.0 to t=4.0 | ||||
|
||||
def test_add_interval_single_timeseries_rate(self): | ||||
"""Test adding interval with single TimeSeries using starting_time and rate""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
ts = TimeSeries( | ||||
name='test_ts', | ||||
data=list(range(100)), | ||||
unit='units', | ||||
starting_time=0.0, | ||||
rate=10.0 # 10 samples per second | ||||
) | ||||
epochs.add_interval(start_time=2.0, stop_time=4.0, timeseries=ts) | ||||
row = epochs[0] | ||||
self.assertEqual(len(row.loc[0]['timeseries']), 1) | ||||
ts_ref = row.loc[0]['timeseries'][0] | ||||
self.assertEqual(ts_ref.idx_start, 20) # at t=2.0 | ||||
self.assertEqual(ts_ref.count, 20) # from t=2.0 to t=4.0 | ||||
|
||||
def test_add_interval_multiple_timeseries(self): | ||||
"""Test adding interval with multiple TimeSeries""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
ts1 = TimeSeries( | ||||
name='test_ts1', | ||||
data=list(range(100)), | ||||
unit='units', | ||||
timestamps=np.linspace(0, 10, 100) | ||||
) | ||||
ts2 = TimeSeries( | ||||
name='test_ts2', | ||||
data=list(range(50)), | ||||
unit='units', | ||||
starting_time=0.0, | ||||
rate=5.0 | ||||
) | ||||
epochs.add_interval(start_time=2.0, stop_time=4.0, timeseries=[ts1, ts2]) | ||||
row = epochs[0] | ||||
self.assertEqual(len(row.loc[0]['timeseries']), 2) | ||||
ts1_ref = row.loc[0]['timeseries'][0] | ||||
ts2_ref = row.loc[0]['timeseries'][1] | ||||
self.assertEqual(ts1_ref.idx_start, 20) | ||||
self.assertEqual(ts1_ref.count, 20) | ||||
self.assertEqual(ts2_ref.idx_start, 10) | ||||
self.assertEqual(ts2_ref.count, 10) | ||||
|
||||
def test_add_interval_timeseries_missing_timing(self): | ||||
"""Test error when TimeSeries has neither timestamps nor starting_time/rate""" | ||||
epochs = TimeIntervals(name='test_epochs') | ||||
ts = TimeSeries( | ||||
name='test_ts', | ||||
data=list(range(100)), | ||||
unit='units', | ||||
timestamps=np.linspace(0, 10, 100) | ||||
) | ||||
ts.fields['timestamps'] = None # remove timestamps to trigger error | ||||
msg = "TimeSeries object must have timestamps or starting_time and rate" | ||||
with self.assertRaisesWith(ValueError, msg): | ||||
epochs.add_interval(start_time=2.0, stop_time=4.0, timeseries=ts) | ||||
|
||||
def test_no_tags(self): | ||||
nwbfile = NWBFile("a file with header data", "NB123A", datetime(1970, 1, 1, tzinfo=tz.tzutc())) | ||||
df = self.get_dataframe() | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.