main.rs (2871B)
1 #![forbid(unsafe_code)] 2 #![cfg_attr(coverage_nightly, feature(coverage_attribute))] 3 4 use std::process::ExitCode; 5 6 use anyhow::Result; 7 8 #[cfg(not(test))] 9 #[cfg_attr(coverage_nightly, coverage(off))] 10 #[tokio::main] 11 async fn main() -> ExitCode { 12 exit_code_from_run(run().await) 13 } 14 15 #[cfg(test)] 16 fn main() -> ExitCode { 17 exit_code_from_run(Ok(())) 18 } 19 20 fn exit_code_from_run(result: Result<()>) -> ExitCode { 21 match result { 22 Ok(()) => ExitCode::SUCCESS, 23 Err(err) => { 24 tracing::error!(error = ?err, "Fatal error"); 25 eprintln!("Fatal error: {err:#}"); 26 ExitCode::FAILURE 27 } 28 } 29 } 30 31 #[cfg(test)] 32 static RUN_HOOK: std::sync::OnceLock<std::sync::Mutex<Option<Result<(), String>>>> = 33 std::sync::OnceLock::new(); 34 35 #[cfg(test)] 36 fn run_hook() -> &'static std::sync::Mutex<Option<Result<(), String>>> { 37 RUN_HOOK.get_or_init(|| std::sync::Mutex::new(None)) 38 } 39 40 #[cfg(test)] 41 fn take_run_hook_result() -> Option<Result<(), String>> { 42 run_hook() 43 .lock() 44 .unwrap_or_else(std::sync::PoisonError::into_inner) 45 .take() 46 } 47 48 #[cfg(test)] 49 async fn run() -> Result<()> { 50 if let Some(result) = take_run_hook_result() { 51 return result.map_err(anyhow::Error::msg); 52 } 53 Err(anyhow::anyhow!("run hook not set")) 54 } 55 56 #[cfg(not(test))] 57 #[cfg_attr(coverage_nightly, coverage(off))] 58 async fn run() -> Result<()> { 59 radrootsd::run_daemon().await.map_err(anyhow::Error::new) 60 } 61 62 #[cfg(test)] 63 #[cfg_attr(coverage_nightly, coverage(off))] 64 mod tests { 65 use super::{exit_code_from_run, main, run, run_hook}; 66 use std::process::ExitCode; 67 use tokio::sync::{Mutex, MutexGuard}; 68 69 static TEST_LOCK: Mutex<()> = Mutex::const_new(()); 70 71 async fn test_guard() -> MutexGuard<'static, ()> { 72 let guard = TEST_LOCK.lock().await; 73 *run_hook() 74 .lock() 75 .unwrap_or_else(std::sync::PoisonError::into_inner) = None; 76 guard 77 } 78 79 #[test] 80 fn exit_code_from_run_maps_success_and_error() { 81 assert_eq!(exit_code_from_run(Ok(())), ExitCode::SUCCESS); 82 assert_eq!( 83 exit_code_from_run(Err(anyhow::anyhow!("boom"))), 84 ExitCode::FAILURE 85 ); 86 } 87 88 #[test] 89 fn main_returns_success_in_test_build() { 90 assert_eq!(main(), ExitCode::SUCCESS); 91 } 92 93 #[tokio::test] 94 async fn run_returns_error_when_hook_is_missing() { 95 let _guard = test_guard().await; 96 let err = run().await.expect_err("hook missing should error"); 97 let msg = format!("{err:#}"); 98 assert!(msg.contains("run hook not set")); 99 } 100 101 #[tokio::test] 102 async fn run_uses_hook_result() { 103 let _guard = test_guard().await; 104 *run_hook() 105 .lock() 106 .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(Ok(())); 107 assert!(run().await.is_ok()); 108 } 109 }