main.rs (2883B)
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::app::run().await 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 std::sync::Mutex; 68 69 static TEST_LOCK: Mutex<()> = Mutex::new(()); 70 71 fn test_guard() -> std::sync::MutexGuard<'static, ()> { 72 let guard = TEST_LOCK 73 .lock() 74 .unwrap_or_else(std::sync::PoisonError::into_inner); 75 *run_hook() 76 .lock() 77 .unwrap_or_else(std::sync::PoisonError::into_inner) = None; 78 guard 79 } 80 81 #[test] 82 fn exit_code_from_run_maps_success_and_error() { 83 assert_eq!(exit_code_from_run(Ok(())), ExitCode::SUCCESS); 84 assert_eq!( 85 exit_code_from_run(Err(anyhow::anyhow!("boom"))), 86 ExitCode::FAILURE 87 ); 88 } 89 90 #[test] 91 fn main_returns_success_in_test_build() { 92 assert_eq!(main(), ExitCode::SUCCESS); 93 } 94 95 #[tokio::test] 96 async fn run_returns_error_when_hook_is_missing() { 97 let _guard = test_guard(); 98 let err = run().await.expect_err("hook missing should error"); 99 let msg = format!("{err:#}"); 100 assert!(msg.contains("run hook not set")); 101 } 102 103 #[tokio::test] 104 async fn run_uses_hook_result() { 105 let _guard = test_guard(); 106 *run_hook() 107 .lock() 108 .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(Ok(())); 109 assert!(run().await.is_ok()); 110 } 111 }