diff --git a/src/doc/rustdoc.md b/src/doc/rustdoc.md index af69c6c1f7f1..2287bcabff76 100644 --- a/src/doc/rustdoc.md +++ b/src/doc/rustdoc.md @@ -171,6 +171,18 @@ You can specify that the code block should be compiled but not run with the ``` ~~~ +Lastly, you can specify that a code block be compiled as if `--test` +were passed to the compiler using the `test_harness` directive. + +~~~md +```test_harness +#[test] +fn foo() { + fail!("oops! (will run & register as failure)") +} +``` +~~~ + Rustdoc also supplies some extra sugar for helping with some tedious documentation examples. If a line is prefixed with `# `, then the line will not show up in the HTML documentation, but it will be used when diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 625dd3b4810a..ccd11c676110 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -195,7 +195,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { stripped_filtered_line(l).unwrap_or(l) }).collect::>().connect("\n"); let krate = krate.as_ref().map(|s| s.as_slice()); - let test = test::maketest(test.as_slice(), krate, false); + let test = test::maketest(test.as_slice(), krate, false, false); s.push_str(format!("{}", i, Escape(test.as_slice())).as_slice()); @@ -328,7 +328,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { let text = lines.collect::>().connect("\n"); tests.add_test(text.to_string(), block_info.should_fail, block_info.no_run, - block_info.ignore); + block_info.ignore, block_info.test_harness); }) } } @@ -372,6 +372,7 @@ struct LangString { no_run: bool, ignore: bool, notrust: bool, + test_harness: bool, } impl LangString { @@ -381,6 +382,7 @@ impl LangString { no_run: false, ignore: false, notrust: false, + test_harness: false, } } @@ -401,6 +403,7 @@ impl LangString { "ignore" => { data.ignore = true; seen_rust_tags = true; }, "notrust" => { data.notrust = true; seen_rust_tags = true; }, "rust" => { data.notrust = false; seen_rust_tags = true; }, + "test_harness" => { data.test_harness = true; seen_rust_tags = true; } _ => { seen_other_tags = true } } } @@ -446,24 +449,28 @@ mod tests { #[test] fn test_lang_string_parse() { - fn t(s: &str, should_fail: bool, no_run: bool, ignore: bool, notrust: bool) { + fn t(s: &str, + should_fail: bool, no_run: bool, ignore: bool, notrust: bool, test_harness: bool) { assert_eq!(LangString::parse(s), LangString { should_fail: should_fail, no_run: no_run, ignore: ignore, notrust: notrust, + test_harness: test_harness, }) } - t("", false,false,false,false); - t("rust", false,false,false,false); - t("sh", false,false,false,true); - t("notrust", false,false,false,true); - t("ignore", false,false,true,false); - t("should_fail", true,false,false,false); - t("no_run", false,true,false,false); - t("{.no_run .example}", false,true,false,false); - t("{.sh .should_fail}", true,false,false,false); - t("{.example .rust}", false,false,false,false); + t("", false,false,false,false,false); + t("rust", false,false,false,false,false); + t("sh", false,false,false,true,false); + t("notrust", false,false,false,true,false); + t("ignore", false,false,true,false,false); + t("should_fail", true,false,false,false,false); + t("no_run", false,true,false,false,false); + t("test_harness", false,false,false,false,true); + t("{.no_run .example}", false,true,false,false,false); + t("{.sh .should_fail}", true,false,false,false,false); + t("{.example .rust}", false,false,false,false,false); + t("{.test_harness .rust}", false,false,false,false,true); } } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 907d9fc7561d..b43af03011c2 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -102,8 +102,10 @@ pub fn run(input: &str, } fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool, - no_run: bool) { - let test = maketest(test, Some(cratename), true); + no_run: bool, as_test_harness: bool) { + // the test harness wants its own `main` & top level functions, so + // never wrap the test in `fn main() { ... }` + let test = maketest(test, Some(cratename), true, as_test_harness); let input = driver::StrInput(test.to_string()); let sessopts = config::Options { @@ -116,6 +118,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool, prefer_dynamic: true, .. config::basic_codegen_options() }, + test: as_test_harness, ..config::basic_options().clone() }; @@ -200,7 +203,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool, } } -pub fn maketest(s: &str, cratename: Option<&str>, lints: bool) -> String { +pub fn maketest(s: &str, cratename: Option<&str>, lints: bool, dont_insert_main: bool) -> String { let mut prog = String::new(); if lints { prog.push_str(r" @@ -220,7 +223,7 @@ pub fn maketest(s: &str, cratename: Option<&str>, lints: bool) -> String { None => {} } } - if s.contains("fn main") { + if dont_insert_main || s.contains("fn main") { prog.push_str(s); } else { prog.push_str("fn main() {\n "); @@ -255,7 +258,8 @@ impl Collector { } } - pub fn add_test(&mut self, test: String, should_fail: bool, no_run: bool, should_ignore: bool) { + pub fn add_test(&mut self, test: String, + should_fail: bool, no_run: bool, should_ignore: bool, as_test_harness: bool) { let name = if self.use_headers { let s = self.current_header.as_ref().map(|s| s.as_slice()).unwrap_or(""); format!("{}_{}", s, self.cnt) @@ -277,7 +281,8 @@ impl Collector { cratename.as_slice(), libs, should_fail, - no_run); + no_run, + as_test_harness); }), }); }