1 extern crate alloc;
2 #[cfg(feature = "std")]
3 extern crate std;
4
5 use alloc::{
6 format,
7 string::{String, ToString},
8 sync::Arc,
9 };
10 use core::{
11 fmt,
12 sync::atomic::{AtomicU32, Ordering::SeqCst},
13 };
14 #[cfg(feature = "std")]
15 use std::backtrace::BacktraceStatus;
16 use wasmtime_internal_core::error::{
17 Context, Error, OutOfMemory, Result, bail, ensure, format_err,
18 };
19
20 #[derive(Debug)]
21 struct TestError(u32);
22
23 impl fmt::Display for TestError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 fmt::Debug::fmt(self, f)
26 }
27 }
28
29 impl core::error::Error for TestError {}
30
31 #[derive(Debug)]
32 struct CountDrops(Arc<AtomicU32>);
33
34 impl CountDrops {
new(drops: &Arc<AtomicU32>) -> Self35 fn new(drops: &Arc<AtomicU32>) -> Self {
36 CountDrops(drops.clone())
37 }
38 }
39
40 impl Drop for CountDrops {
drop(&mut self)41 fn drop(&mut self) {
42 self.0.fetch_add(1, SeqCst);
43 }
44 }
45
46 impl fmt::Display for CountDrops {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 fmt::Debug::fmt(self, f)
49 }
50 }
51
52 impl core::error::Error for CountDrops {}
53
54 #[derive(Debug)]
55 struct ChainError {
56 message: String,
57 source: Option<Box<dyn core::error::Error + Send + Sync + 'static>>,
58 }
59
60 impl fmt::Display for ChainError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.write_str(&self.message)
63 }
64 }
65
66 impl core::error::Error for ChainError {
source(&self) -> Option<&(dyn core::error::Error + 'static)>67 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
68 let source = self.source.as_ref()?;
69 Some(&**source)
70 }
71 }
72
73 impl ChainError {
new( message: impl Into<String>, source: Option<Box<dyn core::error::Error + Send + Sync + 'static>>, ) -> Self74 fn new(
75 message: impl Into<String>,
76 source: Option<Box<dyn core::error::Error + Send + Sync + 'static>>,
77 ) -> Self {
78 let message = message.into();
79 Self { message, source }
80 }
81 }
82
83 #[test]
new()84 fn new() {
85 let mut error = Error::new(TestError(42));
86 assert!(error.is::<TestError>());
87 assert_eq!(error.downcast_ref::<TestError>().unwrap().0, 42);
88 error.downcast_mut::<TestError>().unwrap().0 += 1;
89 assert_eq!(error.downcast_ref::<TestError>().unwrap().0, 43);
90 }
91
92 #[test]
new_drops()93 fn new_drops() {
94 let drops = Arc::new(AtomicU32::new(0));
95 let error = Error::new(CountDrops::new(&drops));
96 assert_eq!(drops.load(SeqCst), 0);
97 drop(error);
98 assert_eq!(drops.load(SeqCst), 1);
99 }
100
101 #[test]
from_error_with_large_align()102 fn from_error_with_large_align() {
103 // The `{ConcreteError,DynError}::error` fields are not at the same
104 // offset when the concrete error's type requires greater-than-pointer
105 // alignment. Exercise our various conversions and accesses to make sure
106 // that we do the right thing in this case (that is morally cast `*mut
107 // DynError` to `*mut ConcreteError<E>` rather than casting `*mut
108 // TypeErasedError` to `*mut E`).
109 #[derive(Debug)]
110 #[repr(align(16))]
111 struct LargeAlign {
112 value: u128,
113 }
114
115 impl fmt::Display for LargeAlign {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 fmt::Debug::fmt(self, f)
118 }
119 }
120
121 impl core::error::Error for LargeAlign {}
122
123 let value = 0x1234_6578_1234_5678_1234_6578_1234_5678;
124 let error = Error::from(LargeAlign { value });
125 assert!(error.is::<LargeAlign>());
126 assert_eq!(error.downcast_ref::<LargeAlign>().unwrap().value, value);
127 }
128
129 #[test]
msg()130 fn msg() {
131 let error = Error::msg("uh oh!");
132 assert!(error.is::<&str>());
133 let msg = error.to_string();
134 assert_eq!(msg, "uh oh!");
135 }
136
137 #[test]
msg_drops()138 fn msg_drops() {
139 let drops = Arc::new(AtomicU32::new(0));
140 let error = Error::msg(CountDrops::new(&drops));
141 assert_eq!(drops.load(SeqCst), 0);
142 drop(error);
143 assert_eq!(drops.load(SeqCst), 1);
144 }
145
146 #[test]
from_error()147 fn from_error() {
148 let error = Error::from(TestError(42));
149 assert!(error.is::<TestError>());
150 assert_eq!(error.downcast_ref::<TestError>().unwrap().0, 42);
151 }
152
153 #[test]
into_boxed_dyn_error()154 fn into_boxed_dyn_error() {
155 let error = ChainError::new("ouch", None);
156 let error = ChainError::new("whoops", Some(Box::new(error)));
157 let error = Error::new(error).context("yikes");
158
159 let error = error.into_boxed_dyn_error();
160 assert_eq!(error.to_string(), "yikes");
161
162 let error = error.source().unwrap();
163 assert_eq!(error.to_string(), "whoops");
164
165 let error = error.source().unwrap();
166 assert_eq!(error.to_string(), "ouch");
167
168 assert!(error.source().is_none());
169 }
170
171 #[test]
is()172 fn is() {
173 // `is<T>` is true for `Error::msg(T)`
174 let e = Error::msg("str");
175 assert!(e.is::<&str>());
176 assert!(!e.is::<TestError>());
177 assert!(!e.is::<OutOfMemory>());
178
179 // `is<T>` is true for `T` in the context chain.
180 let e = e.context(TestError(42));
181 assert!(e.is::<&str>());
182 assert!(e.is::<TestError>());
183 assert!(!e.is::<OutOfMemory>());
184
185 // `is<T>` is still true when there are multiple `T`s and `U`s in the
186 // chain.
187 let e = e.context(TestError(36)).context("another str");
188 assert!(e.is::<&str>());
189 assert!(e.is::<TestError>());
190 assert!(!e.is::<OutOfMemory>());
191
192 // `is<T>` is true for `Error::from(T)`.
193 let e = Error::from(TestError(36));
194 assert!(e.is::<TestError>());
195 assert!(!e.is::<&str>());
196 assert!(!e.is::<OutOfMemory>());
197
198 // `is<T>` is true for `Error::from(OutOfMemory)`.
199 let e = Error::from(OutOfMemory::new(5));
200 assert!(e.is::<OutOfMemory>());
201 assert!(!e.is::<TestError>());
202 assert!(!e.is::<&str>());
203 }
204
205 #[test]
is_for_root_cause_with_initial_error_source()206 fn is_for_root_cause_with_initial_error_source() {
207 let error = std::fmt::Error;
208 let error = ChainError::new("whoops", Some(Box::new(error)));
209 let error = Error::new(error);
210 assert!(error.root_cause().is::<std::fmt::Error>());
211 }
212
213 #[test]
214 #[cfg(feature = "backtrace")]
backtrace()215 fn backtrace() {
216 // Backtrace on OOM.
217 let e = Error::from(OutOfMemory::new(5));
218 assert_eq!(e.backtrace().status(), BacktraceStatus::Disabled);
219
220 let backtraces_enabled =
221 match std::env::var("RUST_LIB_BACKTRACE").or_else(|_| std::env::var("RUST_BACKTRACE")) {
222 Err(_) => false,
223 Ok(s) if s == "0" => false,
224 Ok(_) => true,
225 };
226
227 let assert_backtrace = |e: Error| {
228 if backtraces_enabled {
229 assert!(matches!(
230 e.backtrace().status(),
231 BacktraceStatus::Unsupported | BacktraceStatus::Captured
232 ));
233 } else {
234 assert_eq!(e.backtrace().status(), BacktraceStatus::Disabled);
235 }
236 };
237
238 // Backtrace on `Error::msg`.
239 assert_backtrace(Error::msg("whoops"));
240
241 // Backtrace on `Error::new`.
242 assert_backtrace(Error::new(TestError(42)));
243
244 // Backtrace on context chain.
245 assert_backtrace(Error::new(TestError(42)).context("yikes"));
246 }
247
248 #[test]
format_err_macro_string_literal()249 fn format_err_macro_string_literal() {
250 let error = format_err!("literal");
251 assert_eq!(error.to_string(), "literal");
252 }
253
254 #[test]
format_err_macro_format_implicit_args()255 fn format_err_macro_format_implicit_args() {
256 let x = 42;
257 let y = 36;
258 let error = format_err!("implicit args {x} {y}");
259 assert_eq!(error.to_string(), "implicit args 42 36");
260 }
261
262 #[test]
format_err_macro_format_explicit_args()263 fn format_err_macro_format_explicit_args() {
264 let a = 84;
265 let b = 72;
266 let error = format_err!("explicit args {x} {y}", x = a / 2, y = b / 2);
267 assert_eq!(error.to_string(), "explicit args 42 36");
268 }
269
270 #[test]
format_err_macro_core_error()271 fn format_err_macro_core_error() {
272 let error = TestError(42);
273 let error = format_err!(error);
274 assert!(error.is::<TestError>());
275 assert_eq!(error.to_string(), "TestError(42)");
276 }
277
278 #[test]
format_err_macro_core_error_chain()279 fn format_err_macro_core_error_chain() {
280 let error = ChainError::new("ouch", None);
281 let error = ChainError::new("yikes", Some(Box::new(error)));
282 let error = ChainError::new("whoops", Some(Box::new(error)));
283 let error = format_err!(error);
284
285 let mut chain = error.chain();
286
287 let e = chain.next().unwrap();
288 assert_eq!(e.to_string(), "whoops");
289
290 let e = chain.next().unwrap();
291 assert_eq!(e.to_string(), "yikes");
292
293 let e = chain.next().unwrap();
294 assert_eq!(e.to_string(), "ouch");
295
296 assert!(chain.next().is_none());
297 }
298
299 #[test]
format_err_macro_msg()300 fn format_err_macro_msg() {
301 let error = 42;
302 let error = format_err!(error);
303 assert_eq!(error.to_string(), "42");
304 }
305
306 #[test]
307 #[cfg(feature = "anyhow")]
format_err_is_anyhow()308 fn format_err_is_anyhow() {
309 let error: anyhow::Error = anyhow::anyhow!("oof");
310 let error: Error = format_err!(error);
311 assert!(error.is::<anyhow::Error>());
312 assert_eq!(error.to_string(), "oof");
313 }
314
315 #[test]
bail_macro()316 fn bail_macro() {
317 fn bail_string_literal() -> Result<()> {
318 bail!("whoops")
319 }
320 assert_eq!(bail_string_literal().unwrap_err().to_string(), "whoops");
321
322 fn bail_format_implicit(x: u32) -> Result<()> {
323 bail!("yikes {x}")
324 }
325 assert_eq!(
326 bail_format_implicit(42).unwrap_err().to_string(),
327 "yikes 42"
328 );
329
330 fn bail_format_explicit(y: u32) -> Result<()> {
331 bail!("ouch {}", y + 1)
332 }
333 assert_eq!(bail_format_explicit(35).unwrap_err().to_string(), "ouch 36");
334
335 fn bail_core_error() -> Result<()> {
336 bail!(TestError(13))
337 }
338 assert_eq!(bail_core_error().unwrap_err().to_string(), "TestError(13)");
339
340 fn bail_display() -> Result<()> {
341 let x = 1337;
342 bail!(x)
343 }
344 assert_eq!(bail_display().unwrap_err().to_string(), "1337");
345 }
346
347 #[test]
ensure_macro()348 fn ensure_macro() {
349 fn ensure_string_literal(c: bool) -> Result<()> {
350 ensure!(c, "whoops");
351 Ok(())
352 }
353 assert!(ensure_string_literal(true).is_ok());
354 assert_eq!(
355 ensure_string_literal(false).unwrap_err().to_string(),
356 "whoops"
357 );
358
359 fn ensure_format_implicit(c: bool, x: u32) -> Result<()> {
360 ensure!(c, "yikes {x}");
361 Ok(())
362 }
363 assert!(ensure_format_implicit(true, 42).is_ok());
364 assert_eq!(
365 ensure_format_implicit(false, 42).unwrap_err().to_string(),
366 "yikes 42"
367 );
368
369 fn ensure_format_explicit(c: bool, y: u32) -> Result<()> {
370 ensure!(c, "ouch {}", y + 1);
371 Ok(())
372 }
373 assert!(ensure_format_explicit(true, 35).is_ok());
374 assert_eq!(
375 ensure_format_explicit(false, 35).unwrap_err().to_string(),
376 "ouch 36"
377 );
378
379 fn ensure_core_error(c: bool) -> Result<()> {
380 ensure!(c, TestError(13));
381 Ok(())
382 }
383 assert!(ensure_core_error(true).is_ok());
384 assert_eq!(
385 ensure_core_error(false).unwrap_err().to_string(),
386 "TestError(13)"
387 );
388
389 fn ensure_display(c: bool) -> Result<()> {
390 let x = 1337;
391 ensure!(c, x);
392 Ok(())
393 }
394 assert!(ensure_display(true).is_ok());
395 assert_eq!(ensure_display(false).unwrap_err().to_string(), "1337");
396
397 fn ensure_bool_ref(c: &bool) -> Result<()> {
398 ensure!(c, "whoops");
399 Ok(())
400 }
401 assert!(ensure_bool_ref(&true).is_ok());
402 assert_eq!(ensure_bool_ref(&false).unwrap_err().to_string(), "whoops");
403
404 fn ensure_no_message(a: u32) -> Result<()> {
405 ensure!(a == 42);
406 Ok(())
407 }
408 assert!(ensure_no_message(42).is_ok());
409 assert_eq!(
410 ensure_no_message(0).unwrap_err().to_string(),
411 "Condition failed: `a == 42`"
412 );
413 }
414
415 #[test]
downcast()416 fn downcast() {
417 // Error::msg(T)
418 let error = Error::msg("uh oh");
419 let error = error.downcast::<TestError>().unwrap_err();
420 let error = error.downcast::<OutOfMemory>().unwrap_err();
421 assert_eq!(error.downcast::<&str>().unwrap(), "uh oh");
422
423 // Error::new()
424 let error = Error::new(TestError(42));
425 let error = error.downcast::<&str>().unwrap_err();
426 let error = error.downcast::<OutOfMemory>().unwrap_err();
427 assert_eq!(error.downcast::<TestError>().unwrap().0, 42);
428
429 // Error::from(oom)
430 let error = Error::from(OutOfMemory::new(5));
431 let error = error.downcast::<&str>().unwrap_err();
432 let error = error.downcast::<TestError>().unwrap_err();
433 assert!(error.downcast::<OutOfMemory>().is_ok());
434
435 // First in context chain.
436 let error = Error::new(TestError(42))
437 .context("yikes")
438 .context(OutOfMemory::new(5));
439 let error = error.downcast::<String>().unwrap_err();
440 assert!(error.downcast::<OutOfMemory>().is_ok());
441
442 // Middle in context chain.
443 let error = Error::new(TestError(42))
444 .context("yikes")
445 .context(OutOfMemory::new(5));
446 let error = error.downcast::<String>().unwrap_err();
447 assert_eq!(error.downcast::<&str>().unwrap(), "yikes");
448
449 // Last in context chain.
450 let error = Error::new(TestError(42))
451 .context("yikes")
452 .context(OutOfMemory::new(5));
453 let error = error.downcast::<String>().unwrap_err();
454 assert_eq!(error.downcast::<TestError>().unwrap().0, 42);
455
456 // Multiple `T`s in the context chain gives the first one.
457 let error = Error::new(TestError(42)).context(TestError(36));
458 assert_eq!(error.downcast::<TestError>().unwrap().0, 36);
459 }
460
461 #[test]
downcast_drops_everything()462 fn downcast_drops_everything() {
463 // Error::new
464 let drops = Arc::new(AtomicU32::new(0));
465 let error = Error::new(CountDrops::new(&drops))
466 .context(CountDrops::new(&drops))
467 .context(CountDrops::new(&drops));
468 assert_eq!(drops.load(SeqCst), 0);
469 let c = error.downcast::<CountDrops>().unwrap();
470 assert_eq!(drops.load(SeqCst), 2);
471 drop(c);
472 assert_eq!(drops.load(SeqCst), 3);
473
474 // Error::msg
475 let drops = Arc::new(AtomicU32::new(0));
476 let error = Error::msg(CountDrops(drops.clone()))
477 .context(CountDrops(drops.clone()))
478 .context(CountDrops(drops.clone()));
479 assert_eq!(drops.load(SeqCst), 0);
480 let c = error.downcast::<CountDrops>().unwrap();
481 assert_eq!(drops.load(SeqCst), 2);
482 drop(c);
483 assert_eq!(drops.load(SeqCst), 3);
484 }
485
486 #[test]
downcast_ref()487 fn downcast_ref() {
488 // `Error::msg(T)`
489 let e = Error::msg("str");
490 assert_eq!(e.downcast_ref::<&str>().copied().unwrap(), "str");
491 assert!(e.downcast_ref::<TestError>().is_none());
492 assert!(e.downcast_ref::<OutOfMemory>().is_none());
493
494 // Context chain.
495 let e = e.context(TestError(42));
496 assert_eq!(e.downcast_ref::<&str>().copied().unwrap(), "str");
497 assert_eq!(e.downcast_ref::<TestError>().unwrap().0, 42);
498 assert!(e.downcast_ref::<OutOfMemory>().is_none());
499
500 // Multiple `T`s in the context chain gives you the first one.
501 let e = e.context("another str");
502 assert_eq!(e.downcast_ref::<&str>().copied().unwrap(), "another str");
503 assert_eq!(e.downcast_ref::<TestError>().unwrap().0, 42);
504 assert!(e.downcast_ref::<OutOfMemory>().is_none());
505
506 // `Error::from(T)`
507 let e = Error::from(TestError(36));
508 assert_eq!(e.downcast_ref::<TestError>().unwrap().0, 36);
509 assert!(e.downcast_ref::<&str>().is_none());
510 assert!(e.downcast_ref::<OutOfMemory>().is_none());
511
512 // `Error::from(OutOfMemory)`
513 let e = Error::from(OutOfMemory::new(5));
514 assert!(e.downcast_ref::<OutOfMemory>().is_some());
515 assert!(e.downcast_ref::<TestError>().is_none());
516 assert!(e.downcast_ref::<&str>().is_none());
517 }
518
519 #[test]
downcast_mut()520 fn downcast_mut() {
521 // `Error::msg(T)`
522 let mut e = Error::msg("str");
523 assert!(e.downcast_mut::<TestError>().is_none());
524 assert!(e.downcast_mut::<OutOfMemory>().is_none());
525 *e.downcast_mut::<&str>().unwrap() = "whoops";
526 assert_eq!(*e.downcast_ref::<&str>().unwrap(), "whoops");
527
528 // Context chain.
529 let mut e = e.context(TestError(42));
530 assert!(e.downcast_mut::<OutOfMemory>().is_none());
531 *e.downcast_mut::<&str>().unwrap() = "uh oh";
532 assert_eq!(*e.downcast_ref::<&str>().unwrap(), "uh oh");
533 e.downcast_mut::<TestError>().unwrap().0 += 1;
534 assert_eq!(e.downcast_ref::<TestError>().unwrap().0, 43);
535
536 // Multiple `T`s in the context chain gives you the first one.
537 let mut e = e.context("another str");
538 *e.downcast_mut::<&str>().unwrap() = "yikes";
539 assert_eq!(*e.downcast_ref::<&str>().unwrap(), "yikes");
540 assert_eq!(format!("{e:#}"), "yikes: TestError(43): uh oh");
541
542 // `Error::from(T)`
543 let mut e = Error::from(TestError(36));
544 assert!(e.downcast_mut::<&str>().is_none());
545 assert!(e.downcast_mut::<OutOfMemory>().is_none());
546 e.downcast_mut::<TestError>().unwrap().0 += 1;
547 assert_eq!(e.downcast_ref::<TestError>().unwrap().0, 37);
548
549 // `Error::from(OutOfMemory)`
550 let mut e = Error::from(OutOfMemory::new(5));
551 assert!(e.downcast_mut::<OutOfMemory>().is_some());
552 assert!(e.downcast_mut::<TestError>().is_none());
553 assert!(e.downcast_mut::<&str>().is_none());
554 }
555
556 #[test]
context_on_oom()557 fn context_on_oom() {
558 let error = Error::new(OutOfMemory::new(5));
559 let error = error.context("yikes");
560 assert!(error.is::<OutOfMemory>());
561 assert!(
562 !error.is::<&str>(),
563 "shouldn't attempt to box up more context when we've already exhausted memory"
564 );
565 }
566
567 #[test]
context_on_ok_result()568 fn context_on_ok_result() {
569 let result: Result<u32> = Ok(42);
570 let result = result.context("uh oh").context(TestError(1337));
571 assert_eq!(result.unwrap(), 42);
572 }
573
574 #[test]
context_on_err_result()575 fn context_on_err_result() {
576 let result: Result<u32> = Err(Error::new(TestError(42))).context("uh oh");
577 let error = result.unwrap_err();
578
579 assert!(error.is::<TestError>());
580 assert_eq!(error.downcast_ref::<TestError>().unwrap().0, 42);
581
582 assert!(error.is::<&str>());
583 assert_eq!(error.downcast_ref::<&str>().copied().unwrap(), "uh oh");
584 }
585
586 #[test]
context_on_some_option()587 fn context_on_some_option() {
588 let option = Some(42);
589 let result = option.context("uh oh").context(TestError(1337));
590 assert_eq!(result.unwrap(), 42);
591 }
592
593 #[test]
context_on_none_option()594 fn context_on_none_option() {
595 let option: Option<u32> = None;
596 let result = option.context(TestError(42)).context("uh oh");
597 let error = result.unwrap_err();
598
599 assert!(error.is::<TestError>());
600 assert_eq!(error.downcast_ref::<TestError>().unwrap().0, 42);
601
602 assert!(error.is::<&str>());
603 assert_eq!(error.downcast_ref::<&str>().copied().unwrap(), "uh oh");
604 }
605
606 #[test]
with_context_on_ok_result()607 fn with_context_on_ok_result() {
608 let result: Result<u32> = Ok(42);
609 let result = result
610 .with_context(|| "uh oh")
611 .with_context(|| TestError(36));
612 assert_eq!(result.unwrap(), 42);
613 }
614
615 #[test]
with_context_on_err_result()616 fn with_context_on_err_result() {
617 let result: Result<u32> = Err(Error::new(TestError(36)));
618 let result = result.with_context(|| "uh oh");
619 let error = result.unwrap_err();
620
621 assert!(error.is::<TestError>());
622 assert!(error.is::<&str>());
623 assert_eq!(error.downcast_ref::<&str>().copied().unwrap(), "uh oh");
624 }
625
626 #[test]
with_context_on_some_option()627 fn with_context_on_some_option() {
628 let option = Some(36);
629 let result = option
630 .with_context(|| "uh oh")
631 .with_context(|| TestError(42));
632 assert_eq!(result.unwrap(), 36);
633 }
634
635 #[test]
with_context_on_none_option()636 fn with_context_on_none_option() {
637 let option: Option<u32> = None;
638 let result = option
639 .with_context(|| "uh oh")
640 .with_context(|| TestError(42));
641 let error = result.unwrap_err();
642
643 assert!(error.is::<&str>());
644 assert_eq!(error.downcast_ref::<&str>().copied().unwrap(), "uh oh");
645
646 assert!(error.is::<TestError>());
647 assert_eq!(error.downcast_ref::<TestError>().unwrap().0, 42);
648 }
649
650 #[test]
fmt_debug()651 fn fmt_debug() {
652 let error = Error::msg("whoops").context("uh oh").context("yikes");
653 let actual = format!("{error:?}");
654
655 let expected = "\
656 yikes
657
658 Caused by:
659 0: uh oh
660 1: whoops
661 ";
662
663 #[cfg(feature = "backtrace")]
664 {
665 assert!(actual.starts_with(expected));
666 if let BacktraceStatus::Captured = error.backtrace().status() {
667 assert!(actual.contains("Stack backtrace:"));
668 }
669 }
670
671 #[cfg(not(feature = "backtrace"))]
672 {
673 assert_eq!(actual, expected);
674 }
675 }
676 #[test]
fmt_debug_with_single_cause()677 fn fmt_debug_with_single_cause() {
678 let error = Error::msg("whoops").context("uh oh");
679 let actual = format!("{error:?}");
680
681 // NB: the causes are only numbered when there are multiple of them.
682 let expected = "\
683 uh oh
684
685 Caused by:
686 whoops
687 ";
688
689 #[cfg(feature = "backtrace")]
690 {
691 assert!(actual.starts_with(expected));
692 if let BacktraceStatus::Captured = error.backtrace().status() {
693 assert!(actual.contains("Stack backtrace:"));
694 }
695 }
696
697 #[cfg(not(feature = "backtrace"))]
698 {
699 assert_eq!(actual, expected);
700 }
701 }
702
703 #[test]
fmt_debug_alternate()704 fn fmt_debug_alternate() {
705 let error = ChainError::new("root cause", None);
706 let error = ChainError::new("whoops", Some(Box::new(error)));
707 let error = Error::new(error)
708 .context(TestError(42))
709 .context("yikes")
710 .context("ouch");
711
712 let actual = format!("{error:#?}");
713 let actual = actual.trim();
714 println!("actual `{{:#?}}` output:\n{actual}");
715
716 let expected = r#"
717 Error {
718 inner: DynError {
719 error: ouch,
720 source: Error {
721 inner: DynError {
722 error: yikes,
723 source: Error {
724 inner: DynError {
725 error: TestError(
726 42,
727 ),
728 source: Error {
729 inner: DynError {
730 error: ChainError {
731 message: "whoops",
732 source: Some(
733 ChainError {
734 message: "root cause",
735 source: None,
736 },
737 ),
738 },
739 },
740 },
741 },
742 },
743 },
744 },
745 },
746 }
747 "#
748 .trim();
749 println!("expected `{{:#?}}` output:\n{expected}");
750
751 assert_eq!(actual, expected);
752 }
753
754 #[test]
fmt_debug_alternate_with_oom()755 fn fmt_debug_alternate_with_oom() {
756 let error = Error::new(OutOfMemory::new(5));
757
758 let actual = format!("{error:#?}");
759 let actual = actual.trim();
760 println!("actual `{{:#?}}` output:\n{actual}");
761
762 let expected = r#"
763 Error {
764 inner: Oom(
765 OutOfMemory {
766 requested_allocation_size: 5,
767 },
768 ),
769 }
770 "#
771 .trim();
772 println!("expected `{{:#?}}` output:\n{expected}");
773
774 assert_eq!(actual, expected);
775 }
776
777 #[test]
fmt_display()778 fn fmt_display() {
779 let error = Error::msg("whoops").context("uh oh").context("yikes");
780 assert_eq!(format!("{error}"), "yikes");
781 }
782
783 #[test]
fmt_display_alternate()784 fn fmt_display_alternate() {
785 let error = Error::msg("ouch")
786 .context("whoops")
787 .context("uh oh")
788 .context("yikes");
789 assert_eq!(format!("{error:#}"), "yikes: uh oh: whoops: ouch");
790 }
791
792 #[test]
chain()793 fn chain() {
794 let error = Error::msg("failure")
795 .context("uh oh")
796 .context(TestError(42));
797
798 let mut chain = error.chain();
799
800 let e = chain.next().unwrap();
801 assert_eq!(e.to_string(), "TestError(42)");
802
803 let e = chain.next().unwrap();
804 assert_eq!(e.to_string(), "uh oh");
805
806 let e = chain.next().unwrap();
807 assert_eq!(e.to_string(), "failure");
808
809 assert!(chain.next().is_none());
810
811 for _ in 0..100 {
812 assert!(chain.next().is_none(), "`Chain` is a fused iterator");
813 }
814 }
815
816 #[test]
chain_on_error_with_source()817 fn chain_on_error_with_source() {
818 let error = ChainError::new("yikes", None);
819 let error = ChainError::new("whoops", Some(Box::new(error)));
820 let error = ChainError::new("uh oh", Some(Box::new(error)));
821 let error = Error::new(error).context("ouch").context("oof");
822
823 let mut chain = error.chain();
824
825 let e = chain.next().unwrap();
826 assert_eq!(e.to_string(), "oof");
827
828 let e = chain.next().unwrap();
829 assert_eq!(e.to_string(), "ouch");
830
831 let e = chain.next().unwrap();
832 assert_eq!(e.to_string(), "uh oh");
833
834 let e = chain.next().unwrap();
835 assert_eq!(e.to_string(), "whoops");
836
837 let e = chain.next().unwrap();
838 assert_eq!(e.to_string(), "yikes");
839
840 assert!(chain.next().is_none());
841 }
842
843 #[test]
root_cause()844 fn root_cause() {
845 let error = ChainError::new("yikes", None);
846 let error = ChainError::new("whoops", Some(Box::new(error)));
847 let error = ChainError::new("uh oh", Some(Box::new(error)));
848 let error = Error::new(error).context("ouch").context("oof");
849 let root = error.root_cause();
850 assert_eq!(root.to_string(), "yikes");
851 assert!(root.source().is_none());
852 }
853
854 #[test]
chain_with_leaf_sources()855 fn chain_with_leaf_sources() {
856 #[derive(Debug)]
857 struct ErrorWithSource(String, Box<dyn core::error::Error + Send + Sync + 'static>);
858
859 impl fmt::Display for ErrorWithSource {
860 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861 f.write_str(&self.0)
862 }
863 }
864
865 impl core::error::Error for ErrorWithSource {
866 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
867 Some(&*self.1)
868 }
869 }
870
871 let error = Error::new(ErrorWithSource("leaf".to_string(), Box::new(TestError(42))))
872 .context("oof")
873 .context("wow");
874
875 let mut chain = error.chain();
876
877 let e = chain.next().unwrap();
878 assert_eq!(e.to_string(), "wow");
879
880 let e = chain.next().unwrap();
881 assert_eq!(e.to_string(), "oof");
882
883 let e = chain.next().unwrap();
884 assert_eq!(e.to_string(), "leaf");
885
886 let e = chain.next().unwrap();
887 assert_eq!(e.to_string(), "TestError(42)");
888
889 assert!(chain.next().is_none());
890 }
891
892 #[test]
oom_requested_allocation_size()893 fn oom_requested_allocation_size() {
894 // Check that a bunch of interesting allocation sizes roundtrip.
895 for shift in 0..30 {
896 let bytes = (1 << shift) - 1;
897 let oom = OutOfMemory::new(bytes);
898 assert_eq!(oom.requested_allocation_size(), bytes);
899
900 let bytes = 1 << shift;
901 let oom = OutOfMemory::new(bytes);
902 assert_eq!(oom.requested_allocation_size(), bytes);
903
904 let bytes = (1 << shift) + 1;
905 let oom = OutOfMemory::new(bytes);
906 assert_eq!(oom.requested_allocation_size(), bytes);
907 }
908
909 // We will round down if the allocation size is too large, but make no
910 // specific guarantees about that behavior.
911 let oom = OutOfMemory::new(usize::MAX);
912 assert!(oom.requested_allocation_size() <= usize::try_from(isize::MAX).unwrap());
913 }
914
915 #[test]
916 #[cfg(feature = "anyhow")]
anyhow_preserves_downcast() -> Result<()>917 fn anyhow_preserves_downcast() -> Result<()> {
918 {
919 let e: Error = TestError(1).into();
920 assert!(e.downcast_ref::<TestError>().is_some());
921 let e: anyhow::Error = e.into();
922 assert!(e.downcast_ref::<TestError>().is_some());
923 }
924 {
925 let e = Error::from(TestError(1)).context("hi");
926 assert!(e.downcast_ref::<TestError>().is_some());
927 assert!(e.downcast_ref::<&str>().is_some());
928 let e: anyhow::Error = e.into();
929 assert!(e.downcast_ref::<TestError>().is_some());
930 assert!(e.downcast_ref::<&str>().is_some());
931 }
932 Ok(())
933 }
934
935 #[test]
936 #[cfg(feature = "anyhow")]
anyhow_source_downcast() -> Result<()>937 fn anyhow_source_downcast() -> Result<()> {
938 let e: anyhow::Error = TestError(1).into();
939 assert!(e.downcast_ref::<TestError>().is_some());
940
941 let e: Error = Error::from_anyhow(e);
942 assert!(e.downcast_ref::<TestError>().is_some());
943
944 let e: anyhow::Error = e.into();
945 assert!(e.downcast_ref::<TestError>().is_some());
946 Ok(())
947 }
948
949 #[cfg(feature = "anyhow")]
assert_chain<T, U>(actual: impl IntoIterator<Item = T>, expected: impl IntoIterator<Item = U>) where T: PartialEq<U>, T: fmt::Debug, U: fmt::Debug,950 fn assert_chain<T, U>(actual: impl IntoIterator<Item = T>, expected: impl IntoIterator<Item = U>)
951 where
952 T: PartialEq<U>,
953 T: fmt::Debug,
954 U: fmt::Debug,
955 {
956 let mut actual = actual.into_iter();
957 let mut expected = expected.into_iter();
958 loop {
959 match (actual.next(), expected.next()) {
960 (None, None) => return,
961 (None, Some(x)) => {
962 panic!("expected {x:?} next in chain, but actual chain is exhausted")
963 }
964 (Some(x), None) => panic!("expected chain to be exhausted, but found {x:?}"),
965 (Some(x), Some(y)) => assert_eq!(x, y),
966 }
967 }
968 }
969
970 #[test]
971 #[cfg(feature = "anyhow")]
anyhow_wasmtime_anyhow_sandwich_chain() -> Result<()>972 fn anyhow_wasmtime_anyhow_sandwich_chain() -> Result<()> {
973 let e: anyhow::Error = TestError(1).into();
974 let e = e.context(TestError(2));
975
976 assert_chain(
977 e.chain().map(|e| e.to_string()),
978 ["TestError(2)", "TestError(1)"],
979 );
980
981 let e: Error = Error::from_anyhow(e);
982 let e = e.context(TestError(3));
983
984 assert_chain(
985 e.chain().map(|e| e.to_string()),
986 ["TestError(3)", "TestError(2)", "TestError(1)"],
987 );
988
989 let e: anyhow::Error = e.into();
990 let e = e.context(TestError(4));
991
992 assert_chain(
993 e.chain().map(|e| e.to_string()),
994 [
995 "TestError(4)",
996 "TestError(3)",
997 "TestError(2)",
998 "TestError(1)",
999 ],
1000 );
1001
1002 Ok(())
1003 }
1004
1005 #[test]
1006 #[cfg(feature = "anyhow")]
wasmtime_anyhow_wasmtime_sandwich_chain() -> Result<()>1007 fn wasmtime_anyhow_wasmtime_sandwich_chain() -> Result<()> {
1008 let e: Error = TestError(1).into();
1009 let e = e.context(TestError(2));
1010
1011 assert_chain(
1012 e.chain().map(|e| e.to_string()),
1013 ["TestError(2)", "TestError(1)"],
1014 );
1015
1016 let e: anyhow::Error = e.into();
1017 let e = e.context(TestError(3));
1018
1019 assert_chain(
1020 e.chain().map(|e| e.to_string()),
1021 ["TestError(3)", "TestError(2)", "TestError(1)"],
1022 );
1023
1024 let e: Error = Error::from_anyhow(e);
1025 let e = e.context(TestError(4));
1026
1027 assert_chain(
1028 e.chain().map(|e| e.to_string()),
1029 [
1030 "TestError(4)",
1031 "TestError(3)",
1032 "TestError(2)",
1033 "TestError(1)",
1034 ],
1035 );
1036
1037 Ok(())
1038 }
1039