#[macro_export]
macro_rules! debug_trace_data {
( $description:expr, $offset:expr, $data:expr, $data_size:expr $(,)? ) => {
#[cfg(feature = "debug-trace")]
{
let mediator = $crate::mediator::Mediator::current();
if mediator.debug_output {
mediator.debug_print(format!(
"{} data of size: {} at offset: {} (0x{:08x})\n",
$description, $data_size, $offset, $offset
));
mediator.debug_print_data($data, true);
}
}
};
}
#[macro_export]
macro_rules! debug_trace_data_and_structure {
( $description:expr, $offset:expr, $data:expr, $data_size:expr, $structure:expr $(,)? ) => {
#[cfg(feature = "debug-trace")]
{
let mediator = $crate::mediator::Mediator::current();
if mediator.debug_output {
mediator.debug_print(format!(
"{} data of size: {} at offset: {} (0x{:08x})\n",
$description, $data_size, $offset, $offset
));
mediator.debug_print_data($data, true);
mediator.debug_print($structure);
}
}
};
}
#[macro_export]
macro_rules! debug_trace_structure {
( $structure:expr $(,)? ) => {
#[cfg(feature = "debug-trace")]
{
let mediator = $crate::mediator::Mediator::current();
if mediator.debug_output {
mediator.debug_print($structure);
}
}
};
}
#[macro_export]
macro_rules! error_trace_function {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
name.strip_suffix("::f").unwrap()
}};
}
#[macro_export]
macro_rules! error_trace_new {
( $message:expr $(,)? ) => {
$crate::ErrorTrace::new(format!("{}: {}", $crate::error_trace_function!(), $message))
};
}
#[macro_export]
macro_rules! error_trace_new_with_error {
( $message:expr, $error:expr $(,)? ) => {
$crate::ErrorTrace::new(format!(
"{}: {} with error: {}",
$crate::error_trace_function!(),
$message,
$error.to_string(),
))
};
}
#[macro_export]
macro_rules! error_trace_add_frame {
( $error:expr, $message:expr $(,)? ) => {
$error.add_frame(format!("{}: {}", $crate::error_trace_function!(), $message))
};
}
#[macro_export]
macro_rules! data_stream_get_size {
( $data_stream:expr $(,)? ) => {
match $data_stream.write() {
Ok(mut data_stream) => match data_stream.get_size() {
Ok(size) => size,
Err(mut error) => {
$crate::error_trace_add_frame!(
error,
"Unable to determine size of data stream"
);
return Err(error);
}
},
Err(error) => {
return Err($crate::error_trace_new_with_error!(
"Unable to obtain write lock on data stream",
error
));
}
}
};
}
#[macro_export]
macro_rules! data_stream_read_at_position {
( $data_stream:expr, $buf:expr, $pos:expr $(,)? ) => {
match $data_stream.write() {
Ok(mut data_stream) => match data_stream.read_at_position($buf, $pos) {
Ok(read_count) => read_count,
Err(mut error) => {
$crate::error_trace_add_frame!(error, "Unable to read from data stream");
return Err(error);
}
},
Err(error) => {
return Err($crate::error_trace_new_with_error!(
"Unable to obtain write lock on data stream",
error
));
}
}
};
}
#[macro_export]
macro_rules! data_stream_read_exact_at_position {
( $data_stream:expr, $buf:expr, $pos:expr $(,)? ) => {
match $data_stream.write() {
Ok(mut data_stream) => match data_stream.read_exact_at_position($buf, $pos) {
Ok(offset) => offset,
Err(mut error) => {
$crate::error_trace_add_frame!(error, "Unable to read from data stream");
return Err(error);
}
},
Err(error) => {
return Err($crate::error_trace_new_with_error!(
"Unable to obtain write lock on data stream",
error
));
}
}
};
}
#[cfg(test)]
mod tests {
use std::io::SeekFrom;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use std::thread::spawn;
use crate::data_stream::{DataStream, DataStreamReference};
use crate::errors::ErrorTrace;
use crate::fake_data_stream::open_fake_data_stream;
struct TestFailingDataStream {}
impl DataStream for TestFailingDataStream {
fn get_offset(&mut self) -> Result<u64, ErrorTrace> {
Err(ErrorTrace::new(String::from("test error")))
}
fn get_size(&mut self) -> Result<u64, ErrorTrace> {
Err(ErrorTrace::new(String::from("test error")))
}
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, ErrorTrace> {
Err(ErrorTrace::new(String::from("test error")))
}
fn seek(&mut self, _pos: SeekFrom) -> Result<u64, ErrorTrace> {
Err(ErrorTrace::new(String::from("test error")))
}
}
fn get_test_data() -> Vec<u8> {
(0..16).collect()
}
fn get_test_data_stream() -> DataStreamReference {
open_fake_data_stream(&get_test_data())
}
fn get_test_failing_data_stream() -> DataStreamReference {
Arc::new(RwLock::new(TestFailingDataStream {}))
}
fn get_test_poisoned_data_stream() -> DataStreamReference {
let data_stream: DataStreamReference = get_test_data_stream();
let poisoned_data_stream: DataStreamReference = data_stream.clone();
let (sender, receiver) = channel::<()>();
let thread = spawn(move || {
let _guard = poisoned_data_stream
.write()
.expect("unable to obtain write lock");
sender.send(()).expect("unable to send");
panic!("poison the data stream");
});
receiver.recv().expect("unable to receive");
let _thread_result = thread.join();
data_stream
}
fn data_stream_get_size_helper(data_stream: DataStreamReference) -> Result<u64, ErrorTrace> {
let size: u64 = data_stream_get_size!(data_stream);
Ok(size)
}
fn data_stream_read_at_position_helper(
data_stream: DataStreamReference,
buf: &mut [u8],
pos: SeekFrom,
) -> Result<usize, ErrorTrace> {
let read_count: usize = data_stream_read_at_position!(data_stream, buf, pos);
Ok(read_count)
}
fn data_stream_read_exact_at_position_helper(
data_stream: DataStreamReference,
buf: &mut [u8],
pos: SeekFrom,
) -> Result<u64, ErrorTrace> {
let offset: u64 = data_stream_read_exact_at_position!(data_stream, buf, pos);
Ok(offset)
}
#[test]
fn test_error_trace_function() {
let function_name: &str = error_trace_function!();
assert!(function_name.contains("test_error_trace_function"));
}
#[test]
fn test_error_trace_new() {
let error: ErrorTrace = error_trace_new!("test error");
let error_message: String = error.to_string();
assert!(error_message.starts_with("#0 "));
assert!(error_message.contains("test_error_trace_new: test error"));
}
#[test]
fn test_error_trace_new_with_error() {
let error: ErrorTrace = error_trace_new_with_error!(
"test message",
ErrorTrace::new(String::from("inner error")),
);
let error_message: String = error.to_string();
assert!(error_message.starts_with("#0 "));
assert!(
error_message.contains(
"test_error_trace_new_with_error: test message with error: #0 inner error"
)
);
}
#[test]
fn test_error_trace_add_frame() {
let mut error: ErrorTrace = ErrorTrace::new(String::from("original error"));
error_trace_add_frame!(error, "additional frame");
let error_message: String = error.to_string();
assert!(error_message.contains("#0 original error"));
assert!(error_message.contains("#1 "));
assert!(error_message.contains("test_error_trace_add_frame: additional frame"));
}
#[test]
fn test_data_stream_get_size() -> Result<(), ErrorTrace> {
let data_stream: DataStreamReference = get_test_data_stream();
let size: u64 = data_stream_get_size!(data_stream);
assert_eq!(size, 16);
Ok(())
}
#[test]
fn test_data_stream_get_size_with_failing_size() {
let test_data_stream: DataStreamReference = get_test_failing_data_stream();
let result: Result<u64, ErrorTrace> = data_stream_get_size_helper(test_data_stream);
let error: ErrorTrace = result.expect_err("expected error");
let error_message: String = error.to_string();
assert!(error_message.contains("Unable to determine size of data stream"));
assert!(error_message.contains("test error"));
}
#[test]
fn test_data_stream_get_size_with_poisoned_data_stream() {
let test_data_stream: DataStreamReference = get_test_poisoned_data_stream();
let result: Result<u64, ErrorTrace> = data_stream_get_size_helper(test_data_stream);
let error: ErrorTrace = result.expect_err("expected error");
let error_message: String = error.to_string();
assert!(error_message.contains("Unable to obtain write lock on data stream"));
}
#[test]
fn test_data_stream_read_at_position() -> Result<(), ErrorTrace> {
let data_stream: DataStreamReference = get_test_data_stream();
let mut data: Vec<u8> = vec![0; 4];
let read_count: usize =
data_stream_read_at_position!(data_stream, &mut data, SeekFrom::Start(2));
assert_eq!(read_count, 4);
assert_eq!(&data, &[2, 3, 4, 5]);
Ok(())
}
#[test]
fn test_data_stream_read_at_position_beyond_size() -> Result<(), ErrorTrace> {
let data_stream: DataStreamReference = get_test_data_stream();
let mut data: Vec<u8> = vec![0; 24];
let read_count: usize =
data_stream_read_at_position!(data_stream, &mut data, SeekFrom::Start(8));
assert_eq!(read_count, 8);
assert_eq!(&data[0..8], &[8, 9, 10, 11, 12, 13, 14, 15]);
assert_eq!(&data[8..24], &[0; 16]);
Ok(())
}
#[test]
fn test_data_stream_read_at_position_with_failing_read() {
let test_data_stream: DataStreamReference = get_test_failing_data_stream();
let mut data: Vec<u8> = vec![0; 4];
let result: Result<usize, ErrorTrace> =
data_stream_read_at_position_helper(test_data_stream, &mut data, SeekFrom::Start(0));
let error: ErrorTrace = result.expect_err("expected error");
let error_message: String = error.to_string();
assert!(error_message.contains("Unable to read from data stream"));
assert!(error_message.contains("test error"));
}
#[test]
fn test_data_stream_read_at_position_with_poisoned_data_stream() {
let test_data_stream: DataStreamReference = get_test_poisoned_data_stream();
let mut data: Vec<u8> = vec![0; 4];
let result: Result<usize, ErrorTrace> =
data_stream_read_at_position_helper(test_data_stream, &mut data, SeekFrom::Start(0));
let error: ErrorTrace = result.expect_err("expected error");
let error_message: String = error.to_string();
assert!(error_message.contains("Unable to obtain write lock on data stream"));
}
#[test]
fn test_data_stream_read_exact_at_position() -> Result<(), ErrorTrace> {
let data_stream: DataStreamReference = get_test_data_stream();
let mut data: Vec<u8> = vec![0; 4];
let offset: u64 =
data_stream_read_exact_at_position!(data_stream, &mut data, SeekFrom::Start(2));
assert_eq!(offset, 2);
assert_eq!(&data, &[2, 3, 4, 5]);
Ok(())
}
#[test]
fn test_data_stream_read_exact_at_position_beyond_size() {
let data_stream: DataStreamReference = get_test_data_stream();
let mut data: Vec<u8> = vec![0; 24];
let result: Result<u64, ErrorTrace> =
data_stream_read_exact_at_position_helper(data_stream, &mut data, SeekFrom::Start(0));
let error: ErrorTrace = result.expect_err("expected error");
let error_message: String = error.to_string();
assert!(error_message.contains("Unable to read the exact amount"));
assert!(error_message.contains("Unable to read from data stream"));
}
#[test]
fn test_data_stream_read_exact_at_position_with_failing_read() {
let test_data_stream: DataStreamReference = get_test_failing_data_stream();
let mut data: Vec<u8> = vec![0; 4];
let result: Result<u64, ErrorTrace> = data_stream_read_exact_at_position_helper(
test_data_stream,
&mut data,
SeekFrom::Start(0),
);
let error: ErrorTrace = result.expect_err("expected error");
let error_message: String = error.to_string();
assert!(error_message.contains("Unable to read from data stream"));
assert!(error_message.contains("test error"));
}
#[cfg(feature = "debug-trace")]
#[test]
fn test_debug_trace_data() {
crate::mediator::Mediator::new(true).make_current();
let data: Vec<u8> = get_test_data();
debug_trace_data!("test data", 0, &data, 16);
}
#[cfg(feature = "debug-trace")]
#[test]
fn test_debug_trace_data_with_debug_output_disabled() {
crate::mediator::Mediator::new(false).make_current();
let data: Vec<u8> = get_test_data();
debug_trace_data!("test data", 0, &data, 16);
}
#[cfg(feature = "debug-trace")]
#[test]
fn test_debug_trace_data_and_structure() {
crate::mediator::Mediator::new(true).make_current();
let data: Vec<u8> = get_test_data();
debug_trace_data_and_structure!("test data", 0, &data, 16, String::from("structure"));
}
#[cfg(feature = "debug-trace")]
#[test]
fn test_debug_trace_data_and_structure_with_debug_output_disabled() {
crate::mediator::Mediator::new(false).make_current();
let data: Vec<u8> = get_test_data();
debug_trace_data_and_structure!("test data", 0, &data, 16, String::from("structure"));
}
#[cfg(feature = "debug-trace")]
#[test]
fn test_debug_trace_structure() {
crate::mediator::Mediator::new(true).make_current();
debug_trace_structure!(String::from("structure"));
}
#[cfg(feature = "debug-trace")]
#[test]
fn test_debug_trace_structure_with_debug_output_disabled() {
crate::mediator::Mediator::new(false).make_current();
debug_trace_structure!(String::from("structure"));
}
}