Skip to content

Commit 668f2d0

Browse files
Rollup merge of rust-lang#48095 - QuietMisdreavus:doctest-assembly, r=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.
2 parents c86bc4a + 7277993 commit 668f2d0

File tree

2 files changed

+219
-5
lines changed

2 files changed

+219
-5
lines changed

src/librustdoc/test.rs

Lines changed: 217 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ pub fn make_test(s: &str,
345345
opts: &TestOptions)
346346
-> (String, usize) {
347347
let (crate_attrs, everything_else) = partition_source(s);
348+
let everything_else = everything_else.trim();
348349
let mut line_offset = 0;
349350
let mut prog = String::new();
350351

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

394395
if dont_insert_main || already_has_main {
395-
prog.push_str(&everything_else);
396+
prog.push_str(everything_else);
396397
} else {
397398
prog.push_str("fn main() {\n");
398399
line_offset += 1;
399-
prog.push_str(&everything_else);
400-
prog = prog.trim().into();
400+
prog.push_str(everything_else);
401401
prog.push_str("\n}");
402402
}
403403

@@ -753,3 +753,217 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
753753
self.visit_testable(macro_def.name.to_string(), &macro_def.attrs, |_| ());
754754
}
755755
}
756+
757+
#[cfg(test)]
758+
mod tests {
759+
use super::{TestOptions, make_test};
760+
761+
#[test]
762+
fn make_test_basic() {
763+
//basic use: wraps with `fn main`, adds `#![allow(unused)]`
764+
let opts = TestOptions::default();
765+
let input =
766+
"assert_eq!(2+2, 4);";
767+
let expected =
768+
"#![allow(unused)]
769+
fn main() {
770+
assert_eq!(2+2, 4);
771+
}".to_string();
772+
let output = make_test(input, None, false, &opts);
773+
assert_eq!(output, (expected.clone(), 2));
774+
}
775+
776+
#[test]
777+
fn make_test_crate_name_no_use() {
778+
//if you give a crate name but *don't* use it within the test, it won't bother inserting
779+
//the `extern crate` statement
780+
let opts = TestOptions::default();
781+
let input =
782+
"assert_eq!(2+2, 4);";
783+
let expected =
784+
"#![allow(unused)]
785+
fn main() {
786+
assert_eq!(2+2, 4);
787+
}".to_string();
788+
let output = make_test(input, Some("asdf"), false, &opts);
789+
assert_eq!(output, (expected, 2));
790+
}
791+
792+
#[test]
793+
fn make_test_crate_name() {
794+
//if you give a crate name and use it within the test, it will insert an `extern crate`
795+
//statement before `fn main`
796+
let opts = TestOptions::default();
797+
let input =
798+
"use asdf::qwop;
799+
assert_eq!(2+2, 4);";
800+
let expected =
801+
"#![allow(unused)]
802+
extern crate asdf;
803+
fn main() {
804+
use asdf::qwop;
805+
assert_eq!(2+2, 4);
806+
}".to_string();
807+
let output = make_test(input, Some("asdf"), false, &opts);
808+
assert_eq!(output, (expected, 3));
809+
}
810+
811+
#[test]
812+
fn make_test_no_crate_inject() {
813+
//even if you do use the crate within the test, setting `opts.no_crate_inject` will skip
814+
//adding it anyway
815+
let opts = TestOptions {
816+
no_crate_inject: true,
817+
attrs: vec![],
818+
};
819+
let input =
820+
"use asdf::qwop;
821+
assert_eq!(2+2, 4);";
822+
let expected =
823+
"#![allow(unused)]
824+
fn main() {
825+
use asdf::qwop;
826+
assert_eq!(2+2, 4);
827+
}".to_string();
828+
let output = make_test(input, Some("asdf"), false, &opts);
829+
assert_eq!(output, (expected, 2));
830+
}
831+
832+
#[test]
833+
fn make_test_ignore_std() {
834+
//even if you include a crate name, and use it in the doctest, we still won't include an
835+
//`extern crate` statement if the crate is "std" - that's included already by the compiler!
836+
let opts = TestOptions::default();
837+
let input =
838+
"use std::*;
839+
assert_eq!(2+2, 4);";
840+
let expected =
841+
"#![allow(unused)]
842+
fn main() {
843+
use std::*;
844+
assert_eq!(2+2, 4);
845+
}".to_string();
846+
let output = make_test(input, Some("std"), false, &opts);
847+
assert_eq!(output, (expected, 2));
848+
}
849+
850+
#[test]
851+
fn make_test_manual_extern_crate() {
852+
//when you manually include an `extern crate` statement in your doctest, make_test assumes
853+
//you've included one for your own crate too
854+
let opts = TestOptions::default();
855+
let input =
856+
"extern crate asdf;
857+
use asdf::qwop;
858+
assert_eq!(2+2, 4);";
859+
let expected =
860+
"#![allow(unused)]
861+
fn main() {
862+
extern crate asdf;
863+
use asdf::qwop;
864+
assert_eq!(2+2, 4);
865+
}".to_string();
866+
let output = make_test(input, Some("asdf"), false, &opts);
867+
assert_eq!(output, (expected, 2));
868+
}
869+
870+
#[test]
871+
fn make_test_opts_attrs() {
872+
//if you supplied some doctest attributes with #![doc(test(attr(...)))], it will use those
873+
//instead of the stock #![allow(unused)]
874+
let mut opts = TestOptions::default();
875+
opts.attrs.push("feature(sick_rad)".to_string());
876+
let input =
877+
"use asdf::qwop;
878+
assert_eq!(2+2, 4);";
879+
let expected =
880+
"#![feature(sick_rad)]
881+
extern crate asdf;
882+
fn main() {
883+
use asdf::qwop;
884+
assert_eq!(2+2, 4);
885+
}".to_string();
886+
let output = make_test(input, Some("asdf"), false, &opts);
887+
assert_eq!(output, (expected, 3));
888+
889+
//adding more will also bump the returned line offset
890+
opts.attrs.push("feature(hella_dope)".to_string());
891+
let expected =
892+
"#![feature(sick_rad)]
893+
#![feature(hella_dope)]
894+
extern crate asdf;
895+
fn main() {
896+
use asdf::qwop;
897+
assert_eq!(2+2, 4);
898+
}".to_string();
899+
let output = make_test(input, Some("asdf"), false, &opts);
900+
assert_eq!(output, (expected, 4));
901+
}
902+
903+
#[test]
904+
fn make_test_crate_attrs() {
905+
//including inner attributes in your doctest will apply them to the whole "crate", pasting
906+
//them outside the generated main function
907+
let opts = TestOptions::default();
908+
let input =
909+
"#![feature(sick_rad)]
910+
assert_eq!(2+2, 4);";
911+
let expected =
912+
"#![allow(unused)]
913+
#![feature(sick_rad)]
914+
fn main() {
915+
assert_eq!(2+2, 4);
916+
}".to_string();
917+
let output = make_test(input, None, false, &opts);
918+
assert_eq!(output, (expected, 2));
919+
}
920+
921+
#[test]
922+
fn make_test_with_main() {
923+
//including your own `fn main` wrapper lets the test use it verbatim
924+
let opts = TestOptions::default();
925+
let input =
926+
"fn main() {
927+
assert_eq!(2+2, 4);
928+
}";
929+
let expected =
930+
"#![allow(unused)]
931+
fn main() {
932+
assert_eq!(2+2, 4);
933+
}".to_string();
934+
let output = make_test(input, None, false, &opts);
935+
assert_eq!(output, (expected, 1));
936+
}
937+
938+
#[test]
939+
fn make_test_fake_main() {
940+
//...but putting it in a comment will still provide a wrapper
941+
let opts = TestOptions::default();
942+
let input =
943+
"//Ceci n'est pas une `fn main`
944+
assert_eq!(2+2, 4);";
945+
let expected =
946+
"#![allow(unused)]
947+
fn main() {
948+
//Ceci n'est pas une `fn main`
949+
assert_eq!(2+2, 4);
950+
}".to_string();
951+
let output = make_test(input, None, false, &opts);
952+
assert_eq!(output, (expected.clone(), 2));
953+
}
954+
955+
#[test]
956+
fn make_test_dont_insert_main() {
957+
//even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper
958+
let opts = TestOptions::default();
959+
let input =
960+
"//Ceci n'est pas une `fn main`
961+
assert_eq!(2+2, 4);";
962+
let expected =
963+
"#![allow(unused)]
964+
//Ceci n'est pas une `fn main`
965+
assert_eq!(2+2, 4);".to_string();
966+
let output = make_test(input, None, true, &opts);
967+
assert_eq!(output, (expected.clone(), 1));
968+
}
969+
}

src/test/rustdoc/playground.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@
3434
//! }
3535
//! ```
3636
37-
// @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"
37+
// @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"
3838
// @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"
39-
// @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"
39+
// @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 commit comments

Comments
 (0)