use cargotest; use std::str; use std::fs::{self, File}; use std::io::Read; use cargotest::rustc_host; use cargotest::support::{project, execs, path2url}; use cargotest::support::registry::Package; use hamcrest::{assert_that, existing_file, existing_dir, is_not}; use cargo::util::ProcessError; #[test] fn simple() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" "#) .file("build.rs", "fn main() {}") .file("src/lib.rs", r#" pub fn foo() {} "#) .build(); assert_that(p.cargo("doc"), execs().with_status(0).with_stderr(&format!("\ [..] foo v0.0.1 ({dir}) [..] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); } #[test] fn doc_no_libs() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [[bin]] name = "foo" doc = false "#) .file("src/main.rs", r#" bad code "#) .build(); assert_that(p.cargo("doc"), execs().with_status(0)); } #[test] fn doc_twice() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/lib.rs", r#" pub fn foo() {} "#) .build(); assert_that(p.cargo("doc"), execs().with_status(0).with_stderr(&format!("\ [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(p.cargo("doc"), execs().with_status(0).with_stdout("")) } #[test] fn doc_deps() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] path = "bar" "#) .file("src/lib.rs", r#" extern crate bar; pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .build(); assert_that(p.cargo("doc"), execs().with_status(0).with_stderr(&format!("\ [..] bar v0.0.1 ({dir}/bar) [..] bar v0.0.1 ({dir}/bar) [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); assert_that(&p.root().join("target/doc/bar/index.html"), existing_file()); assert_that(p.cargo("doc") .env("RUST_LOG", "cargo::ops::cargo_rustc::fingerprint"), execs().with_status(0).with_stdout("")); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); assert_that(&p.root().join("target/doc/bar/index.html"), existing_file()); } #[test] fn doc_no_deps() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] path = "bar" "#) .file("src/lib.rs", r#" extern crate bar; pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .build(); assert_that(p.cargo("doc").arg("--no-deps"), execs().with_status(0).with_stderr(&format!("\ [COMPILING] bar v0.0.1 ({dir}/bar) [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); assert_that(&p.root().join("target/doc/bar/index.html"), is_not(existing_file())); } #[test] fn doc_only_bin() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] path = "bar" "#) .file("src/main.rs", r#" extern crate bar; pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .build(); assert_that(p.cargo("doc").arg("-v"), execs().with_status(0)); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/bar/index.html"), existing_file()); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); } #[test] fn doc_multiple_targets_same_name_lib() { let p = project("foo") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [lib] name = "foo_lib" "#) .file("foo/src/lib.rs", "") .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [lib] name = "foo_lib" "#) .file("bar/src/lib.rs", "") .build(); assert_that(p.cargo("doc").arg("--all"), execs() .with_status(101) .with_stderr_contains("[..] library `foo_lib` is specified [..]") .with_stderr_contains("[..] `foo v0.1.0[..]` [..]") .with_stderr_contains("[..] `bar v0.1.0[..]` [..]")); } #[test] fn doc_multiple_targets_same_name() { let p = project("foo") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [[bin]] name = "foo_lib" path = "src/foo_lib.rs" "#) .file("foo/src/foo_lib.rs", "") .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [lib] name = "foo_lib" "#) .file("bar/src/lib.rs", "") .build(); let root = path2url(p.root()); assert_that(p.cargo("doc").arg("--all"), execs() .with_status(0) .with_stderr_contains(&format!("[DOCUMENTING] foo v0.1.0 ({}/foo)", root)) .with_stderr_contains(&format!("[DOCUMENTING] bar v0.1.0 ({}/bar)", root)) .with_stderr_contains("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")); assert_that(&p.root().join("target/doc"), existing_dir()); let doc_file = p.root().join("target/doc/foo_lib/index.html"); assert_that(&doc_file, existing_file()); } #[test] fn doc_multiple_targets_same_name_bin() { let p = project("foo") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [[bin]] name = "foo-cli" "#) .file("foo/src/foo-cli.rs", "") .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [[bin]] name = "foo-cli" "#) .file("bar/src/foo-cli.rs", "") .build(); assert_that(p.cargo("doc").arg("--all"), execs() .with_status(101) .with_stderr_contains("[..] binary `foo_cli` is specified [..]") .with_stderr_contains("[..] `foo v0.1.0[..]` [..]") .with_stderr_contains("[..] `bar v0.1.0[..]` [..]")); } #[test] fn doc_multiple_targets_same_name_undoced() { let p = project("foo") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [[bin]] name = "foo-cli" "#) .file("foo/src/foo-cli.rs", "") .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [[bin]] name = "foo-cli" doc = false "#) .file("bar/src/foo-cli.rs", "") .build(); assert_that(p.cargo("doc").arg("--all"), execs().with_status(0)); } #[test] fn doc_lib_bin_same_name_documents_lib() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#) .file("src/lib.rs", r#" //! Library documentation pub fn foo() {} "#) .build(); assert_that(p.cargo("doc"), execs().with_status(0).with_stderr(&format!("\ [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); let doc_file = p.root().join("target/doc/foo/index.html"); assert_that(&doc_file, existing_file()); let mut doc_html = String::new(); File::open(&doc_file).unwrap().read_to_string(&mut doc_html).unwrap(); assert!(doc_html.contains("Library")); assert!(!doc_html.contains("Binary")); } #[test] fn doc_lib_bin_same_name_documents_lib_when_requested() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#) .file("src/lib.rs", r#" //! Library documentation pub fn foo() {} "#) .build(); assert_that(p.cargo("doc").arg("--lib"), execs().with_status(0).with_stderr(&format!("\ [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); let doc_file = p.root().join("target/doc/foo/index.html"); assert_that(&doc_file, existing_file()); let mut doc_html = String::new(); File::open(&doc_file).unwrap().read_to_string(&mut doc_html).unwrap(); assert!(doc_html.contains("Library")); assert!(!doc_html.contains("Binary")); } #[test] fn doc_lib_bin_same_name_documents_named_bin_when_requested() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#) .file("src/lib.rs", r#" //! Library documentation pub fn foo() {} "#) .build(); assert_that(p.cargo("doc").arg("--bin").arg("foo"), execs().with_status(0).with_stderr(&format!("\ [COMPILING] foo v0.0.1 ({dir}) [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); let doc_file = p.root().join("target/doc/foo/index.html"); assert_that(&doc_file, existing_file()); let mut doc_html = String::new(); File::open(&doc_file).unwrap().read_to_string(&mut doc_html).unwrap(); assert!(!doc_html.contains("Library")); assert!(doc_html.contains("Binary")); } #[test] fn doc_lib_bin_same_name_documents_bins_when_requested() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/main.rs", r#" //! Binary documentation extern crate foo; fn main() { foo::foo(); } "#) .file("src/lib.rs", r#" //! Library documentation pub fn foo() {} "#) .build(); assert_that(p.cargo("doc").arg("--bins"), execs().with_status(0).with_stderr(&format!("\ [COMPILING] foo v0.0.1 ({dir}) [DOCUMENTING] foo v0.0.1 ({dir}) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", dir = path2url(p.root())))); assert_that(&p.root().join("target/doc"), existing_dir()); let doc_file = p.root().join("target/doc/foo/index.html"); assert_that(&doc_file, existing_file()); let mut doc_html = String::new(); File::open(&doc_file).unwrap().read_to_string(&mut doc_html).unwrap(); assert!(!doc_html.contains("Library")); assert!(doc_html.contains("Binary")); } #[test] fn doc_dash_p() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.a] path = "a" "#) .file("src/lib.rs", "extern crate a;") .file("a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" authors = [] [dependencies.b] path = "../b" "#) .file("a/src/lib.rs", "extern crate b;") .file("b/Cargo.toml", r#" [package] name = "b" version = "0.0.1" authors = [] "#) .file("b/src/lib.rs", "") .build(); assert_that(p.cargo("doc").arg("-p").arg("a"), execs().with_status(0) .with_stderr("\ [..] b v0.0.1 (file://[..]) [..] b v0.0.1 (file://[..]) [DOCUMENTING] a v0.0.1 (file://[..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ")); } #[test] fn doc_same_name() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/lib.rs", "") .file("src/bin/main.rs", "fn main() {}") .file("examples/main.rs", "fn main() {}") .file("tests/main.rs", "fn main() {}") .build(); assert_that(p.cargo("doc"), execs().with_status(0)); } #[test] fn doc_target() { const TARGET: &'static str = "arm-unknown-linux-gnueabihf"; let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/lib.rs", r#" #![feature(no_core)] #![no_core] extern { pub static A: u32; } "#) .build(); assert_that(p.cargo("doc").arg("--target").arg(TARGET).arg("--verbose"), execs().with_status(0)); assert_that(&p.root().join(&format!("target/{}/doc", TARGET)), existing_dir()); assert_that(&p.root().join(&format!("target/{}/doc/foo/index.html", TARGET)), existing_file()); } #[test] fn target_specific_not_documented() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [target.foo.dependencies] a = { path = "a" } "#) .file("src/lib.rs", "") .file("a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" authors = [] "#) .file("a/src/lib.rs", "not rust") .build(); assert_that(p.cargo("doc"), execs().with_status(0)); } #[test] fn output_not_captured() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies] a = { path = "a" } "#) .file("src/lib.rs", "") .file("a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" authors = [] "#) .file("a/src/lib.rs", " /// ``` /// ☃ /// ``` pub fn foo() {} ") .build(); let error = p.cargo("doc").exec_with_output().err().unwrap(); if let Ok(perr) = error.downcast::() { let output = perr.output.unwrap(); let stderr = str::from_utf8(&output.stderr).unwrap(); assert!(stderr.contains("☃"), "no snowman\n{}", stderr); assert!(stderr.contains("unknown start of token"), "no message{}", stderr); } else { assert!(false, "an error kind other than ProcessErrorKind was encountered"); } } #[test] fn target_specific_documented() { let p = project("foo") .file("Cargo.toml", &format!(r#" [package] name = "foo" version = "0.0.1" authors = [] [target.foo.dependencies] a = {{ path = "a" }} [target.{}.dependencies] a = {{ path = "a" }} "#, rustc_host())) .file("src/lib.rs", " extern crate a; /// test pub fn foo() {} ") .file("a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" authors = [] "#) .file("a/src/lib.rs", " /// test pub fn foo() {} ") .build(); assert_that(p.cargo("doc"), execs().with_status(0)); } #[test] fn no_document_build_deps() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [build-dependencies] a = { path = "a" } "#) .file("src/lib.rs", " pub fn foo() {} ") .file("a/Cargo.toml", r#" [package] name = "a" version = "0.0.1" authors = [] "#) .file("a/src/lib.rs", " /// ``` /// ☃ /// ``` pub fn foo() {} ") .build(); assert_that(p.cargo("doc"), execs().with_status(0)); } #[test] fn doc_release() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/lib.rs", "") .build(); assert_that(p.cargo("build").arg("--release"), execs().with_status(0)); assert_that(p.cargo("doc").arg("--release").arg("-v"), execs().with_status(0) .with_stderr("\ [DOCUMENTING] foo v0.0.1 ([..]) [RUNNING] `rustdoc [..] src[/]lib.rs [..]` [FINISHED] release [optimized] target(s) in [..] ")); } #[test] fn doc_multiple_deps() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] path = "bar" [dependencies.baz] path = "baz" "#) .file("src/lib.rs", r#" extern crate bar; pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .file("baz/Cargo.toml", r#" [package] name = "baz" version = "0.0.1" authors = [] "#) .file("baz/src/lib.rs", r#" pub fn baz() {} "#) .build(); assert_that(p.cargo("doc") .arg("-p").arg("bar") .arg("-p").arg("baz") .arg("-v"), execs().with_status(0)); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/bar/index.html"), existing_file()); assert_that(&p.root().join("target/doc/baz/index.html"), existing_file()); } #[test] fn features() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [dependencies.bar] path = "bar" [features] foo = ["bar/bar"] "#) .file("src/lib.rs", r#" #[cfg(feature = "foo")] pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] [features] bar = [] "#) .file("bar/build.rs", r#" fn main() { println!("cargo:rustc-cfg=bar"); } "#) .file("bar/src/lib.rs", r#" #[cfg(feature = "bar")] pub fn bar() {} "#) .build(); assert_that(p.cargo("doc").arg("--features").arg("foo"), execs().with_status(0)); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that(&p.root().join("target/doc/foo/fn.foo.html"), existing_file()); assert_that(&p.root().join("target/doc/bar/fn.bar.html"), existing_file()); } #[test] fn rerun_when_dir_removed() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/lib.rs", r#" /// dox pub fn foo() {} "#) .build(); assert_that(p.cargo("doc"), execs().with_status(0)); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); fs::remove_dir_all(p.root().join("target/doc/foo")).unwrap(); assert_that(p.cargo("doc"), execs().with_status(0)); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); } #[test] fn document_only_lib() { let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] "#) .file("src/lib.rs", r#" /// dox pub fn foo() {} "#) .file("src/bin/bar.rs", r#" /// ``` /// ☃ /// ``` pub fn foo() {} fn main() { foo(); } "#) .build(); assert_that(p.cargo("doc").arg("--lib"), execs().with_status(0)); assert_that(&p.root().join("target/doc/foo/index.html"), existing_file()); } #[test] fn plugins_no_use_target() { if !cargotest::is_nightly() { return } let p = project("foo") .file("Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] [lib] proc-macro = true "#) .file("src/lib.rs", "") .build(); assert_that(p.cargo("doc") .arg("--target=x86_64-unknown-openbsd") .arg("-v"), execs().with_status(0)); } #[test] fn doc_all_workspace() { let p = project("foo") .file("Cargo.toml", r#" [project] name = "foo" version = "0.1.0" [dependencies] bar = { path = "bar" } [workspace] "#) .file("src/main.rs", r#" fn main() {} "#) .file("bar/Cargo.toml", r#" [project] name = "bar" version = "0.1.0" "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .build(); // The order in which bar is compiled or documented is not deterministic assert_that(p.cargo("doc") .arg("--all"), execs().with_status(0) .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])") .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])")); } #[test] fn doc_all_virtual_manifest() { let p = project("workspace") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [project] name = "foo" version = "0.1.0" "#) .file("foo/src/lib.rs", r#" pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [project] name = "bar" version = "0.1.0" "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .build(); // The order in which foo and bar are documented is not guaranteed assert_that(p.cargo("doc") .arg("--all"), execs().with_status(0) .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])")); } #[test] fn doc_virtual_manifest_all_implied() { let p = project("workspace") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [project] name = "foo" version = "0.1.0" "#) .file("foo/src/lib.rs", r#" pub fn foo() {} "#) .file("bar/Cargo.toml", r#" [project] name = "bar" version = "0.1.0" "#) .file("bar/src/lib.rs", r#" pub fn bar() {} "#) .build(); // The order in which foo and bar are documented is not guaranteed assert_that(p.cargo("doc"), execs().with_status(0) .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])")); } #[test] fn doc_all_member_dependency_same_name() { let p = project("workspace") .file("Cargo.toml", r#" [workspace] members = ["a"] "#) .file("a/Cargo.toml", r#" [project] name = "a" version = "0.1.0" [dependencies] a = "0.1.0" "#) .file("a/src/lib.rs", r#" pub fn a() {} "#) .build(); Package::new("a", "0.1.0").publish(); assert_that(p.cargo("doc") .arg("--all"), execs().with_status(0) .with_stderr_contains("[..] Updating registry `[..]`") .with_stderr_contains("[..] Documenting a v0.1.0 ([..])")); } #[test] fn doc_workspace_open_help_message() { let p = project("foo") .file("Cargo.toml", r#" [workspace] members = ["foo", "bar"] "#) .file("foo/Cargo.toml", r#" [package] name = "foo" version = "0.1.0" "#) .file("foo/src/lib.rs", "") .file("bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" "#) .file("bar/src/lib.rs", "") .build(); // The order in which bar is compiled or documented is not deterministic assert_that(p.cargo("doc") .arg("--all") .arg("--open"), execs().with_status(101) .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") .with_stderr_contains("error: Passing multiple packages and `open` is not supported.") .with_stderr_contains("Please re-run this command with `-p ` where `` is one of the following:") .with_stderr_contains(" foo") .with_stderr_contains(" bar")); }