audio-plugin-bsd 0.1.1

Dynamic .so audio-plugin loader with ABI verification and FreeBSD Capsicum/pdfork per-process sandboxing for real-time audio in Rust
Documentation
//! Shared helpers for the `tests/` integration-test crates.
//!
//! The headline lifecycle test needs a *real* compiled `cdylib` to `dlopen`.
//! [`ensure_test_plugin_built`] builds the `test_plugin` example on demand and
//! returns the path to the resulting shared object.

use std::path::PathBuf;
use std::process::Command;

/// Build the `test_plugin` cdylib example and return the path to the built
/// shared object.
///
/// Runs `cargo build --example test_plugin` (using the `CARGO` env var set by
/// the test harness, falling back to `"cargo"`), then locates the artifact
/// under `target/{profile}/examples/`. The build is idempotent: `cargo`
/// short-circuits when the artifact is already up to date, so calling this
/// from multiple tests is cheap.
///
/// # Panics
///
/// Panics with a clear message if the build fails or the artifact cannot be
/// located. Integration tests are not expected to recover from a build
/// failure, so a panic is the appropriate signal.
pub fn ensure_test_plugin_built() -> PathBuf {
    let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
    let output = Command::new(&cargo)
        .args(["build", "--example", "test_plugin"])
        .output()
        .unwrap_or_else(|e| panic!("failed to spawn `{cargo:?} build --example test_plugin`: {e}"));
    if !output.status.success() {
        panic!(
            "`cargo build --example test_plugin` failed (exit {:?}):\n\
             --- stdout ---\n{}\n\
             --- stderr ---\n{}",
            output.status.code(),
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr),
        );
    }
    find_test_plugin_artifact().unwrap_or_else(|| {
        panic!(
            "test_plugin artifact not found after a successful \
             `cargo build --example test_plugin`; verify the example declares \
             `crate-type = [\"cdylib\"]` in Cargo.toml and that the target dir \
             layout matches the search roots probed in `find_test_plugin_artifact`"
        )
    })
}

/// Locate the built `test_plugin` shared object under the target dir.
///
/// Probes `CARGO_TARGET_DIR` (if set) and `{CARGO_MANIFEST_DIR}/target`, in
/// both `debug` and `release` profiles, for the platform-shaped cdylib name.
fn find_test_plugin_artifact() -> Option<PathBuf> {
    let (prefix, suffix) = platform_prefix_suffix();
    let file_name = format!("{prefix}test_plugin{suffix}");

    let mut roots = Vec::new();
    if let Some(td) = std::env::var_os("CARGO_TARGET_DIR") {
        roots.push(PathBuf::from(td));
    }
    if let Some(md) = std::env::var_os("CARGO_MANIFEST_DIR") {
        roots.push(PathBuf::from(md).join("target"));
    }
    // Last-resort relative path (works when the test is run from the crate
    // root, e.g. under some IDEs).
    roots.push(PathBuf::from("target"));

    // Probe the profile that matches the running test first, then the other,
    // so a `--release` test run still finds a release example and vice-versa.
    let profiles: [&str; 2] = if cfg!(debug_assertions) {
        ["debug", "release"]
    } else {
        ["release", "debug"]
    };

    for root in &roots {
        for profile in profiles {
            let candidate = root.join(profile).join("examples").join(&file_name);
            if candidate.is_file() {
                return Some(candidate);
            }
        }
    }
    None
}

/// `(prefix, suffix)` for a cdylib artifact on the current platform.
fn platform_prefix_suffix() -> (&'static str, &'static str) {
    if cfg!(target_os = "windows") {
        ("", ".dll")
    } else if cfg!(target_os = "macos") {
        ("lib", ".dylib")
    } else {
        // Linux, FreeBSD, and other Unix use the `.so` extension with the
        // `lib` prefix.
        ("lib", ".so")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn platform_prefix_suffix_matches_cargo_convention() {
        let (prefix, suffix) = platform_prefix_suffix();
        if cfg!(target_os = "windows") {
            assert_eq!((prefix, suffix), ("", ".dll"));
        } else if cfg!(target_os = "macos") {
            assert_eq!((prefix, suffix), ("lib", ".dylib"));
        } else {
            assert_eq!((prefix, suffix), ("lib", ".so"));
        }
    }
}