diff --git a/quicklog/Cargo.toml b/quicklog/Cargo.toml index 67e5199..3b99b60 100644 --- a/quicklog/Cargo.toml +++ b/quicklog/Cargo.toml @@ -10,11 +10,17 @@ keywords = ["quicklog", "logger"] workspace = "../" readme = "../README.md" build = "build.rs" +# passing all test cases through trybuild +autotests = false [lib] name = "quicklog" path = "src/lib.rs" +[[test]] +name = "tests" +path = "tests/ui.rs" + [dependencies] lazy_format = "2.0.0" quicklog-clock = { path = "../quicklog-clock", version = "0.1.3" } @@ -33,6 +39,7 @@ tracing = "0.1.37" tracing-appender = "0.2.2" tracing-subscriber = "0.3.16" recycle-box = { version = "0.2.0"} +trybuild = "1.0.85" [[bench]] name = "logger_benchmark" diff --git a/quicklog/src/lib.rs b/quicklog/src/lib.rs index 6bdd1d7..e243004 100644 --- a/quicklog/src/lib.rs +++ b/quicklog/src/lib.rs @@ -407,531 +407,3 @@ impl Log for Quicklog { } } } - -#[cfg(test)] -mod tests { - use std::{str::from_utf8, sync::Mutex}; - - use chrono::{DateTime, Utc}; - use quicklog_flush::Flush; - - use crate::{ - debug, error, flush, info, - serialize::{Serialize, Store}, - trace, warn, LogRecord, PatternFormatter, - }; - - pub struct TestFormatter; - - impl TestFormatter { - fn new() -> Self { - Self {} - } - } - - impl PatternFormatter for TestFormatter { - fn custom_format(&mut self, time: DateTime, log_record: LogRecord) -> String { - format!( - "[{:?}][{}]\t{}\n", - time, log_record.level, log_record.log_line - ) - } - } - - struct VecFlusher { - pub vec: &'static mut Vec, - } - - impl VecFlusher { - pub fn new(vec: &'static mut Vec) -> VecFlusher { - VecFlusher { vec } - } - } - - impl Flush for VecFlusher { - fn flush_one(&mut self, display: String) { - self.vec.push(display); - } - } - - #[derive(Clone, Debug)] - struct Something { - some_str: &'static str, - } - - fn message_from_log_line(log_line: &str) -> String { - log_line - .split('\t') - .last() - .map(|s| s.chars().take(s.len() - 1).collect::()) - .unwrap() - } - - fn message_and_level_from_log_line(log_line: &str) -> String { - let timestamp_end_idx = log_line.find(']').unwrap() + 1; - log_line - .chars() - .skip(timestamp_end_idx) - .take(log_line.len() - timestamp_end_idx - 1) - .collect::() - } - - /// tests need to be single threaded, this mutex ensures - /// tests are only executed in single threaded mode - /// and [`Quicklog::init!`] is only called once. - static TEST_LOCK: Mutex = Mutex::new(0); - - macro_rules! setup { - () => { - // acquire lock within scope of each test - let mut guard = TEST_LOCK.lock().unwrap(); - if *guard == 0 { - crate::init!(); - *guard += 1; - } - static mut VEC: Vec = Vec::new(); - let vec_flusher = unsafe { VecFlusher::new(&mut VEC) }; - crate::logger().use_flush(Box::new(vec_flusher)); - crate::logger().use_formatter(Box::new(TestFormatter::new())) - }; - } - - fn from_log_lines String>(lines: &[String], f: F) -> Vec { - lines.iter().map(|s| f(s.as_str())).collect::>() - } - - #[doc(hidden)] - macro_rules! helper_assert { - (@ $f:expr, $format_string:expr, $check_f:expr) => { - $f; - flush!(); - assert_eq!( - unsafe { from_log_lines(&VEC, $check_f) }, - vec![$format_string] - ); - unsafe { - let _ = &VEC.clear(); - } - }; - } - - macro_rules! assert_message_equal { - ($f:expr, $format_string:expr) => { helper_assert!(@ $f, $format_string, message_from_log_line) }; - } - - macro_rules! assert_message_with_level_equal { - ($f:expr, $format_string:expr) => { helper_assert!(@ $f, $format_string, message_and_level_from_log_line) }; - } - - #[derive(Clone, Debug)] - struct NestedSomething { - thing: Something, - } - - impl std::fmt::Display for Something { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Something display: {}", self.some_str) - } - } - - #[test] - fn has_all_levels() { - setup!(); - - assert_message_with_level_equal!( - trace!("Hello world {}", "Another"), - format!("[TRACE]\tHello world {}", "Another") - ); - assert_message_with_level_equal!( - debug!("Hello world {}", "Another"), - format!("[DEBUG]\tHello world {}", "Another") - ); - assert_message_with_level_equal!( - info!("Hello world {}", "Another"), - format!("[INFO]\tHello world {}", "Another") - ); - assert_message_with_level_equal!( - warn!("Hello world {}", "Another"), - format!("[WARN]\tHello world {}", "Another") - ); - assert_message_with_level_equal!( - error!("Hello world {}", "Another"), - format!("[ERROR]\tHello world {}", "Another") - ); - } - - #[test] - fn works_in_closure() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 2", - }; - - let f = || { - assert_message_equal!( - info!("Hello world {} {:?}", s1, s2), - format!("Hello world {} {:?}", s1, s2) - ); - }; - - f(); - } - - #[test] - fn works_with_attributes() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 2", - }; - let nested = NestedSomething { - thing: Something { - some_str: "hello nested", - }, - }; - - assert_message_equal!( - info!("log one attr {}", nested.thing.some_str), - format!("log one attr {}", nested.thing.some_str) - ); - assert_message_equal!( - info!("hello world {} {:?}", s1.some_str, s2.some_str), - format!("hello world {} {:?}", s1.some_str, s2.some_str) - ); - } - - #[test] - fn works_with_box_ref() { - setup!(); - - let s1 = Box::new(Something { - some_str: "Hello world 1", - }); - let s2 = Box::new(Something { - some_str: "Hello world 2", - }); - - assert_message_equal!( - info!("log single box ref {}", s1.as_ref()), - format!("log single box ref {}", s1.as_ref()) - ); - assert_message_equal!( - info!("log multi box ref {} {:?}", s1.as_ref(), s2.as_ref()), - format!("log multi box ref {} {:?}", s1.as_ref(), s2.as_ref()) - ); - } - - #[test] - fn works_with_move() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 2", - }; - let s3 = Something { - some_str: "Hello world 3", - }; - - assert_message_equal!( - info!("log multi move {} {:?}", s1, s2), - format!("log multi move {} {:?}", s1, s2) - ); - assert_message_equal!( - info!("log single move {}", s3), - format!("log single move {}", s3) - ); - } - - #[test] - fn works_with_references() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 2", - }; - - assert_message_equal!( - info!("log single ref: {}", &s1), - format!("log single ref: {}", &s1) - ); - assert_message_equal!( - info!("log multi ref: {} {:?}", &s1, &s2), - format!("log multi ref: {} {:?}", &s1, &s2) - ); - } - - fn log_multi_ref_helper(thing: &Something, thing2: &Something) { - info!("log multi ref {} {:?}", thing, thing2); - } - - fn log_ref_helper(thing: &Something) { - info!("log single ref: {}", thing) - } - - #[test] - fn works_with_ref_lifetime_inside_fn() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 2", - }; - - assert_message_equal!(log_ref_helper(&s1), format!("log single ref: {}", &s1)); - assert_message_equal!( - log_multi_ref_helper(&s2, &s1), - format!("log multi ref {} {:?}", &s2, &s1) - ); - } - - #[derive(Clone)] - struct A { - price: u64, - symbol: &'static str, - exch_id: u64, - } - - impl A { - fn get_price(&self) -> u64 { - self.price - } - - fn get_exch_id(&self) -> u64 { - self.exch_id - } - - fn get_symbol(&self) -> &'static str { - self.symbol - } - } - - #[test] - fn works_with_fn_return_val() { - setup!(); - - let a = A { - price: 1_521_523, - symbol: "SomeSymbol", - exch_id: 642_153_768, - }; - - assert_message_equal!( - info!( - "A: price: {} symbol: {} exch_id: {}", - a.get_price(), - ?a.get_symbol(), - %a.get_exch_id() - ), - format!( - "A: price: {} symbol: \"{}\" exch_id: {:?}", - a.get_price(), - a.get_symbol(), - a.get_exch_id() - ) - ); - assert_message_equal!( - info!("single call {}", a.get_price()), - format!("single call {}", a.get_price()) - ); - } - - fn log_ref_and_move(s1: Something, s2r: &Something) { - info!("Hello world {} {:?}", s1, s2r); - } - - #[test] - fn works_with_ref_and_move() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s1_clone = s1.clone(); - let s2 = Something { - some_str: "Hello world 2", - }; - - assert_message_equal!( - log_ref_and_move(s1, &s2), - format!("Hello world {} {:?}", s1_clone, &s2) - ); - let s3 = Something { - some_str: "Hello world 3", - }; - let s4 = Something { - some_str: "Hello world 4", - }; - - assert_message_equal!( - info!("ref: {:?}, move: {}", &s2, s3), - format!("ref: {:?}, move: {}", &s2, s3) - ); - assert_message_equal!(info!("single ref: {}", &s2), format!("single ref: {}", &s2)); - assert_message_equal!(info!("single move: {}", s4), format!("single move: {}", s4)); - } - - #[test] - fn works_with_eager_debug_display_hints() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 2", - }; - let some_str = "hello world"; - - assert_message_equal!( - info!("display {}; eager debug {}; eager display {}, eager display inner field {}", some_str, ?s2, %s1, %s1.some_str), - format!( - "display {}; eager debug {:?}; eager display {}, eager display inner field {}", - some_str, s2, s1, s1.some_str - ) - ); - assert_message_equal!( - info!("single eager display: {}", %s2), - format!("single eager display: {}", s2) - ); - assert_message_equal!( - info!("single eager display with prefix: {}", a = %s2), - format!("single eager display with prefix: a={}", s2) - ); - } - - #[test] - fn works_with_fields() { - setup!(); - - let s1 = Something { - some_str: "Hello world 1", - }; - let s2 = Something { - some_str: "Hello world 1", - }; - let s3 = Something { - some_str: "Hello world 3", - }; - let s3_clone = s3.clone(); - - assert_message_equal!( - info!("pass by ref {}", some_struct.field1.innerfield.inner = &s1), - format!("pass by ref some_struct.field1.innerfield.inner={}", &s1) - ); - assert_message_equal!( - info!("pass by move {}", some.inner.field = s3), - format!("pass by move some.inner.field={}", s3_clone) - ); - assert_message_equal!( - info!( - "non-nested field: {}, nested field: {}, pure lit: {}", - borrow_s2_field = %s2, - some_inner_field.inner.field.inner.arg = "hello world", - "pure lit arg" = "another lit arg" - ), - format!("non-nested field: borrow_s2_field={}, nested field: some_inner_field.inner.field.inner.arg=hello world, pure lit: pure lit arg=another lit arg", &s2) - ); - assert_message_equal!( - info!( - "pure lit: {}, reuse debug: {}, nested field: {}, able to reuse after pass by ref: {}", - "pure lit arg" = "another lit arg", - "able to reuse s1" = ?s1, - some_inner_field.some.field.included = "hello world", - able.to.reuse.s2.borrow = &s2 - ), - format!("pure lit: pure lit arg=another lit arg, reuse debug: able to reuse s1={:?}, nested field: some_inner_field.some.field.included=hello world, able to reuse after pass by ref: able.to.reuse.s2.borrow={}", s1, &s2) - ); - } - - struct S { - symbol: String, - } - - impl Serialize for S { - fn encode(&self, write_buf: &'static mut [u8]) -> Store { - fn decode(read_buf: &[u8]) -> String { - let x = from_utf8(read_buf).unwrap(); - x.to_string() - } - write_buf.copy_from_slice(self.symbol.as_bytes()); - Store::new(decode, write_buf) - } - - fn buffer_size_required(&self) -> usize { - self.symbol.len() - } - } - - #[derive(Debug, Clone, Copy)] - struct BigStruct { - vec: [i32; 100], - some: &'static str, - } - - impl Serialize for BigStruct { - fn encode(&self, write_buf: &'static mut [u8]) -> Store { - fn decode(buf: &[u8]) -> String { - let (mut _head, mut tail) = buf.split_at(0); - let mut vec = vec![]; - for _ in 0..100 { - (_head, tail) = tail.split_at(4); - vec.push(i32::from_le_bytes(_head.try_into().unwrap())); - } - let s = from_utf8(tail).unwrap(); - format!("vec: {:?}, str: {}", vec, s) - } - - let (mut _head, mut tail) = write_buf.split_at_mut(0); - for i in 0..100 { - (_head, tail) = tail.split_at_mut(4); - _head.copy_from_slice(&self.vec[i].to_le_bytes()) - } - - tail.copy_from_slice(self.some.as_bytes()); - - Store::new(decode, write_buf) - } - - fn buffer_size_required(&self) -> usize { - std::mem::size_of::() * 100 + self.some.len() - } - } - - #[test] - fn works_with_serialize() { - setup!(); - - let s = S { - symbol: String::from("Hello"), - }; - let bs = BigStruct { - vec: [1; 100], - some: "The quick brown fox jumps over the lazy dog", - }; - - assert_message_equal!(info!("s: {} {}", ^s, ^s), "s: Hello Hello"); - assert_message_equal!( - info!("bs: {}", ^bs), - format!( - "bs: vec: {:?}, str: {}", - vec![1; 100], - "The quick brown fox jumps over the lazy dog" - ) - ); - } -} diff --git a/quicklog/tests/closure.rs b/quicklog/tests/closure.rs new file mode 100644 index 0000000..cbbc592 --- /dev/null +++ b/quicklog/tests/closure.rs @@ -0,0 +1,25 @@ +use quicklog::info; + +use crate::common::Something; + +mod common; + +fn main() { + setup!(); + + let s1 = Something { + some_str: "Hello world 1", + }; + let s2 = Something { + some_str: "Hello world 2", + }; + + let f = || { + assert_message_equal!( + info!("Hello world {} {:?}", s1, s2), + format!("Hello world {} {:?}", s1, s2) + ); + }; + + f(); +} diff --git a/quicklog/tests/common/mod.rs b/quicklog/tests/common/mod.rs new file mode 100644 index 0000000..8e03324 --- /dev/null +++ b/quicklog/tests/common/mod.rs @@ -0,0 +1,189 @@ +// Some functions only emitted in macros +#![allow(dead_code)] + +use chrono::{DateTime, Utc}; +use quicklog::{ + serialize::{Serialize, Store}, + LogRecord, PatternFormatter, +}; +use quicklog_flush::Flush; + +pub(crate) struct VecFlusher { + pub(crate) vec: &'static mut Vec, +} + +impl VecFlusher { + pub fn new(vec: &'static mut Vec) -> VecFlusher { + VecFlusher { vec } + } +} + +impl Flush for VecFlusher { + fn flush_one(&mut self, display: String) { + self.vec.push(display); + } +} + +pub(crate) struct TestFormatter; + +impl TestFormatter { + pub(crate) fn new() -> Self { + Self {} + } +} + +impl PatternFormatter for TestFormatter { + fn custom_format(&mut self, time: DateTime, log_record: LogRecord) -> String { + format!( + "[{:?}][{}]\t{}\n", + time, log_record.level, log_record.log_line + ) + } +} + +pub(crate) fn message_from_log_line(log_line: &str) -> String { + log_line + .split('\t') + .last() + .map(|s| s.chars().take(s.len() - 1).collect::()) + .unwrap() +} + +pub(crate) fn message_and_level_from_log_line(log_line: &str) -> String { + let timestamp_end_idx = log_line.find(']').unwrap() + 1; + log_line + .chars() + .skip(timestamp_end_idx) + .take(log_line.len() - timestamp_end_idx - 1) + .collect::() +} + +pub(crate) fn from_log_lines String>(lines: &[String], f: F) -> Vec { + lines.iter().map(|s| f(s.as_str())).collect::>() +} + +#[derive(Clone, Debug)] +pub(crate) struct Something { + pub(crate) some_str: &'static str, +} + +impl std::fmt::Display for Something { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Something display: {}", self.some_str) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct NestedSomething { + pub(crate) thing: Something, +} + +#[derive(Clone)] +pub(crate) struct A { + pub(crate) price: u64, + pub(crate) symbol: &'static str, + pub(crate) exch_id: u64, +} + +impl A { + pub(crate) fn get_price(&self) -> u64 { + self.price + } + + pub(crate) fn get_exch_id(&self) -> u64 { + self.exch_id + } + + pub(crate) fn get_symbol(&self) -> &'static str { + self.symbol + } +} + +pub(crate) struct SerializeStruct { + pub(crate) symbol: String, +} + +impl Serialize for SerializeStruct { + fn encode(&self, write_buf: &'static mut [u8]) -> Store { + fn decode(read_buf: &[u8]) -> String { + let x = std::str::from_utf8(read_buf).unwrap(); + x.to_string() + } + write_buf.copy_from_slice(self.symbol.as_bytes()); + Store::new(decode, write_buf) + } + + fn buffer_size_required(&self) -> usize { + self.symbol.len() + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct BigStruct { + pub(crate) vec: [i32; 100], + pub(crate) some: &'static str, +} + +impl Serialize for BigStruct { + fn encode(&self, write_buf: &'static mut [u8]) -> Store { + fn decode(buf: &[u8]) -> String { + let (mut _head, mut tail) = buf.split_at(0); + let mut vec = vec![]; + for _ in 0..100 { + (_head, tail) = tail.split_at(4); + vec.push(i32::from_le_bytes(_head.try_into().unwrap())); + } + let s = std::str::from_utf8(tail).unwrap(); + format!("vec: {:?}, str: {}", vec, s) + } + + let (mut _head, mut tail) = write_buf.split_at_mut(0); + for i in 0..100 { + (_head, tail) = tail.split_at_mut(4); + _head.copy_from_slice(&self.vec[i].to_le_bytes()) + } + + tail.copy_from_slice(self.some.as_bytes()); + + Store::new(decode, write_buf) + } + + fn buffer_size_required(&self) -> usize { + std::mem::size_of::() * 100 + self.some.len() + } +} + +#[macro_export] +macro_rules! setup { + () => { + quicklog::init!(); + static mut VEC: Vec = Vec::new(); + let vec_flusher = unsafe { crate::common::VecFlusher::new(&mut VEC) }; + quicklog::logger().use_flush(Box::new(vec_flusher)); + quicklog::logger().use_formatter(Box::new(crate::common::TestFormatter::new())) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! helper_assert { + (@ $f:expr, $format_string:expr, $check_f:expr) => { + $f; + quicklog::flush!(); + let output = unsafe { crate::common::from_log_lines(&VEC, $check_f) }; + assert_eq!(output, vec![$format_string]); + unsafe { + let _ = &VEC.clear(); + } + }; +} + +#[macro_export] +macro_rules! assert_message_equal { + ($f:expr, $format_string:expr) => { helper_assert!(@ $f, $format_string, crate::common::message_from_log_line) }; +} + +#[macro_export] +macro_rules! assert_message_with_level_equal { + ($f:expr, $format_string:expr) => { helper_assert!(@ $f, $format_string, crate::common::message_and_level_from_log_line) }; +} diff --git a/quicklog/tests/eager.rs b/quicklog/tests/eager.rs new file mode 100644 index 0000000..de86cc2 --- /dev/null +++ b/quicklog/tests/eager.rs @@ -0,0 +1,33 @@ +use quicklog::info; + +use crate::common::Something; + +mod common; + +fn main() { + setup!(); + + let s1 = Something { + some_str: "Hello world 1", + }; + let s2 = Something { + some_str: "Hello world 2", + }; + let some_str = "hello world"; + + assert_message_equal!( + info!("display {}; eager debug {}; eager display {}, eager display inner field {}", some_str, ?s2, %s1, %s1.some_str), + format!( + "display {}; eager debug {:?}; eager display {}, eager display inner field {}", + some_str, s2, s1, s1.some_str + ) + ); + assert_message_equal!( + info!("single eager display: {}", %s2), + format!("single eager display: {}", s2) + ); + assert_message_equal!( + info!("single eager display with prefix: {}", a = %s2), + format!("single eager display with prefix: a={}", s2) + ); +} diff --git a/quicklog/tests/fields.rs b/quicklog/tests/fields.rs new file mode 100644 index 0000000..d04570a --- /dev/null +++ b/quicklog/tests/fields.rs @@ -0,0 +1,62 @@ +use quicklog::info; + +use crate::common::{NestedSomething, Something}; + +mod common; + +fn main() { + setup!(); + + let s1 = Something { + some_str: "Hello world 1", + }; + let s2 = Something { + some_str: "Hello world 2", + }; + let s3 = Something { + some_str: "Hello world 3", + }; + let s3_clone = s3.clone(); + let nested = NestedSomething { + thing: Something { + some_str: "hello nested", + }, + }; + + assert_message_equal!( + info!("log one attr {}", nested.thing.some_str), + format!("log one attr {}", nested.thing.some_str) + ); + assert_message_equal!( + info!("hello world {} {:?}", s1.some_str, s2.some_str), + format!("hello world {} {:?}", s1.some_str, s2.some_str) + ); + + assert_message_equal!( + info!("pass by ref {}", some_struct.field1.innerfield.inner = &s1), + format!("pass by ref some_struct.field1.innerfield.inner={}", &s1) + ); + assert_message_equal!( + info!("pass by move {}", some.inner.field = s3), + format!("pass by move some.inner.field={}", s3_clone) + ); + assert_message_equal!( + info!( + "non-nested field: {}, nested field: {}, pure lit: {}", + borrow_s2_field = %s2, + some_inner_field.inner.field.inner.arg = "hello world", + "pure lit arg" = "another lit arg" + ), + format!("non-nested field: borrow_s2_field={}, nested field: some_inner_field.inner.field.inner.arg=hello world, pure lit: pure lit arg=another lit arg", &s2) + ); + assert_message_equal!( + info!( + "pure lit: {}, reuse debug: {}, nested field: {}, able to reuse after pass by ref: {}", + "pure lit arg" = "another lit arg", + "able to reuse s1" = ?s1, + some_inner_field.some.field.included = "hello world", + able.to.reuse.s2.borrow = &s2 + ), + format!("pure lit: pure lit arg=another lit arg, reuse debug: able to reuse s1={:?}, nested field: some_inner_field.some.field.included=hello world, able to reuse after pass by ref: able.to.reuse.s2.borrow={}", s1, &s2) + ); +} diff --git a/quicklog/tests/function.rs b/quicklog/tests/function.rs new file mode 100644 index 0000000..3a3a394 --- /dev/null +++ b/quicklog/tests/function.rs @@ -0,0 +1,55 @@ +use quicklog::info; + +use crate::common::{Something, A}; + +mod common; + +fn log_multi_ref_helper(thing: &Something, thing2: &Something) { + info!("log multi ref {} {:?}", thing, thing2); +} + +fn log_ref_helper(thing: &Something) { + info!("log single ref: {}", thing) +} + +fn main() { + setup!(); + + let s1 = Something { + some_str: "Hello world 1", + }; + let s2 = Something { + some_str: "Hello world 2", + }; + + assert_message_equal!(log_ref_helper(&s1), format!("log single ref: {}", &s1)); + assert_message_equal!( + log_multi_ref_helper(&s2, &s1), + format!("log multi ref {} {:?}", &s2, &s1) + ); + + let a = A { + price: 1_521_523, + symbol: "SomeSymbol", + exch_id: 642_153_768, + }; + + assert_message_equal!( + info!( + "A: price: {} symbol: {} exch_id: {}", + a.get_price(), + ?a.get_symbol(), + %a.get_exch_id() + ), + format!( + "A: price: {} symbol: \"{}\" exch_id: {:?}", + a.get_price(), + a.get_symbol(), + a.get_exch_id() + ) + ); + assert_message_equal!( + info!("single call {}", a.get_price()), + format!("single call {}", a.get_price()) + ); +} diff --git a/quicklog/tests/level.rs b/quicklog/tests/level.rs new file mode 100644 index 0000000..183c92c --- /dev/null +++ b/quicklog/tests/level.rs @@ -0,0 +1,28 @@ +use quicklog::{debug, error, info, trace, warn}; + +mod common; + +fn main() { + setup!(); + + assert_message_with_level_equal!( + trace!("Hello world {}", "Another"), + format!("[TRACE]\tHello world {}", "Another") + ); + assert_message_with_level_equal!( + debug!("Hello world {}", "Another"), + format!("[DEBUG]\tHello world {}", "Another") + ); + assert_message_with_level_equal!( + info!("Hello world {}", "Another"), + format!("[INFO]\tHello world {}", "Another") + ); + assert_message_with_level_equal!( + warn!("Hello world {}", "Another"), + format!("[WARN]\tHello world {}", "Another") + ); + assert_message_with_level_equal!( + error!("Hello world {}", "Another"), + format!("[ERROR]\tHello world {}", "Another") + ); +} diff --git a/quicklog/tests/literal.rs b/quicklog/tests/literal.rs new file mode 100644 index 0000000..59a8098 --- /dev/null +++ b/quicklog/tests/literal.rs @@ -0,0 +1,16 @@ +use quicklog::{debug, error, info, init, trace, warn, with_flush}; +use quicklog_flush::noop_flusher::NoopFlusher; + +fn main() { + init!(); + with_flush!(NoopFlusher); + + trace!("hello world"); + debug!("hello world"); + info!("hello world"); + warn!("hello world"); + error!("hello world"); + + // Allow single trailing comma + info!("hello world",); +} diff --git a/quicklog/tests/move.rs b/quicklog/tests/move.rs new file mode 100644 index 0000000..0c4e37d --- /dev/null +++ b/quicklog/tests/move.rs @@ -0,0 +1,48 @@ +use quicklog::info; + +use crate::common::Something; + +mod common; + +fn log_ref_and_move(s1: Something, s2r: &Something) { + info!("Hello world {} {:?}", s1, s2r); +} + +fn main() { + setup!(); + + let s1 = Something { + some_str: "Hello world 1", + }; + let s1_clone = s1.clone(); + let s2 = Something { + some_str: "Hello world 2", + }; + let s3 = Something { + some_str: "Hello world 3", + }; + let s4 = Something { + some_str: "Hello world 4", + }; + + assert_message_equal!( + info!("log multi move {} {:?}", s1, s2), + format!("log multi move {} {:?}", s1, s2) + ); + assert_message_equal!( + log_ref_and_move(s1, &s2), + format!("Hello world {} {:?}", s1_clone, &s2) + ); + + assert_message_equal!( + info!("log single move {}", s3), + format!("log single move {}", s3) + ); + + assert_message_equal!( + info!("ref: {:?}, move: {}", &s2, s3), + format!("ref: {:?}, move: {}", &s2, s3) + ); + assert_message_equal!(info!("single ref: {}", &s2), format!("single ref: {}", &s2)); + assert_message_equal!(info!("single move: {}", s4), format!("single move: {}", s4)); +} diff --git a/quicklog/tests/reference.rs b/quicklog/tests/reference.rs new file mode 100644 index 0000000..64a2f6c --- /dev/null +++ b/quicklog/tests/reference.rs @@ -0,0 +1,44 @@ +use quicklog::info; + +use crate::common::Something; + +mod common; + +fn main() { + setup!(); + + let s1 = Something { + some_str: "Hello world 1", + }; + let s2 = Something { + some_str: "Hello world 2", + }; + assert_message_equal!( + info!("log single ref: {}", &s1), + format!("log single ref: {}", &s1) + ); + assert_message_equal!( + info!("log multi ref: {} {:?}", &s1, &s2), + format!("log multi ref: {} {:?}", &s1, &s2) + ); + + let s1_boxed = Box::new(s1); + let s2_boxed = Box::new(s2); + + assert_message_equal!( + info!("log single box ref {}", s1_boxed.as_ref()), + format!("log single box ref {}", s1_boxed.as_ref()) + ); + assert_message_equal!( + info!( + "log multi box ref {} {:?}", + s1_boxed.as_ref(), + s2_boxed.as_ref() + ), + format!( + "log multi box ref {} {:?}", + s1_boxed.as_ref(), + s2_boxed.as_ref() + ) + ); +} diff --git a/quicklog/tests/serialize.rs b/quicklog/tests/serialize.rs new file mode 100644 index 0000000..d88609d --- /dev/null +++ b/quicklog/tests/serialize.rs @@ -0,0 +1,27 @@ +use quicklog::info; + +use crate::common::{BigStruct, SerializeStruct}; + +mod common; + +fn main() { + setup!(); + + let s = SerializeStruct { + symbol: String::from("Hello"), + }; + let bs = BigStruct { + vec: [1; 100], + some: "The quick brown fox jumps over the lazy dog", + }; + + assert_message_equal!(info!("s: {} {}", ^s, ^s), "s: Hello Hello"); + assert_message_equal!( + info!("bs: {}", ^bs), + format!( + "bs: vec: {:?}, str: {}", + vec![1; 100], + "The quick brown fox jumps over the lazy dog" + ) + ); +} diff --git a/quicklog/tests/ui.rs b/quicklog/tests/ui.rs new file mode 100644 index 0000000..90c1bca --- /dev/null +++ b/quicklog/tests/ui.rs @@ -0,0 +1,13 @@ +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.pass("tests/literal.rs"); + t.pass("tests/level.rs"); + t.pass("tests/closure.rs"); + t.pass("tests/reference.rs"); + t.pass("tests/move.rs"); + t.pass("tests/function.rs"); + t.pass("tests/eager.rs"); + t.pass("tests/fields.rs"); + t.pass("tests/serialize.rs"); +}