Skip to content

Commit e39afea

Browse files
authored
Merge pull request #310 from bendichter/validate_read_probe_dicts
add json validation to tests
2 parents 1020965 + 11e1242 commit e39afea

File tree

10 files changed

+107
-7
lines changed

10 files changed

+107
-7
lines changed

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ package-dir = {"probeinterface" = "src/probeinterface"}
4040
[project.optional-dependencies]
4141

4242
test = [
43+
"jsonschema",
4344
"pytest",
4445
"pytest-cov",
4546
"matplotlib",

resources/probe.json.schema

+6-2
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
"annotations": {
2727
"type": "object",
2828
"properties": {
29-
"name": { "type": "string" },
29+
"model_name": { "type": "string" },
3030
"manufacturer": { "type": "string" }
3131
},
32-
"required": ["name", "manufacturer"],
32+
"required": ["model_name", "manufacturer"],
3333
"additionalProperties": true
3434
},
3535
"contact_annotations": {
@@ -101,6 +101,10 @@
101101
"shank_ids": {
102102
"type": "array",
103103
"items": { "type": "string" }
104+
},
105+
"device_channel_indices": {
106+
"type": "array",
107+
"items": { "type": "integer" }
104108
}
105109
},
106110
"required": [

src/probeinterface/probe.py

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import numpy as np
33
from typing import Optional
44
from pathlib import Path
5-
import json
65

76
from .shank import Shank
87

src/probeinterface/testing.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import json
2+
from pathlib import Path
3+
4+
from probeinterface import __version__ as version
5+
import jsonschema
6+
7+
json_schema_file = Path(__file__).absolute().parent.parent.parent / "resources" / "probe.json.schema"
8+
schema = json.load(open(json_schema_file, "r"))
9+
10+
11+
def validate_probe_dict(probe_dict):
12+
instance = dict(specification="probeinterface", version=version, probes=[probe_dict])
13+
jsonschema.validate(instance=instance, schema=schema)

tests/test_io/test_3brain.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
1+
import glob
12
from pathlib import Path
23
import numpy as np
34

45
import pytest
56

67
from probeinterface import read_3brain
78

9+
from probeinterface.testing import validate_probe_dict
10+
11+
812
data_path = Path(__file__).absolute().parent.parent / "data" / "3brain"
13+
brw_files = glob.glob(str(data_path / "*.brw"))
14+
15+
16+
@pytest.mark.parametrize("file_", brw_files)
17+
def test_valid_probe_dict(file_: str):
18+
probe = read_3brain(data_path / file_)
19+
probe_dict = probe.to_dict(array_as_list=True)
20+
probe_dict["annotations"].update(model_name="placeholder")
21+
validate_probe_dict(probe_dict)
922

1023

1124
def test_3brain():

tests/test_io/test_imro.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
import glob
12
from pathlib import Path
23

34
import pytest
45
import numpy as np
56

67
from probeinterface import read_imro, write_imro
8+
from probeinterface.testing import validate_probe_dict
79

810
data_path = Path(__file__).absolute().parent.parent / "data" / "imro"
11+
imro_files = glob.glob(str(data_path / "*.imro"))
12+
13+
imro_files.pop(imro_files.index(str(data_path / "test_non_standard.imro")))
14+
15+
16+
@pytest.mark.parametrize("file_", imro_files)
17+
def test_valid_probe_dict(file_: str):
18+
probe = read_imro(data_path / file_)
19+
validate_probe_dict(probe.to_dict(array_as_list=True))
920

1021

1122
def test_reading_multishank_imro(tmp_path):

tests/test_io/test_maxwell.py

+9
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@
44
import pytest
55

66
from probeinterface import read_maxwell
7+
from probeinterface.testing import validate_probe_dict
78

89
data_path = Path(__file__).absolute().parent.parent / "data" / "maxwell"
910

1011

12+
def test_valid_probe_dict():
13+
file_ = "data.raw.h5"
14+
probe = read_maxwell(data_path / file_)
15+
probe_dict = probe.to_dict(array_as_list=True)
16+
probe_dict["annotations"].update(model_name="placeholder")
17+
validate_probe_dict(probe_dict)
18+
19+
1120
def test_maxwell():
1221
"""Basic file taken from the ephys data repository and provided by Alessio Buccino"""
1322

tests/test_io/test_openephys.py

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
from pathlib import Path
22

33
import numpy as np
4+
import glob
45

56
import pytest
67

78
from probeinterface import read_openephys
9+
from probeinterface.testing import validate_probe_dict
810

911
data_path = Path(__file__).absolute().parent.parent / "data" / "openephys"
1012

1113

1214
def test_NP2_OE_1_0():
1315
# NP2 1-shank
1416
probeA = read_openephys(data_path / "OE_1.0_Neuropix-PXI-multi-probe" / "settings.xml", probe_name="ProbeA")
17+
probe_dict = probeA.to_dict(array_as_list=True)
18+
validate_probe_dict(probe_dict)
1519
assert probeA.get_shank_count() == 1
1620
assert "2.0" in probeA.model_name
1721
assert probeA.get_contact_count() == 384
@@ -20,13 +24,17 @@ def test_NP2_OE_1_0():
2024
def test_NP2():
2125
# NP2
2226
probe = read_openephys(data_path / "OE_Neuropix-PXI" / "settings.xml")
27+
probe_dict = probe.to_dict(array_as_list=True)
28+
validate_probe_dict(probe_dict)
2329
assert probe.get_shank_count() == 1
2430
assert "2.0 - Single Shank" in probe.model_name
2531

2632

2733
def test_NP2_four_shank():
2834
# NP2
2935
probe = read_openephys(data_path / "OE_Neuropix-PXI-NP2-4shank" / "settings.xml")
36+
probe_dict = probe.to_dict(array_as_list=True)
37+
validate_probe_dict(probe_dict)
3038
# on this case, only shanks 2-3 are used
3139
assert probe.get_shank_count() == 2
3240
assert "2.0 - Four Shank" in probe.model_name
@@ -38,6 +46,8 @@ def test_NP_Ultra():
3846
data_path / "OE_Neuropix-PXI-NP-Ultra" / "settings.xml",
3947
probe_name="ProbeA",
4048
)
49+
probe_dict = probeA.to_dict(array_as_list=True)
50+
validate_probe_dict(probe_dict)
4151
assert "Ultra" in probeA.model_name
4252
assert probeA.get_shank_count() == 1
4353
assert probeA.get_contact_count() == 384
@@ -46,6 +56,8 @@ def test_NP_Ultra():
4656
data_path / "OE_Neuropix-PXI-NP-Ultra" / "settings.xml",
4757
probe_name="ProbeB",
4858
)
59+
probe_dict = probeB.to_dict(array_as_list=True)
60+
validate_probe_dict(probe_dict)
4961
assert "Ultra" in probeB.model_name
5062
assert probeB.get_shank_count() == 1
5163
assert probeB.get_contact_count() == 384
@@ -54,6 +66,8 @@ def test_NP_Ultra():
5466
data_path / "OE_Neuropix-PXI-NP-Ultra" / "settings.xml",
5567
probe_name="ProbeF",
5668
)
69+
probe_dict = probeF.to_dict(array_as_list=True)
70+
validate_probe_dict(probe_dict)
5771
assert "Ultra" in probeF.model_name
5872
assert probeF.get_shank_count() == 1
5973
assert probeF.get_contact_count() == 384
@@ -62,6 +76,8 @@ def test_NP_Ultra():
6276
data_path / "OE_Neuropix-PXI-NP-Ultra" / "settings.xml",
6377
probe_name="ProbeD",
6478
)
79+
probe_dict = probeD.to_dict(array_as_list=True)
80+
validate_probe_dict(probe_dict)
6581
assert "Ultra" in probeD.model_name and "Type 2" in probeD.model_name
6682
assert probeD.get_shank_count() == 1
6783
assert probeD.get_contact_count() == 384
@@ -72,12 +88,16 @@ def test_NP_Ultra():
7288
def test_NP1_subset():
7389
# NP1 - 200 channels selected by recording_state in Record Node
7490
probe_ap = read_openephys(data_path / "OE_Neuropix-PXI-subset" / "settings.xml", stream_name="ProbeA-AP")
91+
probe_dict = probe_ap.to_dict(array_as_list=True)
92+
validate_probe_dict(probe_dict)
7593

