tui_piechart/macros/test.rs
1//! Test utility macros for reducing boilerplate code.
2//!
3//! This module contains macros specifically designed for testing patterns,
4//! helping eliminate repetitive test code across the crate.
5//!
6//! # Overview
7//!
8//! The macros in this module help with:
9//! - Enum testing (default, clone, debug)
10//! - Assertion tests
11//! - Debug format verification
12//! - Type conversions
13//! - String transformations
14//! - Render/visual tests (widget rendering without panics)
15//!
16//! # Organization
17//!
18//! This is part of the `macros` module family:
19//! - **`macros::test`** - Test utilities (this module)
20//! - Module-specific macros live in their respective files (e.g., `unicode_converter!` in `title.rs`)
21//!
22//! # Usage Examples
23//!
24//! ## Testing Enums
25//!
26//! ```
27//! # use tui_piechart::enum_tests;
28//! #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
29//! enum MyPosition {
30//! #[default]
31//! Top,
32//! Bottom,
33//! }
34//!
35//! #[cfg(test)]
36//! mod tests {
37//! use super::*;
38//!
39//! enum_tests! {
40//! enum_type: MyPosition,
41//! default_test: (test_default, MyPosition::Top),
42//! clone_test: (test_clone, MyPosition::Bottom),
43//! debug_test: (test_debug, MyPosition::Bottom, "Bottom"),
44//! }
45//! }
46//! ```
47//!
48//! ## Testing Multiple Debug Formats
49//!
50//! ```
51//! # use tui_piechart::debug_format_tests;
52//! # #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
53//! # enum MyAlignment {
54//! # #[default]
55//! # Start,
56//! # Center,
57//! # End,
58//! # }
59//! #[cfg(test)]
60//! mod tests {
61//! use super::*;
62//!
63//! debug_format_tests! {
64//! enum_type: MyAlignment,
65//! tests: [
66//! (test_start_debug, MyAlignment::Start, "Start"),
67//! (test_center_debug, MyAlignment::Center, "Center"),
68//! (test_end_debug, MyAlignment::End, "End"),
69//! ]
70//! }
71//! }
72//! ```
73//!
74//! ## Simple Assertions
75//!
76//! ```
77//! # use tui_piechart::assert_test;
78//! # use tui_piechart::assert_eq_test;
79//! #[cfg(test)]
80//! mod tests {
81//! assert_test!(test_is_empty, "".is_empty());
82//! assert_eq_test!(test_addition, 2 + 2, 4);
83//! }
84//! ```
85//!
86//! ## Render Tests
87//!
88//! ```ignore
89//! use tui_piechart::{render_test, render_with_size_test, render_empty_test};
90//! use tui_piechart::PieChart;
91//! use ratatui::layout::Rect;
92//!
93//! #[cfg(test)]
94//! mod tests {
95//! render_test!(test_basic_render, PieChart::default(), Rect::new(0, 0, 40, 20));
96//! render_with_size_test!(test_small_size, PieChart::default(), width: 20, height: 10);
97//! render_empty_test!(test_empty_area, PieChart::default());
98//! }
99//! ```
100//!
101//! # Benefits
102//!
103//! - **Reduces boilerplate**: Write less repetitive test code
104//! - **Consistency**: All tests follow the same pattern
105//! - **Maintainability**: Change patterns in one place
106//! - **Readability**: Declarative test definitions
107//! - **Visual testing**: Ensure widgets render without panics
108
109/// Generate standard enum tests (default, clone, debug).
110///
111/// This macro generates common test cases for enums that implement Default, Clone,
112/// Copy, Debug, and `PartialEq`. It reduces boilerplate in test modules.
113///
114/// # Examples
115///
116/// ```ignore
117/// #[cfg(test)]
118/// mod tests {
119/// use super::*;
120///
121/// enum_tests! {
122/// enum_type: MyEnum,
123/// default_test: (test_default, MyEnum::DefaultVariant),
124/// clone_test: (test_clone, MyEnum::OtherVariant),
125/// debug_test: (test_debug, MyEnum::OtherVariant, "OtherVariant"),
126/// }
127/// }
128/// ```
129#[macro_export]
130macro_rules! enum_tests {
131 (
132 enum_type: $enum_name:ty,
133 default_test: ($default_test_name:ident, $default_variant:expr),
134 clone_test: ($clone_test_name:ident, $test_variant:expr),
135 debug_test: ($debug_test_name:ident, $debug_variant:expr, $debug_str:expr $(,)?),
136 ) => {
137 #[test]
138 fn $default_test_name() {
139 assert_eq!(<$enum_name>::default(), $default_variant);
140 }
141
142 #[test]
143 fn $clone_test_name() {
144 let value = $test_variant;
145 let cloned = value;
146 assert_eq!(value, cloned);
147 }
148
149 #[test]
150 fn $debug_test_name() {
151 let value = $debug_variant;
152 let debug = format!("{:?}", value);
153 assert_eq!(debug, $debug_str);
154 }
155 };
156}
157
158/// Generate a simple assertion test.
159///
160/// Creates a test function with a given name and assertion.
161///
162/// # Examples
163///
164/// ```ignore
165/// assert_test!(test_addition, 2 + 2 == 4);
166/// assert_test!(test_string, "hello".len() == 5);
167/// ```
168#[macro_export]
169macro_rules! assert_test {
170 ($test_name:ident, $assertion:expr) => {
171 #[test]
172 fn $test_name() {
173 assert!($assertion);
174 }
175 };
176}
177
178/// Generate an equality assertion test.
179///
180/// Creates a test that checks if two expressions are equal.
181///
182/// # Examples
183///
184/// ```ignore
185/// assert_eq_test!(test_math, 2 + 2, 4);
186/// assert_eq_test!(test_default, MyType::default().value(), 0);
187/// ```
188#[macro_export]
189macro_rules! assert_eq_test {
190 ($test_name:ident, $left:expr, $right:expr) => {
191 #[test]
192 fn $test_name() {
193 assert_eq!($left, $right);
194 }
195 };
196}
197
198/// Generate debug format tests for multiple enum variants.
199///
200/// This macro creates a test for each variant that checks its Debug output.
201///
202/// # Examples
203///
204/// ```ignore
205/// debug_format_tests! {
206/// enum_type: MyEnum,
207/// tests: [
208/// (test_first_debug, MyEnum::First, "First"),
209/// (test_second_debug, MyEnum::Second, "Second"),
210/// (test_third_debug, MyEnum::Third, "Third"),
211/// ]
212/// }
213/// ```
214#[macro_export]
215macro_rules! debug_format_tests {
216 (
217 enum_type: $enum_name:ty,
218 tests: [
219 $(($test_name:ident, $variant:expr, $expected:expr)),+ $(,)?
220 ]
221 ) => {
222 $(
223 #[test]
224 fn $test_name() {
225 let value = $variant;
226 let debug = format!("{:?}", value);
227 assert_eq!(debug, $expected);
228 }
229 )+
230 };
231}
232
233/// Test that a conversion (From/Into) works correctly.
234///
235/// # Examples
236///
237/// ```ignore
238/// conversion_test!(
239/// test_alignment_to_ratatui,
240/// TitleAlignment::Start,
241/// Alignment,
242/// Alignment::Left
243/// );
244/// ```
245#[macro_export]
246macro_rules! conversion_test {
247 ($test_name:ident, $from:expr, $to_type:ty, $expected:expr) => {
248 #[test]
249 fn $test_name() {
250 let result: $to_type = $from.into();
251 assert_eq!(result, $expected);
252 }
253 };
254}
255
256/// Test that multiple enum variants can be instantiated.
257///
258/// Useful for compile-time verification that all variants are accessible.
259///
260/// # Examples
261///
262/// ```ignore
263/// instantiate_variants_test!(
264/// test_all_border_styles,
265/// BorderStyle,
266/// [Standard, Rounded, Dashed, CornerGapped]
267/// );
268/// ```
269#[macro_export]
270macro_rules! instantiate_variants_test {
271 ($test_name:ident, $enum_name:ident, [$($variant:ident),+ $(,)?]) => {
272 #[test]
273 fn $test_name() {
274 $(
275 let _ = $enum_name::$variant;
276 )+
277 }
278 };
279}
280
281/// Test that a method doesn't panic with a given input.
282///
283/// # Examples
284///
285/// ```ignore
286/// no_panic_test!(test_apply_bold, {
287/// let result = TitleStyle::Bold.apply("Test");
288/// assert!(!result.is_empty());
289/// });
290/// ```
291#[macro_export]
292macro_rules! no_panic_test {
293 ($test_name:ident, $body:block) => {
294 #[test]
295 fn $test_name() {
296 $body
297 }
298 };
299}
300
301/// Test that a string transformation preserves certain properties.
302///
303/// # Examples
304///
305/// ```ignore
306/// string_transform_test!(
307/// test_bold_preserves_length,
308/// TitleStyle::Bold.apply("Test"),
309/// original: "Test",
310/// length_preserved: true
311/// );
312/// ```
313#[macro_export]
314macro_rules! string_transform_test {
315 (
316 $test_name:ident,
317 $transform:expr,
318 original: $original:expr,
319 length_preserved: $should_preserve:expr
320 ) => {
321 #[test]
322 fn $test_name() {
323 let original = $original;
324 let result = $transform;
325 if $should_preserve {
326 assert_eq!(result.chars().count(), original.chars().count());
327 }
328 }
329 };
330}
331
332/// Test that rendering to a buffer doesn't panic.
333///
334/// This macro creates a test that ensures a widget can be rendered without
335/// panicking, which is useful for visual regression testing.
336///
337/// # Examples
338///
339/// ```ignore
340/// use ratatui::buffer::Buffer;
341/// use ratatui::layout::Rect;
342///
343/// render_test!(
344/// test_piechart_renders,
345/// PieChart::default(),
346/// Rect::new(0, 0, 40, 20)
347/// );
348/// ```
349#[macro_export]
350macro_rules! render_test {
351 ($test_name:ident, $widget:expr, $area:expr) => {
352 #[test]
353 fn $test_name() {
354 use ratatui::buffer::Buffer;
355 let mut buffer = Buffer::empty($area);
356 ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
357 }
358 };
359}
360
361/// Test that rendering with specific dimensions doesn't panic.
362///
363/// This is a convenience wrapper around `render_test` that creates the Rect for you.
364///
365/// # Examples
366///
367/// ```ignore
368/// render_with_size_test!(
369/// test_chart_small,
370/// PieChart::default(),
371/// width: 20,
372/// height: 10
373/// );
374/// ```
375#[macro_export]
376macro_rules! render_with_size_test {
377 (
378 $test_name:ident,
379 $widget:expr,
380 width: $width:expr,
381 height: $height:expr
382 ) => {
383 #[test]
384 fn $test_name() {
385 use ratatui::buffer::Buffer;
386 use ratatui::layout::Rect;
387 let area = Rect::new(0, 0, $width, $height);
388 let mut buffer = Buffer::empty(area);
389 ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
390 }
391 };
392}
393
394/// Test rendering with multiple widget configurations.
395///
396/// Useful for testing that various configurations all render without panicking.
397///
398/// # Examples
399///
400/// ```ignore
401/// multi_render_test!(test_pie_configurations, [
402/// (PieChart::default(), Rect::new(0, 0, 20, 10)),
403/// (PieChart::default().show_legend(false), Rect::new(0, 0, 30, 15)),
404/// ]);
405/// ```
406#[macro_export]
407macro_rules! multi_render_test {
408 ($test_name:ident, [$(($widget:expr, $area:expr)),+ $(,)?]) => {
409 #[test]
410 fn $test_name() {
411 use ratatui::buffer::Buffer;
412 $(
413 let mut buffer = Buffer::empty($area);
414 ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
415 )+
416 }
417 };
418}
419
420/// Test that rendering to an empty area doesn't panic.
421///
422/// # Examples
423///
424/// ```ignore
425/// render_empty_test!(test_chart_empty, PieChart::default());
426/// ```
427#[macro_export]
428macro_rules! render_empty_test {
429 ($test_name:ident, $widget:expr) => {
430 #[test]
431 fn $test_name() {
432 use ratatui::buffer::Buffer;
433 use ratatui::layout::Rect;
434 let mut buffer = Buffer::empty(Rect::new(0, 0, 0, 0));
435 ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
436 }
437 };
438}
439
440#[cfg(test)]
441#[allow(unnameable_test_items)]
442mod tests {
443 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
444 enum TestEnum {
445 #[default]
446 First,
447 Second,
448 Third,
449 }
450
451 // Test the macros themselves
452 assert_test!(macro_assert_test_works, true);
453 assert_eq_test!(macro_assert_eq_test_works, 2 + 2, 4);
454
455 instantiate_variants_test!(test_enum_variants, TestEnum, [First, Second, Third]);
456
457 debug_format_tests! {
458 enum_type: TestEnum,
459 tests: [
460 (test_first_fmt, TestEnum::First, "First"),
461 (test_second_fmt, TestEnum::Second, "Second"),
462 ]
463 }
464
465 enum_tests! {
466 enum_type: TestEnum,
467 default_test: (test_enum_default, TestEnum::First),
468 clone_test: (test_enum_clone, TestEnum::Second),
469 debug_test: (test_enum_debug, TestEnum::Third, "Third"),
470 }
471}