cosmwasm_std/testing/
assertions.rs1use crate::{Decimal, Uint128};
2
3use alloc::string::String;
4#[cfg(test)]
5use core::hash::{Hash, Hasher};
6use core::str::FromStr as _;
7
8#[macro_export]
19macro_rules! assert_approx_eq {
20 ($left:expr, $right:expr, $max_rel_diff:expr $(,)?) => {{
21 $crate::testing::assert_approx_eq_impl($left, $right, $max_rel_diff, None);
22 }};
23 ($left:expr, $right:expr, $max_rel_diff:expr, $($args:tt)+) => {{
24 $crate::testing::assert_approx_eq_impl($left, $right, $max_rel_diff, Some(format!($($args)*)));
25 }};
26}
27
28#[macro_export]
37#[cfg(test)]
38macro_rules! assert_hash_works {
39 ($left:expr, $right:expr $(,)?) => {{
40 $crate::testing::assert_hash_works_impl($left, $right, None);
41 }};
42 ($left:expr, $right:expr, $($args:tt)+) => {{
43 $crate::testing::assert_hash_works_impl($left, $right, Some(format!($($args)*)));
44 }};
45}
46
47#[track_caller]
50#[doc(hidden)]
51pub fn assert_approx_eq_impl<U: Into<Uint128>>(
52 left: U,
53 right: U,
54 max_rel_diff: &str,
55 panic_msg: Option<String>,
56) {
57 let left = left.into();
58 let right = right.into();
59
60 if left == right {
61 return;
64 }
65
66 let max_rel_diff = Decimal::from_str(max_rel_diff).unwrap();
67
68 let largest = core::cmp::max(left, right);
69 let rel_diff = Decimal::from_ratio(left.abs_diff(right), largest);
70
71 if rel_diff > max_rel_diff {
72 do_panic(format_args!("assertion failed: `(left ≈ right)`\nleft: {left}\nright: {right}\nrelative difference: {rel_diff}\nmax allowed relative difference: {max_rel_diff}"), panic_msg);
73 }
74}
75
76#[track_caller]
84#[doc(hidden)]
85#[cfg(test)]
86pub fn assert_hash_works_impl<T: Clone + Eq + Hash>(left: T, right: T, panic_msg: Option<String>) {
87 fn hash(value: &impl Hash) -> u64 {
88 let mut hasher = crc32fast::Hasher::default();
89 value.hash(&mut hasher);
90 hasher.finish()
91 }
92
93 #[allow(clippy::redundant_clone)]
95 let clone = left.clone();
96 if left != clone {
97 do_panic("assertion failed: `left == left.clone()`", panic_msg);
98 }
99 if hash(&left) != hash(&clone) {
100 do_panic(
101 "assertion failed: `hash(left) == hash(left.clone())`",
102 panic_msg,
103 );
104 }
105
106 if left == right {
108 do_panic("assertion failed: `left != right`", panic_msg);
109 }
110 if hash(&left) == hash(&right) {
111 do_panic("assertion failed: `hash(left) != hash(right)`", panic_msg);
112 }
113}
114
115#[track_caller]
120fn do_panic(reason: impl core::fmt::Display, panic_msg: Option<String>) -> ! {
121 match panic_msg {
122 Some(panic_msg) => panic!("{reason}:\n{panic_msg}"),
123 None => panic!("{reason}"),
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 #[test]
130 fn assert_approx() {
131 assert_approx_eq!(9_u32, 10_u32, "0.12");
132 assert_approx_eq!(9_u64, 10_u64, "0.12");
133 assert_approx_eq!(
134 9_000_000_000_000_000_000_000_000_000_000_000_000_u128,
135 10_000_000_000_000_000_000_000_000_000_000_000_000_u128,
136 "0.10"
137 );
138 assert_approx_eq!(0_u32, 0_u32, "0.12");
139 assert_approx_eq!(1_u64, 0_u64, "1");
140 assert_approx_eq!(0_u64, 1_u64, "1");
141 assert_approx_eq!(5_u64, 0_u64, "1");
142 assert_approx_eq!(0_u64, 5_u64, "1");
143 }
144
145 #[test]
146 fn assert_approx_with_vars() {
147 let a = 66_u32;
148 let b = 67_u32;
149 assert_approx_eq!(a, b, "0.02");
150
151 let a = 66_u64;
152 let b = 67_u64;
153 assert_approx_eq!(a, b, "0.02");
154
155 let a = 66_u128;
156 let b = 67_u128;
157 assert_approx_eq!(a, b, "0.02");
158 }
159
160 #[test]
161 #[should_panic(
162 expected = "assertion failed: `(left ≈ right)`\nleft: 8\nright: 10\nrelative difference: 0.2\nmax allowed relative difference: 0.12"
163 )]
164 fn assert_approx_fail() {
165 assert_approx_eq!(8_u32, 10_u32, "0.12");
166 }
167
168 #[test]
169 #[should_panic(
170 expected = "assertion failed: `(left ≈ right)`\nleft: 17\nright: 20\nrelative difference: 0.15\nmax allowed relative difference: 0.12:\nsome extra info about the error: Foo(8)"
171 )]
172 fn assert_approx_with_custom_panic_msg() {
173 let adjective = "extra";
174 #[allow(dead_code)]
175 #[derive(Debug)]
176 struct Foo(u32);
177 assert_approx_eq!(
178 17_u32,
179 20_u32,
180 "0.12",
181 "some {adjective} {} about the error: {:?}",
182 "info",
183 Foo(8),
184 );
185 }
186}