use crate::backend::{Assertion, TestSessionResult};
use crate::config::Config;
use crate::events::{AssertionEvent, EventEmitter, on_failure, on_success};
use crate::frontend::ConsoleRenderer;
use once_cell::sync::Lazy;
use std::cell::RefCell;
use std::collections::HashSet;
use std::sync::RwLock;
pub(crate) static GLOBAL_CONFIG: Lazy<RwLock<Config>> = Lazy::new(|| RwLock::new(Config::new()));
thread_local! {
static TEST_SESSION: RefCell<TestSessionResult> = RefCell::new(TestSessionResult::default());
static REPORTED_MESSAGES: RefCell<HashSet<String>> = RefCell::new(HashSet::new());
static DEDUPLICATE_ENABLED: RefCell<bool> = const { RefCell::new(true) };
static SILENT_MODE: RefCell<bool> = const { RefCell::new(false) };
}
pub struct Reporter;
impl Reporter {
pub fn init() {
on_success(|result| {
Self::handle_success_event(result);
});
on_failure(|result| {
Self::handle_failure_event(result);
});
}
fn handle_success_event(result: Assertion<()>) {
TEST_SESSION.with(|session| {
let mut session = session.borrow_mut();
session.passed_count += 1;
});
let silent = SILENT_MODE.with(|silent| *silent.borrow());
if silent {
return;
}
let should_report = DEDUPLICATE_ENABLED.with(|enabled| {
if !*enabled.borrow() {
return true;
}
REPORTED_MESSAGES.with(|msgs| {
let key = format!("{:?}", result);
let mut messages = msgs.borrow_mut();
if !messages.contains(&key) {
messages.insert(key);
true
} else {
false
}
})
});
if should_report {
let config = GLOBAL_CONFIG.read().unwrap();
let renderer = ConsoleRenderer::new(Config {
use_colors: config.use_colors,
use_unicode_symbols: config.use_unicode_symbols,
show_success_details: config.show_success_details,
enhanced_output: config.enhanced_output,
});
renderer.print_success(&result);
}
}
fn handle_failure_event(result: Assertion<()>) {
TEST_SESSION.with(|session| {
let mut session = session.borrow_mut();
session.failed_count += 1;
session.failures.push(result.clone());
});
let silent = SILENT_MODE.with(|silent| *silent.borrow());
if silent {
return;
}
let should_report = DEDUPLICATE_ENABLED.with(|enabled| {
if !*enabled.borrow() {
return true;
}
let key = format!("{:?}", result);
REPORTED_MESSAGES.with(|msgs| {
let mut messages = msgs.borrow_mut();
if !messages.contains(&key) {
messages.insert(key);
true
} else {
false
}
})
});
if should_report {
let config = GLOBAL_CONFIG.read().unwrap();
let renderer = ConsoleRenderer::new(Config {
use_colors: config.use_colors,
use_unicode_symbols: config.use_unicode_symbols,
show_success_details: config.show_success_details,
enhanced_output: config.enhanced_output,
});
renderer.print_failure(&result);
}
}
pub fn reset_message_cache() {
REPORTED_MESSAGES.with(|msgs| {
msgs.borrow_mut().clear();
});
}
pub fn enable_deduplication() {
DEDUPLICATE_ENABLED.with(|enabled| {
*enabled.borrow_mut() = true;
});
}
pub fn disable_deduplication() {
DEDUPLICATE_ENABLED.with(|enabled| {
*enabled.borrow_mut() = false;
});
}
pub fn enable_silent_mode() {
SILENT_MODE.with(|silent| {
*silent.borrow_mut() = true;
});
}
pub fn disable_silent_mode() {
SILENT_MODE.with(|silent| {
*silent.borrow_mut() = false;
});
}
pub fn summarize() {
TEST_SESSION.with(|session| {
let session = session.borrow();
let config = GLOBAL_CONFIG.read().unwrap();
let renderer = ConsoleRenderer::new(Config {
use_colors: config.use_colors,
use_unicode_symbols: config.use_unicode_symbols,
show_success_details: config.show_success_details,
enhanced_output: config.enhanced_output,
});
renderer.print_session_summary(&session);
});
EventEmitter::emit(AssertionEvent::SessionCompleted);
Self::reset_message_cache();
Self::enable_deduplication();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::assertions::AssertionStep;
use crate::backend::assertions::sentence::AssertionSentence;
fn create_test_assertion(passed: bool) -> Assertion<()> {
let mut assertion = Assertion::new((), "test_value");
assertion.steps.push(AssertionStep {
sentence: AssertionSentence::new("be", if passed { "correct" } else { "incorrect" }),
passed,
logical_op: None,
});
assertion.is_final = false;
assertion
}
fn record_failure(assertion: Assertion<()>) {
TEST_SESSION.with(|session| {
let mut session = session.borrow_mut();
session.failed_count += 1;
session.failures.push(assertion);
});
}
#[test]
fn test_reporter_deduplication_flags() {
Reporter::enable_deduplication();
DEDUPLICATE_ENABLED.with(|enabled| {
assert_eq!(*enabled.borrow(), true);
});
Reporter::disable_deduplication();
DEDUPLICATE_ENABLED.with(|enabled| {
assert_eq!(*enabled.borrow(), false);
});
Reporter::enable_deduplication();
}
#[test]
fn test_reporter_silent_mode() {
Reporter::enable_silent_mode();
SILENT_MODE.with(|silent| {
assert_eq!(*silent.borrow(), true);
});
Reporter::disable_silent_mode();
SILENT_MODE.with(|silent| {
assert_eq!(*silent.borrow(), false);
});
}
#[test]
fn test_reporter_message_cache() {
REPORTED_MESSAGES.with(|msgs| {
msgs.borrow_mut().insert("test_message".to_string());
});
REPORTED_MESSAGES.with(|msgs| {
assert!(msgs.borrow().contains("test_message"));
});
Reporter::reset_message_cache();
REPORTED_MESSAGES.with(|msgs| {
assert!(!msgs.borrow().contains("test_message"));
});
}
#[test]
fn test_handle_success_event() {
TEST_SESSION.with(|session| {
*session.borrow_mut() = TestSessionResult::default();
});
Reporter::disable_deduplication();
let assertion = create_test_assertion(true);
Reporter::handle_success_event(assertion);
TEST_SESSION.with(|session| {
let session = session.borrow();
assert_eq!(session.passed_count, 1);
assert_eq!(session.failed_count, 0);
assert_eq!(session.failures.len(), 0);
});
Reporter::enable_deduplication();
Reporter::reset_message_cache();
}
#[test]
fn test_session_tracking() {
TEST_SESSION.with(|session| {
*session.borrow_mut() = TestSessionResult::default();
});
let assertion = create_test_assertion(false);
record_failure(assertion.clone());
TEST_SESSION.with(|session| {
let session = session.borrow();
assert_eq!(session.passed_count, 0);
assert_eq!(session.failed_count, 1);
assert_eq!(session.failures.len(), 1);
if !session.failures.is_empty() {
let first_failure = &session.failures[0];
assert_eq!(first_failure.expr_str, assertion.expr_str);
assert_eq!(first_failure.steps.len(), assertion.steps.len());
assert_eq!(first_failure.steps[0].passed, assertion.steps[0].passed);
}
});
TEST_SESSION.with(|session| {
*session.borrow_mut() = TestSessionResult::default();
});
}
#[test]
fn test_silent_mode() {
Reporter::enable_silent_mode();
SILENT_MODE.with(|silent| {
assert_eq!(*silent.borrow(), true);
});
TEST_SESSION.with(|session| {
*session.borrow_mut() = TestSessionResult::default();
});
Reporter::handle_success_event(create_test_assertion(true));
TEST_SESSION.with(|session| {
let session = session.borrow();
assert_eq!(session.passed_count, 1);
});
Reporter::disable_silent_mode();
SILENT_MODE.with(|silent| {
assert_eq!(*silent.borrow(), false);
});
TEST_SESSION.with(|session| {
*session.borrow_mut() = TestSessionResult::default();
});
}
#[test]
fn test_deduplication() {
Reporter::enable_deduplication();
Reporter::reset_message_cache();
TEST_SESSION.with(|session| {
*session.borrow_mut() = TestSessionResult::default();
});
let assertion = create_test_assertion(true);
Reporter::handle_success_event(assertion.clone());
Reporter::handle_success_event(assertion);
REPORTED_MESSAGES.with(|msgs| {
assert_eq!(msgs.borrow().len(), 1);
});
TEST_SESSION.with(|session| {
let session = session.borrow();
assert_eq!(session.passed_count, 2);
});
Reporter::reset_message_cache();
}
}