tytanic_core/test/
mod.rs

1//! Test loading and on-disk manipulation.
2
3use std::fmt::Debug;
4use std::time::Duration;
5use std::time::Instant;
6
7use ecow::EcoVec;
8use ecow::eco_vec;
9use typst::diag::SourceDiagnostic;
10
11use crate::doc::compare;
12use crate::doc::compile;
13
14mod annotation;
15mod id;
16pub mod template;
17pub mod unit;
18
19pub use self::annotation::Annotation;
20pub use self::annotation::ParseAnnotationError;
21pub use self::id::Id;
22pub use self::id::ParseIdError;
23pub use self::template::Test as TemplateTest;
24pub use self::unit::Test as UnitTest;
25
26/// A test within a test suite.
27#[derive(Debug, Clone, PartialEq)]
28pub enum Test {
29    /// A standalone unit test.
30    Unit(UnitTest),
31
32    /// A virtual designated template test.
33    Template(TemplateTest),
34}
35
36impl Test {
37    /// The unique id of this test.
38    pub fn id(&self) -> &Id {
39        match self {
40            Test::Unit(test) => test.id(),
41            Test::Template(test) => test.id(),
42        }
43    }
44
45    /// Returns the inner unit test, or `None` if this is a template test.
46    pub fn as_unit_test(&self) -> Option<&UnitTest> {
47        match self {
48            Test::Unit(test) => Some(test),
49            Test::Template(_) => None,
50        }
51    }
52
53    /// Returns the inner template test, or `None` if this is a unit test.
54    pub fn as_template_test(&self) -> Option<&TemplateTest> {
55        match self {
56            Test::Unit(_) => None,
57            Test::Template(test) => Some(test),
58        }
59    }
60}
61
62/// The stage of a single test run.
63#[derive(Debug, Clone, Default)]
64pub enum Stage {
65    /// The test was canceled or not started in the first place.
66    #[default]
67    Skipped,
68
69    /// The test was filtered out by a [`Filter`].
70    ///
71    /// [`Filter`]: crate::suite::Filter
72    Filtered,
73
74    /// The test failed compilation.
75    FailedCompilation {
76        /// The inner error.
77        error: compile::Error,
78
79        /// Whether this was a compilation failure of the reference.
80        reference: bool,
81    },
82
83    /// The test passed compilation, but failed comparison.
84    FailedComparison(compare::Error),
85
86    /// The test passed compilation, but did not run comparison.
87    PassedCompilation,
88
89    /// The test passed compilation and comparison.
90    PassedComparison,
91
92    /// The test passed compilation and updated its references.
93    Updated {
94        /// Whether the references were optimized.
95        optimized: bool,
96    },
97}
98
99/// The result of a single test run.
100#[derive(Debug, Clone)]
101pub struct TestResult {
102    stage: Stage,
103    warnings: EcoVec<SourceDiagnostic>,
104    timestamp: Instant,
105    duration: Duration,
106}
107
108impl TestResult {
109    /// Create a result for a test for a skipped test. This will set the
110    /// starting time to now, the duration to zero and the result to `None`.
111    ///
112    /// This can be used for constructing test results in advance to ensure an
113    /// aborted test run contains a skip result for all yet-to-be-run tests.
114    pub fn skipped() -> Self {
115        Self {
116            stage: Stage::Skipped,
117            warnings: eco_vec![],
118            timestamp: Instant::now(),
119            duration: Duration::ZERO,
120        }
121    }
122
123    /// Create a result for a test for a filtered test. This will set the
124    /// starting time to now, the duration to zero and the result to filtered.
125    pub fn filtered() -> Self {
126        Self {
127            stage: Stage::Filtered,
128            warnings: eco_vec![],
129            timestamp: Instant::now(),
130            duration: Duration::ZERO,
131        }
132    }
133}
134
135impl TestResult {
136    /// The stage of this rest result, if it was started.
137    pub fn stage(&self) -> &Stage {
138        &self.stage
139    }
140
141    /// The warnings of the test emitted by the compiler.
142    pub fn warnings(&self) -> &[SourceDiagnostic] {
143        &self.warnings
144    }
145
146    /// The timestamp at which the suite run started.
147    pub fn timestamp(&self) -> Instant {
148        self.timestamp
149    }
150
151    /// The duration of the test, this a zero if this test wasn't started.
152    pub fn duration(&self) -> Duration {
153        self.duration
154    }
155
156    /// Whether the test was not started.
157    pub fn is_skipped(&self) -> bool {
158        matches!(&self.stage, Stage::Skipped)
159    }
160
161    /// Whether the test was filtered out.
162    pub fn is_filtered(&self) -> bool {
163        matches!(&self.stage, Stage::Filtered)
164    }
165
166    /// Whether the test passed compilation and/or comparison/update.
167    pub fn is_pass(&self) -> bool {
168        matches!(
169            &self.stage,
170            Stage::PassedCompilation | Stage::PassedComparison | Stage::Updated { .. }
171        )
172    }
173
174    /// Whether the test failed compilation or comparison.
175    pub fn is_fail(&self) -> bool {
176        matches!(
177            &self.stage,
178            Stage::FailedCompilation { .. } | Stage::FailedComparison(..),
179        )
180    }
181
182    /// The errors emitted by the compiler if compilation failed.
183    pub fn errors(&self) -> Option<&[SourceDiagnostic]> {
184        match &self.stage {
185            Stage::FailedCompilation { error, .. } => Some(&error.0),
186            _ => None,
187        }
188    }
189}
190
191impl TestResult {
192    /// Sets the timestamp to [`Instant::now`].
193    ///
194    /// See [`TestResult::end`].
195    pub fn start(&mut self) {
196        self.timestamp = Instant::now();
197    }
198
199    /// Sets the duration to the time elapsed since [`TestResult::start`] was
200    /// called.
201    pub fn end(&mut self) {
202        self.duration = self.timestamp.elapsed();
203    }
204
205    /// Sets the kind for this test to a compilation pass.
206    pub fn set_passed_compilation(&mut self) {
207        self.stage = Stage::PassedCompilation;
208    }
209
210    /// Sets the kind for this test to a reference compilation failure.
211    pub fn set_failed_reference_compilation(&mut self, error: compile::Error) {
212        self.stage = Stage::FailedCompilation {
213            error,
214            reference: true,
215        };
216    }
217
218    /// Sets the kind for this test to a test compilation failure.
219    pub fn set_failed_test_compilation(&mut self, error: compile::Error) {
220        self.stage = Stage::FailedCompilation {
221            error,
222            reference: false,
223        };
224    }
225
226    /// Sets the kind for this test to a test comparison pass.
227    pub fn set_passed_comparison(&mut self) {
228        self.stage = Stage::PassedComparison;
229    }
230
231    /// Sets the kind for this test to a comparison failure.
232    pub fn set_failed_comparison(&mut self, error: compare::Error) {
233        self.stage = Stage::FailedComparison(error);
234    }
235
236    /// Sets the kind for this test to a test update.
237    pub fn set_updated(&mut self, optimized: bool) {
238        self.stage = Stage::Updated { optimized };
239    }
240
241    /// Sets the warnings for this test.
242    pub fn set_warnings<I>(&mut self, warnings: I)
243    where
244        I: Into<EcoVec<SourceDiagnostic>>,
245    {
246        self.warnings = warnings.into();
247    }
248}
249
250impl Default for TestResult {
251    fn default() -> Self {
252        Self::skipped()
253    }
254}