Skip to content

Commit

Permalink
Rollup merge of rust-lang#48095 - QuietMisdreavus:doctest-assembly, r…
Browse files Browse the repository at this point in the history
…=GuillaumeGomez

add unit tests for rustdoc's processing of doctests

cc rust-lang#42018

There's a lot of things that rustdoc will do to massage doctests into something that can be compiled, and a lot of options that can be toggled to affect this. Hopefully this list of tests can show off that functionality.

The first commit is slightly unrelated but doesn't touch public functionality, because i found that if you have a manual `fn main`, it adds an extra line break at the end, whereas it would trim this extra line break if it were putting a `fn main` in automatically. That first commit makes it trim out that whitespace ahead of time.
  • Loading branch information
GuillaumeGomez authored Feb 17, 2018
2 parents b85bd51 + 7277993 commit 8aa2852
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 5 deletions.
220 changes: 217 additions & 3 deletions src/librustdoc/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ pub fn make_test(s: &str,
opts: &TestOptions)
-> (String, usize) {
let (crate_attrs, everything_else) = partition_source(s);
let everything_else = everything_else.trim();
let mut line_offset = 0;
let mut prog = String::new();

Expand Down Expand Up @@ -392,12 +393,11 @@ pub fn make_test(s: &str,
.any(|code| code.contains("fn main"));

if dont_insert_main || already_has_main {
prog.push_str(&everything_else);
prog.push_str(everything_else);
} else {
prog.push_str("fn main() {\n");
line_offset += 1;
prog.push_str(&everything_else);
prog = prog.trim().into();
prog.push_str(everything_else);
prog.push_str("\n}");
}

Expand Down Expand Up @@ -753,3 +753,217 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
self.visit_testable(macro_def.name.to_string(), &macro_def.attrs, |_| ());
}
}

#[cfg(test)]
mod tests {
use super::{TestOptions, make_test};

#[test]
fn make_test_basic() {
//basic use: wraps with `fn main`, adds `#![allow(unused)]`
let opts = TestOptions::default();
let input =
"assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
fn main() {
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, None, false, &opts);
assert_eq!(output, (expected.clone(), 2));
}

#[test]
fn make_test_crate_name_no_use() {
//if you give a crate name but *don't* use it within the test, it won't bother inserting
//the `extern crate` statement
let opts = TestOptions::default();
let input =
"assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
fn main() {
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("asdf"), false, &opts);
assert_eq!(output, (expected, 2));
}

#[test]
fn make_test_crate_name() {
//if you give a crate name and use it within the test, it will insert an `extern crate`
//statement before `fn main`
let opts = TestOptions::default();
let input =
"use asdf::qwop;
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
extern crate asdf;
fn main() {
use asdf::qwop;
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("asdf"), false, &opts);
assert_eq!(output, (expected, 3));
}

#[test]
fn make_test_no_crate_inject() {
//even if you do use the crate within the test, setting `opts.no_crate_inject` will skip
//adding it anyway
let opts = TestOptions {
no_crate_inject: true,
attrs: vec![],
};
let input =
"use asdf::qwop;
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
fn main() {
use asdf::qwop;
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("asdf"), false, &opts);
assert_eq!(output, (expected, 2));
}

#[test]
fn make_test_ignore_std() {
//even if you include a crate name, and use it in the doctest, we still won't include an
//`extern crate` statement if the crate is "std" - that's included already by the compiler!
let opts = TestOptions::default();
let input =
"use std::*;
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
fn main() {
use std::*;
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("std"), false, &opts);
assert_eq!(output, (expected, 2));
}

#[test]
fn make_test_manual_extern_crate() {
//when you manually include an `extern crate` statement in your doctest, make_test assumes
//you've included one for your own crate too
let opts = TestOptions::default();
let input =
"extern crate asdf;
use asdf::qwop;
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
fn main() {
extern crate asdf;
use asdf::qwop;
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("asdf"), false, &opts);
assert_eq!(output, (expected, 2));
}

#[test]
fn make_test_opts_attrs() {
//if you supplied some doctest attributes with #![doc(test(attr(...)))], it will use those
//instead of the stock #![allow(unused)]
let mut opts = TestOptions::default();
opts.attrs.push("feature(sick_rad)".to_string());
let input =
"use asdf::qwop;
assert_eq!(2+2, 4);";
let expected =
"#![feature(sick_rad)]
extern crate asdf;
fn main() {
use asdf::qwop;
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("asdf"), false, &opts);
assert_eq!(output, (expected, 3));

//adding more will also bump the returned line offset
opts.attrs.push("feature(hella_dope)".to_string());
let expected =
"#![feature(sick_rad)]
#![feature(hella_dope)]
extern crate asdf;
fn main() {
use asdf::qwop;
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, Some("asdf"), false, &opts);
assert_eq!(output, (expected, 4));
}

#[test]
fn make_test_crate_attrs() {
//including inner attributes in your doctest will apply them to the whole "crate", pasting
//them outside the generated main function
let opts = TestOptions::default();
let input =
"#![feature(sick_rad)]
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
#![feature(sick_rad)]
fn main() {
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, None, false, &opts);
assert_eq!(output, (expected, 2));
}

#[test]
fn make_test_with_main() {
//including your own `fn main` wrapper lets the test use it verbatim
let opts = TestOptions::default();
let input =
"fn main() {
assert_eq!(2+2, 4);
}";
let expected =
"#![allow(unused)]
fn main() {
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, None, false, &opts);
assert_eq!(output, (expected, 1));
}

#[test]
fn make_test_fake_main() {
//...but putting it in a comment will still provide a wrapper
let opts = TestOptions::default();
let input =
"//Ceci n'est pas une `fn main`
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
fn main() {
//Ceci n'est pas une `fn main`
assert_eq!(2+2, 4);
}".to_string();
let output = make_test(input, None, false, &opts);
assert_eq!(output, (expected.clone(), 2));
}

#[test]
fn make_test_dont_insert_main() {
//even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper
let opts = TestOptions::default();
let input =
"//Ceci n'est pas une `fn main`
assert_eq!(2+2, 4);";
let expected =
"#![allow(unused)]
//Ceci n'est pas une `fn main`
assert_eq!(2+2, 4);".to_string();
let output = make_test(input, None, true, &opts);
assert_eq!(output, (expected.clone(), 1));
}
}
4 changes: 2 additions & 2 deletions src/test/rustdoc/playground.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@
//! }
//! ```
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A"]' "Run"
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D"]' "Run"
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D"]' "Run"
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A&version=nightly"]' "Run"
// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&version=nightly"]' "Run"

0 comments on commit 8aa2852

Please sign in to comment.