Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add unit tests for rustdoc's processing of doctests #48095

Merged
merged 3 commits into from
Feb 17, 2018
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Member Author

@QuietMisdreavus QuietMisdreavus Feb 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to change this, but i felt it didn't fit in this PR, so i'll add it after this merges.

(EDIT: What i mean is, i want manual extern crate statements to go outside the automatic fn main wrapper.)

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));
}
}