Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8mod util;
9use buttplug_server_device_config::Endpoint;
10use util::test_server;
11pub use util::{
12 create_test_dcm,
13 test_device_manager::{
14 TestDeviceCommunicationManagerBuilder,
15 TestDeviceIdentifier,
16 check_test_recv_value,
17 },
18 test_server_with_comm_manager,
19 test_server_with_device,
20};
21
22use buttplug_core::{
23 errors::{ButtplugDeviceError, ButtplugError, ButtplugHandshakeError},
24 message::{
25 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
26 BUTTPLUG_CURRENT_API_MINOR_VERSION,
27 ButtplugClientMessageV4,
28 ButtplugMessageSpecVersion,
29 ButtplugServerMessageV4,
30 ErrorCode,
31 OutputCmdV4,
32 OutputCommand,
33 OutputValue,
34 PingV0,
35 RequestServerInfoV4,
36 ServerInfoV4,
37 StartScanningV0,
38 },
39};
40use buttplug_server::{
41 ButtplugServer,
42 ButtplugServerBuilder,
43 device::{
44 ServerDeviceManagerBuilder,
45 hardware::{HardwareCommand, HardwareWriteCmd},
46 },
47 message::{
48 ButtplugClientMessageV3,
49 ButtplugClientMessageVariant,
50 ButtplugServerMessageV2,
51 ButtplugServerMessageV3,
52 ButtplugServerMessageVariant,
53 RequestServerInfoV1,
54 ServerInfoV2,
55 checked_output_cmd::CheckedOutputCmdV4,
56 spec_enums::ButtplugCheckedClientMessageV4,
57 },
58};
59use futures::{Stream, StreamExt, pin_mut};
60use std::time::Duration;
61use tokio::time::sleep;
62use uuid::Uuid;
63
64async fn setup_test_server(
65 msg_union: ButtplugClientMessageV4,
66) -> (
67 ButtplugServer,
68 impl Stream<Item = ButtplugServerMessageVariant>,
69) {
70 let server = test_server();
71 let recv = server.event_stream();
72 // assert_eq!(server.server_name, "Test Server");
73 match server
74 .parse_message(msg_union.into())
75 .await
76 .expect("Test, assuming infallible.")
77 {
78 ButtplugServerMessageVariant::V4(ButtplugServerMessageV4::ServerInfo(s)) => assert_eq!(
79 s,
80 ServerInfoV4::new(
81 "Buttplug Server",
82 ButtplugMessageSpecVersion::Version4,
83 0,
84 0
85 )
86 ),
87 _ => panic!("Should've received ok"),
88 }
89 (server, recv)
90}
91
92#[tokio::test]
93async fn test_server_handshake() {
94 let msg = RequestServerInfoV4::new(
95 "Test Client",
96 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
97 BUTTPLUG_CURRENT_API_MINOR_VERSION,
98 )
99 .into();
100 let (server, _recv) = setup_test_server(msg).await;
101 assert!(server.connected());
102}
103
104#[tokio::test]
105async fn test_server_handshake_not_done_first_v4() {
106 let msg = ButtplugCheckedClientMessageV4::Ping(PingV0::default().into());
107 let server = test_server();
108 // assert_eq!(server.server_name, "Test Server");
109 let result = server.parse_checked_message(msg).await;
110 assert!(result.is_err());
111 assert!(matches!(
112 result.unwrap_err().original_error(),
113 ButtplugError::ButtplugHandshakeError(ButtplugHandshakeError::RequestServerInfoExpected)
114 ));
115 assert!(!server.connected());
116}
117
118#[tokio::test]
119async fn test_server_handshake_not_done_first_v3() {
120 let msg = ButtplugClientMessageV3::Ping(PingV0::default().into());
121 let server = test_server();
122 // assert_eq!(server.server_name, "Test Server");
123 let result = server.parse_message(msg.try_into().unwrap()).await;
124 assert!(result.is_err());
125 if let Err(ButtplugServerMessageVariant::V3(ButtplugServerMessageV3::Error(e))) = result {
126 assert!(matches!(
127 e.original_error(),
128 ButtplugError::ButtplugHandshakeError(ButtplugHandshakeError::RequestServerInfoExpected)
129 ));
130 } else {
131 panic!("Should've gotten error")
132 }
133 assert!(!server.connected());
134}
135
136#[tokio::test]
137async fn test_client_version_older_than_server() {
138 let msg = ButtplugClientMessageVariant::V2(
139 RequestServerInfoV1::new("Test Client", ButtplugMessageSpecVersion::Version2).into(),
140 );
141 let server = test_server();
142 // assert_eq!(server.server_name, "Test Server");
143 match server
144 .parse_message(msg)
145 .await
146 .expect("Test, assuming infallible.")
147 {
148 ButtplugServerMessageVariant::V2(ButtplugServerMessageV2::ServerInfo(s)) => assert_eq!(
149 s,
150 ServerInfoV2::new("Buttplug Server", ButtplugMessageSpecVersion::Version2, 0)
151 ),
152 _ => panic!("Should've received ok"),
153 }
154}
155
156#[tokio::test]
157#[ignore = "Needs to be rewritten to send in via the JSON parser, otherwise we're type bound due to the enum and can't fail"]
158async fn test_server_version_older_than_client() {
159 let server = test_server();
160 let msg = ButtplugClientMessageVariant::V2(
161 RequestServerInfoV1::new("Test Client", ButtplugMessageSpecVersion::Version2).into(),
162 );
163 assert!(
164 server.parse_message(msg).await.is_err(),
165 "Client having higher version than server should fail"
166 );
167}
168
169#[tokio::test]
170async fn test_ping_timeout() {
171 let server = ButtplugServerBuilder::default()
172 .max_ping_time(100)
173 .finish()
174 .expect("Test, assuming infallible.");
175 let recv = server.event_stream();
176 pin_mut!(recv);
177 let msg = RequestServerInfoV4::new(
178 "Test Client",
179 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
180 BUTTPLUG_CURRENT_API_MINOR_VERSION,
181 );
182 sleep(Duration::from_millis(150)).await;
183 let reply = server
184 .parse_checked_message(ButtplugCheckedClientMessageV4::RequestServerInfo(msg))
185 .await;
186 assert!(
187 reply.is_ok(),
188 "ping timer shouldn't start until handshake finished. {:?}",
189 reply
190 );
191 sleep(Duration::from_millis(300)).await;
192 let pingmsg = PingV0::default();
193 let result = server
194 .parse_checked_message(ButtplugCheckedClientMessageV4::Ping(pingmsg.into()))
195 .await;
196 let err = result.unwrap_err();
197 if !matches!(err.original_error(), ButtplugError::ButtplugPingError(_)) {
198 panic!("Got wrong type of error back!");
199 }
200 // Check that we got an event back about the ping out.
201 let msg = recv.next().await.expect("Test, assuming infallible.");
202 if let ButtplugServerMessageVariant::V4(ButtplugServerMessageV4::Error(e)) = msg {
203 if ErrorCode::ErrorPing != e.error_code() {
204 panic!("Didn't get a ping error");
205 }
206 } else {
207 panic!("Didn't get an error message back");
208 }
209}
210
211#[tokio::test]
212async fn test_device_stop_on_ping_timeout() {
213 let mut builder = TestDeviceCommunicationManagerBuilder::default();
214 let mut device = builder.add_test_device(&TestDeviceIdentifier::new("Massage Demo", None));
215
216 let dm_builder = ServerDeviceManagerBuilder::new(create_test_dcm())
217 .comm_manager(builder)
218 .finish()
219 .unwrap();
220
221 let mut server_builder = ButtplugServerBuilder::new(dm_builder);
222 server_builder.max_ping_time(100);
223 let server = server_builder.finish().unwrap();
224
225 let recv = server.server_version_event_stream();
226 pin_mut!(recv);
227
228 let msg = RequestServerInfoV4::new(
229 "Test Client",
230 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
231 BUTTPLUG_CURRENT_API_MINOR_VERSION,
232 );
233 let mut reply = server
234 .parse_checked_message(ButtplugCheckedClientMessageV4::from(msg))
235 .await;
236 assert!(reply.is_ok());
237 reply = server
238 .parse_checked_message(ButtplugCheckedClientMessageV4::from(
239 StartScanningV0::default(),
240 ))
241 .await;
242 assert!(reply.is_ok());
243 // Check that we got an event back about a new device.
244 let mut device_index = 100;
245 while let Some(msg) = recv.next().await {
246 if let ButtplugServerMessageV4::ScanningFinished(_) = msg {
247 continue;
248 } else if let ButtplugServerMessageV4::DeviceList(list) = msg {
249 let da = &list.devices()[&0];
250 assert_eq!(da.device_name(), "Aneros Vivi");
251 device_index = da.device_index();
252 break;
253 } else {
254 panic!(
255 "Returned message was not a DeviceAdded message or timed out: {:?}",
256 msg
257 );
258 }
259 }
260
261 server
262 .parse_checked_message(ButtplugCheckedClientMessageV4::from(
263 CheckedOutputCmdV4::new(
264 0,
265 device_index,
266 0,
267 "f50a528b-b023-40f0-9906-df037443950a".try_into().unwrap(),
268 OutputCommand::Vibrate(OutputValue::new(64)),
269 ),
270 ))
271 .await
272 .expect("Test, assuming infallible.");
273
274 check_test_recv_value(
275 &Duration::from_millis(150),
276 &mut device,
277 HardwareCommand::Write(HardwareWriteCmd::new(
278 &[Uuid::nil()],
279 Endpoint::Tx,
280 vec![0xF1, 64],
281 false,
282 )),
283 )
284 .await;
285 /*
286 // Wait out the ping, we should get a stop message.
287 let mut i = 0u32;
288 while command_receiver.is_empty() {
289 Delay::new(Duration::from_millis(150)).await;
290 // Breaks out of loop if we wait for too long.
291 i += 1;
292 assert!(i < 10, "Slept for too long while waiting for stop command!");
293 }
294 check_test_recv_value(
295 &command_receiver,
296 HardwareCommand::Write(DeviceWriteCmd::new(Endpoint::Tx, vec![0xF1, 0], false)),
297 );
298 */
299}
300
301#[tokio::test]
302async fn test_repeated_handshake() {
303 let msg = RequestServerInfoV4::new(
304 "Test Client",
305 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
306 BUTTPLUG_CURRENT_API_MINOR_VERSION,
307 );
308
309 let (server, _recv) = setup_test_server((msg.clone()).into()).await;
310 assert!(server.connected());
311 let err = server
312 .parse_message(ButtplugClientMessageVariant::V4(msg.into()))
313 .await
314 .unwrap_err();
315 if let ButtplugServerMessageVariant::V4(ButtplugServerMessageV4::Error(e)) = err {
316 assert!(matches!(
317 e.original_error(),
318 ButtplugError::ButtplugHandshakeError(ButtplugHandshakeError::HandshakeAlreadyHappened)
319 ));
320 } else {
321 panic!("Should've gotten error")
322 }
323}
324
325#[tokio::test]
326async fn test_invalid_device_index() {
327 let msg = RequestServerInfoV4::new(
328 "Test Client",
329 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
330 BUTTPLUG_CURRENT_API_MINOR_VERSION,
331 );
332 let (server, _) = setup_test_server(msg.into()).await;
333 let err = server
334 .parse_message(ButtplugClientMessageVariant::V4(
335 OutputCmdV4::new(10, 0, OutputCommand::Vibrate(OutputValue::new(0))).into(),
336 ))
337 .await
338 .unwrap_err();
339 if let ButtplugServerMessageVariant::V4(ButtplugServerMessageV4::Error(e)) = err {
340 assert!(matches!(
341 e.original_error(),
342 ButtplugError::ButtplugDeviceError(ButtplugDeviceError::DeviceNotAvailable(_))
343 ));
344 } else {
345 panic!("Should've gotten error")
346 }
347}
348
349#[tokio::test]
350async fn test_device_index_generation() {
351 let mut builder = TestDeviceCommunicationManagerBuilder::default();
352 let mut _device1 = builder.add_test_device(&TestDeviceIdentifier::new("Massage Demo", None));
353 let mut _device2 = builder.add_test_device(&TestDeviceIdentifier::new("Massage Demo", None));
354
355 let server = test_server_with_comm_manager(builder);
356
357 let recv = server.server_version_event_stream();
358 pin_mut!(recv);
359 assert!(
360 server
361 .parse_checked_message(
362 RequestServerInfoV4::new(
363 "Test Client",
364 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
365 BUTTPLUG_CURRENT_API_MINOR_VERSION
366 )
367 .into()
368 )
369 .await
370 .is_ok()
371 );
372 assert!(
373 server
374 .parse_checked_message(StartScanningV0::default().into())
375 .await
376 .is_ok()
377 );
378 // Check that we got an event back about a new device.
379 let mut index = 0u32;
380 while let Some(msg) = recv.next().await {
381 if let ButtplugServerMessageV4::ScanningFinished(_) = msg {
382 continue;
383 } else if let ButtplugServerMessageV4::DeviceList(list) = msg {
384 let da = &list.devices()[&0];
385 assert_eq!(da.device_name(), "Aneros Vivi");
386 // Devices aren't guaranteed to be added in any specific order, the
387 // scheduler will do whatever it wants. So check boundaries instead of
388 // exact.
389 assert!(da.device_index() < 2);
390 index += 1;
391 // Found both devices we're looking for, finish test.
392 if index == 2 {
393 break;
394 }
395 } else {
396 panic!(
397 "Returned message was not a DeviceAdded message or timed out: {:?}",
398 msg
399 );
400 }
401 }
402}
403
404#[tokio::test]
405async fn test_server_scanning_finished() {
406 let mut builder = TestDeviceCommunicationManagerBuilder::default();
407 let mut _device1 = builder.add_test_device(&TestDeviceIdentifier::new("Massage Demo", None));
408 let mut _device2 = builder.add_test_device(&TestDeviceIdentifier::new("Massage Demo", None));
409
410 let server = test_server_with_comm_manager(builder);
411
412 let recv = server.server_version_event_stream();
413 pin_mut!(recv);
414 assert!(
415 server
416 .parse_checked_message(
417 RequestServerInfoV4::new(
418 "Test Client",
419 BUTTPLUG_CURRENT_API_MAJOR_VERSION,
420 BUTTPLUG_CURRENT_API_MINOR_VERSION
421 )
422 .into()
423 )
424 .await
425 .is_ok()
426 );
427 assert!(
428 server
429 .parse_checked_message(StartScanningV0::default().into())
430 .await
431 .is_ok()
432 );
433 // Check that we got an event back about a new device.
434 let mut count = 0u32;
435 let mut finish_received = false;
436 // We should get 3 messages: 2 DeviceAdded, 1 ScanningFinished.
437 while let Some(msg) = recv.next().await {
438 if matches!(msg, ButtplugServerMessageV4::ScanningFinished(_)) {
439 finish_received = true;
440 break;
441 }
442 count += 1;
443 if count == 3 {
444 break;
445 }
446 }
447 assert!(finish_received);
448}
449
450// TODO Test sending system message (Id 0)
451// TODO Test sending system message (Ok but Id > 0)
452// TODO Test scan with no comm managers
453// TODO Test message with no RequestServerInfo first
454// TODO Test sending device command for device that doesn't exist (in server)