Skip to content

Commit 5a71079

Browse files
committed
Add unit tests for image nodes
1 parent 7813843 commit 5a71079

6 files changed

Lines changed: 234 additions & 0 deletions

File tree

synapse/client/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
from synapse.client.channel import Channel
88
from synapse.client.signal_config import SignalConfig, ElectrodeConfig, PixelConfig
9+
from synapse.api.datatype_pb2 import PixelFormat
910

1011
from synapse.client.nodes.broadband_source import BroadbandSource
1112
from synapse.client.nodes.disk_writer import DiskWriter
1213
from synapse.client.nodes.electrical_stimulation import ElectricalStimulation
14+
from synapse.client.nodes.image_sink import ImageSink
15+
from synapse.client.nodes.image_source import ImageSource
1316
from synapse.client.nodes.optical_stimulation import OpticalStimulation
1417
from synapse.client.nodes.spike_source import SpikeSource
1518
from synapse.client.nodes.spike_binner import SpikeBinner

synapse/client/nodes/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from synapse.client.nodes.broadband_source import BroadbandSource
22
from synapse.client.nodes.electrical_stimulation import ElectricalStimulation
3+
from synapse.client.nodes.image_sink import ImageSink
4+
from synapse.client.nodes.image_source import ImageSource
35
from synapse.client.nodes.optical_stimulation import OpticalStimulation
46
from synapse.client.nodes.spectral_filter import SpectralFilter
57
from synapse.client.nodes.spike_binner import SpikeBinner
@@ -14,6 +16,8 @@
1416
NodeType.kBroadbandSource: BroadbandSource,
1517
NodeType.kDiskWriter: DiskWriter,
1618
NodeType.kElectricalStimulation: ElectricalStimulation,
19+
NodeType.kImageSink: ImageSink,
20+
NodeType.kImageSource: ImageSource,
1721
NodeType.kOpticalStimulation: OpticalStimulation,
1822
NodeType.kSpectralFilter: SpectralFilter,
1923
NodeType.kSpikeBinner: SpikeBinner,

