From c8c891fbc35c3f0caba90d0e304e7a25f259d9f6 Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Wed, 19 Mar 2025 12:02:40 +0800 Subject: [PATCH] feat: pass tests if warnings happens (#1535) --- .gitignore | 1 + crates/tinymist-debug/src/instrument.rs | 4 ++ crates/tinymist-project/src/world.rs | 2 +- crates/tinymist-world/src/system/diag.rs | 12 ++--- crates/tinymist-world/src/world.rs | 65 +++++++++++------------- crates/tinymist/src/tool/testing.rs | 28 ++++++---- 6 files changed, 61 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index 66c38317..f278aa65 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ editors/lapce/out/ *.pdf .vscode/*.code-workspace +refs/ \ No newline at end of file diff --git a/crates/tinymist-debug/src/instrument.rs b/crates/tinymist-debug/src/instrument.rs index c1ccf2ea..9b6f4f36 100644 --- a/crates/tinymist-debug/src/instrument.rs +++ b/crates/tinymist-debug/src/instrument.rs @@ -66,6 +66,10 @@ where } impl SourceWorld for InstrumentWorld<'_, F, I> { + fn as_world(&self) -> &dyn typst::World { + self + } + fn path_for_id(&self, id: FileId) -> FileResult { self.base.path_for_id(id) } diff --git a/crates/tinymist-project/src/world.rs b/crates/tinymist-project/src/world.rs index dfba140c..d40c392c 100644 --- a/crates/tinymist-project/src/world.rs +++ b/crates/tinymist-project/src/world.rs @@ -7,5 +7,5 @@ pub use tinymist_world::entry::*; pub use tinymist_world::{font, package, system, vfs}; pub use tinymist_world::{ with_main, CompilerUniverse, CompilerWorld, DiagnosticFormat, EntryOpts, EntryState, - RevisingUniverse, TaskInputs, + RevisingUniverse, SourceWorld, TaskInputs, }; diff --git a/crates/tinymist-world/src/system/diag.rs b/crates/tinymist-world/src/system/diag.rs index 66230c37..748a0c5b 100644 --- a/crates/tinymist-world/src/system/diag.rs +++ b/crates/tinymist-world/src/system/diag.rs @@ -11,9 +11,9 @@ use tinymist_std::Result; use tinymist_vfs::FileId; use typst::diag::{eco_format, Severity, SourceDiagnostic}; use typst::syntax::Span; -use typst::{World, WorldExt}; +use typst::WorldExt; -use crate::{DiagnosticFormat, SourceWorld}; +use crate::{CodeSpanReportWorld, DiagnosticFormat, SourceWorld}; /// Get stderr with color support if desirable. fn color_stream() -> StandardStream { @@ -25,12 +25,12 @@ fn color_stream() -> StandardStream { } /// Print diagnostic messages to the terminal. -pub fn print_diagnostics<'d, 'files, W: SourceWorld>( - world: &'files W, +pub fn print_diagnostics<'d, 'files>( + world: &'files dyn SourceWorld, errors: impl Iterator, diagnostic_format: DiagnosticFormat, ) -> Result<(), codespan_reporting::files::Error> { - let world = world.for_codespan_reporting(); + let world = CodeSpanReportWorld::new(world); let mut w = match diagnostic_format { DiagnosticFormat::Human => color_stream(), @@ -77,6 +77,6 @@ pub fn print_diagnostics<'d, 'files, W: SourceWorld>( } /// Create a label for a span. -fn label(world: &W, span: Span) -> Option> { +fn label(world: &dyn SourceWorld, span: Span) -> Option> { Some(Label::primary(span.id()?, world.range(span)?)) } diff --git a/crates/tinymist-world/src/world.rs b/crates/tinymist-world/src/world.rs index e1ebd892..c108bb8d 100644 --- a/crates/tinymist-world/src/world.rs +++ b/crates/tinymist-world/src/world.rs @@ -772,43 +772,37 @@ impl typst::World for WorldWithMain<'_> { } pub trait SourceWorld: World { + fn as_world(&self) -> &dyn World; + fn path_for_id(&self, id: FileId) -> Result; fn lookup(&self, id: FileId) -> Source { self.source(id) .expect("file id does not point to any source file") } - fn map_source_or_default( - &self, - id: FileId, - default_v: T, - f: impl FnOnce(Source) -> CodespanResult, - ) -> CodespanResult { - match self.source(id).ok() { - Some(source) => f(source), - None => Ok(default_v), - } - } - - fn for_codespan_reporting(&self) -> CodeSpanReportWorld - where - Self: Sized, - { - CodeSpanReportWorld { world: self } - } } impl SourceWorld for CompilerWorld { + fn as_world(&self) -> &dyn World { + self + } + /// Resolve the real path for a file id. fn path_for_id(&self, id: FileId) -> Result { self.path_for_id(id) } } -pub struct CodeSpanReportWorld<'a, T> { - pub world: &'a T, +pub struct CodeSpanReportWorld<'a> { + pub world: &'a dyn SourceWorld, } -impl<'a, T: SourceWorld> codespan_reporting::files::Files<'a> for CodeSpanReportWorld<'a, T> { +impl<'a> CodeSpanReportWorld<'a> { + pub fn new(world: &'a dyn SourceWorld) -> Self { + Self { world } + } +} + +impl<'a> codespan_reporting::files::Files<'a> for CodeSpanReportWorld<'a> { /// A unique identifier for files in the file provider. This will be used /// for rendering `diagnostic::Label`s in the corresponding source files. type FileId = FileId; @@ -858,14 +852,17 @@ impl<'a, T: SourceWorld> codespan_reporting::files::Files<'a> for CodeSpanReport /// See [`codespan_reporting::files::Files::line_range`]. fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult> { - self.world.map_source_or_default(id, 0..0, |source| { - source - .line_to_range(given) - .ok_or_else(|| CodespanError::LineTooLarge { - given, - max: source.len_lines(), - }) - }) + match self.world.source(id).ok() { + Some(source) => { + source + .line_to_range(given) + .ok_or_else(|| CodespanError::LineTooLarge { + given, + max: source.len_lines(), + }) + } + None => Ok(0..0), + } } } @@ -883,27 +880,27 @@ impl<'a, F: CompilerFeat> codespan_reporting::files::Files<'a> for CompilerWorld /// The user-facing name of a file. fn name(&'a self, id: FileId) -> CodespanResult { - self.for_codespan_reporting().name(id) + CodeSpanReportWorld::new(self).name(id) } /// The source code of a file. fn source(&'a self, id: FileId) -> CodespanResult { - self.for_codespan_reporting().source(id) + CodeSpanReportWorld::new(self).source(id) } /// See [`codespan_reporting::files::Files::line_index`]. fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult { - self.for_codespan_reporting().line_index(id, given) + CodeSpanReportWorld::new(self).line_index(id, given) } /// See [`codespan_reporting::files::Files::column_number`]. fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult { - self.for_codespan_reporting().column_number(id, 0, given) + CodeSpanReportWorld::new(self).column_number(id, 0, given) } /// See [`codespan_reporting::files::Files::line_range`]. fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult> { - self.for_codespan_reporting().line_range(id, given) + CodeSpanReportWorld::new(self).line_range(id, given) } } diff --git a/crates/tinymist/src/tool/testing.rs b/crates/tinymist/src/tool/testing.rs index a95d171f..64716aba 100644 --- a/crates/tinymist/src/tool/testing.rs +++ b/crates/tinymist/src/tool/testing.rs @@ -10,14 +10,14 @@ use itertools::Either; use parking_lot::Mutex; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use reflexo::ImmutPath; -use reflexo_typst::{vfs::FileId, SourceWorld, TypstDocument, TypstHtmlDocument}; +use reflexo_typst::{vfs::FileId, TypstDocument, TypstHtmlDocument}; use tinymist_debug::CoverageResult; use tinymist_project::world::{system::print_diagnostics, DiagnosticFormat}; use tinymist_query::analysis::Analysis; use tinymist_query::syntax::{cast_include_expr, find_source_by_expr, node_ancestors}; use tinymist_query::testing::{TestCaseKind, TestSuites}; use tinymist_std::{bail, error::prelude::*, fs::paths::write_atomic, typst::TypstPagedDocument}; -use typst::diag::SourceDiagnostic; +use typst::diag::{Severity, SourceDiagnostic}; use typst::ecow::EcoVec; use typst::foundations::{Context, Label}; use typst::syntax::{ast, LinkedNode, Source, Span}; @@ -25,7 +25,7 @@ use typst::{utils::PicoStr, World}; use typst_shim::eval::TypstEngine; use super::project::{start_project, StartProjectResult}; -use crate::world::with_main; +use crate::world::{with_main, SourceWorld}; use crate::{project::*, utils::exit_on_ctrl_c}; const TEST_EVICT_MAX_AGE: usize = 30; @@ -239,7 +239,7 @@ fn test_once(world: &LspWorld, ctx: &TestContext) -> Result { let result = if ctx.args.coverage { let (cov, result) = tinymist_debug::with_cov(world, |world| { let suites = suites.recheck(world); - let runner = TestRunner::new(ctx, &world, &suites); + let runner = TestRunner::new(ctx, world, &suites); let result = print_diag_or_error(world, runner.run()); comemo::evict(TEST_EVICT_MAX_AGE); result @@ -248,7 +248,7 @@ fn test_once(world: &LspWorld, ctx: &TestContext) -> Result { result } else { let suites = suites.recheck(world); - let runner = TestRunner::new(ctx, &world, &suites); + let runner = TestRunner::new(ctx, world, &suites); comemo::evict(TEST_EVICT_MAX_AGE); runner.run() }; @@ -294,7 +294,7 @@ impl TestContext { struct TestRunner<'a> { ctx: &'a TestContext, - world: &'a dyn World, + world: &'a dyn SourceWorld, suites: &'a TestSuites, diagnostics: Mutex>>, examples: Mutex>, @@ -302,7 +302,7 @@ struct TestRunner<'a> { } impl<'a> TestRunner<'a> { - fn new(ctx: &'a TestContext, world: &'a dyn World, suites: &'a TestSuites) -> Self { + fn new(ctx: &'a TestContext, world: &'a dyn SourceWorld, suites: &'a TestSuites) -> Self { Self { ctx, world, @@ -366,7 +366,7 @@ impl<'a> TestRunner<'a> { let name = &test.name; let func = &test.function; - let world = with_main(self.world, test.location); + let world = with_main(self.world.as_world(), test.location); let mut engine = TypstEngine::new(&world); // Executes the function @@ -409,7 +409,15 @@ impl<'a> TestRunner<'a> { { let diagnostics = self.diagnostics.into_inner(); if !diagnostics.is_empty() { - Err(diagnostics.into_iter().flatten().collect::>())? + let diagnostics = diagnostics.into_iter().flatten().collect::>(); + let any_error = diagnostics.iter().any(|d| d.severity == Severity::Error); + + if any_error { + Err(diagnostics)? + } else { + print_diagnostics(self.world, diagnostics.iter(), DiagnosticFormat::Human) + .context_ut("print diagnostics")?; + } } } Ok(!self.failed.load(std::sync::atomic::Ordering::SeqCst)) @@ -425,7 +433,7 @@ impl<'a> TestRunner<'a> { return; } - let world = with_main(self.world, test.id()); + let world = with_main(self.world.as_world(), test.id()); let mut has_err = false; let (has_err_, doc) = self.build_example::(&world); has_err |= has_err_ || self.render_paged(name, doc.as_ref());