#[cfg(test)]
mod tests {
use comperr::{Error, error};
use proc_macro2::Span;
#[test]
fn error_creates_valid_tokenstream() {
let result = error(Span::call_site(), "test error");
assert!(!result.is_empty());
}
#[test]
fn error_message_is_preserved() {
let result = error(Span::call_site(), "test error message");
let result_str = result.to_string();
assert!(result_str.contains("test error message"));
}
#[test]
fn error_contains_compile_error() {
let result = error(Span::call_site(), "test error");
let result_str = result.to_string();
assert!(result_str.contains("compile_error"));
}
#[test]
fn error_struct_works() {
let err = Error::new(Span::call_site(), "test error");
let result = err.to_compile_error();
assert!(!result.is_empty());
}
#[test]
fn error_with_different_messages() {
let err1 = Error::new(Span::call_site(), "message one");
let err2 = Error::new(Span::call_site(), "message two");
let result1 = err1.to_compile_error();
let result2 = err2.to_compile_error();
assert!(result1.to_string().contains("message one"));
assert!(result2.to_string().contains("message two"));
}
#[test]
fn error_string_message() {
let msg = String::from("owned message");
let result = error(Span::call_site(), msg);
assert!(!result.is_empty());
}
#[test]
fn error_empty_message() {
let result = error(Span::call_site(), "");
let result_str = result.to_string();
assert!(result_str.contains("compile_error"));
}
#[test]
fn error_multiline_message() {
let result = error(Span::call_site(), "line 1\nline 2\nline 3");
let result_str = result.to_string();
assert!(result_str.contains("line 1"));
}
#[test]
fn error_combine_multiple_errors() {
let mut err = Error::new(Span::call_site(), "first");
err.combine(Error::new(Span::call_site(), "second"));
let result = err.to_compile_error();
let result_str = result.to_string();
assert!(result_str.contains("compile_error"));
assert!(result_str.contains("first"));
assert!(result_str.contains("second"));
}
#[test]
fn error_combine_three_errors() {
let mut err = Error::new(Span::call_site(), "one");
err.combine(Error::new(Span::call_site(), "two"));
err.combine(Error::new(Span::call_site(), "three"));
let result = err.to_compile_error();
let result_str = result.to_string();
assert!(result_str.contains("one"));
assert!(result_str.contains("two"));
assert!(result_str.contains("three"));
}
#[test]
fn error_combine_preserves_span_per_message() {
let span1 = Span::call_site();
let span2 = Span::call_site();
let mut err = Error::new(span1, "error at span1");
err.combine(Error::new(span2, "error at span2"));
let result = err.to_compile_error();
let result_str = result.to_string();
assert!(result_str.contains("error at span1"));
assert!(result_str.contains("error at span2"));
}
#[test]
fn error_special_characters_in_message() {
let result = error(Span::call_site(), "expected <T> but found &[i32]");
let result_str = result.to_string();
assert!(result_str.contains("expected <T> but found &[i32]"));
}
#[test]
fn error_quotes_in_message() {
let result = error(Span::call_site(), "expected \"foo\"");
let result_str = result.to_string();
assert!(result_str.contains("expected \\\"foo\\\""));
}
#[test]
fn error_backticks_in_message() {
let result = error(Span::call_site(), "use `Foo` instead");
let result_str = result.to_string();
assert!(result_str.contains("use `Foo` instead"));
}
#[test]
fn error_unicode_message() {
let result = error(Span::call_site(), "héllo wörld");
let result_str = result.to_string();
assert!(result_str.contains("héllo wörld"));
}
#[test]
fn error_emoji_message() {
let result = error(Span::call_site(), "error 💔");
let result_str = result.to_string();
assert!(result_str.contains("💔"));
}
#[test]
fn error_very_long_message() {
let long_msg = "x".repeat(10000);
let result = error(Span::call_site(), long_msg);
let result_str = result.to_string();
assert!(result_str.contains("xxxxx"));
}
#[test]
fn error_single_char_message() {
let result = error(Span::call_site(), "a");
let result_str = result.to_string();
assert!(result_str.contains("a"));
assert!(result_str.contains("compile_error"));
}
#[test]
fn error_new_with_string() {
let err = Error::new(Span::call_site(), String::from("owned"));
let result = err.to_compile_error();
assert!(!result.is_empty());
}
#[test]
fn error_with_colon_in_message() {
let result = error(Span::call_site(), "help: did you mean?");
let result_str = result.to_string();
assert!(result_str.contains("help: did you mean?"));
}
#[test]
fn error_with_arrow_in_message() {
let result = error(Span::call_site(), "expected i32 -> Result");
let result_str = result.to_string();
assert!(result_str.contains("->"));
}
#[test]
fn empty_error_is_empty() {
assert!(Error::empty().is_empty());
}
#[test]
fn new_error_is_not_empty() {
assert!(!Error::new(Span::call_site(), "hello").is_empty());
}
#[test]
fn empty_error_produces_empty_tokenstream() {
assert!(Error::empty().to_compile_error().is_empty());
}
#[test]
fn combine_into_empty_makes_non_empty() {
let mut acc = Error::empty();
assert!(acc.is_empty());
acc.combine(Error::new(Span::call_site(), "oops"));
assert!(!acc.is_empty());
}
#[test]
fn display_single_error() {
let e = Error::new(Span::call_site(), "something broke");
assert_eq!(e.to_string(), "something broke");
}
#[test]
fn display_multiple_errors_joined_by_newline() {
let mut e = Error::new(Span::call_site(), "first");
e.combine(Error::new(Span::call_site(), "second"));
e.combine(Error::new(Span::call_site(), "third"));
assert_eq!(e.to_string(), "first\nsecond\nthird");
}
#[test]
fn display_empty_error_is_empty_string() {
assert_eq!(Error::empty().to_string(), "");
}
#[test]
fn error_implements_debug() {
let e = Error::new(Span::call_site(), "debug me");
let s = format!("{:?}", e);
assert!(s.contains("debug me"));
}
#[test]
fn error_implements_clone() {
let e = Error::new(Span::call_site(), "original");
let cloned = e.clone();
assert_eq!(cloned.to_string(), "original");
}
#[test]
fn clone_is_independent() {
let mut e = Error::new(Span::call_site(), "original");
let cloned = e.clone();
e.combine(Error::new(Span::call_site(), "added to original"));
assert_eq!(cloned.to_string(), "original");
assert_eq!(e.to_string(), "original\nadded to original");
}
#[test]
fn implements_std_error() {
let e = Error::new(Span::call_site(), "std error");
let _: &dyn std::error::Error = &e;
}
#[test]
fn extend_adds_errors() {
let mut base = Error::new(Span::call_site(), "base");
base.extend([
Error::new(Span::call_site(), "extra one"),
Error::new(Span::call_site(), "extra two"),
]);
assert_eq!(base.to_string(), "base\nextra one\nextra two");
}
#[test]
fn extend_with_empty_iter_is_noop() {
let mut base = Error::new(Span::call_site(), "unchanged");
base.extend(std::iter::empty::<Error>());
assert_eq!(base.to_string(), "unchanged");
}
#[test]
fn collect_errors_into_single_error() {
let errors = vec![
Error::new(Span::call_site(), "alpha"),
Error::new(Span::call_site(), "beta"),
Error::new(Span::call_site(), "gamma"),
];
let combined: Error = errors.into_iter().collect();
assert_eq!(combined.to_string(), "alpha\nbeta\ngamma");
}
#[test]
fn collect_empty_iterator_gives_empty_error() {
let combined: Error = std::iter::empty::<Error>().collect();
assert!(combined.is_empty());
}
#[test]
fn from_token_stream_round_trips_single_error() {
let original = Error::new(Span::call_site(), "round trip me");
let ts = original.to_compile_error();
let recovered = Error::from_token_stream(ts);
assert_eq!(recovered.to_string(), "round trip me");
}
#[test]
fn from_token_stream_round_trips_combined_errors() {
let mut original = Error::new(Span::call_site(), "first");
original.combine(Error::new(Span::call_site(), "second"));
original.combine(Error::new(Span::call_site(), "third"));
let ts = original.to_compile_error();
let recovered = Error::from_token_stream(ts);
let s = recovered.to_string();
assert!(s.contains("first"));
assert!(s.contains("second"));
assert!(s.contains("third"));
}
#[test]
fn from_token_stream_preserves_count() {
let mut original = Error::new(Span::call_site(), "a");
original.combine(Error::new(Span::call_site(), "b"));
let ts = original.to_compile_error();
let recovered = Error::from_token_stream(ts);
let output = recovered.to_compile_error().to_string();
assert!(output.contains("\"a\""));
assert!(output.contains("\"b\""));
}
#[test]
fn from_token_stream_empty_stream_returns_fallback() {
use proc_macro2::TokenStream;
let empty: TokenStream = TokenStream::new();
let recovered = Error::from_token_stream(empty);
assert!(!recovered.is_empty());
assert!(recovered.to_string().contains("unexpected error"));
}
#[test]
fn from_token_stream_non_compile_error_returns_fallback() {
use proc_macro2::TokenStream;
let ts: TokenStream = "let x = 1;".parse().unwrap();
let recovered = Error::from_token_stream(ts);
assert!(!recovered.is_empty());
}
#[test]
fn from_token_stream_unicode_message() {
let original = Error::new(Span::call_site(), "héllo wörld");
let ts = original.to_compile_error();
let recovered = Error::from_token_stream(ts);
assert!(recovered.to_string().contains("héllo wörld"));
}
}