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: impl Into<f64>, expected: impl Into<f64>, epsilon: impl Into<f64>) {
80 let actual = actual.into();
81 let expected = expected.into();
82 let epsilon = epsilon.into();
83 let diff = (actual - expected).abs();
84 if diff > epsilon {
85 panic!(
86 "assertion failed: values not approximately equal\n actual: {}\n expected: {}\n epsilon: {}\n diff: {}",
87 actual, expected, epsilon, diff
88 );
89 }
90}
91
92pub fn assert_approx_f32(actual: f32, expected: f32, epsilon: f32) {
94 let diff = (actual - expected).abs();
95 if diff > epsilon {
96 panic!(
97 "assertion failed: values not approximately equal\n actual: {}\n expected: {}\n epsilon: {}\n diff: {}",
98 actual, expected, epsilon, diff
99 );
100 }
101}
102
103pub fn requires(condition: bool, description: &str) {
110 if !condition {
111 panic!("Precondition violation (@requires): {}", description);
112 }
113}
114
115pub fn ensures(condition: bool, description: &str) {
118 if !condition {
119 panic!("Postcondition violation (@ensures): {}", description);
120 }
121}
122
123pub fn invariant(condition: bool, description: &str) {
126 if !condition {
127 panic!("Invariant violation (@invariant): {}", description);
128 }
129}
130
131pub fn assert_contains<T: PartialEq + std::fmt::Debug>(collection: &[T], item: &T) {
133 if !collection.contains(item) {
134 panic!(
135 "assertion failed: collection doesn't contain item\n collection: {:?}\n item: {:?}",
136 collection, item
137 );
138 }
139}
140
141pub fn assert_length<T>(collection: &[T], expected: usize) {
143 let actual = collection.len();
144 if actual != expected {
145 panic!(
146 "assertion failed: collection length mismatch\n expected: {}\n actual: {}",
147 expected, actual
148 );
149 }
150}
151
152pub fn assert_empty<T>(collection: &[T]) {
154 if !collection.is_empty() {
155 panic!(
156 "assertion failed: collection is not empty\n length: {}",
157 collection.len()
158 );
159 }
160}
161
162pub fn assert_not_empty<T>(collection: &[T]) {
164 if collection.is_empty() {
165 panic!("assertion failed: collection is empty");
166 }
167}
168
169pub fn assert_str_contains(string: impl AsRef<str>, substring: impl AsRef<str>) {
172 let string = string.as_ref();
173 let substring = substring.as_ref();
174 if !string.contains(substring) {
175 panic!(
176 "assertion failed: string doesn't contain substring\n string: \"{}\"\n substring: \"{}\"",
177 string, substring
178 );
179 }
180}
181
182pub fn assert_starts_with(string: impl AsRef<str>, prefix: impl AsRef<str>) {
185 let string = string.as_ref();
186 let prefix = prefix.as_ref();
187 if !string.starts_with(prefix) {
188 panic!(
189 "assertion failed: string doesn't start with prefix\n string: \"{}\"\n prefix: \"{}\"",
190 string, prefix
191 );
192 }
193}
194
195pub fn assert_ends_with(string: impl AsRef<str>, suffix: impl AsRef<str>) {
198 let string = string.as_ref();
199 let suffix = suffix.as_ref();
200 if !string.ends_with(suffix) {
201 panic!(
202 "assertion failed: string doesn't end with suffix\n string: \"{}\"\n suffix: \"{}\"",
203 string, suffix
204 );
205 }
206}
207
208pub fn assert_is_some<T: std::fmt::Debug>(option: &Option<T>) {
210 if option.is_none() {
211 panic!("assertion failed: Option is None, expected Some");
212 }
213}
214
215pub fn assert_is_none<T: std::fmt::Debug>(option: &Option<T>) {
217 if let Some(ref value) = option {
218 panic!(
219 "assertion failed: Option is Some, expected None\n value: {:?}",
220 value
221 );
222 }
223}
224
225pub fn assert_is_ok<T: std::fmt::Debug, E: std::fmt::Debug>(result: &Result<T, E>) {
227 if let Err(ref e) = result {
228 panic!(
229 "assertion failed: Result is Err, expected Ok\n error: {:?}",
230 e
231 );
232 }
233}
234
235pub fn assert_is_err<T: std::fmt::Debug, E: std::fmt::Debug>(result: &Result<T, E>) {
237 if let Ok(ref value) = result {
238 panic!(
239 "assertion failed: Result is Ok, expected Err\n value: {:?}",
240 value
241 );
242 }
243}
244
245pub fn assert_in_range<T: PartialOrd + std::fmt::Debug>(value: T, min: T, max: T) {
247 if value < min || value > max {
248 panic!(
249 "assertion failed: value not in range\n value: {:?}\n min: {:?}\n max: {:?}",
250 value, min, max
251 );
252 }
253}
254
255pub fn assert_panics<F: FnOnce() + std::panic::UnwindSafe>(f: F) {
270 let result = std::panic::catch_unwind(f);
271 if result.is_ok() {
272 panic!("assertion failed: expected panic, but function completed successfully");
273 }
274}
275
276pub fn assert_panics_with<F: FnOnce() + std::panic::UnwindSafe>(expected_msg: &str, f: F) {
287 let result = std::panic::catch_unwind(f);
288 match result {
289 Ok(_) => {
290 panic!(
291 "assertion failed: expected panic with message '{}', but function completed successfully",
292 expected_msg
293 );
294 }
295 Err(err) => {
296 let panic_msg = if let Some(s) = err.downcast_ref::<&str>() {
298 s.to_string()
299 } else if let Some(s) = err.downcast_ref::<String>() {
300 s.clone()
301 } else {
302 "unknown panic message".to_string()
303 };
304
305 if !panic_msg.contains(expected_msg) {
306 panic!(
307 "assertion failed: panic message mismatch\n expected (substring): \"{}\"\n actual: \"{}\"",
308 expected_msg, panic_msg
309 );
310 }
311 }
312 }
313}
314
315pub fn assert_result_ok<T, E: std::fmt::Debug>(result: &Result<T, E>) {
318 if let Err(e) = result {
319 panic!("assertion failed: expected Ok(_), got Err({:?})", e);
320 }
321}
322
323pub fn assert_result_err<T: std::fmt::Debug, E>(result: &Result<T, E>) {
325 if let Ok(val) = result {
326 panic!("assertion failed: expected Err(_), got Ok({:?})", val);
327 }
328}
329
330pub fn assert_deep_eq<T: PartialEq + std::fmt::Debug>(left: &T, right: &T) {
332 if left != right {
333 panic!(
334 "assertion failed: deep equality check failed\n left: {:?}\n right: {:?}",
335 left, right
336 );
337 }
338}
339
340pub fn pass() {
342 TESTS_PASSED.fetch_add(1, Ordering::SeqCst);
343}
344
345pub fn fail(message: &str) {
347 TESTS_FAILED.fetch_add(1, Ordering::SeqCst);
348 panic!("Test failed: {}", message);
349}
350
351pub fn passed_count() -> usize {
353 TESTS_PASSED.load(Ordering::SeqCst)
354}
355
356pub fn failed_count() -> usize {
358 TESTS_FAILED.load(Ordering::SeqCst)
359}
360
361pub fn reset() {
363 TESTS_PASSED.store(0, Ordering::SeqCst);
364 TESTS_FAILED.store(0, Ordering::SeqCst);
365}
366
367pub fn set_current_test(name: String) {
369 *CURRENT_TEST.lock().unwrap() = name;
370}
371
372pub fn current_test() -> String {
374 CURRENT_TEST.lock().unwrap().clone()
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_assert() {
383 assert(true, "should pass");
384 }
385
386 #[test]
387 #[should_panic]
388 fn test_assert_fail() {
389 assert(false, "should fail");
390 }
391
392 #[test]
393 fn test_assert_eq() {
394 assert_eq(1, 1);
395 assert_eq("hello", "hello");
396 }
397
398 #[test]
399 #[should_panic]
400 fn test_assert_eq_fail() {
401 assert_eq(1, 2);
402 }
403
404 #[test]
405 fn test_assert_ne() {
406 assert_ne(1, 2);
407 assert_ne("hello", "world");
408 }
409
410 #[test]
411 #[should_panic]
412 fn test_assert_ne_fail() {
413 assert_ne(1, 1);
414 }
415
416 #[test]
418 fn test_assert_gt_passes() {
419 assert_gt(5, 3);
420 assert_gt(10.5, 10.4);
421 }
422
423 #[test]
424 #[should_panic(expected = "assertion failed: left > right")]
425 fn test_assert_gt_fails() {
426 assert_gt(3, 5);
427 }
428
429 #[test]
430 fn test_assert_lt_passes() {
431 assert_lt(3, 5);
432 assert_lt(10.4, 10.5);
433 }
434
435 #[test]
436 #[should_panic(expected = "assertion failed: left < right")]
437 fn test_assert_lt_fails() {
438 assert_lt(5, 3);
439 }
440
441 #[test]
442 fn test_assert_approx_passes() {
443 assert_approx(3.1, 3.0, 0.2);
444 assert_approx(1.0, 1.0001, 0.001);
445 }
446
447 #[test]
448 #[should_panic(expected = "assertion failed: values not approximately equal")]
449 fn test_assert_approx_fails() {
450 assert_approx(3.1, 2.0, 0.01);
451 }
452
453 #[test]
454 fn test_assert_contains_passes() {
455 let vec = vec![1, 2, 3, 4, 5];
456 assert_contains(&vec, &3);
457 }
458
459 #[test]
460 #[should_panic(expected = "assertion failed: collection doesn't contain item")]
461 fn test_assert_contains_fails() {
462 let vec = vec![1, 2, 3, 4, 5];
463 assert_contains(&vec, &10);
464 }
465
466 #[test]
467 fn test_assert_length_passes() {
468 let vec = vec![1, 2, 3];
469 assert_length(&vec, 3);
470 }
471
472 #[test]
473 #[should_panic(expected = "assertion failed: collection length mismatch")]
474 fn test_assert_length_fails() {
475 let vec = vec![1, 2, 3];
476 assert_length(&vec, 5);
477 }
478
479 #[test]
480 fn test_assert_str_contains_passes() {
481 assert_str_contains("hello world", "world");
482 }
483
484 #[test]
485 #[should_panic(expected = "assertion failed: string doesn't contain substring")]
486 fn test_assert_str_contains_fails() {
487 assert_str_contains("hello world", "foo");
488 }
489
490 #[test]
491 fn test_assert_is_some_passes() {
492 let opt = Some(42);
493 assert_is_some(&opt);
494 }
495
496 #[test]
497 #[should_panic(expected = "assertion failed: Option is None")]
498 fn test_assert_is_some_fails() {
499 let opt: Option<i32> = None;
500 assert_is_some(&opt);
501 }
502
503 #[test]
504 fn test_assert_in_range_passes() {
505 assert_in_range(5, 0, 10);
506 assert_in_range(3.1, 3.0, 4.0);
507 }
508
509 #[test]
510 #[should_panic(expected = "assertion failed: value not in range")]
511 fn test_assert_in_range_fails() {
512 assert_in_range(15, 0, 10);
513 }
514
515 #[test]
517 fn test_assert_panics_passes() {
518 assert_panics(|| {
519 panic!("This should panic");
520 });
521 }
522
523 #[test]
524 #[should_panic(expected = "expected panic, but function completed successfully")]
525 fn test_assert_panics_fails() {
526 assert_panics(|| {
527 });
529 }
530
531 #[test]
532 fn test_assert_panics_with_passes() {
533 assert_panics_with("division by zero", || {
534 panic!("Error: division by zero occurred");
535 });
536 }
537
538 #[test]
539 #[should_panic(expected = "panic message mismatch")]
540 fn test_assert_panics_with_fails() {
541 assert_panics_with("expected message", || {
542 panic!("different message");
543 });
544 }
545
546 #[test]
547 fn test_assert_result_ok_passes() {
548 let result: Result<i32, &str> = Ok(42);
549 assert_result_ok(&result);
550 }
551
552 #[test]
553 #[should_panic(expected = "expected Ok(_), got Err")]
554 fn test_assert_result_ok_fails() {
555 let result: Result<i32, &str> = Err("error");
556 assert_result_ok(&result);
557 }
558
559 #[test]
560 fn test_assert_result_err_passes() {
561 let result: Result<i32, &str> = Err("error");
562 assert_result_err(&result);
563 }
564
565 #[test]
566 #[should_panic(expected = "expected Err(_), got Ok")]
567 fn test_assert_result_err_fails() {
568 let result: Result<i32, &str> = Ok(42);
569 assert_result_err(&result);
570 }
571
572 #[test]
573 fn test_assert_deep_eq_passes() {
574 let vec1 = vec![1, 2, 3];
575 let vec2 = vec![1, 2, 3];
576 assert_deep_eq(&vec1, &vec2);
577 }
578
579 #[test]
580 #[should_panic(expected = "deep equality check failed")]
581 fn test_assert_deep_eq_fails() {
582 let vec1 = vec![1, 2, 3];
583 let vec2 = vec![1, 2, 4];
584 assert_deep_eq(&vec1, &vec2);
585 }
586}