Buttplug sex toy control library
at dev 13 kB view raw
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)