1 | //! Common types used by `libtest`. |
2 | |
3 | use std::borrow::Cow; |
4 | use std::fmt; |
5 | use std::sync::mpsc::Sender; |
6 | |
7 | use super::__rust_begin_short_backtrace; |
8 | use super::bench::Bencher; |
9 | use super::event::CompletedTest; |
10 | use super::options; |
11 | |
12 | pub use NamePadding::*; |
13 | pub use TestFn::*; |
14 | pub use TestName::*; |
15 | |
16 | /// Type of the test according to the [Rust book](https://doc.rust-lang.org/cargo/guide/tests.html) |
17 | /// conventions. |
18 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] |
19 | pub enum TestType { |
20 | /// Unit-tests are expected to be in the `src` folder of the crate. |
21 | UnitTest, |
22 | /// Integration-style tests are expected to be in the `tests` folder of the crate. |
23 | IntegrationTest, |
24 | /// Doctests are created by the `librustdoc` manually, so it's a different type of test. |
25 | DocTest, |
26 | /// Tests for the sources that don't follow the project layout convention |
27 | /// (e.g. tests in raw `main.rs` compiled by calling `rustc --test` directly). |
28 | Unknown, |
29 | } |
30 | |
31 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] |
32 | pub enum NamePadding { |
33 | PadNone, |
34 | PadOnRight, |
35 | } |
36 | |
37 | // The name of a test. By convention this follows the rules for rust |
38 | // paths; i.e., it should be a series of identifiers separated by double |
39 | // colons. This way if some test runner wants to arrange the tests |
40 | // hierarchically it may. |
41 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
42 | pub enum TestName { |
43 | StaticTestName(&'static str), |
44 | DynTestName(String), |
45 | AlignedTestName(Cow<'static, str>, NamePadding), |
46 | } |
47 | |
48 | impl TestName { |
49 | pub fn as_slice(&self) -> &str { |
50 | match *self { |
51 | StaticTestName(s) => s, |
52 | DynTestName(ref s) => s, |
53 | AlignedTestName(ref s, _) => s, |
54 | } |
55 | } |
56 | |
57 | pub fn padding(&self) -> NamePadding { |
58 | match self { |
59 | &AlignedTestName(_, p) => p, |
60 | _ => PadNone, |
61 | } |
62 | } |
63 | |
64 | pub fn with_padding(&self, padding: NamePadding) -> TestName { |
65 | let name = match *self { |
66 | TestName::StaticTestName(name) => Cow::Borrowed(name), |
67 | TestName::DynTestName(ref name) => Cow::Owned(name.clone()), |
68 | TestName::AlignedTestName(ref name, _) => name.clone(), |
69 | }; |
70 | |
71 | TestName::AlignedTestName(name, padding) |
72 | } |
73 | } |
74 | impl fmt::Display for TestName { |
75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
76 | fmt::Display::fmt(self.as_slice(), f) |
77 | } |
78 | } |
79 | |
80 | // A function that runs a test. If the function returns successfully, |
81 | // the test succeeds; if the function panics or returns Result::Err |
82 | // then the test fails. We may need to come up with a more clever |
83 | // definition of test in order to support isolation of tests into |
84 | // threads. |
85 | pub enum TestFn { |
86 | StaticTestFn(fn() -> Result<(), String>), |
87 | StaticBenchFn(fn(&mut Bencher) -> Result<(), String>), |
88 | StaticBenchAsTestFn(fn(&mut Bencher) -> Result<(), String>), |
89 | DynTestFn(Box<dyn FnOnce() -> Result<(), String> + Send>), |
90 | DynBenchFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>), |
91 | DynBenchAsTestFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>), |
92 | } |
93 | |
94 | impl TestFn { |
95 | pub fn padding(&self) -> NamePadding { |
96 | match *self { |
97 | StaticTestFn(..) => PadNone, |
98 | StaticBenchFn(..) => PadOnRight, |
99 | StaticBenchAsTestFn(..) => PadNone, |
100 | DynTestFn(..) => PadNone, |
101 | DynBenchFn(..) => PadOnRight, |
102 | DynBenchAsTestFn(..) => PadNone, |
103 | } |
104 | } |
105 | |
106 | pub(crate) fn into_runnable(self) -> Runnable { |
107 | match self { |
108 | StaticTestFn(f: fn() -> {unknown}) => Runnable::Test(RunnableTest::Static(f)), |
109 | StaticBenchFn(f: fn(&mut Bencher) -> {unknown}) => Runnable::Bench(RunnableBench::Static(f)), |
110 | StaticBenchAsTestFn(f: fn(&mut Bencher) -> {unknown}) => Runnable::Test(RunnableTest::StaticBenchAsTest(f)), |
111 | DynTestFn(f) => Runnable::Test(RunnableTest::Dynamic(f)), |
112 | DynBenchFn(f) => Runnable::Bench(RunnableBench::Dynamic(f)), |
113 | DynBenchAsTestFn(f) => Runnable::Test(RunnableTest::DynamicBenchAsTest(f)), |
114 | } |
115 | } |
116 | } |
117 | |
118 | impl fmt::Debug for TestFn { |
119 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
120 | f.write_str(match *self { |
121 | StaticTestFn(..) => "StaticTestFn(..)" , |
122 | StaticBenchFn(..) => "StaticBenchFn(..)" , |
123 | StaticBenchAsTestFn(..) => "StaticBenchAsTestFn(..)" , |
124 | DynTestFn(..) => "DynTestFn(..)" , |
125 | DynBenchFn(..) => "DynBenchFn(..)" , |
126 | DynBenchAsTestFn(..) => "DynBenchAsTestFn(..)" , |
127 | }) |
128 | } |
129 | } |
130 | |
131 | pub(crate) enum Runnable { |
132 | Test(RunnableTest), |
133 | Bench(RunnableBench), |
134 | } |
135 | |
136 | pub(crate) enum RunnableTest { |
137 | Static(fn() -> Result<(), String>), |
138 | Dynamic(Box<dyn FnOnce() -> Result<(), String> + Send>), |
139 | StaticBenchAsTest(fn(&mut Bencher) -> Result<(), String>), |
140 | DynamicBenchAsTest(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>), |
141 | } |
142 | |
143 | impl RunnableTest { |
144 | pub(crate) fn run(self) -> Result<(), String> { |
145 | match self { |
146 | RunnableTest::Static(f: fn() -> {unknown}) => __rust_begin_short_backtrace(f), |
147 | RunnableTest::Dynamic(f) => __rust_begin_short_backtrace(f), |
148 | RunnableTest::StaticBenchAsTest(f: fn(&mut Bencher) -> {unknown}) => { |
149 | crate::bench::run_once(|b: &mut Bencher| __rust_begin_short_backtrace(|| f(b))) |
150 | } |
151 | RunnableTest::DynamicBenchAsTest(f) => { |
152 | crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b))) |
153 | } |
154 | } |
155 | } |
156 | |
157 | pub(crate) fn is_dynamic(&self) -> bool { |
158 | match self { |
159 | RunnableTest::Static(_) => false, |
160 | RunnableTest::StaticBenchAsTest(_) => false, |
161 | RunnableTest::Dynamic(_) => true, |
162 | RunnableTest::DynamicBenchAsTest(_) => true, |
163 | } |
164 | } |
165 | } |
166 | |
167 | pub(crate) enum RunnableBench { |
168 | Static(fn(&mut Bencher) -> Result<(), String>), |
169 | Dynamic(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>), |
170 | } |
171 | |
172 | impl RunnableBench { |
173 | pub(crate) fn run( |
174 | self, |
175 | id: TestId, |
176 | desc: &TestDesc, |
177 | monitor_ch: &Sender<CompletedTest>, |
178 | nocapture: bool, |
179 | ) { |
180 | match self { |
181 | RunnableBench::Static(f: fn(&mut Bencher) -> {unknown}) => { |
182 | crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f) |
183 | } |
184 | RunnableBench::Dynamic(f) => { |
185 | crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f) |
186 | } |
187 | } |
188 | } |
189 | } |
190 | |
191 | // A unique integer associated with each test. |
192 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
193 | pub struct TestId(pub usize); |
194 | |
195 | // The definition of a single test. A test runner will run a list of |
196 | // these. |
197 | #[derive(Clone, Debug)] |
198 | pub struct TestDesc { |
199 | pub name: TestName, |
200 | pub ignore: bool, |
201 | pub ignore_message: Option<&'static str>, |
202 | pub source_file: &'static str, |
203 | pub start_line: usize, |
204 | pub start_col: usize, |
205 | pub end_line: usize, |
206 | pub end_col: usize, |
207 | pub should_panic: options::ShouldPanic, |
208 | pub compile_fail: bool, |
209 | pub no_run: bool, |
210 | pub test_type: TestType, |
211 | } |
212 | |
213 | impl TestDesc { |
214 | pub fn padded_name(&self, column_count: usize, align: NamePadding) -> String { |
215 | let mut name = String::from(self.name.as_slice()); |
216 | let fill = column_count.saturating_sub(name.len()); |
217 | let pad = " " .repeat(fill); |
218 | match align { |
219 | PadNone => name, |
220 | PadOnRight => { |
221 | name.push_str(&pad); |
222 | name |
223 | } |
224 | } |
225 | } |
226 | |
227 | /// Returns None for ignored test or tests that are just run, otherwise returns a description of the type of test. |
228 | /// Descriptions include "should panic", "compile fail" and "compile". |
229 | pub fn test_mode(&self) -> Option<&'static str> { |
230 | if self.ignore { |
231 | return None; |
232 | } |
233 | match self.should_panic { |
234 | options::ShouldPanic::Yes | options::ShouldPanic::YesWithMessage(_) => { |
235 | return Some("should panic" ); |
236 | } |
237 | options::ShouldPanic::No => {} |
238 | } |
239 | if self.compile_fail { |
240 | return Some("compile fail" ); |
241 | } |
242 | if self.no_run { |
243 | return Some("compile" ); |
244 | } |
245 | None |
246 | } |
247 | } |
248 | |
249 | #[derive(Debug)] |
250 | pub struct TestDescAndFn { |
251 | pub desc: TestDesc, |
252 | pub testfn: TestFn, |
253 | } |
254 | |