1 //! Tests for the -Zrustdoc-map feature.
2 
3 use cargo_test_support::registry::{self, Package};
4 use cargo_test_support::{is_nightly, paths, project, Project};
5 
basic_project() -> Project6 fn basic_project() -> Project {
7     Package::new("bar", "1.0.0")
8         .file("src/lib.rs", "pub struct Straw;")
9         .publish();
10 
11     project()
12         .file(
13             "Cargo.toml",
14             r#"
15                 [package]
16                 name = "foo"
17                 version = "0.1.0"
18                 edition = "2018"
19 
20                 [dependencies]
21                 bar = "1.0"
22             "#,
23         )
24         .file(
25             "src/lib.rs",
26             r#"
27                 pub fn myfun() -> Option<bar::Straw> {
28                     None
29                 }
30             "#,
31         )
32         .build()
33 }
34 
35 #[cargo_test]
ignores_on_stable()36 fn ignores_on_stable() {
37     // Requires -Zrustdoc-map to use.
38     let p = basic_project();
39     p.cargo("doc -v --no-deps")
40         .with_stderr_does_not_contain("[..]--extern-html-root-url[..]")
41         .run();
42 }
43 
44 #[cargo_test]
simple()45 fn simple() {
46     // Basic test that it works with crates.io.
47     if !is_nightly() {
48         // --extern-html-root-url is unstable
49         return;
50     }
51     let p = basic_project();
52     p.cargo("doc -v --no-deps -Zrustdoc-map")
53         .masquerade_as_nightly_cargo()
54         .with_stderr_contains(
55             "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
56         )
57         .run();
58     let myfun = p.read_file("target/doc/foo/fn.myfun.html");
59     assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
60 }
61 
62 #[cargo_test]
63 // Broken, temporarily disable until https://github.com/rust-lang/rust/pull/82776 is resolved.
64 #[ignore]
std_docs()65 fn std_docs() {
66     // Mapping std docs somewhere else.
67     if !is_nightly() {
68         // --extern-html-root-url is unstable
69         return;
70     }
71     // For local developers, skip this test if docs aren't installed.
72     let docs = std::path::Path::new(&paths::sysroot()).join("share/doc/rust/html");
73     if !docs.exists() {
74         if cargo_util::is_ci() {
75             panic!("std docs are not installed, check that the rust-docs component is installed");
76         } else {
77             eprintln!(
78                 "documentation not found at {}, \
79                 skipping test (run `rustdoc component add rust-docs` to install",
80                 docs.display()
81             );
82             return;
83         }
84     }
85     let p = basic_project();
86     p.change_file(
87         ".cargo/config",
88         r#"
89             [doc.extern-map]
90             std = "local"
91         "#,
92     );
93     p.cargo("doc -v --no-deps -Zrustdoc-map")
94         .masquerade_as_nightly_cargo()
95         .with_stderr_contains("[RUNNING] `rustdoc [..]--crate-name foo [..]std=file://[..]")
96         .run();
97     let myfun = p.read_file("target/doc/foo/fn.myfun.html");
98     assert!(myfun.contains(r#"share/doc/rust/html/core/option/enum.Option.html""#));
99 
100     p.change_file(
101         ".cargo/config",
102         r#"
103             [doc.extern-map]
104             std = "https://example.com/rust/"
105         "#,
106     );
107     p.cargo("doc -v --no-deps -Zrustdoc-map")
108         .masquerade_as_nightly_cargo()
109         .with_stderr_contains(
110             "[RUNNING] `rustdoc [..]--crate-name foo [..]std=https://example.com/rust/[..]",
111         )
112         .run();
113     let myfun = p.read_file("target/doc/foo/fn.myfun.html");
114     assert!(myfun.contains(r#"href="https://example.com/rust/core/option/enum.Option.html""#));
115 }
116 
117 #[cargo_test]
renamed_dep()118 fn renamed_dep() {
119     // Handles renamed dependencies.
120     if !is_nightly() {
121         // --extern-html-root-url is unstable
122         return;
123     }
124     Package::new("bar", "1.0.0")
125         .file("src/lib.rs", "pub struct Straw;")
126         .publish();
127 
128     let p = project()
129         .file(
130             "Cargo.toml",
131             r#"
132                 [package]
133                 name = "foo"
134                 version = "0.1.0"
135                 edition = "2018"
136 
137                 [dependencies]
138                 groovy = { version = "1.0", package = "bar" }
139             "#,
140         )
141         .file(
142             "src/lib.rs",
143             r#"
144                 pub fn myfun() -> Option<groovy::Straw> {
145                     None
146                 }
147             "#,
148         )
149         .build();
150     p.cargo("doc -v --no-deps -Zrustdoc-map")
151         .masquerade_as_nightly_cargo()
152         .with_stderr_contains(
153             "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
154         )
155         .run();
156     let myfun = p.read_file("target/doc/foo/fn.myfun.html");
157     assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
158 }
159 
160 #[cargo_test]
lib_name()161 fn lib_name() {
162     // Handles lib name != package name.
163     if !is_nightly() {
164         // --extern-html-root-url is unstable
165         return;
166     }
167     Package::new("bar", "1.0.0")
168         .file(
169             "Cargo.toml",
170             r#"
171                 [package]
172                 name = "bar"
173                 version = "1.0.0"
174 
175                 [lib]
176                 name = "rumpelstiltskin"
177             "#,
178         )
179         .file("src/lib.rs", "pub struct Straw;")
180         .publish();
181 
182     let p = project()
183         .file(
184             "Cargo.toml",
185             r#"
186                 [package]
187                 name = "foo"
188                 version = "0.1.0"
189 
190                 [dependencies]
191                 bar = "1.0"
192             "#,
193         )
194         .file(
195             "src/lib.rs",
196             r#"
197                 pub fn myfun() -> Option<rumpelstiltskin::Straw> {
198                     None
199                 }
200             "#,
201         )
202         .build();
203     p.cargo("doc -v --no-deps -Zrustdoc-map")
204         .masquerade_as_nightly_cargo()
205         .with_stderr_contains(
206             "[RUNNING] `rustdoc [..]--crate-name foo [..]rumpelstiltskin=https://docs.rs/bar/1.0.0/[..]",
207         )
208         .run();
209     let myfun = p.read_file("target/doc/foo/fn.myfun.html");
210     assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/rumpelstiltskin/struct.Straw.html""#));
211 }
212 
213 #[cargo_test]
alt_registry()214 fn alt_registry() {
215     // Supports other registry names.
216     if !is_nightly() {
217         // --extern-html-root-url is unstable
218         return;
219     }
220     registry::alt_init();
221     Package::new("bar", "1.0.0")
222         .alternative(true)
223         .file(
224             "src/lib.rs",
225             r#"
226                 extern crate baz;
227                 pub struct Queen;
228                 pub use baz::King;
229             "#,
230         )
231         .registry_dep("baz", "1.0")
232         .publish();
233     Package::new("baz", "1.0.0")
234         .alternative(true)
235         .file("src/lib.rs", "pub struct King;")
236         .publish();
237     Package::new("grimm", "1.0.0")
238         .file("src/lib.rs", "pub struct Gold;")
239         .publish();
240 
241     let p = project()
242         .file(
243             "Cargo.toml",
244             r#"
245                 [package]
246                 name = "foo"
247                 version = "0.1.0"
248                 edition = "2018"
249 
250                 [dependencies]
251                 bar = { version = "1.0", registry="alternative" }
252                 grimm = "1.0"
253             "#,
254         )
255         .file(
256             "src/lib.rs",
257             r#"
258                 pub fn queen() -> bar::Queen { bar::Queen }
259                 pub fn king() -> bar::King { bar::King }
260                 pub fn gold() -> grimm::Gold { grimm::Gold }
261             "#,
262         )
263         .file(
264             ".cargo/config",
265             r#"
266                 [doc.extern-map.registries]
267                 alternative = "https://example.com/{pkg_name}/{version}/"
268                 crates-io = "https://docs.rs/"
269             "#,
270         )
271         .build();
272     p.cargo("doc -v --no-deps -Zrustdoc-map")
273         .masquerade_as_nightly_cargo()
274         .with_stderr_contains(
275             "[RUNNING] `rustdoc [..]--crate-name foo \
276             [..]bar=https://example.com/bar/1.0.0/[..]grimm=https://docs.rs/grimm/1.0.0/[..]",
277         )
278         .run();
279     let queen = p.read_file("target/doc/foo/fn.queen.html");
280     assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#));
281     // The king example fails to link. Rustdoc seems to want the origin crate
282     // name (baz) for re-exports. There are many issues in the issue tracker
283     // for rustdoc re-exports, so I'm not sure, but I think this is maybe a
284     // rustdoc issue. Alternatively, Cargo could provide mappings for all
285     // transitive dependencies to fix this.
286     let king = p.read_file("target/doc/foo/fn.king.html");
287     assert!(king.contains(r#"-&gt; King"#));
288 
289     let gold = p.read_file("target/doc/foo/fn.gold.html");
290     assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#));
291 }
292 
293 #[cargo_test]
multiple_versions()294 fn multiple_versions() {
295     // What happens when there are multiple versions.
296     // NOTE: This is currently broken behavior. Rustdoc does not provide a way
297     // to match renamed dependencies.
298     if !is_nightly() {
299         // --extern-html-root-url is unstable
300         return;
301     }
302     Package::new("bar", "1.0.0")
303         .file("src/lib.rs", "pub struct Spin;")
304         .publish();
305     Package::new("bar", "2.0.0")
306         .file("src/lib.rs", "pub struct Straw;")
307         .publish();
308     let p = project()
309         .file(
310             "Cargo.toml",
311             r#"
312                 [package]
313                 name = "foo"
314                 version = "0.1.0"
315                 edition = "2018"
316 
317                 [dependencies]
318                 bar = "1.0"
319                 bar2 = {version="2.0", package="bar"}
320             "#,
321         )
322         .file(
323             "src/lib.rs",
324             "
325                 pub fn fn1() -> bar::Spin {bar::Spin}
326                 pub fn fn2() -> bar2::Straw {bar2::Straw}
327             ",
328         )
329         .build();
330     p.cargo("doc -v --no-deps -Zrustdoc-map")
331         .masquerade_as_nightly_cargo()
332         .with_stderr_contains(
333             "[RUNNING] `rustdoc [..]--crate-name foo \
334             [..]bar=https://docs.rs/bar/1.0.0/[..]bar=https://docs.rs/bar/2.0.0/[..]",
335         )
336         .run();
337     let fn1 = p.read_file("target/doc/foo/fn.fn1.html");
338     // This should be 1.0.0, rustdoc seems to use the last entry when there
339     // are duplicates.
340     assert!(fn1.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Spin.html""#));
341     let fn2 = p.read_file("target/doc/foo/fn.fn2.html");
342     assert!(fn2.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Straw.html""#));
343 }
344 
345 #[cargo_test]
rebuilds_when_changing()346 fn rebuilds_when_changing() {
347     // Make sure it rebuilds if the map changes.
348     if !is_nightly() {
349         // --extern-html-root-url is unstable
350         return;
351     }
352     let p = basic_project();
353     p.cargo("doc -v --no-deps -Zrustdoc-map")
354         .masquerade_as_nightly_cargo()
355         .with_stderr_contains("[..]--extern-html-root-url[..]")
356         .run();
357 
358     // This also tests that the map for docs.rs can be overridden.
359     p.change_file(
360         ".cargo/config",
361         r#"
362             [doc.extern-map.registries]
363             crates-io = "https://example.com/"
364         "#,
365     );
366     p.cargo("doc -v --no-deps -Zrustdoc-map")
367         .masquerade_as_nightly_cargo()
368         .with_stderr_contains(
369             "[RUNNING] `rustdoc [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..]",
370         )
371         .run();
372 }
373