1use std::sync::atomic::{AtomicUsize, Ordering};
6use std::sync::Mutex;
7
8static TESTS_PASSED: AtomicUsize = AtomicUsize::new(0);
9static TESTS_FAILED: AtomicUsize = AtomicUsize::new(0);
10static CURRENT_TEST: Mutex<String> = Mutex::new(String::new());
11
12pub fn assert(condition: bool, message: &str) {
14 if !condition {
15 panic!("Assertion failed: {}", message);
16 }
17}
18
19pub fn assert_eq<T: PartialEq + std::fmt::Debug>(left: T, right: T) {
21 if left != right {
22 panic!("Assertion failed: {:?} != {:?}", left, right);
23 }
24}
25
26pub fn assert_ne<T: PartialEq + std::fmt::Debug>(left: T, right: T) {
28 if left == right {
29 panic!("Assertion failed: {:?} == {:?}", left, right);
30 }
31}
32
33pub fn assert_gt<T: PartialOrd + std::fmt::Debug>(left: T, right: T) {
39 if left <= right {
40 panic!(
41 "assertion failed: left > right\n left: {:?}\n right: {:?}",
42 left, right
43 );
44 }
45}
46
47pub fn assert_lt<T: PartialOrd + std::fmt::Debug>(left: T, right: T) {
49 if left >= right {
50 panic!(
51 "assertion failed: left < right\n left: {:?}\n right: {:?}",
52 left, right
53 );
54 }
55}
56
57pub fn assert_gte<T: PartialOrd + std::fmt::Debug>(left: T, right: T) {
59 if left < right {
60 panic!(
61 "assertion failed: left >= right\n left: {:?}\n right: {:?}",
62 left, right
63 );
64 }
65}
66
67pub fn assert_lte<T: PartialOrd + std::fmt::Debug>(left: T, right: T) {
69 if left > right {
70 panic!(
71 "assertion failed: left <= right\n left: {:?}\n right: {:?}",
72 left, right
73 );
74 }
75}
76
77pub fn assert_approx(actual: f64, expected: f64, epsilon: f64) {
79 let diff = (actual - expected).abs();
80 if diff > epsilon {
81 panic!(
82 "assertion failed: values not approximately equal\n actual: {}\n expected: {}\n epsilon: {}\n diff: {}",
83 actual, expected, epsilon, diff
84 );
85 }
86}
87
88pub fn assert_approx_f32(actual: f32, expected: f32, epsilon: f32) {
90 let diff = (actual - expected).abs();
91 if diff > epsilon {
92 panic!(
93 "assertion failed: values not approximately equal\n actual: {}\n expected: {}\n epsilon: {}\n diff: {}",
94 actual, expected, epsilon, diff
95 );
96 }
97}
98
99pub fn requires(condition: bool, description: &str) {
106 if !condition {
107 panic!("Precondition violation (@requires): {}", description);
108 }
109}
110
111pub fn ensures(condition: bool, description: &str) {
114 if !condition {
115 panic!("Postcondition violation (@ensures): {}", description);
116 }
117}
118
119pub fn invariant(condition: bool, description: &str) {
122 if !condition {
123 panic!("Invariant violation (@invariant): {}", description);
124 }
125}
126
127pub fn assert_contains<T: PartialEq + std::fmt::Debug>(collection: &[T], item: &T) {
129 if !collection.contains(item) {
130 panic!(
131 "assertion failed: collection doesn't contain item\n collection: {:?}\n item: {:?}",
132 collection, item
133 );
134 }
135}
136
137pub fn assert_length<T>(collection: &[T], expected: usize) {
139 let actual = collection.len();
140 if actual != expected {
141 panic!(
142 "assertion failed: collection length mismatch\n expected: {}\n actual: {}",
143 expected, actual
144 );
145 }
146}
147
148pub fn assert_empty<T>(collection: &[T]) {
150 if !collection.is_empty() {
151 panic!(
152 "assertion failed: collection is not empty\n length: {}",
153 collection.len()
154 );
155 }
156}
157
158pub fn assert_not_empty<T>(collection: &[T]) {
160 if collection.is_empty() {
161 panic!("assertion failed: collection is empty");
162 }
163}
164
165pub fn assert_str_contains(string: &str, substring: &str) {
167 if !string.contains(substring) {
168 panic!(
169 "assertion failed: string doesn't contain substring\n string: \"{}\"\n substring: \"{}\"",
170 string, substring
171 );
172 }
173}
174
175pub fn assert_starts_with(string: &str, prefix: &str) {
177 if !string.starts_with(prefix) {
178 panic!(
179 "assertion failed: string doesn't start with prefix\n string: \"{}\"\n prefix: \"{}\"",
180 string, prefix
181 );
182 }
183}
184
185pub fn assert_ends_with(string: &str, suffix: &str) {
187 if !string.ends_with(suffix) {
188 panic!(
189 "assertion failed: string doesn't end with suffix\n string: \"{}\"\n suffix: \"{}\"",
190 string, suffix
191 );
192 }
193}
194
195pub fn assert_is_some<T: std::fmt::Debug>(option: &Option<T>) {
197 if option.is_none() {
198 panic!("assertion failed: Option is None, expected Some");
199 }
200}
201
202pub fn assert_is_none<T: std::fmt::Debug>(option: &Option<T>) {
204 if let Some(ref value) = option {
205 panic!(
206 "assertion failed: Option is Some, expected None\n value: {:?}",
207 value
208 );
209 }
210}
211
212pub fn assert_is_ok<T: std::fmt::Debug, E: std::fmt::Debug>(result: &Result<T, E>) {
214 if let Err(ref e) = result {
215 panic!(
216 "assertion failed: Result is Err, expected Ok\n error: {:?}",
217 e
218 );
219 }
220}
221
222pub fn assert_is_err<T: std::fmt::Debug, E: std::fmt::Debug>(result: &Result<T, E>) {
224 if let Ok(ref value) = result {
225 panic!(
226 "assertion failed: Result is Ok, expected Err\n value: {:?}",
227 value
228 );
229 }
230}
231
232pub fn assert_in_range<T: PartialOrd + std::fmt::Debug>(value: T, min: T, max: T) {
234 if value < min || value > max {
235 panic!(
236 "assertion failed: value not in range\n value: {:?}\n min: {:?}\n max: {:?}",
237 value, min, max
238 );
239 }
240}
241
242pub fn assert_panics<F: FnOnce() + std::panic::UnwindSafe>(f: F) {
257 let result = std::panic::catch_unwind(f);
258 if result.is_ok() {
259 panic!("assertion failed: expected panic, but function completed successfully");
260 }
261}
262
263pub fn assert_panics_with<F: FnOnce() + std::panic::UnwindSafe>(expected_msg: &str, f: F) {
274 let result = std::panic::catch_unwind(f);
275 match result {
276 Ok(_) => {
277 panic!(
278 "assertion failed: expected panic with message '{}', but function completed successfully",
279 expected_msg
280 );
281 }
282 Err(err) => {
283 let panic_msg = if let Some(s) = err.downcast_ref::<&str>() {
285 s.to_string()
286 } else if let Some(s) = err.downcast_ref::<String>() {
287 s.clone()
288 } else {
289 "unknown panic message".to_string()
290 };
291
292 if !panic_msg.contains(expected_msg) {
293 panic!(
294 "assertion failed: panic message mismatch\n expected (substring): \"{}\"\n actual: \"{}\"",
295 expected_msg, panic_msg
296 );
297 }
298 }
299 }
300}
301
302pub fn assert_result_ok<T, E: std::fmt::Debug>(result: &Result<T, E>) {
305 if let Err(e) = result {
306 panic!("assertion failed: expected Ok(_), got Err({:?})", e);
307 }
308}
309
310pub fn assert_result_err<T: std::fmt::Debug, E>(result: &Result<T, E>) {
312 if let Ok(val) = result {
313 panic!("assertion failed: expected Err(_), got Ok({:?})", val);
314 }
315}
316
317pub fn assert_deep_eq<T: PartialEq + std::fmt::Debug>(left: &T, right: &T) {
319 if left != right {
320 panic!(
321 "assertion failed: deep equality check failed\n left: {:?}\n right: {:?}",
322 left, right
323 );
324 }
325}
326
327pub fn pass() {
329 TESTS_PASSED.fetch_add(1, Ordering::SeqCst);
330}
331
332pub fn fail(message: &str) {
334 TESTS_FAILED.fetch_add(1, Ordering::SeqCst);
335 panic!("Test failed: {}", message);
336}
337
338pub fn passed_count() -> usize {
340 TESTS_PASSED.load(Ordering::SeqCst)
341}
342
343pub fn failed_count() -> usize {
345 TESTS_FAILED.load(Ordering::SeqCst)
346}
347
348pub fn reset() {
350 TESTS_PASSED.store(0, Ordering::SeqCst);
351 TESTS_FAILED.store(0, Ordering::SeqCst);
352}
353
354pub fn set_current_test(name: String) {
356 *CURRENT_TEST.lock().unwrap() = name;
357}
358
359pub fn current_test() -> String {
361 CURRENT_TEST.lock().unwrap().clone()
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn test_assert() {
370 assert(true, "should pass");
371 }
372
373 #[test]
374 #[should_panic]
375 fn test_assert_fail() {
376 assert(false, "should fail");
377 }
378
379 #[test]
380 fn test_assert_eq() {
381 assert_eq(1, 1);
382 assert_eq("hello", "hello");
383 }
384
385 #[test]
386 #[should_panic]
387 fn test_assert_eq_fail() {
388 assert_eq(1, 2);
389 }
390
391 #[test]
392 fn test_assert_ne() {
393 assert_ne(1, 2);
394 assert_ne("hello", "world");
395 }
396
397 #[test]
398 #[should_panic]
399 fn test_assert_ne_fail() {
400 assert_ne(1, 1);
401 }
402
403 #[test]
405 fn test_assert_gt_passes() {
406 assert_gt(5, 3);
407 assert_gt(10.5, 10.4);
408 }
409
410 #[test]
411 #[should_panic(expected = "assertion failed: left > right")]
412 fn test_assert_gt_fails() {
413 assert_gt(3, 5);
414 }
415
416 #[test]
417 fn test_assert_lt_passes() {
418 assert_lt(3, 5);
419 assert_lt(10.4, 10.5);
420 }
421
422 #[test]
423 #[should_panic(expected = "assertion failed: left < right")]
424 fn test_assert_lt_fails() {
425 assert_lt(5, 3);
426 }
427
428 #[test]
429 fn test_assert_approx_passes() {
430 assert_approx(3.1, 3.0, 0.2);
431 assert_approx(1.0, 1.0001, 0.001);
432 }
433
434 #[test]
435 #[should_panic(expected = "assertion failed: values not approximately equal")]
436 fn test_assert_approx_fails() {
437 assert_approx(3.1, 2.0, 0.01);
438 }
439
440 #[test]
441 fn test_assert_contains_passes() {
442 let vec = vec![1, 2, 3, 4, 5];
443 assert_contains(&vec, &3);
444 }
445
446 #[test]
447 #[should_panic(expected = "assertion failed: collection doesn't contain item")]
448 fn test_assert_contains_fails() {
449 let vec = vec![1, 2, 3, 4, 5];
450 assert_contains(&vec, &10);
451 }
452
453 #[test]
454 fn test_assert_length_passes() {
455 let vec = vec![1, 2, 3];
456 assert_length(&vec, 3);
457 }
458
459 #[test]
460 #[should_panic(expected = "assertion failed: collection length mismatch")]
461 fn test_assert_length_fails() {
462 let vec = vec![1, 2, 3];
463 assert_length(&vec, 5);
464 }
465
466 #[test]
467 fn test_assert_str_contains_passes() {
468 assert_str_contains("hello world", "world");
469 }
470
471 #[test]
472 #[should_panic(expected = "assertion failed: string doesn't contain substring")]
473 fn test_assert_str_contains_fails() {
474 assert_str_contains("hello world", "foo");
475 }
476
477 #[test]
478 fn test_assert_is_some_passes() {
479 let opt = Some(42);
480 assert_is_some(&opt);
481 }
482
483 #[test]
484 #[should_panic(expected = "assertion failed: Option is None")]
485 fn test_assert_is_some_fails() {
486 let opt: Option<i32> = None;
487 assert_is_some(&opt);
488 }
489
490 #[test]
491 fn test_assert_in_range_passes() {
492 assert_in_range(5, 0, 10);
493 assert_in_range(3.1, 3.0, 4.0);
494 }
495
496 #[test]
497 #[should_panic(expected = "assertion failed: value not in range")]
498 fn test_assert_in_range_fails() {
499 assert_in_range(15, 0, 10);
500 }
501
502 #[test]
504 fn test_assert_panics_passes() {
505 assert_panics(|| {
506 panic!("This should panic");
507 });
508 }
509
510 #[test]
511 #[should_panic(expected = "expected panic, but function completed successfully")]
512 fn test_assert_panics_fails() {
513 assert_panics(|| {
514 });
516 }
517
518 #[test]
519 fn test_assert_panics_with_passes() {
520 assert_panics_with("division by zero", || {
521 panic!("Error: division by zero occurred");
522 });
523 }
524
525 #[test]
526 #[should_panic(expected = "panic message mismatch")]
527 fn test_assert_panics_with_fails() {
528 assert_panics_with("expected message", || {
529 panic!("different message");
530 });
531 }
532
533 #[test]
534 fn test_assert_result_ok_passes() {
535 let result: Result<i32, &str> = Ok(42);
536 assert_result_ok(&result);
537 }
538
539 #[test]
540 #[should_panic(expected = "expected Ok(_), got Err")]
541 fn test_assert_result_ok_fails() {
542 let result: Result<i32, &str> = Err("error");
543 assert_result_ok(&result);
544 }
545
546 #[test]
547 fn test_assert_result_err_passes() {
548 let result: Result<i32, &str> = Err("error");
549 assert_result_err(&result);
550 }
551
552 #[test]
553 #[should_panic(expected = "expected Err(_), got Ok")]
554 fn test_assert_result_err_fails() {
555 let result: Result<i32, &str> = Ok(42);
556 assert_result_err(&result);
557 }
558
559 #[test]
560 fn test_assert_deep_eq_passes() {
561 let vec1 = vec![1, 2, 3];
562 let vec2 = vec![1, 2, 3];
563 assert_deep_eq(&vec1, &vec2);
564 }
565
566 #[test]
567 #[should_panic(expected = "deep equality check failed")]
568 fn test_assert_deep_eq_fails() {
569 let vec1 = vec![1, 2, 3];
570 let vec2 = vec![1, 2, 4];
571 assert_deep_eq(&vec1, &vec2);
572 }
573}