1#![deny(missing_docs)]
16
17pub use fuchsia_macro::{main, test};
18use libc as _;
19#[doc(hidden)]
20pub use log::error;
21use std::future::Future;
22
23#[cfg(fuchsia_api_level_less_than = "27")]
24pub use fidl_fuchsia_diagnostics::{Interest, Severity};
25#[cfg(fuchsia_api_level_at_least = "27")]
26pub use fidl_fuchsia_diagnostics_types::{Interest, Severity};
27
28#[derive(Default, Clone)]
34pub struct LoggingOptions<'a> {
35 pub tags: &'a [&'static str],
38
39 pub interest: Interest,
42
43 pub blocking: bool,
49
50 pub panic_prefix: &'static str,
52
53 pub always_log_file_line: bool,
56}
57
58#[cfg(not(target_os = "fuchsia"))]
59impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
60 fn from(logging: LoggingOptions<'a>) -> Self {
61 let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
62 if let Some(severity) = logging.interest.min_severity {
63 options = options.minimum_severity(severity);
64 }
65 options = options.panic_prefix(logging.panic_prefix);
66 options
67 }
68}
69
70#[cfg(target_os = "fuchsia")]
71impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
72 fn from(logging: LoggingOptions<'a>) -> Self {
73 let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
74 options = options.blocking(logging.blocking);
75 if let Some(severity) = logging.interest.min_severity {
76 options = options.minimum_severity(severity);
77 }
78 if logging.always_log_file_line {
79 options = options.always_log_file_line();
80 }
81 options = options.panic_prefix(logging.panic_prefix);
82 options
83 }
84}
85
86#[doc(hidden)]
88pub fn init_logging_for_component_with_executor<'a, R>(
89 func: impl FnOnce() -> R + 'a,
90 logging: LoggingOptions<'a>,
91) -> impl FnOnce() -> R + 'a {
92 move || {
93 diagnostics_log::initialize(logging.into()).expect("initialize_logging");
94 func()
95 }
96}
97
98#[doc(hidden)]
100pub fn init_logging_for_component_with_threads<'a, R>(
101 func: impl FnOnce() -> R + 'a,
102 logging: LoggingOptions<'a>,
103) -> impl FnOnce() -> R + 'a {
104 move || {
105 let _guard = init_logging_with_threads(logging);
106 func()
107 }
108}
109
110#[doc(hidden)]
112#[cfg(target_os = "fuchsia")]
113pub fn init_logging_for_test_with_executor<'a, R>(
114 func: impl Fn(usize) -> R + 'a,
115 logging: LoggingOptions<'a>,
116) -> impl Fn(usize) -> R + 'a {
117 move |n| {
118 diagnostics_log::initialize(logging.clone().into()).expect("initalize logging");
119 func(n)
120 }
121}
122
123#[doc(hidden)]
125#[cfg(target_os = "fuchsia")]
126pub fn init_logging_for_test_with_threads<'a, R>(
127 func: impl Fn(usize) -> R + 'a,
128 logging: LoggingOptions<'a>,
129) -> impl Fn(usize) -> R + 'a {
130 move |n| {
131 init_logging_with_threads(logging.clone());
132 func(n)
133 }
134}
135
136#[cfg(target_os = "fuchsia")]
138fn init_logging_with_threads(logging: LoggingOptions<'_>) {
139 diagnostics_log::initialize_sync(logging.into());
140}
141
142#[cfg(not(target_os = "fuchsia"))]
143fn init_logging_with_threads(logging: LoggingOptions<'_>) {
144 diagnostics_log::initialize(logging.into()).expect("initialize_logging");
145}
146
147#[doc(hidden)]
149#[cfg(not(target_os = "fuchsia"))]
150pub fn init_logging_for_test_with_executor<'a, R>(
151 func: impl Fn(usize) -> R + 'a,
152 _logging: LoggingOptions<'a>,
153) -> impl Fn(usize) -> R + 'a {
154 move |n| func(n)
155}
156
157#[doc(hidden)]
159#[cfg(not(target_os = "fuchsia"))]
160pub fn init_logging_for_test_with_threads<'a, R>(
161 func: impl Fn(usize) -> R + 'a,
162 _logging: LoggingOptions<'a>,
163) -> impl Fn(usize) -> R + 'a {
164 move |n| func(n)
165}
166
167#[cfg(target_os = "fuchsia")]
168fn set_thread_role(role_name: &str) {
169 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(role_name) {
170 log::warn!(e:%, role_name:%; "Couldn't apply thread role.");
171 }
172}
173
174#[doc(hidden)]
180pub fn main_not_async<F, R>(f: F) -> R
181where
182 F: FnOnce() -> R,
183{
184 f()
185}
186
187#[doc(hidden)]
189pub fn main_not_async_with_role<F, R>(f: F, _role_name: &'static str) -> R
190where
191 F: FnOnce() -> R,
192{
193 #[cfg(target_os = "fuchsia")]
194 set_thread_role(_role_name);
195 f()
196}
197
198#[doc(hidden)]
200pub fn main_singlethreaded<F, Fut, R>(f: F) -> R
201where
202 F: FnOnce() -> Fut,
203 Fut: Future<Output = R> + 'static,
204{
205 fuchsia_async::LocalExecutorBuilder::new().build().run_singlethreaded(f())
206}
207
208#[doc(hidden)]
210pub fn main_singlethreaded_with_role<F, Fut, R>(f: F, _role_name: &'static str) -> R
211where
212 F: FnOnce() -> Fut,
213 Fut: Future<Output = R> + 'static,
214{
215 #[cfg(target_os = "fuchsia")]
216 set_thread_role(_role_name);
217 fuchsia_async::LocalExecutorBuilder::new().build().run_singlethreaded(f())
218}
219
220#[doc(hidden)]
222pub fn main_multithreaded<F, Fut, R>(f: F, num_threads: u8) -> R
223where
224 F: FnOnce() -> Fut,
225 Fut: Future<Output = R> + Send + 'static,
226 R: Send + 'static,
227{
228 fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads).build().run(f())
229}
230
231#[doc(hidden)]
234pub fn main_multithreaded_with_role<F, Fut, R>(f: F, num_threads: u8, _role_name: &'static str) -> R
235where
236 F: FnOnce() -> Fut,
237 Fut: Future<Output = R> + Send + 'static,
238 R: Send + 'static,
239{
240 #[cfg(target_os = "fuchsia")]
241 set_thread_role(_role_name);
242
243 let builder = fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads);
244
245 #[cfg(target_os = "fuchsia")]
246 let builder = builder.worker_init(move || set_thread_role(_role_name));
247
248 builder.build().run(f())
249}
250
251#[doc(hidden)]
257pub fn test_not_async<F, R>(f: F) -> R
258where
259 F: FnOnce(usize) -> R,
260 R: fuchsia_async::test_support::TestResult,
261{
262 let result = f(0);
263 if result.is_ok() {
264 install_lsan_hook();
265 }
266 result
267}
268
269#[doc(hidden)]
271pub fn test_singlethreaded<F, Fut, R>(f: F) -> R
272where
273 F: Fn(usize) -> Fut + Sync + 'static,
274 Fut: Future<Output = R> + 'static,
275 R: fuchsia_async::test_support::TestResult,
276{
277 let result = fuchsia_async::test_support::run_singlethreaded_test(f);
278 if result.is_ok() {
279 install_lsan_hook();
280 }
281 result
282}
283
284#[doc(hidden)]
286pub fn test_multithreaded<F, Fut, R>(f: F, num_threads: u8) -> R
287where
288 F: Fn(usize) -> Fut + Sync + 'static,
289 Fut: Future<Output = R> + Send + 'static,
290 R: fuchsia_async::test_support::MultithreadedTestResult,
291{
292 let result = fuchsia_async::test_support::run_test(f, num_threads);
293 if result.is_ok() {
294 install_lsan_hook();
295 }
296 result
297}
298
299#[doc(hidden)]
301#[cfg(target_os = "fuchsia")]
302pub fn test_until_stalled<F, Fut, R>(f: F) -> R
303where
304 F: 'static + Sync + Fn(usize) -> Fut,
305 Fut: 'static + Future<Output = R>,
306 R: fuchsia_async::test_support::TestResult,
307{
308 let result = fuchsia_async::test_support::run_until_stalled_test(true, f);
309 if result.is_ok() {
310 install_lsan_hook();
311 }
312 result
313}
314
315#[doc(hidden)]
322pub fn adapt_to_parse_arguments<A, R>(f: impl FnOnce(A) -> R) -> impl FnOnce() -> R
323where
324 A: argh::TopLevelCommand,
325{
326 move || f(argh::from_env())
327}
328
329#[doc(hidden)]
332pub fn adapt_to_take_test_run_number<R>(f: impl Fn() -> R) -> impl Fn(usize) -> R {
333 move |_| f()
334}
335
336#[doc(hidden)]
344#[cfg(not(feature = "variant_asan"))]
345pub fn disable_lsan_for_should_panic() {}
346
347#[doc(hidden)]
348#[cfg(feature = "variant_asan")]
349pub fn disable_lsan_for_should_panic() {
350 extern "C" {
351 fn __lsan_disable();
352 }
353 unsafe {
354 __lsan_disable();
355 }
356}
357
358#[cfg(not(feature = "variant_asan"))]
359fn install_lsan_hook() {}
360
361#[cfg(feature = "variant_asan")]
362fn install_lsan_hook() {
363 extern "C" {
364 fn __lsan_do_leak_check();
365 }
366 extern "C" fn lsan_do_leak_check() {
368 unsafe { __lsan_do_leak_check() }
369 }
370 let err = unsafe { libc::atexit(lsan_do_leak_check) };
372 if err != 0 {
373 panic!("Failed to install atexit hook for LeakSanitizer: atexit returned {err}");
374 }
375}