synapse/client/nodes/image_sink.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import Optional
2+
3+
from synapse.api.node_pb2 import NodeConfig, NodeType
4+
from synapse.api.nodes.image_sink_pb2 import ImageSinkConfig
5+
from synapse.client.node import Node
6+
7+
8+
class ImageSink(Node):
9+
type = NodeType.kImageSink
10+
11+
def __init__(
12+
self,
13+
peripheral_id: int,
14+
frame_rate_hz: int,
15+
):
16+
self.peripheral_id: int = peripheral_id
17+
self.frame_rate_hz: int = frame_rate_hz
18+
19+
def _to_proto(self):
20+
n = NodeConfig()
21+
p = ImageSinkConfig(
22+
peripheral_id=self.peripheral_id,
23+
frame_rate_hz=self.frame_rate_hz,
24+
)
25+
n.image_sink.CopyFrom(p)
26+
return n
27+
28+
@staticmethod
29+
def _from_proto(proto: Optional[ImageSinkConfig]):
30+
if not proto:
31+
raise ValueError("parameter 'proto' is missing")
32+
if not isinstance(proto, ImageSinkConfig):
33+
raise ValueError("proto is not of type ImageSinkConfig")
34+
35+
return ImageSink(
36+
peripheral_id=proto.peripheral_id,
37+
frame_rate_hz=proto.frame_rate_hz,
38+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from typing import Optional
2+
3+
from synapse.api.datatype_pb2 import PixelFormat
4+
from synapse.api.node_pb2 import NodeConfig, NodeType
5+
from synapse.api.nodes.image_source_pb2 import ImageSourceConfig
6+
from synapse.client.node import Node
7+
8+
9+
class ImageSource(Node):
10+
type = NodeType.kImageSource
11+
12+
def __init__(
13+
self,
14+
peripheral_id: int,
15+
width: int,
16+
height: int,
17+
format: PixelFormat,
18+
frame_rate_hz: int,
19+
):
20+
self.peripheral_id: int = peripheral_id
21+
self.width: int = width
22+
self.height: int = height
23+
self.format: PixelFormat = format
24+
self.frame_rate_hz: int = frame_rate_hz
25+
26+
def _to_proto(self):
27+
n = NodeConfig()
28+
p = ImageSourceConfig(
29+
peripheral_id=self.peripheral_id,
30+
width=self.width,
31+
height=self.height,
32+
format=self.format,
33+
frame_rate_hz=self.frame_rate_hz,
34+
)
35+
n.image_source.CopyFrom(p)
36+
return n
37+
38+
@staticmethod
39+
def _from_proto(proto: Optional[ImageSourceConfig]):
40+
if not proto:
41+
raise ValueError("parameter 'proto' is missing")
42+
if not isinstance(proto, ImageSourceConfig):
43+
raise ValueError("proto is not of type ImageSourceConfig")
44+
45+
return ImageSource(
46+
peripheral_id=proto.peripheral_id,
47+
width=proto.width,
48+
height=proto.height,
49+
format=proto.format,
50+
frame_rate_hz=proto.frame_rate_hz,
51+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import pytest
2+
from synapse.api.nodes.image_sink_pb2 import ImageSinkConfig
3+
from synapse.client.nodes.image_sink import ImageSink
4+
5+
6+
def test_image_sink_to_proto():
7+
sink = ImageSink(
8+
peripheral_id=1,
9+
frame_rate_hz=30,
10+
)
11+
12+
proto = sink._to_proto()
13+
14+
assert proto.image_sink.peripheral_id == 1
15+
assert proto.image_sink.frame_rate_hz == 30
16+
17+
18+
def test_image_sink_from_proto():
19+
proto = ImageSinkConfig(
20+
peripheral_id=2,
21+
frame_rate_hz=60,
22+
)
23+
24+
sink = ImageSink._from_proto(proto)
25+
26+
assert sink.peripheral_id == 2
27+
assert sink.frame_rate_hz == 60
28+
29+
30+
def test_image_sink_from_proto_roundtrip():
31+
original = ImageSink(
32+
peripheral_id=5,
33+
frame_rate_hz=24,
34+
)
35+
36+
proto = original._to_proto()
37+
restored = ImageSink._from_proto(proto.image_sink)
38+
39+
assert restored.peripheral_id == original.peripheral_id
40+
assert restored.frame_rate_hz == original.frame_rate_hz
41+
42+
43+
def test_image_sink_from_invalid_proto():
44+
with pytest.raises(ValueError, match="missing"):
45+
ImageSink._from_proto(None)
46+
47+
with pytest.raises(ValueError, match="not of type"):
48+
ImageSink._from_proto("invalid")
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import pytest
2+
from synapse.api.datatype_pb2 import PixelFormat
3+
from synapse.api.nodes.image_source_pb2 import ImageSourceConfig
4+
from synapse.client.nodes.image_source import ImageSource
5+
6+
7+
def test_image_source_to_proto():
8+
source = ImageSource(
9+
peripheral_id=1,
10+
width=1920,
11+
height=1080,
12+
format=PixelFormat.kRGB888,
13+
frame_rate_hz=30,
14+
)
15+
16+
proto = source._to_proto()
17+
18+
assert proto.image_source.peripheral_id == 1
19+
assert proto.image_source.width == 1920
20+
assert proto.image_source.height == 1080
21+
assert proto.image_source.format == PixelFormat.kRGB888
22+
assert proto.image_source.frame_rate_hz == 30
23+
24+
25+
def test_image_source_to_proto_pixel_formats():
26+
for fmt in [
27+
PixelFormat.kPixelFormatUnknown,
28+
PixelFormat.kYUV420_888,
29+
PixelFormat.kRGB888,
30+
PixelFormat.kRGBA8888,
31+
PixelFormat.kGrayscale8,
32+
PixelFormat.kRAW10,
33+
PixelFormat.kRAW16,
34+
PixelFormat.kNV12,
35+
PixelFormat.kNV21,
36+
]:
37+
source = ImageSource(
38+
peripheral_id=0,
39+
width=640,
40+
height=480,
41+
format=fmt,
42+
frame_rate_hz=60,
43+
)
44+
proto = source._to_proto()
45+
assert proto.image_source.format == fmt
46+
47+
48+
def test_image_source_from_proto():
49+
proto = ImageSourceConfig(
50+
peripheral_id=2,
51+
width=3840,
52+
height=2160,
53+
format=PixelFormat.kNV21,
54+
frame_rate_hz=15,
55+
)
56+
57+
source = ImageSource._from_proto(proto)
58+
59+
assert source.peripheral_id == 2
60+
assert source.width == 3840
61+
assert source.height == 2160
62+
assert source.format == PixelFormat.kNV21
63+
assert source.frame_rate_hz == 15
64+
65+
66+
def test_image_source_from_proto_roundtrip():
67+
original = ImageSource(
68+
peripheral_id=3,
69+
width=1280,
70+
height=720,
71+
format=PixelFormat.kGrayscale8,
72+
frame_rate_hz=120,
73+
)
74+
75+
proto = original._to_proto()
76+
restored = ImageSource._from_proto(proto.image_source)
77+
78+
assert restored.peripheral_id == original.peripheral_id
79+
assert restored.width == original.width
80+
assert restored.height == original.height
81+
assert restored.format == original.format
82+
assert restored.frame_rate_hz == original.frame_rate_hz
83+
84+
85+
def test_image_source_from_invalid_proto():
86+
with pytest.raises(ValueError, match="missing"):
87+
ImageSource._from_proto(None)
88+
89+
with pytest.raises(ValueError, match="not of type"):
90+
ImageSource._from_proto("invalid")

0 commit comments

Comments
 (0)