1
- - Feature Name: post_build_contexts
1
+ - Feature Name: custom_test_frameworks
2
2
- Start Date: 2018-01-25
3
3
- RFC PR: (leave this empty)
4
4
- Rust Issue: (leave this empty)
5
5
6
6
# Summary
7
7
[ summary ] : #summary
8
8
9
- This is an * experimental RFC* for adding the ability to integrate custom test/bench/etc frameworks ("post-build frameworks") in Rust.
9
+ This is an * experimental RFC* for adding the ability to integrate custom test/bench/etc frameworks ("test frameworks") in Rust.
10
10
11
11
# Motivation
12
12
[ motivation ] : #motivation
@@ -46,8 +46,8 @@ The main two features proposed are:
46
46
47
47
- An API for crates that generate custom binaries, including
48
48
introspection into the target crate.
49
- - A mechanism for ` cargo ` integration so that custom post-build
50
- contexts are at the same level of integration as ` test ` or ` bench ` as
49
+ - A mechanism for ` cargo ` integration so that custom test frameworks
50
+ are at the same level of integration as ` test ` or ` bench ` as
51
51
far as build processes are concerned.
52
52
53
53
[ cargo-fuzz ] : https://github.com/rust-fuzz/cargo-fuzz
@@ -60,28 +60,27 @@ The main two features proposed are:
60
60
(As an eRFC I'm merging the "guide-level/reference-level" split for now; when we have more concrete
61
61
ideas we can figure out how to frame it and then the split will make more sense)
62
62
63
- The basic idea is that crates can define post-build contexts , which specify
63
+ The basic idea is that crates can define test frameworks , which specify
64
64
how to transform collected test functions and construct a ` main() ` function,
65
65
and then crates using these can declare them in their Cargo.toml, which will let
66
- crate developers invoke various test-like post-build steps using the post-build
67
- context.
66
+ crate developers invoke various test-like steps using the framework.
68
67
69
68
70
- ## Procedural macro for a new post-build context
69
+ ## Procedural macro for a new test framework
71
70
72
- A custom post-build context is like a whole-crate procedural
71
+ A test framework is like a whole-crate procedural
73
72
macro that is evaluated after all other macros in the target crate have
74
73
been evaluated. It is passed the ` TokenStream ` for every element in the
75
- target crate that has a set of attributes the post-build context has
76
- registered interest in. For example, to declare a post-build context
74
+ target crate that has a set of attributes the test framework has
75
+ registered interest in. For example, to declare a test framework
77
76
called ` mytest ` :
78
77
79
78
``` rust
80
79
extern crate proc_macro;
81
80
use proc_macro :: TokenStream ;
82
81
83
82
// attributes() is optional
84
- #[post_build_context (attributes(foo, bar))]
83
+ #[test_framework (attributes(foo, bar))]
85
84
pub fn mytest (items : & [AnnotatedItem ]) -> TokenStream {
86
85
// ...
87
86
}
@@ -100,17 +99,17 @@ struct AnnotatedItem
100
99
101
100
` items ` here contains an ` AnnotatedItem ` for every item in the
102
101
target crate that has one of the attributes declared in ` attributes `
103
- along with attributes sharing the name of the context (` mytest ` , here).
102
+ along with attributes sharing the name of the framework (` mytest ` , here).
104
103
105
- A post-build context could declare that it reacts to multiple different
104
+ A test framework could declare that it reacts to multiple different
106
105
attributes, in which case it would get all items with any of the
107
106
listed attributes. These items be modules, functions, structs,
108
- statics, or whatever else the post-build context wants to support. Note
109
- that the post-build context function can only see all the annotated
107
+ statics, or whatever else the test framework wants to support. Note
108
+ that the test framework function can only see all the annotated
110
109
items, not modify them; modification would have to happen with regular
111
110
procedural macros. The returned ` TokenStream ` must declare the ` main() `
112
111
that is to become the entry-point for the binary produced when this
113
- post-build context is used.
112
+ test framework is used.
114
113
115
114
So an example transformation would be to take something like this:
116
115
@@ -143,99 +142,109 @@ fn main() {
143
142
```
144
143
145
144
Because this procedural macro is only loaded when it is used as the
146
- post-build context , the ` #[mytest] ` annotation should probably be kept
145
+ test framework , the ` #[mytest] ` annotation should probably be kept
147
146
behind ` #[cfg(mytest)] ` (which is automatically set when we are currently running
148
- with the ` mytest ` context , i.e. ` cargo post-build mytest ` is being run)
147
+ with the ` mytest ` framework , i.e. ` cargo test --framework mytest ` is being run)
149
148
so that you don't get unknown attribute warnings
150
- whilst loading, and to avoid conflicts with other post-build contexts
149
+ whilst loading, and to avoid conflicts with other frameworks
151
150
that may use the same attributes. (We could change this by asking
152
151
attributes to be registered in Cargo.toml, but we don't find this
153
152
necessary)
154
153
155
154
156
- A crate may only define a single post-build context .
155
+ A crate may only define a single framework .
157
156
158
157
## Cargo integration
159
158
160
- Alternative post-build contexts need to integrate with cargo.
159
+ Alternative frameworks need to integrate with cargo.
161
160
In particular, when crate ` a ` uses a crate ` b ` which provides an
162
- post-build context, ` a ` needs to be able to specify when ` b ` 's post-build
163
- context should be used. Furthermore, cargo needs to understand that when
164
- ` b ` 's post-build context is used, ` b ` 's dependencies must also be linked.
165
- Note that ` b ` could potentially provide multiple post-build contexts ---
166
- these are named according to the name of their ` #[post_build_context] `
167
- function.
161
+ framework, ` a ` needs to be able to specify when ` b ` 's framework
162
+ should be used. Furthermore, cargo needs to understand that when
163
+ ` b ` 's framework is used, ` b ` 's dependencies must also be linked.
168
164
169
- Crates which define a post-build context must have an ` post-build-context = true `
170
- key in their ` Cargo.toml ` . They cannot be used as regular dependencies. It can also specify
165
+ Crates which define a test framework must have a ` [testing.framework] `
166
+ key in their ` Cargo.toml ` . They cannot be used as regular dependencies.
167
+ This section works like this:
171
168
172
169
``` rust
170
+ [testing . framework]
171
+ name = " bench" # mandatory
172
+ folders = [" bench/" ]
173
+ lib = true # true by default
173
174
single - target = true # false by default
174
175
```
175
176
177
+ ` lib ` specifies if the ` --lib ` mode exists for this framework by default,
178
+ and ` folders ` specifies which folders the framework applies to. Both can be overridden
179
+ by consumers.
180
+
176
181
` single-target ` indicates that only a single target can be run with this
177
- context at once (some tools, like cargo-fuzz, run forever, and so it
182
+ framework at once (some tools, like cargo-fuzz, run forever, and so it
178
183
does not make sense to specify multiple targets).
179
184
180
- Crates that wish to * use* a custom post-build context , do so by defining
181
- a new post-build context under a new ` build-context ` section in their
185
+ Crates that wish to * use* a custom test framework , do so by including a framework
186
+ under a new ` [[testing.frameworks]] ` section in their
182
187
` Cargo.toml ` :
183
188
184
189
``` toml
185
- [post-build . context . fuzz ]
190
+ [[ testing . frameworks ] ]
186
191
provider = { rust-fuzz = " 1.0" }
187
- folders = [" fuzz/" ]
192
+ name = " fuzz" # optional, overrides `name` on framework crate
193
+ folders = [" fuzz/" ] # optional, overrides `folders` on framework crate
194
+ lib = false # optional, overrides `lib` on framework crate
188
195
```
189
196
190
- This defines a post-build context named ` fuzz ` , which uses the
197
+ This pulls in the framework named " fuzz" , which uses the
191
198
implementation provided by the ` rust-fuzz ` crate. When run, it will be
192
199
applied to all files in the ` fuzz ` directory. By default, the following
193
- contexts are defined:
200
+ frameworks are defined:
194
201
195
202
``` toml
196
- [post-build .context .test ]
203
+ [[testing .frameworks ]]
204
+ name = " test"
197
205
provider = { test = " 1.0" }
198
206
folders = [" tests/" ]
199
207
200
- [post-build .context .bench ]
208
+ [[testing .frameworks ]]
209
+ name = " bench"
201
210
provider = { ?? = "1.0" }
202
211
folders = [" benches/" ]
203
212
204
- [post-build .context .examples ]
213
+ [[testing .frameworks ]]
214
+ name = " example"
205
215
provider = { ?? = "1.0" }
206
216
folders = [" examples/" ]
207
217
```
208
218
209
- There's also an ` example ` context defined that just runs the ` main() ` of
210
- any files given.
211
-
212
- These can be overridden by a crate's ` Cargo.toml ` .
219
+ Whereas having two frameworks of the same name is an error, if you define
220
+ a framework named "test", "bench", or "example", it will override the implicitly
221
+ defined builtin frameworks.
213
222
214
- To invoke a particular post-build context , a user invokes `cargo context
215
- <context >` . ` cargo test ` and ` cargo bench` are aliases for ` cargo
216
- context test` and ` cargo context bench` respectively . Any additional
217
- arguments are passed to the post-build context binary. By convention, the
223
+ To invoke a particular framework , a user invokes `cargo test --framework
224
+ <name >` . ` cargo bench` is an alias for ` cargo
225
+ test --framework bench`. Any additional
226
+ arguments are passed to the testing binary. By convention, the
218
227
first position argument should allow filtering which targets
219
228
(tests/benchmarks/etc.) are run.
220
229
221
- To run multiple contexts at once, a crate can declare post-build context
222
- * sets* . One such example is the ` test ` post-build context set, which
223
- will run doctests and the ` test ` and ` examples ` context . This can be
230
+ To run multiple frameworks at once, a crate can declare testing
231
+ * sets* . One such example is the ` test ` testing set, which
232
+ will run doctests and the ` test ` and ` examples ` framework . This can be
224
233
customized:
225
234
226
235
``` toml
227
- [post-build .set .test ]
228
- contexts = [test, quickcheck, examples]
236
+ [testing .set .test ]
237
+ frameworks = [test, quickcheck, examples]
229
238
```
230
239
231
240
This means that `cargo test` will, aside from doctests, run `cargo
232
- context test`, `cargo context quickcheck`, and `cargo context examples`
241
+ test --framework test`, `cargo test --framework quickcheck`, and `cargo test --framework examples`
233
242
(and similar stuff for `cargo bench`). It is not possible to make `cargo
234
- test` _not_ run doctests. If both a context and a set exists with a
243
+ test` _not_ run doctests. If both a framework and a set exists with a
235
244
given name, the set takes precedence.
236
245
237
- `[[test]]` and `[[example]]` in a crate's `Cargo.toml` add files to the
238
- `test` and `example` contexts respectively.
246
+ `[[test]]` and `[[example]]` in a crate's `Cargo.toml` add targets to the
247
+ `test` and `example` frameworks respectively.
239
248
240
249
# # To be designed
241
250
@@ -255,9 +264,9 @@ like json output and whatnot.
255
264
256
265
- This adds more sections to `Cargo.toml`.
257
266
- This complicates the execution path for cargo, in that it now needs
258
- to know about post-build contexts and sets.
267
+ to know about testing frameworks and sets.
259
268
- Flags and command-line parameters for test and bench will now vary
260
- between post-build contexts , which may confuse users as they move
269
+ between testing frameworks , which may confuse users as they move
261
270
between crates.
262
271
263
272
# Rationale and alternatives
@@ -273,7 +282,7 @@ forms of testing or benchmarking.
273
282
An alternative proposal was to expose an extremely general whole-crate proc macro:
274
283
275
284
```rust
276
- # [post_build_context (attributes(foo, bar))]
285
+ # [test_framework (attributes(foo, bar))]
277
286
pub fn mytest(crate: TokenStream) -> TokenStream {
278
287
// ...
279
288
}
@@ -318,7 +327,7 @@ compiler already has)
318
327
Test crates are now simply proc macro attributes:
319
328
320
329
```rust
321
- # [proc_macro_attr (attributes(test, foo, bar))]
330
+ # [test_framework (attributes(test, foo, bar))]
322
331
pub fn harness(crate: TokenStream) -> TokenStream {
323
332
// ...
324
333
}
@@ -339,31 +348,15 @@ there's consensus on some of these we can move them into the main RFC.
339
348
340
349
Documentation tests are somewhat special, in that they cannot easily be
341
350
expressed as `TokenStream` manipulations. In the first instance, the
342
- right thing to do is probably to have an implicitly defined post-build
343
- context called `doctest` which is included in the post-build context set
351
+ right thing to do is probably to have an implicitly defined framework
352
+ called `doctest` which is included in the testing set
344
353
`test` by default (as proposed above).
345
354
346
355
Another argument for punting on doctests is that they are intended to
347
356
demonstrate code that the user of a library would write. They're there
348
357
to document *how* something should be used, and it then makes somewhat
349
358
less sense to have different "ways" of running them.
350
359
351
- # # Translating existing cargo test flags
352
-
353
- Today, `cargo test` takes a number of flags such as `--lib`, `--test
354
- foo`, and `--doc`. As it would be a breaking change to change these,
355
- cargo should recognize them and map to the appropriate post-build
356
- contexts.
357
-
358
- Furthermore, `cargo test` lets you pick a single testing target via `--test`,
359
- and `cargo bench` via `--bench`. We'll need to create an agnostic flag
360
- for `cargo context` (we cannot use `--target` because it is already used for
361
- the target architecture, and `--test` is too specific for tests). `--post-build-target`
362
- is one rather verbose suggestion.
363
-
364
- We also need to settle on a command name, `cargo context` and `cargo post-build`
365
- don't quite capture what's going on.
366
-
367
360
# # Standardizing the output
368
361
369
362
We should probably provide a crate with useful output formatters and
@@ -373,27 +366,27 @@ to standardize things like json output and whatnot.
373
366
374
367
# # Namespacing
375
368
376
- Currently, two post-build contexts can both declare interest in the same
369
+ Currently, two frameworks can both declare interest in the same
377
370
attributes. How do we deal with collisions (e.g., most test crates will
378
371
want the attribute `#[test]`). Do we namespace the attributes by the
379
- context name (e.g., `#[mytest::test]`)? Do we require them to be behind
372
+ framework name (e.g., `#[mytest::test]`)? Do we require them to be behind
380
373
`#[cfg(mytest)]`?
381
374
382
375
# # Runtime dependencies and flags
383
376
384
- The code generated by the post-build context may itself have dependencies.
385
- Currently there's no way for the post-build context to specify this. One
377
+ The code generated by the framework may itself have dependencies.
378
+ Currently there's no way for the framework to specify this. One
386
379
proposal is for the crate to specify _runtime_ dependencies of the
387
- post-build context via:
380
+ framework via:
388
381
389
382
```toml
390
- [context- dependencies]
383
+ [testing.framework. dependencies]
391
384
libfuzzer-sys = ...
392
385
```
393
386
394
- If a crate is currently running this post-build context , its
395
- dev-dependencies will be semver-merged with the post-build context 's
396
- `context- dependencies`. However, this may not be strictly necessary.
387
+ If a crate is currently running this framework , its
388
+ dev-dependencies will be semver-merged with the frameworks 's
389
+ `framework. dependencies`. However, this may not be strictly necessary.
397
390
Custom derives have a similar problem and they solve it by just asking
398
391
users to import the correct crate.
399
392
@@ -402,27 +395,13 @@ users to import the correct crate.
402
395
The general syntax and toml stuff should be approximately settled on before this eRFC merges, but
403
396
iterated on later. Naming the feature is hard, some candidates are:
404
397
405
- - test framework
398
+ - testing framework
399
+ - post-build context
406
400
- build context
407
401
- execution context
408
402
409
403
None of these are particularly great, ideas would be nice.
410
404
411
- # # Default folders and sets
412
-
413
- Should a post-build context be able to declare "defaults" for what folders and post-build sets it
414
- should be added to? This might save users from some boilerplate in a large number of situations.
415
-
416
- This could be done in the Cargo.toml as:
417
-
418
- ```toml
419
- [post-build.defaults]
420
- folders = ["tests/"]
421
- set = "test" # will automatically be added to the `test` set
422
- ```
423
-
424
- This is useful if a crate wishes to standardize things.
425
-
426
405
# # Bencher
427
406
428
407
Should we be shipping a bencher by default at all (i.e., in libtest)? Could we instead default
0 commit comments