1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
//! # `tested-trait`
//!
//! `tested-trait` provides two macros -- [`tested_trait`] and [`test_impl`] -- that make it
//! possible to include associated tests in trait definitions and instantiate associated tests
//! to test implementations of the trait.
//!
//! ## Example
//!
//! Consider a memory allocator trait like [`GlobalAlloc`](core::alloc::GlobalAlloc).
//!
//! The [`alloc`](core::alloc::GlobalAlloc::alloc) method takes a [`Layout`](core::alloc::Layout)
//! describing size and alignment requirements, and returns a pointer -- the returned pointer
//! *should* adhere to layout description, but nothing enforces this contract.
//!
//! By annotating the trait definition with the [`tested_trait`] macro, a test can be associated
//! with the trait to verify that allocations result in validly aligned pointers -- at least for a
//! simple sequence of allocations:
//!
//! ```
//! # use tested_trait::{tested_trait, test_impl};
//! use std::alloc::Layout;
//!
//! #[tested_trait]
//! trait Allocator {
//! unsafe fn alloc(&mut self, layout: Layout) -> *mut u8;
//!
//! #[test]
//! fn alloc_respects_alignment() where Self: Default {
//! let mut alloc = Self::default();
//! let layout = Layout::from_size_align(10, 4).unwrap();
//! for _ in 0..10 {
//! let ptr = unsafe { alloc.alloc(layout) };
//! assert_eq!(ptr.align_offset(layout.align()), 0);
//! }
//! }
//! }
//! ```
//!
//! Note the test's `where Self: Default` bound, which it uses to construct an allocator.
//! Unlike freestanding `#[test]`s, associated tests may have `where` clauses to require additional
//! functionality for testing purposes.
//!
//! Implementers can then use [`test_impl`] to verify that their allocators pass this tests and any
//! others associated with the trait.
//! For instance, we can test the default system allocator:
//!
//! ```
//! # use tested_trait::{tested_trait, test_impl};
//! # use std::alloc::Layout;
//! # #[tested_trait]
//! # trait Allocator {
//! # unsafe fn alloc(&mut self, layout: Layout) -> *mut u8;
//! # #[test]
//! # fn alloc_respects_alignment() where Self: Default {
//! # let mut alloc = Self::default();
//! # let layout = Layout::from_size_align(10, 4).unwrap();
//! # for _ in 0..10 {
//! # let ptr = unsafe { alloc.alloc(layout) };
//! # assert_eq!(ptr.align_offset(layout.align()), 0);
//! # }
//! # }
//! # }
//! use std::alloc;
//!
//! #[test_impl]
//! # #[in_integration_test]
//! impl Allocator for alloc::System {
//! unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
//! alloc::GlobalAlloc::alloc(self, layout)
//! }
//! }
//! ```
//!
//! ... and a flawed allocator that ignores alignment:
//!
//! ```should_panic
//! # use tested_trait::{tested_trait, test_impl};
//! # use std::alloc::Layout;
//! # #[tested_trait]
//! # trait Allocator {
//! # unsafe fn alloc(&mut self, layout: Layout) -> *mut u8;
//! # #[test]
//! # fn alloc_respects_alignment() where Self: Default {
//! # let mut alloc = Self::default();
//! # let layout = Layout::from_size_align(10, 4).unwrap();
//! # for _ in 0..10 {
//! # let ptr = unsafe { alloc.alloc(layout) };
//! # assert_eq!(ptr.align_offset(layout.align()), 0);
//! # }
//! # }
//! # }
//! struct BadAllocator<const SIZE: usize> {
//! buf: Box<[u8; SIZE]>,
//! next: usize,
//! }
//!
//! // Note the `BadAllocator<1024>: Allocator` argument here -- the implementation is generic,
//! // so we use it to specify which concrete implementation should be tested.
//! #[test_impl(BadAllocator<1024>: Allocator)]
//! # #[in_integration_test]
//! impl<const SIZE: usize> Allocator for BadAllocator<SIZE> {
//! unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
//! if self.next + layout.size() <= self.buf.len() {
//! let ptr = &mut self.buf[self.next] as *mut u8;
//! self.next += layout.size();
//! ptr
//! } else {
//! core::ptr::null_mut()
//! }
//! }
//! }
//!
//! // Implement Default since the associated tests require it -- if this implementation
//! // is omitted, the #[test_impl] attribute will emit a compilation error.
//! impl<const SIZE: usize> Default for BadAllocator<SIZE> {
//! fn default() -> Self {
//! Self { buf: Box::new([0; SIZE]), next: 0 }
//! }
//! }
//! ```
//!
//! ## Features
//!
//! - [x] Associating tests with trait definitions
//! - [x] Running associated tests against non-generic trait implementations and concrete
//! instantiations of generic implementations (see [below](#testing-generic-implementations))
//! - [x] Most of the standard `#[test]` syntax (see [below](#supported-test-syntax))
//! - [ ] Understandable names for generated tests: currently, annotating `impl<T> Foo<T> for
//! Bar<T>` with [`test_impl`] generates tests named `tested_trait_test_impl_Foo_{N}` --
//! ideally they'd be named `tested_trait_test_impl_Foo<{T}>_for_Bar<{T}>`, but converting
//! types into valid identifiers is difficult
//! - [ ] Testing trait implementations for unsized types
//! - [ ] Support for property-based tests with
//! [`quickcheck`](https://docs.rs/quickcheck/latest/quickcheck/) and
//! [`proptest`](https://docs.rs/proptest/latest/proptest/)
//! - [ ] `#![no_std]` support: this crate itself is `#![no-std]`, but the tests it defines require
//! [`std::println!`] and [`std::panic::catch_unwind()`]
//!
//! ### Testing generic implementations
//!
//! Generic implementations of traits generate *concrete implementations* for each instantiation of
//! their generic parameters. It's impossible to test all of these implementations, so annotating a
//! generic implementation with *just* [`#[test_impl]`](test_impl) fails to compile:
//!
//! ```compile_fail
//! # use tested_trait::{tested_trait, test_impl};
//! #[tested_trait]
//! trait Wrapper<T> {
//! fn wrap(value: T) -> Self;
//! fn unwrap(self) -> T;
//!
//! #[test]
//! fn wrap_then_unwrap() where T: Default + PartialEq + Clone {
//! let value = T::default();
//! assert!(Self::wrap(value.clone()).unwrap() == value);
//! }
//! }
//!
//! #[test_impl]
//! impl<T> Wrapper<T> for Option<T> {
//! fn wrap(value: T) -> Self {
//! Some(value)
//! }
//! fn unwrap(self) -> T {
//! self.unwrap()
//! }
//! }
//! ```
//!
//! To test such an implementation, pass a non-empty list of `Type: Trait` arguments to
//! [`test_impl`] to specify which concrete implementations to test:
//!
//! ```
//! # use tested_trait::{tested_trait, test_impl};
//! # #[tested_trait]
//! # trait Wrapper<T> {
//! # fn wrap(value: T) -> Self;
//! # fn unwrap(self) -> T;
//! # #[test]
//! # fn wrap_then_unwrap() where T: Default + PartialEq + Clone {
//! # let value = T::default();
//! # assert!(Self::wrap(value.clone()).unwrap() == value);
//! # }
//! # }
//! #[test_impl(Option<u32>: Wrapper<u32>, Option<String>: Wrapper<String>)]
//! impl<T> Wrapper<T> for Option<T> {
//! fn wrap(value: T) -> Self {
//! Some(value)
//! }
//! fn unwrap(self) -> T {
//! self.unwrap()
//! }
//! }
//! ```
//!
//! ### Supported `#[test]` syntax
//!
//! Most of the standard `#[test]` syntax is supported:
//!
//! ```
//! # use tested_trait::{tested_trait, test_impl};
//! #[tested_trait]
//! trait Foo {
//! #[test]
//! fn standard_test() {}
//!
//! #[test]
//! fn result_returning_test() -> Result<(), String> {
//! Ok(())
//! }
//!
//! #[test]
//! #[should_panic]
//! fn should_panic_test1() {
//! panic!()
//! }
//!
//! #[test]
//! #[should_panic = "ahhh"]
//! fn should_panic_test2() {
//! panic!("ahhhhh")
//! }
//!
//! #[test]
//! #[should_panic(expected = "ahhh")]
//! fn should_panic_test3() {
//! panic!("ahhhhh")
//! }
//! }
//!
//! #[test_impl]
//! # #[in_integration_test]
//! impl Foo for () {}
//! ```
//!
//! ## Comparison to `trait_tests`
//!
//! This crate provides similar functionality to the [`trait_tests`] crate, with the following
//! notable differences:
//!
//! - `trait_tests` defines tests in separate `FooTests` traits,
//! while this crate defines them inline in trait definitions
//! - `trait_tests` allows placing bounds on `FooTests` traits,
//! while this crate allows placing them on test functions themselves
//! - `trait_tests` defines tests as unmarked associated functions,
//! while this crate supports the standard `#[test]` syntax and the niceties that come with it
//! - From my testing, this crate's macros are more hygienic and robust to varying inputs than those
//! of `trait_tests`
//!
//! [`trait_tests`]: https://crates.io/crates/trait_tests
/// Compiles functions marked with `#[test]` in the definition of the annotated trait into a test
/// suite that can be instantiated with [`test_impl`] to verify an implementation of the trait.
///
/// See the [crate-level docs](crate) for examples and more details.
pub use tested_trait;
/// Tests the annotated trait implementation against associated tests defined with [`tested_trait`].
///
/// See the [crate-level docs](crate) for examples and more details.
pub use test_impl;