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
8/*
9mod util;
10use util::{test_client, test_client_with_delayed_device_manager, test_client_with_device};
11extern crate buttplug;
12extern crate tracing;
13
14use buttplug::{
15 client::{connector::ButtplugInProcessClientConnectorBuilder, ButtplugClient, ButtplugClientError, ButtplugClientEvent},
16 core::{
17 connector::{
18 ButtplugConnector,
19 ButtplugConnectorError,
20 ButtplugConnectorResultFuture,
21 },
22 errors::{ButtplugDeviceError, ButtplugError},
23 message::{ButtplugClientMessageCurrent, ButtplugClientMessageV4, ButtplugServerMessageCurrent, ButtplugServerMessageV4},
24 },
25 server::{message::{ButtplugClientMessageVariant, ButtplugServerMessageVariant}, ButtplugServerBuilder},
26};
27
28use futures::{future::BoxFuture, StreamExt};
29use std::time::Duration;
30use tokio::{sync::mpsc::Sender, time::sleep};
31
32#[derive(Default)]
33struct ButtplugFailingConnector {}
34
35impl ButtplugConnector<ButtplugClientMessageVariant, ButtplugServerMessageVariant>
36 for ButtplugFailingConnector
37{
38 fn connect(
39 &mut self,
40 _: Sender<ButtplugServerMessageVariant>,
41 ) -> BoxFuture<'static, Result<(), ButtplugConnectorError>> {
42 ButtplugConnectorError::ConnectorNotConnected.into()
43 }
44
45 fn disconnect(&self) -> ButtplugConnectorResultFuture {
46 ButtplugConnectorError::ConnectorNotConnected.into()
47 }
48
49 fn send(&self, _msg: ButtplugClientMessageVariant) -> ButtplugConnectorResultFuture {
50 panic!("Should never be called")
51 }
52}
53
54
55#[tokio::test]
56async fn test_failing_connection() {
57 let client = ButtplugClient::new("Test Client");
58 assert!(client
59 .connect(ButtplugFailingConnector::default())
60 .await
61 .is_err());
62}
63
64
65#[tokio::test]
66async fn test_disconnect_status() {
67 let client = test_client().await;
68 assert!(client.disconnect().await.is_ok());
69 assert!(!client.connected());
70}
71
72
73#[tokio::test]
74async fn test_double_disconnect() {
75 let client = test_client().await;
76 assert!(client.disconnect().await.is_ok());
77 assert!(client.disconnect().await.is_err());
78}
79
80
81#[tokio::test]
82async fn test_connect_init() {
83 let client = test_client().await;
84 assert_eq!(client.server_name(), Some("Buttplug Server".to_owned()));
85}
86
87
88#[tokio::test]
89async fn test_client_connected_status() {
90 let client = test_client().await;
91 client
92 .disconnect()
93 .await
94 .expect("Test, assuming infallible.");
95 assert!(!client.connected());
96}
97
98
99#[tokio::test]
100async fn test_start_scanning() {
101 let (client, _) = test_client_with_device().await;
102 assert!(client.start_scanning().await.is_ok());
103}
104
105
106#[tokio::test]
107#[ignore = "We may want to just call this Ok now?"]
108async fn test_stop_scanning_when_not_scanning() {
109 let (client, _) = test_client_with_device().await;
110 let should_be_err = client.stop_scanning().await;
111 if let Err(ButtplugClientError::ButtplugError(bp_err)) = should_be_err {
112 assert!(matches!(
113 bp_err,
114 ButtplugError::ButtplugDeviceError(ButtplugDeviceError::DeviceScanningAlreadyStopped)
115 ));
116 } else {
117 panic!("Should've thrown error!");
118 }
119 assert!(client.stop_scanning().await.is_err());
120}
121
122
123#[tokio::test]
124async fn test_start_scanning_when_already_scanning() {
125 let client = test_client_with_delayed_device_manager().await;
126 assert!(client.start_scanning().await.is_ok());
127 assert!(client.start_scanning().await.is_ok());
128}
129
130
131#[tokio::test]
132async fn test_successive_start_scanning() {
133 let (client, _) = test_client_with_device().await;
134 assert!(client.start_scanning().await.is_ok());
135 assert!(client.start_scanning().await.is_ok());
136}
137
138
139#[tokio::test]
140async fn test_client_scanning_finished() {
141 let (client, _) = test_client_with_device().await;
142 let mut recv = client.event_stream();
143 assert!(client.start_scanning().await.is_ok());
144 assert!(matches!(
145 recv.next().await.expect("Test, assuming infallible."),
146 ButtplugClientEvent::ScanningFinished
147 ));
148}
149
150
151#[tokio::test]
152async fn test_client_ping() {
153 let server = ButtplugServerBuilder::default()
154 .max_ping_time(200)
155 .finish()
156 .expect("Test, assuming infallible.");
157 let connector = ButtplugInProcessClientConnectorBuilder::default()
158 .server(server)
159 .finish();
160 let client = ButtplugClient::new("Test Client");
161 client
162 .connect(connector)
163 .await
164 .expect("Test, assuming infallible.");
165 assert!(client.ping().await.is_ok());
166 sleep(Duration::from_millis(800)).await;
167 // TODO Watch for ping events
168 assert!(client.ping().await.is_err());
169}
170/*
171// Tests both the stop all devices functionality, as well as both ends of the
172// command range for is_in_command_range message validation.
173
174#[tokio::test]
175async fn test_stop_all_devices_and_device_command_range() {
176 let (client, test_device) = test_client_with_device().await;
177 let mut event_stream = client.event_stream();
178 assert!(client.start_scanning().await.is_ok());
179
180 while let Some(event) = event_stream.next().await {
181 if let ButtplugClientEvent::DeviceAdded(dev) = event {
182 info!("{:?}", dev.vibrate(ScalarCommand::Scalar(0.5)).await);
183 assert!(dev.vibrate(ScalarCommand::Scalar(0.5)).await.is_ok());
184 // Unlike protocol unit tests, here the endpoint doesn't exist until
185 // after device creation, so create the test receiver later.
186 let command_receiver = test_device
187 .endpoint_receiver(&Endpoint::Tx)
188 .expect("Test, assuming infallible.");
189 check_test_recv_value(
190 &command_receiver,
191 HardwareCommand::Write(HardwareWriteCmd::new(Endpoint::Tx, vec![0xF1, 64], false)),
192 );
193 check_test_recv_value(
194 &command_receiver,
195 HardwareCommand::Write(HardwareWriteCmd::new(Endpoint::Tx, vec![0xF2, 64], false)),
196 );
197 assert!(dev.vibrate(ScalarCommand::Scalar(1.0)).await.is_ok());
198 check_test_recv_value(
199 &command_receiver,
200 HardwareCommand::Write(HardwareWriteCmd::new(Endpoint::Tx, vec![0xF1, 127], false)),
201 );
202 check_test_recv_value(
203 &command_receiver,
204 HardwareCommand::Write(HardwareWriteCmd::new(Endpoint::Tx, vec![0xF2, 127], false)),
205 );
206 assert!(client.stop_all_devices().await.is_ok());
207 check_test_recv_value(
208 &command_receiver,
209 HardwareCommand::Write(HardwareWriteCmd::new(Endpoint::Tx, vec![0xF1, 0], false)),
210 );
211 check_test_recv_value(
212 &command_receiver,
213 HardwareCommand::Write(HardwareWriteCmd::new(Endpoint::Tx, vec![0xF2, 0], false)),
214 );
215 break;
216 }
217 }
218 assert!(client.stop_all_devices().await.is_ok());
219}
220*/
221// TODO Test calling connect twice
222// TODO Test calling disconnect twice w/o connection
223// TODO Test invalid return on RequestServerInfo
224// TODO Test invalid return on DeviceList
225// TODO Test receiving unmatched Ok (should emit error)
226// TODO Test receiving unmatched DeviceRemoved
227// TODO Test receiving Error when expecting Ok (i.e. StartScanning returns an error)
228// TODO Test receiving wrong message expecting Ok (i.e. StartScanning returns DeviceList)
229*/