desktop: Add tests for connect_socket
Add tests for ExternalNavigatorBackend::connect_socket.
This commit is contained in:
parent
2535b926a7
commit
f6907a5c73
|
@ -28,6 +28,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
|
@ -457,6 +466,21 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
|
@ -2333,6 +2357,12 @@ dependencies = [
|
|||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "gl_generator"
|
||||
version = "0.14.0"
|
||||
|
@ -3169,6 +3199,22 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a82271f7bc033d84bbca59a3ce3e4159938cb08a9c3aebbe54d215131518a13"
|
||||
dependencies = [
|
||||
"macro_rules_attribute-proc_macro",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute-proc_macro"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568"
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
|
@ -3686,6 +3732,15 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.5.0"
|
||||
|
@ -4414,6 +4469,7 @@ dependencies = [
|
|||
"generational-arena",
|
||||
"image",
|
||||
"isahc",
|
||||
"macro_rules_attribute",
|
||||
"os_info",
|
||||
"rfd",
|
||||
"ruffle_core",
|
||||
|
@ -4421,6 +4477,7 @@ dependencies = [
|
|||
"ruffle_render_wgpu",
|
||||
"ruffle_video_software",
|
||||
"sys-locale",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-tracy",
|
||||
|
@ -4681,6 +4738,12 @@ dependencies = [
|
|||
name = "ruffle_wstr"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
@ -5415,6 +5478,28 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"pin-project-lite",
|
||||
"tokio-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.51",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.10"
|
||||
|
|
|
@ -46,14 +46,14 @@ impl<'gc> Socket<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ConnectionState {
|
||||
Connected,
|
||||
Failed,
|
||||
TimedOut,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum SocketAction {
|
||||
Connect(SocketHandle, ConnectionState),
|
||||
Data(SocketHandle, Vec<u8>),
|
||||
|
|
|
@ -73,3 +73,7 @@ render_trace = ["ruffle_render_wgpu/render_trace"]
|
|||
|
||||
# sandboxing
|
||||
sandbox = []
|
||||
|
||||
[dev-dependencies]
|
||||
macro_rules_attribute = "0.2.0"
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
|
|
|
@ -621,3 +621,254 @@ impl<F: FutureSpawner> NavigatorBackend for ExternalNavigatorBackend<F> {
|
|||
self.spawn_future(future);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use async_net::TcpListener;
|
||||
use ruffle_core::socket::SocketAction::{Close, Connect, Data};
|
||||
use std::net::SocketAddr;
|
||||
use tokio::task;
|
||||
|
||||
use super::*;
|
||||
|
||||
const TIMEOUT_ZERO: Duration = Duration::ZERO;
|
||||
// The timeout has to be large enough to allow "instantaneous" actions
|
||||
// and local IO to execute, but small enough to fail tests quickly.
|
||||
const TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
struct TestFutureSpawner;
|
||||
|
||||
impl FutureSpawner for TestFutureSpawner {
|
||||
fn spawn(&self, future: OwnedFuture<(), Error>) {
|
||||
task::spawn_local(future);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! async_timeout {
|
||||
() => {
|
||||
async {
|
||||
Timer::after(TIMEOUT).await;
|
||||
panic!("An action which should complete timed out")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! async_test {
|
||||
(
|
||||
async fn $test_name:ident() $content:block
|
||||
) => {
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn $test_name() {
|
||||
task::LocalSet::new().run_until(async move $content).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! dummy_handle {
|
||||
() => {
|
||||
SocketHandle::from_raw_parts(4, 2)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_next_socket_actions {
|
||||
($receiver:expr;) => {
|
||||
// no more actions
|
||||
};
|
||||
($receiver:expr; $action:expr, $($more:expr,)*) => {
|
||||
assert_eq!($receiver.recv().or(async_timeout!()).await.expect("receive action"), $action);
|
||||
assert_next_socket_actions!($receiver; $($more,)*);
|
||||
};
|
||||
}
|
||||
|
||||
fn new_test_backend(socket_allow: bool) -> ExternalNavigatorBackend<TestFutureSpawner> {
|
||||
ExternalNavigatorBackend::new(
|
||||
Url::parse("https://example.com/path/").unwrap(),
|
||||
TestFutureSpawner,
|
||||
None,
|
||||
false,
|
||||
OpenURLMode::Allow,
|
||||
Default::default(),
|
||||
if socket_allow {
|
||||
SocketMode::Allow
|
||||
} else {
|
||||
SocketMode::Deny
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn start_test_server() -> (task::JoinHandle<TcpStream>, SocketAddr) {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
let accept_task = task::spawn_local(async move {
|
||||
let (socket, _) = listener.accept().or(async_timeout!()).await.unwrap();
|
||||
socket
|
||||
});
|
||||
(accept_task, addr)
|
||||
}
|
||||
|
||||
fn connect_test_socket(
|
||||
addr: SocketAddr,
|
||||
timeout: Duration,
|
||||
socket_allow: bool,
|
||||
) -> (Sender<Vec<u8>>, Receiver<SocketAction>) {
|
||||
let mut backend = new_test_backend(socket_allow);
|
||||
|
||||
let (write, receiver) = async_channel::unbounded();
|
||||
let (sender, read) = async_channel::unbounded();
|
||||
|
||||
backend.connect_socket(
|
||||
addr.ip().to_string(),
|
||||
addr.port(),
|
||||
timeout,
|
||||
dummy_handle!(),
|
||||
receiver,
|
||||
sender,
|
||||
);
|
||||
|
||||
(write, read)
|
||||
}
|
||||
|
||||
async fn write_server(server_socket: &mut TcpStream, data: &str) {
|
||||
server_socket
|
||||
.write(data.as_bytes())
|
||||
.or(async_timeout!())
|
||||
.await
|
||||
.expect("server write");
|
||||
}
|
||||
|
||||
async fn read_server(server_socket: &mut TcpStream) -> String {
|
||||
let mut buffer = [0; 4096];
|
||||
|
||||
let read = match server_socket.read(&mut buffer).await {
|
||||
Err(e) => {
|
||||
panic!("server read error: {}", e);
|
||||
}
|
||||
Ok(read) => read,
|
||||
};
|
||||
|
||||
let buffer = buffer.into_iter().take(read).collect::<Vec<_>>();
|
||||
String::from_utf8(buffer).unwrap()
|
||||
}
|
||||
|
||||
async fn write_client(client_write: &Sender<Vec<u8>>, data: &str) {
|
||||
client_write
|
||||
.send(data.as_bytes().to_vec())
|
||||
.or(async_timeout!())
|
||||
.await
|
||||
.expect("client write");
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_timeout() {
|
||||
let (_accept_task, addr) = start_test_server().await;
|
||||
let (_client_write, client_read) = connect_test_socket(addr, TIMEOUT_ZERO, true);
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::TimedOut),
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_connect() {
|
||||
let (accept_task, addr) = start_test_server().await;
|
||||
let (_client_write, client_read) = connect_test_socket(addr, TIMEOUT, true);
|
||||
let _server_socket = accept_task.await.unwrap();
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::Connected),
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_deny() {
|
||||
let (_accept_task, addr) = start_test_server().await;
|
||||
let (_client_write, client_read) = connect_test_socket(addr, TIMEOUT, false);
|
||||
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::Failed),
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_fail() {
|
||||
let addr = SocketAddr::from_str("[100::]:42").expect("black hole address");
|
||||
let (_client_write, client_read) = connect_test_socket(addr, TIMEOUT, true);
|
||||
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::Failed),
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_server_close() {
|
||||
let (accept_task, addr) = start_test_server().await;
|
||||
let (_client_write, client_read) = connect_test_socket(addr, TIMEOUT, true);
|
||||
|
||||
let server_socket = accept_task.await.unwrap();
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::Connected),
|
||||
);
|
||||
|
||||
drop(server_socket);
|
||||
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Close(dummy_handle!()),
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_client_close() {
|
||||
let (accept_task, addr) = start_test_server().await;
|
||||
let (client_write, client_read) = connect_test_socket(addr, TIMEOUT, true);
|
||||
|
||||
let mut server_socket = accept_task.await.unwrap();
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::Connected),
|
||||
);
|
||||
|
||||
drop(client_write);
|
||||
|
||||
assert_eq!(read_server(&mut server_socket).await, "");
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(async_test)]
|
||||
async fn test_socket_basic_communication() {
|
||||
let (accept_task, addr) = start_test_server().await;
|
||||
let (client_write, client_read) = connect_test_socket(addr, TIMEOUT, true);
|
||||
|
||||
let mut server_socket = accept_task.await.unwrap();
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Connect(dummy_handle!(), ConnectionState::Connected),
|
||||
);
|
||||
|
||||
write_server(&mut server_socket, "Hello ").await;
|
||||
write_server(&mut server_socket, "World!").await;
|
||||
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Data(dummy_handle!(), "Hello World!".as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
write_client(&client_write, "Hello from").await;
|
||||
write_client(&client_write, " client").await;
|
||||
|
||||
assert_eq!(read_server(&mut server_socket).await, "Hello from client");
|
||||
|
||||
write_server(&mut server_socket, "from server 2").await;
|
||||
write_client(&client_write, "from client 2").await;
|
||||
|
||||
assert_next_socket_actions!(
|
||||
client_read;
|
||||
Data(dummy_handle!(), "from server 2".as_bytes().to_vec()),
|
||||
);
|
||||
assert_eq!(read_server(&mut server_socket).await, "from client 2");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue