Skip to content

Commit 11bdeea

Browse files
committed
rustdoc: add the ability to run tests with --test.
This adds the `test_harness` directive that runs a code block using the test runner, to allow for `#[test]` items to be demonstrated and still tested (currently they are just stripped and not even compiled, let alone run).
1 parent a17b042 commit 11bdeea

File tree

3 files changed

+43
-19
lines changed

3 files changed

+43
-19
lines changed

src/doc/rustdoc.md

+12
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,18 @@ You can specify that the code block should be compiled but not run with the
171171
```
172172
~~~
173173

174+
Lastly, you can specify that a code block be compiled as if `--test`
175+
were passed to the compiler using the `test_harness` directive.
176+
177+
~~~md
178+
```test_harness
179+
#[test]
180+
fn foo() {
181+
fail!("oops! (will run & register as failure)")
182+
}
183+
```
184+
~~~
185+
174186
Rustdoc also supplies some extra sugar for helping with some tedious
175187
documentation examples. If a line is prefixed with `# `, then the line
176188
will not show up in the HTML documentation, but it will be used when

src/librustdoc/html/markdown.rs

+20-13
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
195195
stripped_filtered_line(l).unwrap_or(l)
196196
}).collect::<Vec<&str>>().connect("\n");
197197
let krate = krate.as_ref().map(|s| s.as_slice());
198-
let test = test::maketest(test.as_slice(), krate, false);
198+
let test = test::maketest(test.as_slice(), krate, false, false);
199199
s.push_str(format!("<span id='rust-example-raw-{}' \
200200
class='rusttest'>{}</span>",
201201
i, Escape(test.as_slice())).as_slice());
@@ -328,7 +328,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
328328
let text = lines.collect::<Vec<&str>>().connect("\n");
329329
tests.add_test(text.to_string(),
330330
block_info.should_fail, block_info.no_run,
331-
block_info.ignore);
331+
block_info.ignore, block_info.test_harness);
332332
})
333333
}
334334
}
@@ -372,6 +372,7 @@ struct LangString {
372372
no_run: bool,
373373
ignore: bool,
374374
notrust: bool,
375+
test_harness: bool,
375376
}
376377

377378
impl LangString {
@@ -381,6 +382,7 @@ impl LangString {
381382
no_run: false,
382383
ignore: false,
383384
notrust: false,
385+
test_harness: false,
384386
}
385387
}
386388

@@ -401,6 +403,7 @@ impl LangString {
401403
"ignore" => { data.ignore = true; seen_rust_tags = true; },
402404
"notrust" => { data.notrust = true; seen_rust_tags = true; },
403405
"rust" => { data.notrust = false; seen_rust_tags = true; },
406+
"test_harness" => { data.test_harness = true; seen_rust_tags = true; }
404407
_ => { seen_other_tags = true }
405408
}
406409
}
@@ -446,24 +449,28 @@ mod tests {
446449

447450
#[test]
448451
fn test_lang_string_parse() {
449-
fn t(s: &str, should_fail: bool, no_run: bool, ignore: bool, notrust: bool) {
452+
fn t(s: &str,
453+
should_fail: bool, no_run: bool, ignore: bool, notrust: bool, test_harness: bool) {
450454
assert_eq!(LangString::parse(s), LangString {
451455
should_fail: should_fail,
452456
no_run: no_run,
453457
ignore: ignore,
454458
notrust: notrust,
459+
test_harness: test_harness,
455460
})
456461
}
457462

458-
t("", false,false,false,false);
459-
t("rust", false,false,false,false);
460-
t("sh", false,false,false,true);
461-
t("notrust", false,false,false,true);
462-
t("ignore", false,false,true,false);
463-
t("should_fail", true,false,false,false);
464-
t("no_run", false,true,false,false);
465-
t("{.no_run .example}", false,true,false,false);
466-
t("{.sh .should_fail}", true,false,false,false);
467-
t("{.example .rust}", false,false,false,false);
463+
t("", false,false,false,false,false);
464+
t("rust", false,false,false,false,false);
465+
t("sh", false,false,false,true,false);
466+
t("notrust", false,false,false,true,false);
467+
t("ignore", false,false,true,false,false);
468+
t("should_fail", true,false,false,false,false);
469+
t("no_run", false,true,false,false,false);
470+
t("test_harness", false,false,false,false,true);
471+
t("{.no_run .example}", false,true,false,false,false);
472+
t("{.sh .should_fail}", true,false,false,false,false);
473+
t("{.example .rust}", false,false,false,false,false);
474+
t("{.test_harness .rust}", false,false,false,false,true);
468475
}
469476
}

src/librustdoc/test.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,10 @@ pub fn run(input: &str,
102102
}
103103

104104
fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
105-
no_run: bool) {
106-
let test = maketest(test, Some(cratename), true);
105+
no_run: bool, as_test_harness: bool) {
106+
// the test harness wants its own `main` & top level functions, so
107+
// never wrap the test in `fn main() { ... }`
108+
let test = maketest(test, Some(cratename), true, as_test_harness);
107109
let input = driver::StrInput(test.to_string());
108110

109111
let sessopts = config::Options {
@@ -116,6 +118,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
116118
prefer_dynamic: true,
117119
.. config::basic_codegen_options()
118120
},
121+
test: as_test_harness,
119122
..config::basic_options().clone()
120123
};
121124

@@ -200,7 +203,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
200203
}
201204
}
202205

203-
pub fn maketest(s: &str, cratename: Option<&str>, lints: bool) -> String {
206+
pub fn maketest(s: &str, cratename: Option<&str>, lints: bool, dont_insert_main: bool) -> String {
204207
let mut prog = String::new();
205208
if lints {
206209
prog.push_str(r"
@@ -220,7 +223,7 @@ pub fn maketest(s: &str, cratename: Option<&str>, lints: bool) -> String {
220223
None => {}
221224
}
222225
}
223-
if s.contains("fn main") {
226+
if dont_insert_main || s.contains("fn main") {
224227
prog.push_str(s);
225228
} else {
226229
prog.push_str("fn main() {\n ");
@@ -255,7 +258,8 @@ impl Collector {
255258
}
256259
}
257260

258-
pub fn add_test(&mut self, test: String, should_fail: bool, no_run: bool, should_ignore: bool) {
261+
pub fn add_test(&mut self, test: String,
262+
should_fail: bool, no_run: bool, should_ignore: bool, as_test_harness: bool) {
259263
let name = if self.use_headers {
260264
let s = self.current_header.as_ref().map(|s| s.as_slice()).unwrap_or("");
261265
format!("{}_{}", s, self.cnt)
@@ -277,7 +281,8 @@ impl Collector {
277281
cratename.as_slice(),
278282
libs,
279283
should_fail,
280-
no_run);
284+
no_run,
285+
as_test_harness);
281286
}),
282287
});
283288
}

0 commit comments

Comments
 (0)