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