From b179419e8c3a211785fc3b9f556c6e1a3b46a3c4 Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:03:05 +0700 Subject: [PATCH] Add formatter coverage tests and bump version --- CHANGELOG.md | 8 +++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 14 ++-- tests/error_derive.rs | 68 +++++++++++++++++-- tests/error_derive_from_trybuild.rs | 12 ++++ tests/ui/formatter/fail/unsupported_flag.rs | 9 +++ .../ui/formatter/fail/unsupported_flag.stderr | 11 +++ .../formatter/fail/unsupported_formatter.rs | 9 +++ .../fail/unsupported_formatter.stderr | 11 +++ tests/ui/formatter/pass/all_formatters.rs | 36 ++++++++++ 11 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 tests/ui/formatter/fail/unsupported_flag.rs create mode 100644 tests/ui/formatter/fail/unsupported_flag.stderr create mode 100644 tests/ui/formatter/fail/unsupported_formatter.rs create mode 100644 tests/ui/formatter/fail/unsupported_formatter.stderr create mode 100644 tests/ui/formatter/pass/all_formatters.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 054b05f..b42f842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,14 @@ All notable changes to this project will be documented in this file. - `masterror::Error` now uses the in-tree derive, removing the dependency on `thiserror` while keeping the same runtime behaviour and diagnostics. +## [0.5.6] - 2025-09-28 + +### Tests +- Added runtime coverage exercising every derive formatter variant (including + case-sensitive formatters) and asserted the rendered output. +- Added `trybuild` suites that compile successful formatter usage and verify the + emitted diagnostics for unsupported specifiers. + ## [0.5.5] - 2025-09-27 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index cfef7f9..88861ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1527,7 +1527,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.5.5" +version = "0.5.6" dependencies = [ "actix-web", "axum", diff --git a/Cargo.toml b/Cargo.toml index 927a370..4779d51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masterror" -version = "0.5.5" +version = "0.5.6" rust-version = "1.90" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 3afdd70..551a9f0 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Stable categories, conservative HTTP mapping, no `unsafe`. ~~~toml [dependencies] -masterror = { version = "0.5.5", default-features = false } +masterror = { version = "0.5.6", default-features = false } # or with features: -# masterror = { version = "0.5.5", features = [ +# masterror = { version = "0.5.6", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", @@ -66,10 +66,10 @@ masterror = { version = "0.5.5", default-features = false } ~~~toml [dependencies] # lean core -masterror = { version = "0.5.5", default-features = false } +masterror = { version = "0.5.6", default-features = false } # with Axum/Actix + JSON + integrations -# masterror = { version = "0.5.5", features = [ +# masterror = { version = "0.5.6", features = [ # "axum", "actix", "openapi", "serde_json", # "sqlx", "sqlx-migrate", "reqwest", "redis", # "validator", "config", "tokio", "multipart", @@ -262,13 +262,13 @@ assert_eq!(resp.status, 401); Minimal core: ~~~toml -masterror = { version = "0.5.5", default-features = false } +masterror = { version = "0.5.6", default-features = false } ~~~ API (Axum + JSON + deps): ~~~toml -masterror = { version = "0.5.5", features = [ +masterror = { version = "0.5.6", features = [ "axum", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } @@ -277,7 +277,7 @@ masterror = { version = "0.5.5", features = [ API (Actix + JSON + deps): ~~~toml -masterror = { version = "0.5.5", features = [ +masterror = { version = "0.5.6", features = [ "actix", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } diff --git a/tests/error_derive.rs b/tests/error_derive.rs index d4b70bf..af896c7 100644 --- a/tests/error_derive.rs +++ b/tests/error_derive.rs @@ -1,8 +1,8 @@ #![allow(unused_variables, non_shorthand_field_patterns)] -use std::error::Error as StdError; #[cfg(error_generic_member_access)] use std::ptr; +use std::{error::Error as StdError, fmt}; use masterror::Error; @@ -152,9 +152,9 @@ enum EnumWithBacktrace { #[derive(Debug, Error)] #[error( - "x={value:x} X={value:X} #x={value:#x} #X={value:#X} b={value:b} #b={value:#b} \ - o={value:o} #o={value:#o} e={float:e} #e={float:#e} E={float:E} #E={float:#E} \ - p={ptr:p} #p={ptr:#p}" + "display={value} debug={value:?} #debug={value:#?} x={value:x} X={value:X} \ + #x={value:#x} #X={value:#X} b={value:b} #b={value:#b} o={value:o} #o={value:#o} \ + e={float:e} #e={float:#e} E={float:E} #E={float:#E} p={ptr:p} #p={ptr:#p}" )] struct FormatterShowcase { value: u32, @@ -162,6 +162,24 @@ struct FormatterShowcase { ptr: *const u32 } +#[derive(Debug)] +struct PrettyDebugValue { + label: &'static str +} + +impl fmt::Display for PrettyDebugValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.label) + } +} + +#[derive(Debug, Error)] +#[error("display={value} debug={value:?} #debug={value:#?} tuple={tuple:?} #tuple={tuple:#?}")] +struct FormatterDebugShowcase { + value: PrettyDebugValue, + tuple: (&'static str, u8) +} + #[cfg(error_generic_member_access)] fn assert_backtrace_interfaces(error: &E, expected: &std::backtrace::Backtrace) where @@ -361,6 +379,34 @@ fn enum_backtrace_field_is_returned() { } } +#[test] +fn supports_display_and_debug_formatters() { + let value = PrettyDebugValue { + label: "Alpha" + }; + let tuple = ("tuple", 7u8); + + let expected = format!( + "display={value} debug={value:?} #debug={value:#?} tuple={tuple:?} #tuple={tuple:#?}", + ); + + let standard_debug = format!("{value:?}"); + let alternate_debug = format!("{value:#?}"); + assert_ne!(standard_debug, alternate_debug); + + let tuple_debug = format!("{tuple:?}"); + let tuple_alternate_debug = format!("{tuple:#?}"); + assert_ne!(tuple_debug, tuple_alternate_debug); + + let err = FormatterDebugShowcase { + value, + tuple + }; + + assert_eq!(err.to_string(), expected); + assert!(StdError::source(&err).is_none()); +} + #[test] fn supports_extended_formatters() { let value = 0x5A5Au32; @@ -374,11 +420,19 @@ fn supports_extended_formatters() { }; let expected = format!( - "x={value:x} X={value:X} #x={value:#x} #X={value:#X} b={value:b} #b={value:#b} \ - o={value:o} #o={value:#o} e={float:e} #e={float:#e} E={float:E} #E={float:#E} \ - p={ptr:p} #p={ptr:#p}" + "display={value} debug={value:?} #debug={value:#?} x={value:x} X={value:X} \ + #x={value:#x} #X={value:#X} b={value:b} #b={value:#b} o={value:o} #o={value:#o} \ + e={float:e} #e={float:#e} E={float:E} #E={float:#E} p={ptr:p} #p={ptr:#p}" ); + let lower_hex = format!("{value:x}"); + let upper_hex = format!("{value:X}"); + assert_ne!(lower_hex, upper_hex); + + let lower_exp = format!("{float:e}"); + let upper_exp = format!("{float:E}"); + assert_ne!(lower_exp, upper_exp); + assert_eq!(err.to_string(), expected); assert!(StdError::source(&err).is_none()); } diff --git a/tests/error_derive_from_trybuild.rs b/tests/error_derive_from_trybuild.rs index 2853697..b088485 100644 --- a/tests/error_derive_from_trybuild.rs +++ b/tests/error_derive_from_trybuild.rs @@ -17,3 +17,15 @@ fn backtrace_attribute_compile_failures() { let t = TestCases::new(); t.compile_fail("tests/ui/backtrace/*.rs"); } + +#[test] +fn formatter_attribute_passes() { + let t = TestCases::new(); + t.pass("tests/ui/formatter/pass/*.rs"); +} + +#[test] +fn formatter_attribute_compile_failures() { + let t = TestCases::new(); + t.compile_fail("tests/ui/formatter/fail/*.rs"); +} diff --git a/tests/ui/formatter/fail/unsupported_flag.rs b/tests/ui/formatter/fail/unsupported_flag.rs new file mode 100644 index 0000000..b04dad4 --- /dev/null +++ b/tests/ui/formatter/fail/unsupported_flag.rs @@ -0,0 +1,9 @@ +use masterror::Error; + +#[derive(Debug, Error)] +#[error("{value:##x}")] +struct UnsupportedFlag { + value: u32, +} + +fn main() {} diff --git a/tests/ui/formatter/fail/unsupported_flag.stderr b/tests/ui/formatter/fail/unsupported_flag.stderr new file mode 100644 index 0000000..c7b58b4 --- /dev/null +++ b/tests/ui/formatter/fail/unsupported_flag.stderr @@ -0,0 +1,11 @@ +error: placeholder spanning bytes 0..11 uses an unsupported formatter + --> tests/ui/formatter/fail/unsupported_flag.rs:4:9 + | +4 | #[error("{value:##x}")] + | ^^^^^^^^^^^^^ + +error: missing #[error(...)] attribute + --> tests/ui/formatter/fail/unsupported_flag.rs:5:8 + | +5 | struct UnsupportedFlag { + | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/unsupported_formatter.rs b/tests/ui/formatter/fail/unsupported_formatter.rs new file mode 100644 index 0000000..330f16f --- /dev/null +++ b/tests/ui/formatter/fail/unsupported_formatter.rs @@ -0,0 +1,9 @@ +use masterror::Error; + +#[derive(Debug, Error)] +#[error("{value:y}")] +struct UnsupportedFormatter { + value: u32, +} + +fn main() {} diff --git a/tests/ui/formatter/fail/unsupported_formatter.stderr b/tests/ui/formatter/fail/unsupported_formatter.stderr new file mode 100644 index 0000000..aa11c64 --- /dev/null +++ b/tests/ui/formatter/fail/unsupported_formatter.stderr @@ -0,0 +1,11 @@ +error: placeholder spanning bytes 0..9 uses an unsupported formatter + --> tests/ui/formatter/fail/unsupported_formatter.rs:4:9 + | +4 | #[error("{value:y}")] + | ^^^^^^^^^^^ + +error: missing #[error(...)] attribute + --> tests/ui/formatter/fail/unsupported_formatter.rs:5:8 + | +5 | struct UnsupportedFormatter { + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/formatter/pass/all_formatters.rs b/tests/ui/formatter/pass/all_formatters.rs new file mode 100644 index 0000000..0c74be1 --- /dev/null +++ b/tests/ui/formatter/pass/all_formatters.rs @@ -0,0 +1,36 @@ +use masterror::Error; + +#[derive(Debug, Error)] +#[error( + "display={pretty} debug={pretty:?} #debug={pretty:#?} x={value:x} X={value:X} \ + #x={value:#x} #X={value:#X} b={value:b} #b={value:#b} o={value:o} #o={value:#o} \ + e={float:e} #e={float:#e} E={float:E} #E={float:#E} p={ptr:p} #p={ptr:#p}" +)] +struct FormatterVariants { + value: u32, + float: f64, + ptr: *const u32, + pretty: PrettyDebugValue, +} + +#[derive(Debug)] +struct PrettyDebugValue { + label: &'static str, +} + +impl core::fmt::Display for PrettyDebugValue { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(self.label) + } +} + +fn main() { + let showcase = FormatterVariants { + value: 0x5A5Au32, + float: 1234.5, + ptr: core::ptr::null(), + pretty: PrettyDebugValue { label: "alpha" }, + }; + + let _ = showcase.to_string(); +}