use fluent_test::prelude::*;
use std::cell::RefCell;
use std::sync::atomic::{AtomicUsize, Ordering};
static BEFORE_ALL_COUNTER: AtomicUsize = AtomicUsize::new(0);
static SETUP_COUNTER: AtomicUsize = AtomicUsize::new(0);
static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
static TEARDOWN_COUNTER: AtomicUsize = AtomicUsize::new(0);
static AFTER_ALL_COUNTER: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static TEST_VALUE: RefCell<String> = RefCell::new(String::new());
}
fn set_test_value(value: &str) {
TEST_VALUE.with(|v| {
*v.borrow_mut() = value.to_string();
});
}
fn get_test_value() -> String {
TEST_VALUE.with(|v| v.borrow().clone())
}
fn print_module_state(stage: &str) {
println!("{}", "-".repeat(50));
println!("{}", stage);
println!(" Before All count: {}", BEFORE_ALL_COUNTER.load(Ordering::SeqCst));
println!(" Setup count : {}", SETUP_COUNTER.load(Ordering::SeqCst));
println!(" Test count : {}", TEST_COUNTER.load(Ordering::SeqCst));
println!(" Teardown count : {}", TEARDOWN_COUNTER.load(Ordering::SeqCst));
println!(" After All count : {}", AFTER_ALL_COUNTER.load(Ordering::SeqCst));
println!(" Test value : {}", get_test_value());
println!("{}", "-".repeat(50));
}
#[with_fixtures_module]
mod lifecycle_test {
use super::*;
#[before_all]
fn setup_module() {
println!("Running before_all setup...");
BEFORE_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
set_test_value("Initialized by before_all");
}
#[setup]
fn setup_test() {
println!("Running setup for a test...");
SETUP_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + setup", current));
}
#[test]
pub fn first_test() {
println!("Running first test...");
TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
expect!(BEFORE_ALL_COUNTER.load(Ordering::SeqCst)).to_equal(1);
expect!(SETUP_COUNTER.load(Ordering::SeqCst)).to_equal(1);
let current = get_test_value();
set_test_value(&format!("{} + first_test", current));
print_module_state("During first test");
}
#[test]
pub fn second_test() {
println!("Running second test...");
TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
expect!(BEFORE_ALL_COUNTER.load(Ordering::SeqCst)).to_equal(1); expect!(SETUP_COUNTER.load(Ordering::SeqCst)).to_equal(2);
let current = get_test_value();
set_test_value(&format!("{} + second_test", current));
print_module_state("During second test");
}
#[tear_down]
fn teardown_test() {
println!("Running teardown after a test...");
TEARDOWN_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + teardown", current));
}
#[after_all]
fn teardown_module() {
println!("Running after_all teardown...");
AFTER_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + after_all", current));
print_module_state("After all tests completed");
}
}
fn run_simulated_tests() {
println!("\nRunning example of module lifecycle fixtures:");
println!("This demonstrates the order of execution for fixture types:");
println!(" 1. #[before_all] - Runs once before any test in the module");
println!(" 2. #[setup] - Runs before each test");
println!(" 3. Test function - The actual test");
println!(" 4. #[tear_down] - Runs after each test");
println!(" 5. #[after_all] - Runs once after all tests in the module\n");
print_module_state("Initial state");
println!("\nIn normal test execution, the lifecycle would be:");
println!("1. Running before_all setup once at the beginning");
BEFORE_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
set_test_value("Initialized by before_all");
println!("2. For test #1: Running setup");
SETUP_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + setup", current));
println!("3. For test #1: Running the test itself");
TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + first_test", current));
println!("4. For test #1: Running teardown");
TEARDOWN_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + teardown", current));
println!("5. For test #2: Running setup again");
SETUP_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + setup", current));
println!("6. For test #2: Running the test itself");
TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + second_test", current));
println!("7. For test #2: Running teardown");
TEARDOWN_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + teardown", current));
println!("8. After all tests: Running after_all cleanup once at the end");
AFTER_ALL_COUNTER.fetch_add(1, Ordering::SeqCst);
let current = get_test_value();
set_test_value(&format!("{} + after_all", current));
print_module_state("Final state");
}
fn main() {
config().enhanced_output(true).apply();
run_simulated_tests();
println!("\nNOTE: In real tests with cargo test:");
println!("- #[before_all] will run once before any test in the module");
println!("- #[after_all] will run at process exit");
println!("\nAll tests passed!");
}