diff --git a/src/bin/spawn_receiver_sender_client.rs b/src/bin/spawn_receiver_sender_client.rs new file mode 100644 index 00000000..4fd15066 --- /dev/null +++ b/src/bin/spawn_receiver_sender_client.rs @@ -0,0 +1,30 @@ +// Copyright 2025 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use std::{env, process}; + +/// Test executable which connects to the one-shot server with name +/// passed in as an argument and then sends a receiver to the server +/// and then a test message to the receiver. +fn main() { + let args: Vec = env::args().collect(); + let token = args.get(1).expect("missing argument"); + + let (sub_tx, sub_rx) = ipc::channel().unwrap(); + + let tx: IpcSender> = + IpcSender::connect(token.to_string()).expect("connect failed"); + tx.send(sub_rx).expect("send failed"); + sub_tx + .send("test message".to_string()) + .expect("send failed"); + + process::exit(0); +} diff --git a/src/bin/spawn_used_receiver_sender_client.rs b/src/bin/spawn_used_receiver_sender_client.rs new file mode 100644 index 00000000..0743083a --- /dev/null +++ b/src/bin/spawn_used_receiver_sender_client.rs @@ -0,0 +1,36 @@ +// Copyright 2025 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use std::{env, process}; + +/// Test executable which connects to the one-shot server with name +/// passed in as an argument and then sends a receiver which it has +/// already used to the server and then sends a test message to the +/// receiver. +fn main() { + let args: Vec = env::args().collect(); + let token = args.get(1).expect("missing argument"); + + let (sub_tx, sub_rx) = ipc::channel().unwrap(); + sub_tx + .send("test message".to_string()) + .expect("send failed"); + let msg = sub_rx.recv().unwrap(); + assert_eq!("test message", msg); + + let tx: IpcSender> = + IpcSender::connect(token.to_string()).expect("connect failed"); + tx.send(sub_rx).expect("send failed"); + sub_tx + .send("test message".to_string()) + .expect("send failed"); + + process::exit(0); +} diff --git a/src/test.rs b/src/test.rs index e87840aa..1d483dda 100644 --- a/src/test.rs +++ b/src/test.rs @@ -163,6 +163,26 @@ fn embedded_receivers() { assert_eq!(received_person, person); } +#[test] +fn embedded_receivers_used_before_and_after_transmission() { + let person = ("Patrick Walton".to_owned(), 29); + let (sub_tx, sub_rx) = ipc::channel().unwrap(); + + sub_tx.send(person.clone()).unwrap(); + let received_person1 = sub_rx.recv().unwrap(); + assert_eq!(received_person1, person); + + let person_and_receiver = (person.clone(), sub_rx); + let (super_tx, super_rx) = ipc::channel().unwrap(); + super_tx.send(person_and_receiver).unwrap(); + let received_person_and_receiver = super_rx.recv().unwrap(); + assert_eq!(received_person_and_receiver.0, person); + + sub_tx.send(person.clone()).unwrap(); + let received_person2 = received_person_and_receiver.1.recv().unwrap(); + assert_eq!(received_person2, person); +} + #[test] fn select() { let (tx0, rx0) = ipc::channel().unwrap(); @@ -216,27 +236,6 @@ fn select() { } } -#[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] -#[test] -fn cross_process_embedded_senders_spawn() { - let person = ("Patrick Walton".to_owned(), 29); - - let server0_name = get_channel_name_arg("server0"); - let server2_name = get_channel_name_arg("server2"); - if let (Some(server0_name), Some(server2_name)) = (server0_name, server2_name) { - let (tx1, rx1): (IpcSender, IpcReceiver) = ipc::channel().unwrap(); - let tx0 = IpcSender::connect(server0_name).unwrap(); - tx0.send(tx1).unwrap(); - rx1.recv().unwrap(); - let tx2: IpcSender = IpcSender::connect(server2_name).unwrap(); - tx2.send(person.clone()).unwrap(); - - unsafe { - libc::exit(0); - } - } -} - #[cfg(not(any( feature = "force-inprocess", target_os = "windows", diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 92d69e1c..62b9eea5 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -8,7 +8,7 @@ // except according to those terms. #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] -use ipc_channel::ipc::IpcOneShotServer; +use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver}; #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] use std::{env, process}; @@ -42,3 +42,67 @@ fn spawn_one_shot_server_client() { result.code().expect("exit status code not available") ); } + +/// Test spawing a process which then acts as a client to a +/// one-shot server in the parent process. The client sends a +/// receiver and then send a message to the receiver. +#[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] +#[test] +fn spawn_receiver_sender_client() { + let executable_path: String = env!("CARGO_BIN_EXE_spawn_receiver_sender_client").to_string(); + + let (server, token) = IpcOneShotServer::>::new() + .expect("Failed to create IPC one-shot server."); + + let mut command = process::Command::new(executable_path); + let child_process = command.arg(token); + + let mut child = child_process + .spawn() + .expect("Failed to start child process"); + + let (_rx, sub_rx) = server.accept().expect("accept failed"); + + let msg = sub_rx.recv().unwrap(); + assert_eq!("test message", msg); + + let result = child.wait().expect("wait for child process failed"); + assert!( + result.success(), + "child process failed with exit status code {}", + result.code().expect("exit status code not available") + ); +} + +/// Test spawing a process which then acts as a client to a +/// one-shot server in the parent process. The client sends a +/// receiver, which it has already used, and then send a +/// message to the receiver. +#[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] +#[test] +fn spawn_used_receiver_sender_client() { + let executable_path: String = + env!("CARGO_BIN_EXE_spawn_used_receiver_sender_client").to_string(); + + let (server, token) = IpcOneShotServer::>::new() + .expect("Failed to create IPC one-shot server."); + + let mut command = process::Command::new(executable_path); + let child_process = command.arg(token); + + let mut child = child_process + .spawn() + .expect("Failed to start child process"); + + let (_rx, sub_rx) = server.accept().expect("accept failed"); + + let msg = sub_rx.recv().unwrap(); + assert_eq!("test message", msg); + + let result = child.wait().expect("wait for child process failed"); + assert!( + result.success(), + "child process failed with exit status code {}", + result.code().expect("exit status code not available") + ); +}