7694
assert probe_ap.get_shank_count() == 1
7795
assert "1.0" in probe_ap.model_name
7896
assert probe_ap.get_contact_count() == 200
7997

8098
probe_lf = read_openephys(data_path / "OE_Neuropix-PXI-subset" / "settings.xml", stream_name="ProbeA-LFP")
99+
probe_dict = probe_lf.to_dict(array_as_list=True)
100+
validate_probe_dict(probe_dict)
81101

82102
assert probe_lf.get_shank_count() == 1
83103
assert "1.0" in probe_lf.model_name
@@ -92,6 +112,8 @@ def test_NP1_subset():
92112
def test_multiple_probes():
93113
# multiple probes
94114
probeA = read_openephys(data_path / "OE_Neuropix-PXI-multi-probe" / "settings.xml", probe_name="ProbeA")
115+
probe_dict = probeA.to_dict(array_as_list=True)
116+
validate_probe_dict(probe_dict)
95117

96118
assert probeA.get_shank_count() == 1
97119
assert "1.0" in probeA.model_name
@@ -100,17 +122,23 @@ def test_multiple_probes():
100122
data_path / "OE_Neuropix-PXI-multi-probe" / "settings.xml",
101123
stream_name="RecordNode#ProbeB",
102124
)
125+
probe_dict = probeB.to_dict(array_as_list=True)
126+
validate_probe_dict(probe_dict)
103127

104128
assert probeB.get_shank_count() == 1
105129

106130
probeC = read_openephys(
107131
data_path / "OE_Neuropix-PXI-multi-probe" / "settings.xml",
108132
serial_number="20403311714",
109133
)
134+
probe_dict = probeC.to_dict(array_as_list=True)
135+
validate_probe_dict(probe_dict)
110136

