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#"-> 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