use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use async_trait::async_trait;
use tokio::sync::{broadcast, mpsc};
use tokio_util::sync::CancellationToken;
use crate::desktop::ipc::{IpcEvent, IpcMessage};
use crate::desktop::ipc_server::IpcServer;
use crate::desktop::transport::{self, TransportStream};
use crate::error::ServiceError;
use crate::manager::{manager_loop, ServiceFactory};
use crate::models::ServiceContext;
use crate::service_trait::BackgroundService;
static TEST_ID: AtomicU64 = AtomicU64::new(0);
pub fn unique_socket_path() -> PathBuf {
let id = TEST_ID.fetch_add(1, Ordering::Relaxed);
std::env::temp_dir().join(format!("ipc-test-{}-{id}.sock", std::process::id()))
}
pub struct BlockingService;
#[async_trait]
impl BackgroundService<tauri::test::MockRuntime> for BlockingService {
async fn init(
&mut self,
_ctx: &ServiceContext<tauri::test::MockRuntime>,
) -> Result<(), ServiceError> {
Ok(())
}
async fn run(
&mut self,
ctx: &ServiceContext<tauri::test::MockRuntime>,
) -> Result<(), ServiceError> {
ctx.shutdown.cancelled().await;
Ok(())
}
}
pub struct ImmediateSuccessService;
#[async_trait]
impl BackgroundService<tauri::test::MockRuntime> for ImmediateSuccessService {
async fn init(
&mut self,
_ctx: &ServiceContext<tauri::test::MockRuntime>,
) -> Result<(), ServiceError> {
Ok(())
}
async fn run(
&mut self,
_ctx: &ServiceContext<tauri::test::MockRuntime>,
) -> Result<(), ServiceError> {
Ok(())
}
}
pub fn setup_server_with_factory(
factory: ServiceFactory<tauri::test::MockRuntime>,
) -> (PathBuf, CancellationToken, broadcast::Sender<IpcEvent>) {
let path = unique_socket_path();
let app = tauri::test::mock_app();
let (cmd_tx, cmd_rx) = mpsc::channel(16);
tokio::spawn(manager_loop(
cmd_rx, factory, 0.0, 0.0, 0.0, 0.0, false, false, None,
));
let server = IpcServer::bind(path.clone(), cmd_tx, app.handle().clone()).unwrap();
let event_tx = server.event_sender();
let shutdown = CancellationToken::new();
let s = shutdown.clone();
tokio::spawn(async move { server.run(s).await });
(path, shutdown, event_tx)
}
pub fn setup_server() -> (PathBuf, CancellationToken, broadcast::Sender<IpcEvent>) {
setup_server_with_factory(Box::new(|| Box::new(BlockingService)))
}
pub fn setup_server_raw(
factory: ServiceFactory<tauri::test::MockRuntime>,
) -> (
IpcServer<tauri::test::MockRuntime>,
PathBuf,
CancellationToken,
) {
let path = unique_socket_path();
let app = tauri::test::mock_app();
let (cmd_tx, cmd_rx) = mpsc::channel(16);
tokio::spawn(manager_loop(
cmd_rx, factory, 0.0, 0.0, 0.0, 0.0, false, false, None,
));
let server = IpcServer::bind(path.clone(), cmd_tx, app.handle().clone()).unwrap();
let shutdown = CancellationToken::new();
(server, path, shutdown)
}
pub async fn connect(path: &PathBuf) -> TransportStream {
transport::connect(path).await.unwrap()
}
pub async fn send_request(stream: &mut TransportStream, request: &crate::desktop::ipc::IpcRequest) {
use tokio::io::AsyncWriteExt;
let msg = IpcMessage::Request(request.clone());
let frame = crate::desktop::ipc::encode_frame(&msg).unwrap();
stream.write_all(&frame).await.unwrap();
}
pub async fn read_response(stream: &mut TransportStream) -> crate::desktop::ipc::IpcResponse {
use tokio::io::AsyncReadExt;
let mut len_buf = [0u8; 4];
stream.read_exact(&mut len_buf).await.unwrap();
let len = u32::from_be_bytes(len_buf) as usize;
let mut payload = vec![0u8; len];
stream.read_exact(&mut payload).await.unwrap();
match serde_json::from_slice::<IpcMessage>(&payload).unwrap() {
IpcMessage::Response(resp) => resp,
other => panic!("Expected IpcMessage::Response, got {other:?}"),
}
}
pub async fn read_event(stream: &mut TransportStream) -> crate::desktop::ipc::IpcEvent {
use tokio::io::AsyncReadExt;
let mut len_buf = [0u8; 4];
stream.read_exact(&mut len_buf).await.unwrap();
let len = u32::from_be_bytes(len_buf) as usize;
let mut payload = vec![0u8; len];
stream.read_exact(&mut payload).await.unwrap();
match serde_json::from_slice::<IpcMessage>(&payload).unwrap() {
IpcMessage::Event(event) => event,
other => panic!("Expected IpcMessage::Event, got {other:?}"),
}
}