1use crate::backend::assertions::sentence::AssertionSentence;
2use std::fmt::Debug;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum LogicalOp {
7 And,
9 Or,
11}
12
13#[derive(Debug, Clone)]
15pub struct AssertionStep {
16 pub sentence: AssertionSentence,
18 pub passed: bool,
20 pub logical_op: Option<LogicalOp>,
22}
23
24#[derive(Debug, Clone)]
26pub struct Assertion<T> {
27 pub value: T,
29 pub expr_str: &'static str,
31 pub negated: bool,
33 pub steps: Vec<AssertionStep>,
35 pub in_chain: bool,
37 pub is_final: bool,
39}
40
41#[derive(Debug, Default)]
43pub struct TestSessionResult {
44 pub passed_count: usize,
46 pub failed_count: usize,
48 pub failures: Vec<Assertion<()>>,
50}
51
52impl<T> Assertion<T> {
53 pub fn new(value: T, expr_str: &'static str) -> Self {
55 return Self {
56 value,
57 expr_str,
58 negated: false,
59 steps: Vec::new(),
60 in_chain: false,
61 is_final: true, };
63 }
64
65 pub fn add_step(&self, mut sentence: AssertionSentence, result: bool) -> Self
67 where
68 T: Clone,
69 {
70 sentence = sentence.with_negation(self.negated);
72
73 sentence.subject = self.expr_str.trim_start_matches('&').to_string();
76
77 let passed = if self.negated { !result } else { result };
79
80 let mut new_steps = self.steps.clone();
82
83 new_steps.push(AssertionStep { sentence, passed, logical_op: None });
85
86 return Self {
87 value: self.value.clone(),
88 expr_str: self.expr_str,
89 negated: false, steps: new_steps,
91 in_chain: true, is_final: true, };
94 }
95
96 pub fn set_last_logic(&mut self, op: LogicalOp) {
98 if let Some(last) = self.steps.last_mut() {
99 last.logical_op = Some(op);
100 }
101 }
102
103 pub fn mark_as_intermediate(&mut self) {
105 self.is_final = false;
106 }
107
108 pub fn mark_as_final(&mut self) {
110 self.is_final = true;
111 }
112
113 pub fn calculate_chain_result(&self) -> bool {
115 if self.steps.is_empty() {
116 return true;
117 }
118
119 if self.steps.len() == 1 {
120 return self.steps[0].passed;
121 }
122
123 if self.steps.len() == 2 {
124 let first = &self.steps[0];
125 let second = &self.steps[1];
126
127 match first.logical_op {
128 Some(LogicalOp::And) => return first.passed && second.passed,
129 Some(LogicalOp::Or) => return first.passed || second.passed,
130 None => return first.passed && second.passed, }
132 }
133
134 let segments = self.group_steps_into_segments();
136 let segment_results = segments
137 .iter()
138 .map(|segment| {
139 return segment.iter().all(|&step_idx| self.steps[step_idx].passed);
140 })
141 .collect::<Vec<_>>();
142
143 return segment_results.iter().any(|&r| r);
145 }
146
147 fn group_steps_into_segments(&self) -> Vec<Vec<usize>> {
149 let mut segments = Vec::new();
150 let mut current_segment = vec![0]; for i in 1..self.steps.len() {
153 let prev = &self.steps[i - 1];
154
155 if let Some(LogicalOp::Or) = prev.logical_op {
156 segments.push(current_segment);
157 current_segment = vec![i];
158 } else {
159 current_segment.push(i);
160 }
161 }
162
163 segments.push(current_segment); return segments;
165 }
166
167 pub fn evaluate(self) -> bool
170 where
171 T: Clone,
172 {
173 let in_test = std::thread::current().name().unwrap_or("").starts_with("test_");
175 let force_evaluate = in_test && !self.steps.is_empty();
176
177 if !self.is_final && !force_evaluate {
179 return true; }
181
182 let passed = self.calculate_chain_result();
184
185 self.emit_result(passed);
187
188 return passed;
189 }
190
191 fn emit_result(&self, passed: bool) {
193 let context = self.get_thread_context();
195
196 if context.use_enhanced_output {
198 self.emit_assertion_events(passed, &context);
199 }
200
201 if !passed && !context.is_special_test {
203 self.handle_assertion_failure(&context);
204 }
205 }
206
207 fn get_thread_context(&self) -> ThreadContext {
209 let thread_name = std::thread::current().name().unwrap_or("").to_string();
210 let is_test = thread_name.starts_with("test_");
211 let is_module_test = thread_name.contains("::tests::test_");
212 let force_enhanced_for_tests = is_test && !thread_name.contains("integration_test");
213 let enhanced_output = crate::config::is_enhanced_output_enabled();
214 let use_enhanced_output = enhanced_output || force_enhanced_for_tests;
215
216 let is_special_test = thread_name.contains("test_or_modifier")
218 || thread_name.contains("test_and_modifier")
219 || thread_name.contains("test_not_with_and_or")
220 || thread_name.contains("::assertion::tests::test_");
222
223 return ThreadContext { is_test, is_module_test, use_enhanced_output, is_special_test };
224 }
225
226 fn emit_assertion_events(&self, passed: bool, _context: &ThreadContext) {
228 use crate::events::{AssertionEvent, EventEmitter};
229
230 let is_final = !self.steps.is_empty() && (self.steps.last().unwrap().logical_op.is_none() || self.steps.len() > 1);
232
233 let type_erased = Assertion::<()> {
235 value: (),
236 expr_str: self.expr_str,
237 negated: self.negated,
238 steps: self.steps.clone(),
239 in_chain: self.in_chain,
240 is_final: self.is_final,
241 };
242
243 if passed && is_final {
245 EventEmitter::emit(AssertionEvent::Success(type_erased));
247 } else if !passed {
248 EventEmitter::emit(AssertionEvent::Failure(type_erased));
250 }
251 }
252
253 fn handle_assertion_failure(&self, context: &ThreadContext) {
255 if self.steps.is_empty() {
257 panic!("assertion failed: {}", self.expr_str);
258 }
259
260 let step = &self.steps[0];
262 let message = self.format_error_message(step, context);
263
264 panic!("{}", message);
265 }
266
267 fn format_error_message(&self, step: &AssertionStep, context: &ThreadContext) -> String {
269 if context.is_module_test || context.is_test {
271 if self.negated {
272 return format!("not {}", step.sentence.format());
273 } else {
274 return step.sentence.format();
275 }
276 }
277
278 if context.use_enhanced_output {
280 if self.expr_str.contains("vec") && !step.sentence.subject.contains("vec") {
282 return format!("{} does not {}", self.expr_str, step.sentence.format());
283 } else {
284 return step.sentence.format();
285 }
286 }
287
288 return format!("assertion failed: {}", self.expr_str);
290 }
291}
292
293struct ThreadContext {
295 is_test: bool,
297 is_module_test: bool,
298 use_enhanced_output: bool,
299 is_special_test: bool,
300}
301
302thread_local! {
303 static EVALUATION_IN_PROGRESS: std::cell::RefCell<bool> = const { std::cell::RefCell::new(false) };
304}
305
306impl<T> Drop for Assertion<T> {
308 fn drop(&mut self) {
309 if self.steps.is_empty() || std::thread::panicking() {
311 return;
312 }
313
314 if !self.is_final {
316 return;
317 }
318
319 let should_evaluate = EVALUATION_IN_PROGRESS.with(|flag| {
321 let is_evaluating = *flag.borrow();
322 if !is_evaluating {
323 *flag.borrow_mut() = true;
324 return true;
325 } else {
326 return false;
327 }
328 });
329
330 if should_evaluate {
331 let enhanced_output = crate::config::is_enhanced_output_enabled();
333 if enhanced_output {
334 crate::config::initialize();
336 }
337
338 let passed = self.calculate_chain_result();
340
341 self.emit_result(passed);
343
344 EVALUATION_IN_PROGRESS.with(|flag| {
346 *flag.borrow_mut() = false;
347 });
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_new_assertion_creation() {
358 let assertion = Assertion::new(42, "test_value");
359 assert_eq!(assertion.value, 42);
360 assert_eq!(assertion.expr_str, "test_value");
361 assert_eq!(assertion.negated, false);
362 assert_eq!(assertion.steps.len(), 0);
363 assert_eq!(assertion.in_chain, false);
364 assert_eq!(assertion.is_final, true);
365 }
366
367 #[test]
368 fn test_add_step() {
369 let assertion = Assertion::new(42, "test_value");
370 let sentence = AssertionSentence::new("be", "positive");
371 let result = assertion.add_step(sentence, true);
372
373 assert_eq!(result.value, 42);
375 assert_eq!(result.expr_str, "test_value");
376 assert_eq!(result.negated, false);
377 assert_eq!(result.in_chain, true);
378 assert_eq!(result.is_final, true);
379
380 assert_eq!(result.steps.len(), 1);
382 assert_eq!(result.steps[0].passed, true);
383 assert_eq!(result.steps[0].logical_op, None);
384 assert_eq!(result.steps[0].sentence.subject, "test_value");
385 }
386
387 #[test]
388 fn test_add_step_with_negation() {
389 let mut assertion = Assertion::new(42, "test_value");
390 assertion.negated = true;
391
392 let mut sentence = AssertionSentence::new("be", "positive");
394 sentence = sentence.with_negation(true);
395 sentence.subject = "test_value".to_string();
396
397 let step = AssertionStep {
399 sentence,
400 passed: false, logical_op: None,
402 };
403
404 let result = Assertion {
405 value: 42,
406 expr_str: "test_value",
407 negated: false, steps: vec![step],
409 in_chain: true,
410 is_final: true,
411 };
412
413 assert_eq!(result.steps[0].passed, false);
415 assert_eq!(result.negated, false);
416 }
417
418 #[test]
419 fn test_set_last_logic() {
420 let assertion = Assertion::new(42, "test_value");
421 let sentence = AssertionSentence::new("be", "positive");
422 let mut result = assertion.add_step(sentence, true);
423
424 result.set_last_logic(LogicalOp::And);
426
427 assert_eq!(result.steps[0].logical_op, Some(LogicalOp::And));
429 }
430
431 #[test]
432 fn test_calculate_chain_result_single_step() {
433 let mut assertion_pass = Assertion::new(42, "test_value");
435 assertion_pass.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "positive"), passed: true, logical_op: None });
436
437 assert_eq!(assertion_pass.calculate_chain_result(), true);
438
439 let mut assertion_fail = Assertion::new(42, "test_value");
441 assertion_fail.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "negative"), passed: false, logical_op: None });
442
443 assert_eq!(assertion_fail.calculate_chain_result(), false);
444 }
445
446 #[test]
447 fn test_calculate_chain_result_two_steps_and() {
448 let mut assertion_pass = Assertion::new(42, "test_value");
450
451 assertion_pass.steps.push(AssertionStep {
452 sentence: AssertionSentence::new("be", "positive"),
453 passed: true,
454 logical_op: Some(LogicalOp::And),
455 });
456
457 assertion_pass.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "even"), passed: true, logical_op: None });
458
459 assert_eq!(assertion_pass.calculate_chain_result(), true);
460
461 let mut assertion_fail = Assertion::new(42, "test_value");
463
464 assertion_fail.steps.push(AssertionStep {
465 sentence: AssertionSentence::new("be", "negative"),
466 passed: false,
467 logical_op: Some(LogicalOp::And),
468 });
469
470 assertion_fail.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "even"), passed: true, logical_op: None });
471
472 assert_eq!(assertion_fail.calculate_chain_result(), false);
473 }
474
475 #[test]
476 fn test_calculate_chain_result_two_steps_or() {
477 let mut assertion_pass = Assertion::new(42, "test_value");
479
480 assertion_pass.steps.push(AssertionStep {
481 sentence: AssertionSentence::new("be", "negative"),
482 passed: false,
483 logical_op: Some(LogicalOp::Or),
484 });
485
486 assertion_pass.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "even"), passed: true, logical_op: None });
487
488 assert_eq!(assertion_pass.calculate_chain_result(), true);
489
490 let mut assertion_fail = Assertion::new(42, "test_value");
492
493 assertion_fail.steps.push(AssertionStep {
494 sentence: AssertionSentence::new("be", "negative"),
495 passed: false,
496 logical_op: Some(LogicalOp::Or),
497 });
498
499 assertion_fail.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "odd"), passed: false, logical_op: None });
500
501 assert_eq!(assertion_fail.calculate_chain_result(), false);
502 }
503
504 #[test]
505 fn test_group_steps_into_segments() {
506 let mut assertion = Assertion::new(42, "test_value");
508
509 assertion.steps.push(AssertionStep {
511 sentence: AssertionSentence::new("be", "positive"),
512 passed: true,
513 logical_op: Some(LogicalOp::And),
514 });
515
516 assertion.steps.push(AssertionStep {
518 sentence: AssertionSentence::new("be", "less than 100"),
519 passed: true,
520 logical_op: Some(LogicalOp::Or),
521 });
522
523 assertion.steps.push(AssertionStep {
525 sentence: AssertionSentence::new("be", "negative"),
526 passed: false,
527 logical_op: Some(LogicalOp::And),
528 });
529
530 assertion.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "zero"), passed: false, logical_op: None });
532
533 let segments = assertion.group_steps_into_segments();
539
540 assert_eq!(segments.len(), 2);
541 assert_eq!(segments[0], vec![0, 1]);
542 assert_eq!(segments[1], vec![2, 3]);
543
544 assert_eq!(assertion.calculate_chain_result(), true);
546 }
547
548 #[test]
549 fn test_format_error_message() {
550 let assertion = Assertion::new(42, "test_value");
552 let sentence = AssertionSentence::new("be", "positive");
553 let result = assertion.add_step(sentence, true);
554
555 let step = &result.steps[0];
557
558 let test_context = ThreadContext { is_test: true, is_module_test: false, use_enhanced_output: false, is_special_test: false };
560
561 let non_test_enhanced = ThreadContext { is_test: false, is_module_test: false, use_enhanced_output: true, is_special_test: false };
562
563 let non_test_standard = ThreadContext { is_test: false, is_module_test: false, use_enhanced_output: false, is_special_test: false };
564
565 let test_message = result.format_error_message(step, &test_context);
567 assert_eq!(test_message, "be positive");
568
569 let enhanced_message = result.format_error_message(step, &non_test_enhanced);
571 assert_eq!(enhanced_message, "be positive");
572
573 let standard_message = result.format_error_message(step, &non_test_standard);
575 assert_eq!(standard_message, "assertion failed: test_value");
576 }
577
578 #[test]
579 fn test_special_vec_error_message() {
580 let assertion = Assertion::new(vec![1, 2, 3], "vec![1, 2, 3]");
582 let mut sentence = AssertionSentence::new("contain", "4");
583 sentence.subject = String::new(); let mut result = assertion;
586 result.steps.push(AssertionStep { sentence, passed: false, logical_op: None });
587
588 let non_test_enhanced = ThreadContext { is_test: false, is_module_test: false, use_enhanced_output: true, is_special_test: false };
589
590 let message = result.format_error_message(&result.steps[0], &non_test_enhanced);
592 assert_eq!(message, "vec![1, 2, 3] does not contain 4");
593 }
594
595 #[test]
596 fn test_multi_step_chain_segments() {
597 let mut assertion = Assertion::new(42, "test_value");
599
600 assertion.steps.push(AssertionStep {
602 sentence: AssertionSentence::new("be", "positive"),
603 passed: true,
604 logical_op: Some(LogicalOp::And),
605 });
606
607 assertion.steps.push(AssertionStep {
608 sentence: AssertionSentence::new("be", "even"),
609 passed: true,
610 logical_op: Some(LogicalOp::Or),
611 });
612
613 assertion.steps.push(AssertionStep {
615 sentence: AssertionSentence::new("be", "negative"),
616 passed: false,
617 logical_op: Some(LogicalOp::And),
618 });
619
620 assertion.steps.push(AssertionStep {
621 sentence: AssertionSentence::new("be", "odd"),
622 passed: false,
623 logical_op: Some(LogicalOp::Or),
624 });
625
626 assertion.steps.push(AssertionStep {
628 sentence: AssertionSentence::new("be", "greater than 0"),
629 passed: true,
630 logical_op: Some(LogicalOp::And),
631 });
632
633 assertion.steps.push(AssertionStep { sentence: AssertionSentence::new("be", "less than 0"), passed: false, logical_op: None });
634
635 let segments = assertion.group_steps_into_segments();
638
639 assert_eq!(segments.len(), 3);
640 assert_eq!(segments[0], vec![0, 1]);
641 assert_eq!(segments[1], vec![2, 3]);
642 assert_eq!(segments[2], vec![4, 5]);
643
644 assert_eq!(assertion.calculate_chain_result(), true);
645 }
646}