1 /*
2  * This file is derived from src/types.rs in the Rust test library, used under
3  * the Apache License, Version 2.0. The following is the original copyright
4  * information from the Rust project:
5  *
6  * Copyrights in the Rust project are retained by their contributors. No
7  * copyright assignment is required to contribute to the Rust project.
8  *
9  * Some files include explicit copyright notices and/or license notices.
10  * For full authorship information, see the version control history or
11  * https://thanks.rust-lang.org
12  *
13  * Except as otherwise noted (below and/or in individual files), Rust is
14  * licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or
15  * <http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
16  * <LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option.
17  *
18  *
19  * Licensed under the Apache License, Version 2.0 (the "License");
20  * you may not use this file except in compliance with the License.
21  * You may obtain a copy of the License at
22  *
23  *      http://www.apache.org/licenses/LICENSE-2.0
24  *
25  * Unless required by applicable law or agreed to in writing, software
26  * distributed under the License is distributed on an "AS IS" BASIS,
27  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28  * See the License for the specific language governing permissions and
29  * limitations under the License.
30  */
31 
32 //! Common types used by `libtest`.
33 
34 use alloc::borrow::Cow;
35 use alloc::boxed::Box;
36 use alloc::string::String;
37 use core::fmt;
38 
39 use super::bench::Bencher;
40 use super::options;
41 
42 pub use NamePadding::*;
43 pub use TestFn::*;
44 pub use TestName::*;
45 
46 /// Type of the test according to the [rust book](https://doc.rust-lang.org/cargo/guide/tests.html)
47 /// conventions.
48 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
49 pub enum TestType {
50     /// Unit-tests are expected to be in the `src` folder of the crate.
51     UnitTest,
52     /// Integration-style tests are expected to be in the `tests` folder of the crate.
53     IntegrationTest,
54     /// Doctests are created by the `librustdoc` manually, so it's a different type of test.
55     DocTest,
56     /// Tests for the sources that don't follow the project layout convention
57     /// (e.g. tests in raw `main.rs` compiled by calling `rustc --test` directly).
58     Unknown,
59 }
60 
61 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
62 pub enum NamePadding {
63     PadNone,
64     PadOnRight,
65 }
66 
67 // The name of a test. By convention this follows the rules for rust
68 // paths; i.e., it should be a series of identifiers separated by double
69 // colons. This way if some test runner wants to arrange the tests
70 // hierarchically it may.
71 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
72 pub enum TestName {
73     StaticTestName(&'static str),
74     DynTestName(String),
75     AlignedTestName(Cow<'static, str>, NamePadding),
76 }
77 
78 impl TestName {
as_slice(&self) -> &str79     pub fn as_slice(&self) -> &str {
80         match *self {
81             StaticTestName(s) => s,
82             DynTestName(ref s) => s,
83             AlignedTestName(ref s, _) => &*s,
84         }
85     }
86 
padding(&self) -> NamePadding87     pub fn padding(&self) -> NamePadding {
88         match self {
89             &AlignedTestName(_, p) => p,
90             _ => PadNone,
91         }
92     }
93 
with_padding(&self, padding: NamePadding) -> TestName94     pub fn with_padding(&self, padding: NamePadding) -> TestName {
95         let name = match *self {
96             TestName::StaticTestName(name) => Cow::Borrowed(name),
97             TestName::DynTestName(ref name) => Cow::Owned(name.clone()),
98             TestName::AlignedTestName(ref name, _) => name.clone(),
99         };
100 
101         TestName::AlignedTestName(name, padding)
102     }
103 }
104 impl fmt::Display for TestName {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result105     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106         fmt::Display::fmt(self.as_slice(), f)
107     }
108 }
109 
110 /// Represents a benchmark function.
111 pub trait TDynBenchFn: Send {
run(&self, harness: &mut Bencher)112     fn run(&self, harness: &mut Bencher);
113 }
114 
115 // A function that runs a test. If the function returns successfully,
116 // the test succeeds; if the function panics then the test fails. We
117 // may need to come up with a more clever definition of test in order
118 // to support isolation of tests into threads.
119 pub enum TestFn {
120     StaticTestFn(fn()),
121     StaticBenchFn(fn(&mut Bencher)),
122     DynTestFn(Box<dyn FnOnce() + Send>),
123     DynBenchFn(Box<dyn TDynBenchFn + 'static>),
124 }
125 
126 impl TestFn {
padding(&self) -> NamePadding127     pub fn padding(&self) -> NamePadding {
128         match *self {
129             StaticTestFn(..) => PadNone,
130             StaticBenchFn(..) => PadOnRight,
131             DynTestFn(..) => PadNone,
132             DynBenchFn(..) => PadOnRight,
133         }
134     }
135 }
136 
137 impl fmt::Debug for TestFn {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result138     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139         f.write_str(match *self {
140             StaticTestFn(..) => "StaticTestFn(..)",
141             StaticBenchFn(..) => "StaticBenchFn(..)",
142             DynTestFn(..) => "DynTestFn(..)",
143             DynBenchFn(..) => "DynBenchFn(..)",
144         })
145     }
146 }
147 
148 // A unique integer associated with each test.
149 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
150 pub struct TestId(pub usize);
151 
152 // The definition of a single test. A test runner will run a list of
153 // these.
154 #[derive(Clone, Debug)]
155 pub struct TestDesc {
156     pub name: TestName,
157     pub ignore: bool,
158     #[cfg(not(bootstrap))]
159     pub ignore_message: Option<&'static str>,
160     pub should_panic: options::ShouldPanic,
161     pub compile_fail: bool,
162     pub no_run: bool,
163     pub test_type: TestType,
164     pub source_file: &'static str,
165     pub start_line: usize,
166     pub start_col: usize,
167     pub end_line: usize,
168     pub end_col: usize,
169 }
170 
171 impl TestDesc {
padded_name(&self, column_count: usize, align: NamePadding) -> String172     pub fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
173         let mut name = String::from(self.name.as_slice());
174         let fill = column_count.saturating_sub(name.len());
175         let pad = " ".repeat(fill);
176         match align {
177             PadNone => name,
178             PadOnRight => {
179                 name.push_str(&pad);
180                 name
181             }
182         }
183     }
184 
185     /// Returns None for ignored test or that that are just run, otherwise give a description of the type of test.
186     /// Descriptions include "should panic", "compile fail" and "compile".
test_mode(&self) -> Option<&'static str>187     pub fn test_mode(&self) -> Option<&'static str> {
188         if self.ignore {
189             return None;
190         }
191         match self.should_panic {
192             options::ShouldPanic::Yes | options::ShouldPanic::YesWithMessage(_) => {
193                 return Some("should panic");
194             }
195             options::ShouldPanic::No => {}
196         }
197         if self.compile_fail {
198             return Some("compile fail");
199         }
200         if self.no_run {
201             return Some("compile");
202         }
203         None
204     }
205 }
206 
207 #[derive(Debug)]
208 pub struct TestDescAndFn {
209     pub desc: TestDesc,
210     pub testfn: TestFn,
211 }
212