Skip to content

Commit 6667f90

Browse files
committed
Update rustdoc testing to test all code blocks
It's too easy to forget the `rust` tag to have a code example tested, and it's far more common to have testable code than untestable code. This alters rustdoc to have only two directives, `ignore` and `should_fail`. The `ignore` directive ignores the code block entirely, and the `should_fail` directive has been fixed to only fail the test if the code execution fails, not also compilation.
1 parent 3496e93 commit 6667f90

File tree

3 files changed

+30
-35
lines changed

3 files changed

+30
-35
lines changed

src/doc/rustdoc.md

+12-20
Original file line numberDiff line numberDiff line change
@@ -100,34 +100,29 @@ rustdoc --test crate.rs
100100

101101
## Defining tests
102102

103-
Rust documentation currently uses the markdown format, and code blocks can refer
104-
to any piece of code-related documentation, which isn't always rust. Because of
105-
this, only code blocks with the language of "rust" will be considered for
106-
testing.
103+
Rust documentation currently uses the markdown format, and rustdoc treats all
104+
code blocks as testable-by-default. In order to not run a test over a block of
105+
code, the `ignore` string can be added to the three-backtick form of markdown
106+
code block.
107107

108108
~~~
109-
```rust
109+
```
110110
// This is a testable code block
111111
```
112112
113-
```
113+
```ignore
114114
// This is not a testable code block
115115
```
116116
117-
// This is not a testable code block (4-space indent)
117+
// This is a testable code block (4-space indent)
118118
~~~
119119

120-
In addition to only testing "rust"-language code blocks, there are additional
121-
specifiers that can be used to dictate how a code block is tested:
120+
In addition to the `ignore` directive, you can specify that the test's execution
121+
should fail with the `should_fail` directive.
122122

123123
~~~
124-
```rust,ignore
125-
// This code block is ignored by rustdoc, but is passed through to the test
126-
// harness
127-
```
128-
129-
```rust,should_fail
130-
// This code block is expected to generate a failure
124+
```should_fail
125+
// This code block is expected to generate a failure when run
131126
```
132127
~~~
133128

@@ -143,7 +138,7 @@ that one can still write things like `#[deriving(Eq)]`).
143138
# the doc-generating tool. In order to display them anyway in this particular
144139
# case, the character following the leading '#' is not a usual space like in
145140
# these first five lines but a non breakable one.
146-
#
141+
#
147142
# // showing 'fib' in this documentation would just be tedious and detracts from
148143
# // what's actualy being documented.
149144
# fn fib(n: int) { n + 2 }
@@ -169,9 +164,6 @@ rustdoc --test lib.rs --test-args 'foo'
169164
170165
// See what's possible when running tests
171166
rustdoc --test lib.rs --test-args '--help'
172-
173-
// Run all ignored tests
174-
rustdoc --test lib.rs --test-args '--ignored'
175167
~~~
176168

177169
When testing a library, code examples will often show how functions are used,

src/librustdoc/html/markdown.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,23 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result {
172172
pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
173173
extern fn block(_ob: *buf, text: *buf, lang: *buf, opaque: *libc::c_void) {
174174
unsafe {
175-
if text.is_null() || lang.is_null() { return }
176-
let (test, shouldfail, ignore) =
175+
if text.is_null() { return }
176+
let (shouldfail, ignore) = if lang.is_null() {
177+
(false, false)
178+
} else {
177179
vec::raw::buf_as_slice((*lang).data,
178180
(*lang).size as uint, |lang| {
179181
let s = str::from_utf8(lang).unwrap();
180-
(s.contains("rust"), s.contains("should_fail"),
181-
s.contains("ignore"))
182-
});
183-
if !test { return }
182+
(s.contains("should_fail"), s.contains("ignore"))
183+
})
184+
};
185+
if ignore { return }
184186
vec::raw::buf_as_slice((*text).data, (*text).size as uint, |text| {
185187
let tests: &mut ::test::Collector = intrinsics::transmute(opaque);
186188
let text = str::from_utf8(text).unwrap();
187189
let mut lines = text.lines().map(|l| stripped_filtered_line(l).unwrap_or(l));
188190
let text = lines.to_owned_vec().connect("\n");
189-
tests.add_test(text, ignore, shouldfail);
191+
tests.add_test(text, shouldfail);
190192
})
191193
}
192194
}

src/librustdoc/test.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int {
9494
0
9595
}
9696

97-
fn runtest(test: &str, cratename: &str, libs: HashSet<Path>) {
97+
fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool) {
9898
let test = maketest(test, cratename);
9999
let parsesess = parse::new_parse_sess();
100100
let input = driver::StrInput(test);
@@ -130,9 +130,10 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>) {
130130
match out {
131131
Err(e) => fail!("couldn't run the test: {}", e),
132132
Ok(out) => {
133-
if !out.status.success() {
134-
fail!("test executable failed:\n{}",
135-
str::from_utf8(out.error));
133+
if should_fail && out.status.success() {
134+
fail!("test executable succeeded when it should have failed");
135+
} else if !should_fail && !out.status.success() {
136+
fail!("test executable failed:\n{}", str::from_utf8(out.error));
136137
}
137138
}
138139
}
@@ -169,7 +170,7 @@ pub struct Collector {
169170
}
170171

171172
impl Collector {
172-
pub fn add_test(&mut self, test: &str, ignore: bool, should_fail: bool) {
173+
pub fn add_test(&mut self, test: &str, should_fail: bool) {
173174
let test = test.to_owned();
174175
let name = format!("{}_{}", self.names.connect("::"), self.cnt);
175176
self.cnt += 1;
@@ -180,11 +181,11 @@ impl Collector {
180181
self.tests.push(test::TestDescAndFn {
181182
desc: test::TestDesc {
182183
name: test::DynTestName(name),
183-
ignore: ignore,
184-
should_fail: should_fail,
184+
ignore: false,
185+
should_fail: false, // compiler failures are test failures
185186
},
186187
testfn: test::DynTestFn(proc() {
187-
runtest(test, cratename, libs);
188+
runtest(test, cratename, libs, should_fail);
188189
}),
189190
});
190191
}

0 commit comments

Comments
 (0)