Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: Improve test bed #300

Merged
merged 7 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions .github/workflows/github-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ jobs:
toolchain: stable
components: rustfmt
- run: cargo fmt --all -- --check

env:
RUSTFLAGS: --cfg=socketioxide_test

test:
runs-on: ubuntu-latest

Expand All @@ -37,7 +39,8 @@ jobs:
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- run: cargo test --tests --all-features --workspace

env:
RUSTFLAGS: --cfg=socketioxide_test
udeps:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -87,6 +90,8 @@ jobs:

- name: check crates
run: cargo check -p socketioxide -p engineioxide --all-features
env:
RUSTFLAGS: --cfg=socketioxide_test

feature_set:
runs-on: ubuntu-latest
Expand All @@ -111,7 +116,25 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: check --feature-powerset
run: cargo hack check --feature-powerset --no-dev-deps --skip test-utils -p socketioxide -p engineioxide
run: cargo hack check --feature-powerset --no-dev-deps -p socketioxide -p engineioxide

examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
examples/target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-examples
- run: cd examples && cargo check --all-features

doctest:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -165,6 +188,8 @@ jobs:
--all-features
--message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
continue-on-error: true
env:
RUSTFLAGS: --cfg=socketioxide_test

- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v3
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ You will need [rustc and cargo](www.rust-lang.org/tools/install).
```shell
git clone https://github.com/totodore/socketioxide
```
2. To test socketioxide don't forget to enable the flag `socketioxide-test` through the `RUSTFLAGS` environment variable:

```shell
export RUSTFLAGS="--cfg socketioxide-test"
```
2. Depending on what you want to change, clone the [socketio/engine.io-protocol](https://github.com/socketio/engine.io-protocol) repo or the [socketio/socket.io-protocol](https://github.com/socketio/socket.io-protocol) repo or both
```shell
git clone https://github.com/socketio/engine.io-protocol
Expand Down
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ categories = [
license = "MIT"

[workspace]
members = ["engineioxide", "socketioxide", "e2e/*", "examples/*"]
members = ["engineioxide", "socketioxide", "e2e/*"]
default-members = ["engineioxide", "socketioxide"]
resolver = "2"

Expand All @@ -40,4 +40,3 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
criterion = { version = "0.5.1", features = ["html_reports"] }
axum = "0.7.2"
salvo = { version = "0.66.0", features = ["tower-compat"] }
rust_socketio = { version = "0.4.2", features = ["async"] }
1 change: 0 additions & 1 deletion engineioxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ hyper-util = { workspace = true, features = ["tokio", "client-legacy"] }

[features]
v3 = ["memchr", "unicode-segmentation"]
test-utils = []
tracing = ["dep:tracing"]

[[bench]]
Expand Down
2 changes: 1 addition & 1 deletion engineioxide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
pub use service::{ProtocolVersion, TransportType};
pub use socket::{DisconnectReason, Socket};

#[cfg(feature = "test-utils")]
#[cfg(any(test, socketioxide_test))]
pub use packet::*;

pub mod config;
Expand Down
36 changes: 30 additions & 6 deletions engineioxide/src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ impl<D: Default + Send + Sync + 'static> std::fmt::Debug for Socket<D> {
}
}

#[cfg(feature = "test-utils")]
#[cfg(socketioxide_test)]
impl<D> Drop for Socket<D>
where
D: Default + Send + Sync + 'static,
Expand All @@ -478,7 +478,7 @@ where
}
}

#[cfg(feature = "test-utils")]
#[cfg(any(socketioxide_test, test))]
impl<D> Socket<D>
where
D: Default + Send + Sync + 'static,
Expand All @@ -487,11 +487,21 @@ where
pub fn new_dummy(
sid: Sid,
close_fn: Box<dyn Fn(Sid, DisconnectReason) + Send + Sync>,
) -> Socket<D> {
let (internal_tx, internal_rx) = mpsc::channel(200);
) -> Arc<Socket<D>> {
Socket::new_dummy_piped(sid, close_fn, 1024).0
}

/// Create a dummy socket for testing purpose with a
/// receiver to get the packets sent to the client
pub fn new_dummy_piped(
sid: Sid,
close_fn: Box<dyn Fn(Sid, DisconnectReason) + Send + Sync>,
buffer_size: usize,
) -> (Arc<Socket<D>>, tokio::sync::mpsc::Receiver<Packet>) {
let (internal_tx, internal_rx) = mpsc::channel(buffer_size);
let (heartbeat_tx, heartbeat_rx) = mpsc::channel(1);

Self {
let sock = Self {
id: sid,
protocol: ProtocolVersion::V4,
transport: AtomicU8::new(TransportType::Websocket as u8),
Expand All @@ -509,6 +519,20 @@ where

#[cfg(feature = "v3")]
supports_binary: true,
}
};
let sock = Arc::new(sock);

let (tx, rx) = mpsc::channel(buffer_size);
let sock_clone = sock.clone();
tokio::spawn(async move {
let mut internal_rx = sock_clone.internal_rx.try_lock().unwrap();
while let Some(packets) = internal_rx.recv().await {
for packet in packets {
tx.send(packet).await.unwrap();
}
}
});

(sock, rx)
}
}
16 changes: 16 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[workspace]
members = ["*"]
exclude = ["target"]
resolver = "2"

[workspace.dependencies]
futures = "0.3.27"
tokio = "1.35.0"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
tower = { version = "0.4.13", default-features = false }
tracing = "0.1.37"
tracing-subscriber = "0.3.18"
axum = "0.7.5"
hyper-util.version = "0.1.1"
hyper = { version = "1.0.1", features = ["http1", "http2", "server"] }
2 changes: 1 addition & 1 deletion examples/salvo-echo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ rust-version = "1.67" # required by salvo

[dependencies]
socketioxide = { path = "../../socketioxide", features = ["tracing"] }
salvo.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
tracing-subscriber.workspace = true
tracing.workspace = true
serde_json.workspace = true
tower.workspace = true
tower-http = { version = "0.5.0", features = ["cors"] }
salvo = { version = "0.66", features = ["tower-compat"] }

[[bin]]
name = "salvo-echo"
Expand Down
8 changes: 1 addition & 7 deletions socketioxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,13 @@ state = { version = "0.6.0", optional = true }

[features]
v4 = ["engineioxide/v3"]
test-utils = []
tracing = ["dep:tracing", "engineioxide/tracing"]
extensions = ["dep:dashmap"]
state = ["dep:state"]

[dev-dependencies]
engineioxide = { path = "../engineioxide", features = [
"v3",
"tracing",
"test-utils",
] }
engineioxide = { path = "../engineioxide", features = ["v3", "tracing"] }
tokio-tungstenite.workspace = true
rust_socketio.workspace = true
axum.workspace = true
salvo.workspace = true
tokio = { workspace = true, features = [
Expand Down
54 changes: 51 additions & 3 deletions socketioxide/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,54 @@ impl<A: Adapter> Client<A> {
#[cfg(feature = "tracing")]
tracing::debug!("all namespaces closed");
}

#[cfg(socketioxide_test)]
pub async fn new_dummy_sock(
self: Arc<Self>,
ns: &'static str,
auth: impl serde::Serialize,
) -> (
tokio::sync::mpsc::Sender<engineioxide::Packet>,
tokio::sync::mpsc::Receiver<engineioxide::Packet>,
) {
let buffer_size = self.config.engine_config.max_buffer_size;
let sid = Sid::new();
let (esock, rx) = EIoSocket::new_dummy_piped(sid, Box::new(|_, _| {}), buffer_size);

let (tx1, mut rx1) = tokio::sync::mpsc::channel(buffer_size);
tokio::spawn({
let esock = esock.clone();
let client = self.clone();
async move {
while let Some(packet) = rx1.recv().await {
match packet {
engineioxide::Packet::Message(msg) => {
client.on_message(msg, esock.clone());
}
engineioxide::Packet::Close => {
client
.on_disconnect(esock.clone(), EIoDisconnectReason::TransportClose);
}
engineioxide::Packet::Binary(bin) => {
client.on_binary(bin, esock.clone());
}
_ => {}
}
}
}
});
let p = Packet {
ns: ns.into(),
inner: PacketData::Connect(Some(serde_json::to_string(&auth).unwrap())),
}
.into();
self.on_message(p, esock.clone());

// wait for the socket to be connected to the namespace
tokio::time::sleep(std::time::Duration::from_millis(10)).await;

(tx1, rx)
}
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -290,6 +338,7 @@ fn apply_payload_on_packet(data: Vec<u8>, socket: &EIoSocket<SocketData>) -> boo

#[cfg(test)]
mod test {
use super::*;
use tokio::sync::mpsc;

use crate::adapter::LocalAdapter;
Expand All @@ -305,13 +354,12 @@ mod test {
client
}

use super::*;
#[tokio::test]
async fn connect_timeout_fail() {
let client = create_client();
let (tx, mut rx) = mpsc::channel(1);
let close_fn = Box::new(move |_, _| tx.try_send(()).unwrap());
let sock = Arc::new(EIoSocket::new_dummy(Sid::new(), close_fn));
let sock = EIoSocket::new_dummy(Sid::new(), close_fn);
client.on_connect(sock.clone());
tokio::time::timeout(CONNECT_TIMEOUT * 2, rx.recv())
.await
Expand All @@ -324,7 +372,7 @@ mod test {
let client = create_client();
let (tx, mut rx) = mpsc::channel(1);
let close_fn = Box::new(move |_, _| tx.try_send(()).unwrap());
let sock = Arc::new(EIoSocket::new_dummy(Sid::new(), close_fn));
let sock = EIoSocket::new_dummy(Sid::new(), close_fn);
client.on_connect(sock.clone());
client.on_message("0".into(), sock.clone());
tokio::time::timeout(CONNECT_TIMEOUT * 2, rx.recv())
Expand Down
19 changes: 17 additions & 2 deletions socketioxide/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,22 @@ impl<A: Adapter> Clone for SocketIo<A> {
}
}

#[cfg(any(test, socketioxide_test))]
impl<A: Adapter> SocketIo<A> {
/// Create a dummy socket for testing purpose with a
/// receiver to get the packets sent to the client
pub async fn new_dummy_sock(
&self,
ns: &'static str,
auth: impl serde::Serialize,
) -> (
tokio::sync::mpsc::Sender<engineioxide::Packet>,
tokio::sync::mpsc::Receiver<engineioxide::Packet>,
) {
self.0.clone().new_dummy_sock(ns, auth).await
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -840,8 +856,7 @@ mod tests {
let sid = Sid::new();
let (_, io) = SocketIo::builder().build_svc();
io.ns("/", || {});

let socket = Socket::new_dummy(sid, Box::new(|_, _| {})).into();
let socket = Socket::new_dummy(sid, Box::new(|_, _| {}));
let config = SocketIoConfig::default().into();
io.0.get_ns("/")
.unwrap()
Expand Down
5 changes: 4 additions & 1 deletion socketioxide/src/ns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ impl<A: Adapter> Namespace<A> {
}

self.sockets.write().unwrap().insert(sid, socket.clone());
#[cfg(feature = "tracing")]
tracing::trace!(?socket.id, ?self.path, "socket added to namespace");

let protocol = esocket.protocol.into();

if let Err(_e) = socket.send(Packet::connect(&self.path, socket.id, protocol)) {
Expand Down Expand Up @@ -129,7 +132,7 @@ impl<A: Adapter> Namespace<A> {
}
}

#[cfg(test)]
#[cfg(any(test, socketioxide_test))]
impl<A: Adapter> Namespace<A> {
pub fn new_dummy<const S: usize>(sockets: [Sid; S]) -> Arc<Self> {
let ns = Namespace::new(Cow::Borrowed("/"), || {});
Expand Down
7 changes: 4 additions & 3 deletions socketioxide/src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,14 +809,15 @@ impl<A: Adapter> PartialEq for Socket<A> {
}
}

#[cfg(test)]
#[cfg(any(test, socketioxide_test))]
impl<A: Adapter> Socket<A> {
/// Creates a dummy socket for testing purposes
pub fn new_dummy(sid: Sid, ns: Arc<Namespace<A>>) -> Socket<A> {
let close_fn = Box::new(move |_, _| ());
let s = Socket::new(
sid,
ns,
engineioxide::Socket::new_dummy(sid, close_fn).into(),
engineioxide::Socket::new_dummy(sid, close_fn),
Arc::new(SocketIoConfig::default()),
);
s.set_connected(true);
Expand All @@ -834,7 +835,7 @@ mod test {
let ns = Namespace::<LocalAdapter>::new_dummy([sid]).into();
let socket: Arc<Socket> = Socket::new_dummy(sid, ns).into();
// Saturate the channel
for _ in 0..200 {
for _ in 0..1024 {
socket
.send(Packet::event("test", "test", Value::Null))
.unwrap();
Expand Down
Loading