module_lifecycle/
module_lifecycle.rs

1use rest::prelude::*;
2use std::cell::RefCell;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5// Setup counters to track execution
6static BEFORE_ALL_COUNTER: AtomicUsize = AtomicUsize::new(0);
7static SETUP_COUNTER: AtomicUsize = AtomicUsize::new(0);
8static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
9static TEARDOWN_COUNTER: AtomicUsize = AtomicUsize::new(0);
10static AFTER_ALL_COUNTER: AtomicUsize = AtomicUsize::new(0);
11
12// A shared value for tests
13thread_local! {
14    static TEST_VALUE: RefCell<String> = RefCell::new(String::new());
15}
16
17// Helper to set the test value
18fn set_test_value(value: &str) {
19    TEST_VALUE.with(|v| {
20        *v.borrow_mut() = value.to_string();
21    });
22}
23
24// Helper to get the test value
25fn get_test_value() -> String {
26    TEST_VALUE.with(|v| v.borrow().clone())
27}
28
29// Helper to print a module state
30fn print_module_state(stage: &str) {
31    println!("{}", "-".repeat(50));
32    println!("{}", stage);
33    println!("  Before All count: {}", BEFORE_ALL_COUNTER.load(Ordering::SeqCst));
34    println!("  Setup count     : {}", SETUP_COUNTER.load(Ordering::SeqCst));
35    println!("  Test count      : {}", TEST_COUNTER.load(Ordering::SeqCst));
36    println!("  Teardown count  : {}", TEARDOWN_COUNTER.load(Ordering::SeqCst));
37    println!("  After All count : {}", AFTER_ALL_COUNTER.load(Ordering::SeqCst));
38    println!("  Test value      : {}", get_test_value());
39    println!("{}", "-".repeat(50));
40}
41
42// Module demonstrating the before_all and after_all attributes
43#[with_fixtures_module]
44mod lifecycle_test {
45    use super::*;
46
47    // Runs once before any test in this module
48    #[before_all]
49    fn setup_module() {
50        println!("Running before_all setup...");
51        BEFORE_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
52        set_test_value("Initialized by before_all");
53    }
54
55    // Runs before each test in this module
56    #[setup]
57    fn setup_test() {
58        println!("Running setup for a test...");
59        SETUP_COUNTER.fetch_add(1, Ordering::SeqCst);
60        // We'll append to the test value to see the sequence
61        let current = get_test_value();
62        set_test_value(&format!("{} + setup", current));
63    }
64
65    // First test
66    #[test]
67    pub fn first_test() {
68        println!("Running first test...");
69        TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
70
71        // Check the counters
72        expect!(BEFORE_ALL_COUNTER.load(Ordering::SeqCst)).to_equal(1);
73        expect!(SETUP_COUNTER.load(Ordering::SeqCst)).to_equal(1);
74
75        // Modify the test value
76        let current = get_test_value();
77        set_test_value(&format!("{} + first_test", current));
78
79        print_module_state("During first test");
80    }
81
82    // Second test
83    #[test]
84    pub fn second_test() {
85        println!("Running second test...");
86        TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
87
88        // Check the counters
89        expect!(BEFORE_ALL_COUNTER.load(Ordering::SeqCst)).to_equal(1); // Still 1 because before_all runs only once
90        expect!(SETUP_COUNTER.load(Ordering::SeqCst)).to_equal(2); // Now 2 because setup runs before each test
91
92        // Modify the test value
93        let current = get_test_value();
94        set_test_value(&format!("{} + second_test", current));
95
96        print_module_state("During second test");
97    }
98
99    // Runs after each test in this module
100    #[tear_down]
101    fn teardown_test() {
102        println!("Running teardown after a test...");
103        TEARDOWN_COUNTER.fetch_add(1, Ordering::SeqCst);
104
105        // Append to the test value
106        let current = get_test_value();
107        set_test_value(&format!("{} + teardown", current));
108    }
109
110    // Runs once after all tests in this module
111    #[after_all]
112    fn teardown_module() {
113        println!("Running after_all teardown...");
114        AFTER_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
115
116        // Append to the test value
117        let current = get_test_value();
118        set_test_value(&format!("{} + after_all", current));
119
120        print_module_state("After all tests completed");
121    }
122}
123
124// Alternative approach to running the example in the main function
125fn run_simulated_tests() {
126    println!("\nRunning example of module lifecycle fixtures:");
127    println!("This demonstrates the order of execution for fixture types:");
128    println!("  1. #[before_all]   - Runs once before any test in the module");
129    println!("  2. #[setup]        - Runs before each test");
130    println!("  3. Test function   - The actual test");
131    println!("  4. #[tear_down]    - Runs after each test");
132    println!("  5. #[after_all]    - Runs once after all tests in the module\n");
133
134    // Print initial state
135    print_module_state("Initial state");
136
137    // Setup handlers for real tests would execute in this order
138    println!("\nIn normal test execution, the lifecycle would be:");
139
140    // Simulate before_all
141    println!("1. Running before_all setup once at the beginning");
142    BEFORE_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
143    set_test_value("Initialized by before_all");
144
145    // Simulate first test
146    println!("2. For test #1: Running setup");
147    SETUP_COUNTER.fetch_add(1, Ordering::SeqCst);
148    let current = get_test_value();
149    set_test_value(&format!("{} + setup", current));
150
151    println!("3. For test #1: Running the test itself");
152    TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
153    let current = get_test_value();
154    set_test_value(&format!("{} + first_test", current));
155
156    println!("4. For test #1: Running teardown");
157    TEARDOWN_COUNTER.fetch_add(1, Ordering::SeqCst);
158    let current = get_test_value();
159    set_test_value(&format!("{} + teardown", current));
160
161    // Simulate second test
162    println!("5. For test #2: Running setup again");
163    SETUP_COUNTER.fetch_add(1, Ordering::SeqCst);
164    let current = get_test_value();
165    set_test_value(&format!("{} + setup", current));
166
167    println!("6. For test #2: Running the test itself");
168    TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
169    let current = get_test_value();
170    set_test_value(&format!("{} + second_test", current));
171
172    println!("7. For test #2: Running teardown");
173    TEARDOWN_COUNTER.fetch_add(1, Ordering::SeqCst);
174    let current = get_test_value();
175    set_test_value(&format!("{} + teardown", current));
176
177    // Simulate after_all
178    println!("8. After all tests: Running after_all cleanup once at the end");
179    AFTER_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
180    let current = get_test_value();
181    set_test_value(&format!("{} + after_all", current));
182
183    // Final state
184    print_module_state("Final state");
185}
186
187fn main() {
188    // Enable enhanced output for better test reporting
189    config().enhanced_output(true).apply();
190
191    // Run the simulated tests
192    run_simulated_tests();
193
194    // Notes about how after_all works in real tests
195    println!("\nNOTE: In real tests with cargo test:");
196    println!("- #[before_all] will run once before any test in the module");
197    println!("- #[after_all] will run at process exit");
198    println!("\nAll tests passed!");
199}