#![deny(missing_docs)]
pub use fidl_fuchsia_diagnostics::{Interest, Severity};
pub use fuchsia_macro::{main, test};
use libc as _;
use std::future::Future;
#[doc(hidden)]
pub use tracing::error;
#[derive(Default, Clone)]
pub struct LoggingOptions<'a> {
pub tags: &'a [&'static str],
pub interest: fidl_fuchsia_diagnostics::Interest,
pub blocking: bool,
pub panic_prefix: &'static str,
pub always_log_file_line: bool,
}
#[cfg(not(target_os = "fuchsia"))]
impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
fn from(logging: LoggingOptions<'a>) -> Self {
let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
if let Some(severity) = logging.interest.min_severity {
options = options.minimum_severity(severity);
}
options = options.panic_prefix(logging.panic_prefix);
options
}
}
#[cfg(target_os = "fuchsia")]
impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
fn from(logging: LoggingOptions<'a>) -> Self {
let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
options = options.blocking(logging.blocking);
if let Some(severity) = logging.interest.min_severity {
options = options.minimum_severity(severity);
}
if logging.always_log_file_line {
options = options.always_log_file_line();
}
options = options.panic_prefix(logging.panic_prefix);
options
}
}
#[doc(hidden)]
pub fn init_logging_for_component_with_executor<'a, R>(
func: impl FnOnce() -> R + 'a,
logging: LoggingOptions<'a>,
) -> impl FnOnce() -> R + 'a {
move || {
diagnostics_log::initialize(logging.into()).expect("initialize_logging");
func()
}
}
#[doc(hidden)]
pub fn init_logging_for_component_with_threads<'a, R>(
func: impl FnOnce() -> R + 'a,
logging: LoggingOptions<'a>,
) -> impl FnOnce() -> R + 'a {
move || {
let _guard = init_logging_with_threads(logging);
func()
}
}
#[doc(hidden)]
pub fn init_logging_for_test_with_executor<'a, R>(
func: impl Fn(usize) -> R + 'a,
logging: LoggingOptions<'a>,
) -> impl Fn(usize) -> R + 'a {
move |n| {
diagnostics_log::initialize(logging.clone().into()).expect("initalize logging");
func(n)
}
}
#[doc(hidden)]
pub fn init_logging_for_test_with_threads<'a, R>(
func: impl Fn(usize) -> R + 'a,
logging: LoggingOptions<'a>,
) -> impl Fn(usize) -> R + 'a {
move |n| {
let _guard = init_logging_with_threads(logging.clone());
func(n)
}
}
#[cfg(target_os = "fuchsia")]
fn init_logging_with_threads(logging: LoggingOptions<'_>) -> impl Drop {
diagnostics_log::initialize_sync(logging.into())
}
#[cfg(not(target_os = "fuchsia"))]
fn init_logging_with_threads(logging: LoggingOptions<'_>) {
diagnostics_log::initialize(logging.into()).expect("initialize logging");
}
#[doc(hidden)]
pub fn main_not_async<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
f()
}
#[doc(hidden)]
pub fn main_singlethreaded<F, Fut, R>(f: F) -> R
where
F: FnOnce() -> Fut,
Fut: Future<Output = R> + 'static,
{
fuchsia_async::LocalExecutor::new().run_singlethreaded(f())
}
#[doc(hidden)]
pub fn main_multithreaded<F, Fut, R>(f: F, num_threads: usize) -> R
where
F: FnOnce() -> Fut,
Fut: Future<Output = R> + Send + 'static,
R: Send + 'static,
{
fuchsia_async::SendExecutor::new(num_threads).run(f())
}
#[doc(hidden)]
pub fn test_not_async<F, R>(f: F) -> R
where
F: FnOnce(usize) -> R,
R: fuchsia_async::test_support::TestResult,
{
let result = f(0);
if result.is_ok() {
install_lsan_hook();
}
result
}
#[doc(hidden)]
pub fn test_singlethreaded<F, Fut, R>(f: F) -> R
where
F: Fn(usize) -> Fut + Sync + 'static,
Fut: Future<Output = R> + 'static,
R: fuchsia_async::test_support::TestResult,
{
let result = fuchsia_async::test_support::run_singlethreaded_test(f);
if result.is_ok() {
install_lsan_hook();
}
result
}
#[doc(hidden)]
pub fn test_multithreaded<F, Fut, R>(f: F, num_threads: usize) -> R
where
F: Fn(usize) -> Fut + Sync + 'static,
Fut: Future<Output = R> + Send + 'static,
R: fuchsia_async::test_support::MultithreadedTestResult,
{
let result = fuchsia_async::test_support::run_test(f, num_threads);
if result.is_ok() {
install_lsan_hook();
}
result
}
#[doc(hidden)]
#[cfg(target_os = "fuchsia")]
pub fn test_until_stalled<F, Fut, R>(f: F) -> R
where
F: 'static + Sync + Fn(usize) -> Fut,
Fut: 'static + Future<Output = R>,
R: fuchsia_async::test_support::TestResult,
{
let result = fuchsia_async::test_support::run_until_stalled_test(true, f);
if result.is_ok() {
install_lsan_hook();
}
result
}
#[doc(hidden)]
pub fn adapt_to_parse_arguments<A, R>(f: impl FnOnce(A) -> R) -> impl FnOnce() -> R
where
A: argh::TopLevelCommand,
{
move || f(argh::from_env())
}
#[doc(hidden)]
pub fn adapt_to_take_test_run_number<R>(f: impl Fn() -> R) -> impl Fn(usize) -> R {
move |_| f()
}
#[doc(hidden)]
#[cfg(not(feature = "variant_asan"))]
pub fn disable_lsan_for_should_panic() {}
#[doc(hidden)]
#[cfg(feature = "variant_asan")]
pub fn disable_lsan_for_should_panic() {
extern "C" {
fn __lsan_disable();
}
unsafe {
__lsan_disable();
}
}
#[cfg(not(feature = "variant_asan"))]
fn install_lsan_hook() {}
#[cfg(feature = "variant_asan")]
fn install_lsan_hook() {
extern "C" {
fn __lsan_do_leak_check();
}
extern "C" fn lsan_do_leak_check() {
unsafe { __lsan_do_leak_check() }
}
let err = unsafe { libc::atexit(lsan_do_leak_check) };
if err != 0 {
panic!("Failed to install atexit hook for LeakSanitizer: atexit returned {err}");
}
}