111137
assert probeC.get_shank_count() == 1
112138

113139
probeD = read_openephys(data_path / "OE_Neuropix-PXI-multi-probe" / "settings.xml", probe_name="ProbeD")
140+
probe_dict = probeD.to_dict(array_as_list=True)
141+
validate_probe_dict(probe_dict)
114142

115143
assert probeD.get_shank_count() == 1
116144

@@ -146,11 +174,16 @@ def test_multiple_probes_enabled():
146174
probe = read_openephys(
147175
data_path / "OE_6.7_enabled_disabled_Neuropix-PXI" / "settings_enabled-enabled.xml", probe_name="ProbeA"
148176
)
177+
probe_dict = probe.to_dict(array_as_list=True)
178+
validate_probe_dict(probe_dict)
179+
149180
assert probe.get_shank_count() == 1
150181

151182
probe = read_openephys(
152183
data_path / "OE_6.7_enabled_disabled_Neuropix-PXI" / "settings_enabled-enabled.xml", probe_name="ProbeB"
153184
)
185+
probe_dict = probe.to_dict(array_as_list=True)
186+
validate_probe_dict(probe_dict)
154187
assert probe.get_shank_count() == 4
155188

156189

@@ -159,7 +192,8 @@ def test_multiple_probes_disabled():
159192
probe = read_openephys(
160193
data_path / "OE_6.7_enabled_disabled_Neuropix-PXI" / "settings_enabled-disabled.xml", probe_name="ProbeA"
161194
)
162-
195+
probe_dict = probe.to_dict(array_as_list=True)
196+
validate_probe_dict(probe_dict)
163197
assert probe.get_shank_count() == 1
164198

165199
# Fail as this is disabled:
@@ -173,6 +207,8 @@ def test_multiple_probes_disabled():
173207

174208
def test_np_opto_with_sync():
175209
probe = read_openephys(data_path / "OE_Neuropix-PXI-opto-with-sync" / "settings.xml")
210+
probe_dict = probe.to_dict(array_as_list=True)
211+
validate_probe_dict(probe_dict)
176212
assert probe.model_name == "Neuropixels Opto"
177213
assert probe.get_shank_count() == 1
178214
assert probe.get_contact_count() == 384
@@ -182,7 +218,8 @@ def test_older_than_06_format():
182218
## Test with the open ephys < 0.6 format
183219

184220
probe = read_openephys(data_path / "OE_5_Neuropix-PXI-multi-probe" / "settings.xml", probe_name="100.0")
185-
221+
probe_dict = probe.to_dict(array_as_list=True)
222+
validate_probe_dict(probe_dict)
186223
assert probe.get_shank_count() == 4
187224
assert "2.0 - Four Shank" in probe.model_name
188225
ypos = probe.contact_positions[:, 1]
@@ -193,6 +230,8 @@ def test_multiple_signal_chains():
193230
# tests that the probe information can be loaded even if the Neuropix-PXI plugin
194231
# is not in the first signalchain
195232
probe = read_openephys(data_path / "OE_Neuropix-PXI-multiple-signalchains" / "settings.xml")
233+
probe_dict = probe.to_dict(array_as_list=True)
234+
validate_probe_dict(probe_dict)
196235
assert probe.model_name == "Neuropixels 1.0"
197236

198237

tests/test_io/test_spikegadgets.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from pathlib import Path
22
from xml.etree import ElementTree
33

4-
import pytest
5-
64
from probeinterface import read_spikegadgets
75
from probeinterface.io import parse_spikegadgets_header
6+
from probeinterface.testing import validate_probe_dict
7+
88

99
data_path = Path(__file__).absolute().parent.parent / "data" / "spikegadgets"
1010
test_file = "SpikeGadgets_test_data_2xNpix1.0_20240318_173658_header_only.rec"
@@ -22,6 +22,8 @@ def test_neuropixels_1_reader():
2222
probe_group = read_spikegadgets(data_path / test_file, raise_error=False)
2323
assert len(probe_group.probes) == 2
2424
for probe in probe_group.probes:
25+
probe_dict = probe.to_dict(array_as_list=True)
26+
validate_probe_dict(probe_dict)
2527
assert "1.0" in probe.model_name
2628
assert probe.get_shank_count() == 1
2729
assert probe.get_contact_count() == 384

tests/test_io/test_spikeglx.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import glob
12
from pathlib import Path
23
import numpy as np
34

@@ -8,8 +9,16 @@
89
parse_spikeglx_meta,
910
get_saved_channel_indices_from_spikeglx_meta,
1011
)
12+
from probeinterface.testing import validate_probe_dict
1113

1214
data_path = Path(__file__).absolute().parent.parent / "data" / "spikeglx"
15+
meta_files = glob.glob(str(data_path / "*.meta"))
16+
17+
18+
@pytest.mark.parametrize("meta_file", meta_files)
19+
def test_valid_probe_dict(meta_file: str):
20+
probe = read_spikeglx(data_path / meta_file)
21+
validate_probe_dict(probe.to_dict(array_as_list=True))
1322

1423

1524
def test_parse_meta():

0 commit comments

Comments
 (0)