From ec7df952eb2f323658da89eb3ef5c34889f3360d Mon Sep 17 00:00:00 2001 From: Rain Date: Mon, 24 Oct 2022 17:35:30 -0700 Subject: [PATCH] tests: add tests to ensure that boxed reports forward all methods In previous commits we found that boxed reports weren't forwarding every method. Add a test to ensure that they do. --- src/eyreish/wrapper.rs | 16 +++- tests/test_boxed.rs | 169 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 4 deletions(-) diff --git a/src/eyreish/wrapper.rs b/src/eyreish/wrapper.rs index b8c55ab..91a5ef3 100644 --- a/src/eyreish/wrapper.rs +++ b/src/eyreish/wrapper.rs @@ -120,7 +120,21 @@ impl Display for BoxedError { } } -impl StdError for BoxedError {} +impl StdError for BoxedError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.0.source() + } + + fn description(&self) -> &str { + #[allow(deprecated)] + self.0.description() + } + + fn cause(&self) -> Option<&dyn StdError> { + #[allow(deprecated)] + self.0.cause() + } +} pub(crate) struct WithSourceCode { pub(crate) error: E, diff --git a/tests/test_boxed.rs b/tests/test_boxed.rs index af16db7..d6fa6f8 100644 --- a/tests/test_boxed.rs +++ b/tests/test_boxed.rs @@ -1,4 +1,4 @@ -use miette::{miette, Diagnostic, Report}; +use miette::{miette, Diagnostic, LabeledSpan, Report, SourceSpan}; use std::error::Error as StdError; use std::io; use thiserror::Error; @@ -43,8 +43,15 @@ fn test_boxed_thiserror() { let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!"), }; - let error: Report = miette!(error); - assert_eq!("oh no!", error.source().unwrap().to_string()); + let report: Report = miette!(error); + assert_eq!("oh no!", report.source().unwrap().to_string()); + + let error = MyError { + source: io::Error::new(io::ErrorKind::Other, "oh no!!!!"), + }; + let error: Box = Box::new(error); + let report = Report::new_boxed(error); + assert_eq!("oh no!!!!", report.source().unwrap().to_string()); } #[test] @@ -54,6 +61,162 @@ fn test_boxed_miette() { assert_eq!("oh no!", error.source().unwrap().to_string()); } +#[derive(Debug)] +struct CustomDiagnostic { + source: Option, + related: Vec>, +} + +impl CustomDiagnostic { + const DISPLAY: &'static str = "CustomDiagnostic display"; + const DESCRIPTION: &'static str = "CustomDiagnostic description"; + const CODE: &'static str = "A042"; + const SEVERITY: miette::Severity = miette::Severity::Advice; + const HELP: &'static str = "CustomDiagnostic help"; + const URL: &'static str = "https://custom-diagnostic-url"; + const LABEL: &'static str = "CustomDiagnostic label"; + const SOURCE_CODE: &'static str = "this-is-some-source-code"; + + fn new() -> Self { + Self { + source: None, + related: Vec::new(), + } + } + + fn with_source(self, source: E) -> Self { + let source = miette!(source); + Self { + source: Some(source), + related: Vec::new(), + } + } + + fn with_related(mut self, diagnostic: D) -> Self { + self.related.push(Box::new(diagnostic)); + self + } +} + +impl std::fmt::Display for CustomDiagnostic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(Self::DISPLAY) + } +} + +impl StdError for CustomDiagnostic { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.source.as_ref().map(|source| source.as_ref()) + } + + fn description(&self) -> &str { + Self::DESCRIPTION + } + + fn cause(&self) -> Option<&dyn StdError> { + self.source.as_ref().map(|source| source.as_ref()) + } +} + +impl Diagnostic for CustomDiagnostic { + fn code<'a>(&'a self) -> Option> { + Some(Box::new(Self::CODE)) + } + + fn severity(&self) -> Option { + Some(miette::Severity::Advice) + } + + fn help<'a>(&'a self) -> Option> { + Some(Box::new(Self::HELP)) + } + + fn url<'a>(&'a self) -> Option> { + Some(Box::new(Self::URL)) + } + + fn labels<'a>(&'a self) -> Option + 'a>> { + let labels = miette::LabeledSpan::new(Some(Self::LABEL.to_owned()), 0, 7); + Some(Box::new(std::iter::once(labels))) + } + + fn source_code(&self) -> Option<&dyn miette::SourceCode> { + Some(&Self::SOURCE_CODE) + } + + fn related<'a>(&'a self) -> Option + 'a>> { + Some(Box::new( + self.related.iter().map(|d| &**d as &'a dyn Diagnostic), + )) + } + + fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { + self.source + .as_ref() + .map(|source| &**source as &dyn Diagnostic) + } +} + +#[test] +fn test_boxed_custom_diagnostic() { + fn assert_report(report: &Report) { + assert_eq!( + report.source().map(|source| source.to_string()), + Some("oh no!".to_owned()), + ); + assert_eq!( + report.code().map(|code| code.to_string()), + Some(CustomDiagnostic::CODE.to_owned()) + ); + assert_eq!(report.severity(), Some(CustomDiagnostic::SEVERITY)); + assert_eq!( + report.help().map(|help| help.to_string()), + Some(CustomDiagnostic::HELP.to_owned()) + ); + assert_eq!( + report.url().map(|url| url.to_string()), + Some(CustomDiagnostic::URL.to_owned()) + ); + assert_eq!( + report.labels().map(|labels| labels.collect::>()), + Some(vec![LabeledSpan::new( + Some(CustomDiagnostic::LABEL.to_owned()), + 0, + 7 + )]), + ); + let span = SourceSpan::from(0..CustomDiagnostic::SOURCE_CODE.len()); + assert_eq!( + report.source_code().map(|source_code| source_code + .read_span(&span, 0, 0) + .expect("read data from source code successfully") + .data() + .to_owned()), + Some(CustomDiagnostic::SOURCE_CODE.to_owned().into_bytes()) + ); + assert_eq!( + report.diagnostic_source().map(|source| source.to_string()), + Some("oh no!".to_owned()), + ); + } + + let related = CustomDiagnostic::new(); + let main_diagnostic = CustomDiagnostic::new() + .with_source(io::Error::new(io::ErrorKind::Other, "oh no!")) + .with_related(related); + + let report = Report::new_boxed(Box::new(main_diagnostic)); + assert_report(&report); + + let related = CustomDiagnostic::new(); + let main_diagnostic = CustomDiagnostic::new() + .with_source(io::Error::new(io::ErrorKind::Other, "oh no!")) + .with_related(related); + let main_diagnostic = Box::new(main_diagnostic) as Box; + let report = miette!(main_diagnostic); + assert_report(&report); +} + #[test] #[ignore = "I don't know why this isn't working but it needs fixing."] fn test_boxed_sources() {