1#[path = "ipc/envelope.rs"]
20mod envelope;
21#[path = "ipc/errors.rs"]
22mod errors;
23#[path = "ipc/methods.rs"]
24mod methods;
25
26pub use envelope::{IpcNotification, IpcRequest, IpcResponse, RequestId};
27pub use errors::{
28 ERROR_INTERNAL, ERROR_INVALID_PARAMS, ERROR_INVALID_REQUEST, ERROR_METHOD_NOT_FOUND,
29 ERROR_PARAM_NOT_FOUND, ERROR_PARAM_OUT_OF_RANGE, ERROR_PARSE, IpcError,
30};
31pub use methods::{
32 AudioDiagnostic, AudioDiagnosticCode, AudioRuntimePhase, AudioRuntimeStatus,
33 GetAllParametersResult, GetAudioStatusResult, GetMeterFrameResult, GetOscilloscopeFrameResult,
34 GetParameterParams, GetParameterResult, METHOD_GET_ALL_PARAMETERS, METHOD_GET_AUDIO_STATUS,
35 METHOD_GET_METER_FRAME, METHOD_GET_OSCILLOSCOPE_FRAME, METHOD_GET_PARAMETER,
36 METHOD_REGISTER_AUDIO, METHOD_REQUEST_RESIZE, METHOD_SET_PARAMETER, MeterFrame,
37 MeterUpdateNotification, NOTIFICATION_AUDIO_STATUS_CHANGED, NOTIFICATION_METER_UPDATE,
38 NOTIFICATION_PARAMETER_CHANGED, OscilloscopeChannelView, OscilloscopeFrame,
39 OscilloscopeTriggerMode, ParameterChangedNotification, ParameterInfo, ParameterType,
40 ProcessorInfo, RegisterAudioParams, RegisterAudioResult, RequestResizeParams,
41 RequestResizeResult, SetParameterParams, SetParameterResult,
42};
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47 use serde::Serialize;
48 use serde::ser::Error as _;
49
50 struct FailingSerialize;
51
52 impl Serialize for FailingSerialize {
53 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
54 where
55 S: serde::Serializer,
56 {
57 Err(S::Error::custom("intentional serialization failure"))
58 }
59 }
60
61 #[test]
62 fn test_request_serialization() {
63 let req = IpcRequest::new(
64 RequestId::Number(1),
65 METHOD_GET_PARAMETER,
66 Some(serde_json::json!({"id": "gain"})),
67 );
68
69 let json = serde_json::to_string(&req).unwrap();
70 assert!(json.contains("\"jsonrpc\":\"2.0\""));
71 assert!(json.contains("\"method\":\"getParameter\""));
72 }
73
74 #[test]
75 fn test_response_serialization() {
76 let resp = IpcResponse::success(
77 RequestId::Number(1),
78 GetParameterResult {
79 id: "gain".to_string(),
80 value: 0.5,
81 },
82 );
83
84 let json = serde_json::to_string(&resp).unwrap();
85 assert!(json.contains("\"jsonrpc\":\"2.0\""));
86 assert!(json.contains("\"result\""));
87 assert!(!json.contains("\"error\""));
88 }
89
90 #[test]
91 fn test_error_response() {
92 let resp = IpcResponse::error(
93 RequestId::String("test".to_string()),
94 IpcError::method_not_found("unknownMethod"),
95 );
96
97 let json = serde_json::to_string(&resp).unwrap();
98 assert!(json.contains("\"error\""));
99 assert!(!json.contains("\"result\""));
100 }
101
102 #[test]
103 fn test_notification_serialization() {
104 let notif = IpcNotification::new(
105 NOTIFICATION_PARAMETER_CHANGED,
106 ParameterChangedNotification {
107 id: "gain".to_string(),
108 value: 0.8,
109 },
110 );
111
112 let json = serde_json::to_string(¬if).unwrap();
113 println!("Notification JSON: {}", json);
114 assert!(json.contains("\"jsonrpc\":\"2.0\""));
115 assert!(json.contains("\"method\":\"parameterChanged\""));
116 }
119
120 #[test]
121 fn test_register_audio_serialization() {
122 let req = IpcRequest::new(
123 RequestId::String("audio-1".to_string()),
124 METHOD_REGISTER_AUDIO,
125 Some(serde_json::json!({
126 "client_id": "dev-audio",
127 "sample_rate": 44100.0,
128 "buffer_size": 512
129 })),
130 );
131
132 let json = serde_json::to_string(&req).unwrap();
133 assert!(json.contains("\"method\":\"registerAudio\""));
134 assert!(json.contains("\"sample_rate\":44100"));
135 }
136
137 #[test]
138 fn test_meter_update_notification() {
139 let notif = IpcNotification::new(
140 NOTIFICATION_METER_UPDATE,
141 MeterUpdateNotification {
142 timestamp_us: 1000,
143 left_peak: 0.5,
144 left_rms: 0.3,
145 right_peak: 0.6,
146 right_rms: 0.4,
147 },
148 );
149
150 let json = serde_json::to_string(¬if).unwrap();
151 assert!(json.contains("\"method\":\"meterUpdate\""));
152 assert!(json.contains("\"left_peak\":0.5"));
153 }
154
155 #[test]
156 fn test_audio_status_serialization() {
157 let result = GetAudioStatusResult {
158 status: Some(AudioRuntimeStatus {
159 phase: AudioRuntimePhase::RunningFullDuplex,
160 diagnostic: None,
161 sample_rate: Some(44100.0),
162 buffer_size: Some(512),
163 updated_at_ms: 123,
164 }),
165 };
166
167 let json = serde_json::to_string(&result).expect("status result should serialize");
168 assert!(json.contains("\"phase\":\"runningFullDuplex\""));
169 assert!(json.contains("\"sample_rate\":44100"));
170 }
171
172 #[test]
173 fn test_oscilloscope_frame_serialization() {
174 let result = GetOscilloscopeFrameResult {
175 frame: Some(OscilloscopeFrame {
176 points_l: vec![0.0; 1024],
177 points_r: vec![0.0; 1024],
178 sample_rate: 44100.0,
179 timestamp: 7,
180 no_signal: true,
181 trigger_mode: OscilloscopeTriggerMode::RisingZeroCrossing,
182 }),
183 };
184
185 let json = serde_json::to_string(&result).expect("oscilloscope result should serialize");
186 assert!(json.contains("\"sample_rate\":44100"));
187 assert!(json.contains("\"trigger_mode\":\"risingZeroCrossing\""));
188 }
189
190 #[test]
191 fn parameter_info_with_variants_serializes_correctly() {
192 let info = ParameterInfo {
193 id: "osc_waveform".to_string(),
194 name: "Waveform".to_string(),
195 param_type: ParameterType::Enum,
196 value: 0.0,
197 default: 0.0,
198 min: 0.0,
199 max: 3.0,
200 unit: None,
201 group: None,
202 variants: Some(vec![
203 "Sine".to_string(),
204 "Square".to_string(),
205 "Saw".to_string(),
206 "Triangle".to_string(),
207 ]),
208 };
209
210 let json = serde_json::to_string(&info).expect("parameter info should serialize");
211 assert!(json.contains("\"variants\""));
212
213 let deserialized: ParameterInfo =
214 serde_json::from_str(&json).expect("parameter info should deserialize");
215 assert_eq!(
216 deserialized.variants.expect("variants should exist").len(),
217 4
218 );
219 }
220
221 #[test]
222 fn parameter_info_without_variants_omits_field() {
223 let info = ParameterInfo {
224 id: "gain".to_string(),
225 name: "Gain".to_string(),
226 param_type: ParameterType::Float,
227 value: 0.5,
228 default: 0.5,
229 min: 0.0,
230 max: 1.0,
231 unit: Some("dB".to_string()),
232 group: None,
233 variants: None,
234 };
235
236 let json = serde_json::to_string(&info).expect("parameter info should serialize");
237 assert!(!json.contains("\"variants\""));
238 }
239
240 #[test]
241 fn try_success_returns_error_on_serialize_failure() {
242 let result = IpcResponse::try_success(RequestId::Number(1), FailingSerialize);
243 assert!(result.is_err());
244 }
245
246 #[test]
247 fn success_does_not_panic_on_serialize_failure() {
248 let response = IpcResponse::success(RequestId::Number(1), FailingSerialize);
249 assert!(response.result.is_none());
250 assert!(response.error.is_some());
251 }
252
253 #[test]
254 fn try_notification_returns_error_on_serialize_failure() {
255 let result = IpcNotification::try_new(NOTIFICATION_PARAMETER_CHANGED, FailingSerialize);
256 assert!(result.is_err());
257 }
258
259 #[test]
260 fn notification_new_does_not_panic_on_serialize_failure() {
261 let notification = IpcNotification::new(NOTIFICATION_PARAMETER_CHANGED, FailingSerialize);
262 assert!(notification.params.is_none());
263 }
264
265 #[test]
266 fn try_with_data_returns_error_on_serialize_failure() {
267 let result = IpcError::try_with_data(ERROR_INTERNAL, "test", FailingSerialize);
268 assert!(result.is_err());
269 }
270
271 #[test]
272 fn with_data_does_not_panic_on_serialize_failure() {
273 let error = IpcError::with_data(ERROR_INTERNAL, "test", FailingSerialize);
274 assert!(error.data.is_none());
275 assert_eq!(error.message, "test");
276 }
277}