Skip to content

Commit 50d468a

Browse files
committed
fix: make logging tests parallel-safe
Signed-off-by: Esteve Fernandez <esteve@apache.org>
1 parent 9b40479 commit 50d468a

4 files changed

Lines changed: 116 additions & 149 deletions

File tree

rclrs/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ uuid = { version = "1", features = ["v4"] }
5959
paste = { version = "1", optional = true}
6060

6161
[dev-dependencies]
62-
# Needed for running logging tests serialized
63-
serial_test = "3.4.0"
6462
# Needed for e.g. writing yaml files in tests
6563
tempfile = "3.3.0"
6664
# Needed for parameter service tests

rclrs/src/logging.rs

Lines changed: 52 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -335,57 +335,37 @@ pub unsafe fn impl_log(
335335
static FORMAT_STRING: OnceLock<CString> = OnceLock::new();
336336
let format_string = FORMAT_STRING.get_or_init(|| CString::new("%s").unwrap());
337337

338-
let severity = severity.as_native();
338+
let severity = severity.as_native() as i32;
339339

340340
let _lifecycle = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
341341

342342
#[cfg(test)]
343-
{
344-
// If we are compiling for testing purposes, when the default log
345-
// output handler is being used we need to use the format_string,
346-
// but when our custom log output handler is being used we need to
347-
// pass the raw message string so that it can be viewed by the
348-
// custom log output handler, allowing us to use it for test assertions.
349-
if log_handler::is_using_custom_handler() {
350-
// We are using the custom log handler that is only used during
351-
// logging tests, so pass the raw message as the format string.
352-
unsafe {
353-
// SAFETY: The global mutex is locked as _lifecycle
354-
rcutils_log(
355-
&location,
356-
severity as i32,
357-
logger_name.as_ptr(),
358-
message.as_ptr(),
359-
);
360-
}
361-
} else {
362-
// We are using the normal log handler so call rcutils_log the normal way.
363-
unsafe {
364-
// SAFETY: The global mutex is locked as _lifecycle
365-
rcutils_log(
366-
&location,
367-
severity as i32,
368-
logger_name.as_ptr(),
369-
format_string.as_ptr(),
370-
message.as_ptr(),
371-
);
372-
}
373-
}
374-
}
375-
376-
#[cfg(not(test))]
343+
if log_handler::is_using_custom_handler()
344+
&& unsafe { rcutils_logging_logger_is_enabled_for(logger_name.as_ptr(), severity) }
377345
{
378346
unsafe {
379-
// SAFETY: The global mutex is locked as _lifecycle
380-
rcutils_log(
347+
// SAFETY: The global mutex is locked as _lifecycle and the pointers are valid for
348+
// the duration of this call.
349+
log_handler::dispatch_logging_output_handler(
381350
&location,
382-
severity as i32,
351+
severity,
383352
logger_name.as_ptr(),
384-
format_string.as_ptr(),
353+
log_handler::null_timestamp(),
385354
message.as_ptr(),
386355
);
387356
}
388357
}
358+
359+
unsafe {
360+
// SAFETY: The global mutex is locked as _lifecycle
361+
rcutils_log(
362+
&location,
363+
severity,
364+
logger_name.as_ptr(),
365+
format_string.as_ptr(),
366+
message.as_ptr(),
367+
);
368+
}
389369
};
390370

391371
match logger_name {
@@ -473,14 +453,12 @@ macro_rules! function {
473453
#[cfg(test)]
474454
mod tests {
475455
use crate::{log_handler::*, test_helpers::*, *};
476-
use serial_test::serial;
477456
use std::{
478457
sync::{Arc, Mutex},
479458
time::Duration,
480459
};
481460

482461
#[test]
483-
#[serial]
484462
fn test_logging_macros() -> Result<(), RclrsError> {
485463
// This test ensures that strings which are being sent to the logger are
486464
// being sanitized correctly. Rust generally and our logging macro in
@@ -497,15 +475,32 @@ mod tests {
497475
log!("please do not crash", "%n");
498476

499477
let graph = construct_test_graph("test_logging_macros")?;
478+
let node = graph.node1;
479+
let node_logger_name = node.logger().name().to_owned();
480+
481+
let custom_logger_name = "test_logging_macros_custom";
482+
let once_logger_name = "test_logging_macros_once";
483+
let skip_logger_name = "test_logging_macros_skip";
484+
let throttle_logger_name = "test_logging_macros_throttle";
500485

501486
let log_collection: Arc<Mutex<Vec<LogEntry<'static>>>> = Arc::new(Mutex::new(Vec::new()));
502487
let inner_log_collection = log_collection.clone();
503488

504489
log_handler::set_logging_output_handler(move |log_entry: log_handler::LogEntry| {
505-
inner_log_collection
506-
.lock()
507-
.unwrap()
508-
.push(log_entry.into_owned());
490+
if [
491+
node_logger_name.as_str(),
492+
custom_logger_name,
493+
once_logger_name,
494+
skip_logger_name,
495+
throttle_logger_name,
496+
]
497+
.contains(&log_entry.logger_name.as_ref())
498+
{
499+
inner_log_collection
500+
.lock()
501+
.unwrap()
502+
.push(log_entry.into_owned());
503+
}
509504
})
510505
.unwrap();
511506

@@ -551,8 +546,6 @@ mod tests {
551546
count
552547
};
553548

554-
let node = graph.node1;
555-
556549
log!(&*node, "Logging with node dereference");
557550
assert_eq!(last_logger_name(), node.logger().name());
558551
assert_eq!(last_message(), "Logging with node dereference");
@@ -621,27 +614,27 @@ mod tests {
621614
log_debug!(node.logger(), "This debug message does not appear");
622615
assert_ne!(last_message(), "This debug message does not appear");
623616

624-
log!("custom logger name", "message for custom logger");
625-
assert_eq!(last_logger_name(), "custom logger name");
617+
log!(custom_logger_name, "message for custom logger");
618+
assert_eq!(last_logger_name(), custom_logger_name);
626619
assert_eq!(last_message(), "message for custom logger");
627620

628621
for _ in 0..3 {
629622
log!(
630-
"custom logger name once".once(),
623+
once_logger_name.once(),
631624
"one-time message for custom logger",
632625
);
633626
}
634-
assert_eq!(last_logger_name(), "custom logger name once");
627+
assert_eq!(last_logger_name(), once_logger_name);
635628
assert_eq!(last_severity(), LogSeverity::Info);
636629
assert_eq!(count_message("one-time message for custom logger"), 1);
637630

638631
for _ in 0..3 {
639632
log!(
640-
"custom logger name skip".error().skip_first(),
633+
skip_logger_name.error().skip_first(),
641634
"error for custom logger",
642635
);
643636
}
644-
assert_eq!(last_logger_name(), "custom logger name skip");
637+
assert_eq!(last_logger_name(), skip_logger_name);
645638
assert_eq!(last_severity(), LogSeverity::Error);
646639
assert_eq!(count_message("error for custom logger"), 2);
647640

@@ -651,7 +644,7 @@ mod tests {
651644

652645
for i in 0..15 {
653646
log!(
654-
"logger"
647+
throttle_logger_name
655648
.throttle(Duration::from_nanos(10))
656649
.throttle_clock(ThrottleClock::Clock(&clock)),
657650
"custom clock throttled message",
@@ -674,7 +667,6 @@ mod tests {
674667
}
675668

676669
#[test]
677-
#[serial]
678670
fn test_rosout_publishing_default() -> Result<(), RclrsError> {
679671
use crate::rcl_bindings::rcl_logging_rosout_enabled;
680672
use ros_env::rcl_interfaces::msg::rmw::Log;
@@ -687,8 +679,11 @@ mod tests {
687679
.unwrap();
688680

689681
// Check if rosout is enabled at the rcl level
690-
// SAFETY: This is a simple query function with no preconditions
691-
let rosout_enabled = unsafe { rcl_logging_rosout_enabled() };
682+
let rosout_enabled = {
683+
let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap();
684+
// SAFETY: The entity lifecycle mutex is locked to protect logging's global state.
685+
unsafe { rcl_logging_rosout_enabled() }
686+
};
692687
assert!(
693688
rosout_enabled,
694689
"rcl_logging_rosout should be enabled for this test"
@@ -814,7 +809,6 @@ mod tests {
814809
}
815810

816811
#[test]
817-
#[serial]
818812
fn test_rosout_disabled() -> Result<(), RclrsError> {
819813
use ros_env::rcl_interfaces::msg::rmw::Log;
820814
use std::sync::{

0 commit comments

Comments
 (0)