Skip to main content

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 a test asserting that an expression matches a pattern.
199///
200/// This is ideal for verifying that a builder-style setter stored the expected
201/// enum variant, collapsing many near-identical tests into single lines.
202///
203/// # Examples
204///
205/// ```ignore
206/// matches_test!(
207///     test_resolution_braille,
208///     PieChart::default().resolution(Resolution::Braille).resolution,
209///     Resolution::Braille
210/// );
211/// ```
212#[macro_export]
213macro_rules! matches_test {
214    ($test_name:ident, $value:expr, $pattern:pat $(,)?) => {
215        #[test]
216        fn $test_name() {
217            assert!(matches!($value, $pattern));
218        }
219    };
220}
221
222/// Generate debug format tests for multiple enum variants.
223///
224/// This macro creates a test for each variant that checks its Debug output.
225///
226/// # Examples
227///
228/// ```ignore
229/// debug_format_tests! {
230///     enum_type: MyEnum,
231///     tests: [
232///         (test_first_debug, MyEnum::First, "First"),
233///         (test_second_debug, MyEnum::Second, "Second"),
234///         (test_third_debug, MyEnum::Third, "Third"),
235///     ]
236/// }
237/// ```
238#[macro_export]
239macro_rules! debug_format_tests {
240    (
241        enum_type: $enum_name:ty,
242        tests: [
243            $(($test_name:ident, $variant:expr, $expected:expr)),+ $(,)?
244        ]
245    ) => {
246        $(
247            #[test]
248            fn $test_name() {
249                let value = $variant;
250                let debug = format!("{:?}", value);
251                assert_eq!(debug, $expected);
252            }
253        )+
254    };
255}
256
257/// Test that a conversion (From/Into) works correctly.
258///
259/// # Examples
260///
261/// ```ignore
262/// conversion_test!(
263///     test_alignment_to_ratatui,
264///     TitleAlignment::Start,
265///     Alignment,
266///     Alignment::Left
267/// );
268/// ```
269#[macro_export]
270macro_rules! conversion_test {
271    ($test_name:ident, $from:expr, $to_type:ty, $expected:expr) => {
272        #[test]
273        fn $test_name() {
274            let result: $to_type = $from.into();
275            assert_eq!(result, $expected);
276        }
277    };
278}
279
280/// Test that multiple enum variants can be instantiated.
281///
282/// Useful for compile-time verification that all variants are accessible.
283///
284/// # Examples
285///
286/// ```ignore
287/// instantiate_variants_test!(
288///     test_all_border_styles,
289///     BorderStyle,
290///     [Standard, Rounded, Dashed, CornerGapped]
291/// );
292/// ```
293#[macro_export]
294macro_rules! instantiate_variants_test {
295    ($test_name:ident, $enum_name:ident, [$($variant:ident),+ $(,)?]) => {
296        #[test]
297        fn $test_name() {
298            $(
299                let _ = $enum_name::$variant;
300            )+
301        }
302    };
303}
304
305/// Test that a method doesn't panic with a given input.
306///
307/// # Examples
308///
309/// ```ignore
310/// no_panic_test!(test_apply_bold, {
311///     let result = TitleStyle::Bold.apply("Test");
312///     assert!(!result.is_empty());
313/// });
314/// ```
315#[macro_export]
316macro_rules! no_panic_test {
317    ($test_name:ident, $body:block) => {
318        #[test]
319        fn $test_name() {
320            $body
321        }
322    };
323}
324
325/// Test that a string transformation preserves certain properties.
326///
327/// # Examples
328///
329/// ```ignore
330/// string_transform_test!(
331///     test_bold_preserves_length,
332///     TitleStyle::Bold.apply("Test"),
333///     original: "Test",
334///     length_preserved: true
335/// );
336/// ```
337#[macro_export]
338macro_rules! string_transform_test {
339    (
340        $test_name:ident,
341        $transform:expr,
342        original: $original:expr,
343        length_preserved: $should_preserve:expr
344    ) => {
345        #[test]
346        fn $test_name() {
347            let original = $original;
348            let result = $transform;
349            if $should_preserve {
350                assert_eq!(result.chars().count(), original.chars().count());
351            }
352        }
353    };
354}
355
356/// Test that rendering to a buffer doesn't panic.
357///
358/// This macro creates a test that ensures a widget can be rendered without
359/// panicking, which is useful for visual regression testing.
360///
361/// # Examples
362///
363/// ```ignore
364/// use ratatui::buffer::Buffer;
365/// use ratatui::layout::Rect;
366///
367/// render_test!(
368///     test_piechart_renders,
369///     PieChart::default(),
370///     Rect::new(0, 0, 40, 20)
371/// );
372/// ```
373#[macro_export]
374macro_rules! render_test {
375    ($test_name:ident, $widget:expr, $area:expr) => {
376        #[test]
377        fn $test_name() {
378            use ratatui::buffer::Buffer;
379            let mut buffer = Buffer::empty($area);
380            ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
381        }
382    };
383}
384
385/// Test that rendering with specific dimensions doesn't panic.
386///
387/// This is a convenience wrapper around `render_test` that creates the Rect for you.
388///
389/// # Examples
390///
391/// ```ignore
392/// render_with_size_test!(
393///     test_chart_small,
394///     PieChart::default(),
395///     width: 20,
396///     height: 10
397/// );
398/// ```
399#[macro_export]
400macro_rules! render_with_size_test {
401    (
402        $test_name:ident,
403        $widget:expr,
404        width: $width:expr,
405        height: $height:expr
406    ) => {
407        #[test]
408        fn $test_name() {
409            use ratatui::buffer::Buffer;
410            use ratatui::layout::Rect;
411            let area = Rect::new(0, 0, $width, $height);
412            let mut buffer = Buffer::empty(area);
413            ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
414        }
415    };
416}
417
418/// Test rendering with multiple widget configurations.
419///
420/// Useful for testing that various configurations all render without panicking.
421///
422/// # Examples
423///
424/// ```ignore
425/// multi_render_test!(test_pie_configurations, [
426///     (PieChart::default(), Rect::new(0, 0, 20, 10)),
427///     (PieChart::default().show_legend(false), Rect::new(0, 0, 30, 15)),
428/// ]);
429/// ```
430#[macro_export]
431macro_rules! multi_render_test {
432    ($test_name:ident, [$(($widget:expr, $area:expr)),+ $(,)?]) => {
433        #[test]
434        fn $test_name() {
435            use ratatui::buffer::Buffer;
436            $(
437                let mut buffer = Buffer::empty($area);
438                ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
439            )+
440        }
441    };
442}
443
444/// Test that rendering to an empty area doesn't panic.
445///
446/// # Examples
447///
448/// ```ignore
449/// render_empty_test!(test_chart_empty, PieChart::default());
450/// ```
451#[macro_export]
452macro_rules! render_empty_test {
453    ($test_name:ident, $widget:expr) => {
454        #[test]
455        fn $test_name() {
456            use ratatui::buffer::Buffer;
457            use ratatui::layout::Rect;
458            let mut buffer = Buffer::empty(Rect::new(0, 0, 0, 0));
459            ratatui::widgets::Widget::render($widget, buffer.area, &mut buffer);
460        }
461    };
462}
463
464#[cfg(test)]
465#[allow(unnameable_test_items)]
466mod tests {
467    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
468    enum TestEnum {
469        #[default]
470        First,
471        Second,
472        Third,
473    }
474
475    // Test the macros themselves
476    assert_test!(macro_assert_test_works, true);
477    assert_eq_test!(macro_assert_eq_test_works, 2 + 2, 4);
478    matches_test!(macro_matches_test_works, TestEnum::Second, TestEnum::Second);
479
480    instantiate_variants_test!(test_enum_variants, TestEnum, [First, Second, Third]);
481
482    debug_format_tests! {
483        enum_type: TestEnum,
484        tests: [
485            (test_first_fmt, TestEnum::First, "First"),
486            (test_second_fmt, TestEnum::Second, "Second"),
487        ]
488    }
489
490    enum_tests! {
491        enum_type: TestEnum,
492        default_test: (test_enum_default, TestEnum::First),
493        clone_test: (test_enum_clone, TestEnum::Second),
494        debug_test: (test_enum_debug, TestEnum::Third, "Third"),
495    }
496}