Skip to content

Commit

Permalink
Move more shared test code to top
Browse files Browse the repository at this point in the history
  • Loading branch information
RReverser committed Aug 31, 2024
1 parent d6c679a commit d77b96d
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 39 deletions.
53 changes: 49 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,11 @@ mod test_utils {
use crate::api::{DevicePath, DeviceType};
use crate::{Client, Server};
use std::process::Stdio;
use std::sync::{Arc, Weak};
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::{Child, Command};
use tokio::sync::Mutex;
use tokio::task::AbortHandle;
use tracing::instrument;
use tracing_subscriber::prelude::*;

#[ctor::ctor]
Expand Down Expand Up @@ -414,14 +415,19 @@ mod test_utils {
.expect("Failed to install color_eyre");
}

pub(crate) enum TestKind {
Protocol,
Conformance,
}

pub(crate) struct PassthroughTestEnv {
simulators: Child,
server_abort: AbortHandle,
api_path: String,
}

impl PassthroughTestEnv {
pub(crate) async fn try_new() -> eyre::Result<Self> {
async fn try_new() -> eyre::Result<Self> {
let simulators =
Command::new(r"C:\Program Files\ASCOM\OmniSimulator\ascom.alpaca.simulators.exe")
.stdin(Stdio::null())
Expand Down Expand Up @@ -453,11 +459,14 @@ mod test_utils {
})
}

pub(crate) async fn test_device_type(&self, ty: DeviceType) -> eyre::Result<()> {
async fn run_test(&self, ty: DeviceType, kind: TestKind) -> eyre::Result<()> {
let api_path = &self.api_path;

let mut conformu = Command::new(r"C:\Program Files\ASCOM\ConformU\conformu.exe")
.arg("alpacaprotocol")
.arg(match kind {
TestKind::Protocol => "alpacaprotocol",
TestKind::Conformance => "conformance",
})
.arg(format!(
"{api_path}{device_path}/0",
device_path = DevicePath(ty)
Expand Down Expand Up @@ -501,6 +510,42 @@ mod test_utils {

Ok(())
}

async fn run_tests(&self, ty: DeviceType) -> eyre::Result<()> {
self.run_test(ty, TestKind::Protocol).await?;
self.run_test(ty, TestKind::Conformance).await
}

/// Get the shared test environment with an incremented refcount.
///
/// If one is already available - which should happen when running tests in parallel - then just acquire a refcounted copy.
/// If not, put a new one in place.
///
/// This way, each test increases the refcount at start and decreases it at the end, so whichever happens to run last,
/// kills the simulator running in the background.
///
/// This is a workaround for Rust test framework not having an "after all" hook.
async fn acquire() -> eyre::Result<Arc<Self>> {
// Note: the static variable should only contain a Weak copy, otherwise the test environment
// would never be dropped, and we want it to be dropped at the end of the last strong copy
// (last running test).
static TEST_ENV: Mutex<Weak<PassthroughTestEnv>> = Mutex::const_new(Weak::new());

let mut lock = TEST_ENV.lock().await;

Ok(match lock.upgrade() {
Some(env) => env,
None => {
let env = Arc::new(Self::try_new().await?);
*lock = Arc::downgrade(&env);
env
}
})
}

pub(crate) async fn acquire_and_test(ty: DeviceType) -> eyre::Result<()> {
Self::acquire().await?.run_tests(ty).await
}
}

impl Drop for PassthroughTestEnv {
Expand Down
38 changes: 3 additions & 35 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,48 +395,16 @@ macro_rules! rpc_mod {
}

#[cfg(test)]
mod test_passthrough {
use std::sync::{Arc, Weak};
use tokio::sync::Mutex;
use $crate::test_utils::PassthroughTestEnv;
mod conformu {
use super::DeviceType;

// Note: the static variable should only contain a Weak copy, otherwise the test environment
// would never be dropped, and we want it to be dropped at the end of the last strong copy
// (last running test).
static TEST_ENV: Mutex<Weak<PassthroughTestEnv>> = Mutex::const_new(Weak::new());

/// Get the shared test environment with an incremented refcount.
///
/// If one is already available - which should happen when running tests in parallel - then just acquire a refcounted copy.
/// If not, put a new one in place.
///
/// This way, each test increases the refcount at start and decreases it at the end, so whichever happens to run last,
/// kills the simulator running in the background.
///
/// This is a workaround for Rust test framework not having an "after all" hook.
async fn acquire_test_env() -> eyre::Result<Arc<PassthroughTestEnv>> {
let mut lock = TEST_ENV.lock().await;

Ok(match lock.upgrade() {
Some(env) => env,
None => {
let env = Arc::new(PassthroughTestEnv::try_new().await?);
*lock = Arc::downgrade(&env);
env
}
})
}
use $crate::test_utils::PassthroughTestEnv;

$(
#[cfg(feature = $path)]
#[tokio::test]
#[allow(non_snake_case)]
async fn $trait_name() -> eyre::Result<()> {
acquire_test_env()
.await?
.test_device_type(DeviceType::$trait_name)
.await
PassthroughTestEnv::acquire_and_test(DeviceType::$trait_name).await
}
)*
}
Expand Down

0 comments on commit d77b96d

Please sign in to comment.