]>
Commit | Line | Data |
---|---|---|
3c0e092e XL |
1 | extern crate cargo_metadata; |
2 | extern crate semver; | |
3 | #[macro_use] | |
4 | extern crate serde_json; | |
5 | ||
6 | use camino::Utf8PathBuf; | |
781aab86 | 7 | use cargo_metadata::{ |
ed00b5ec FG |
8 | workspace_default_members_is_missing, ArtifactDebuginfo, CargoOpt, DependencyKind, Edition, |
9 | Message, Metadata, MetadataCommand, | |
781aab86 | 10 | }; |
3c0e092e | 11 | |
ed00b5ec FG |
12 | /// Output from oldest version ever supported (1.24). |
13 | /// | |
14 | /// This intentionally has as many null fields as possible. | |
15 | /// 1.8 is when metadata was introduced. | |
16 | /// Older versions not supported because the following are required: | |
17 | /// - `workspace_members` added in 1.13 | |
18 | /// - `target_directory` added in 1.19 | |
19 | /// - `workspace_root` added in 1.24 | |
20 | const JSON_OLD_MINIMAL: &str = r#" | |
3c0e092e XL |
21 | { |
22 | "packages": [ | |
23 | { | |
24 | "name": "foo", | |
25 | "version": "0.1.0", | |
26 | "id": "foo 0.1.0 (path+file:///foo)", | |
27 | "license": null, | |
28 | "license_file": null, | |
29 | "description": null, | |
30 | "source": null, | |
31 | "dependencies": [ | |
32 | { | |
33 | "name": "somedep", | |
34 | "source": null, | |
35 | "req": "^1.0", | |
36 | "kind": null, | |
37 | "optional": false, | |
38 | "uses_default_features": true, | |
39 | "features": [], | |
40 | "target": null | |
41 | } | |
42 | ], | |
43 | "targets": [ | |
44 | { | |
45 | "kind": [ | |
46 | "bin" | |
47 | ], | |
48 | "crate_types": [ | |
49 | "bin" | |
50 | ], | |
51 | "name": "foo", | |
52 | "src_path": "/foo/src/main.rs" | |
53 | } | |
54 | ], | |
55 | "features": {}, | |
56 | "manifest_path": "/foo/Cargo.toml" | |
57 | } | |
58 | ], | |
59 | "workspace_members": [ | |
60 | "foo 0.1.0 (path+file:///foo)" | |
61 | ], | |
62 | "resolve": null, | |
63 | "target_directory": "/foo/target", | |
64 | "version": 1, | |
65 | "workspace_root": "/foo" | |
66 | } | |
67 | "#; | |
ed00b5ec FG |
68 | |
69 | #[test] | |
70 | fn old_minimal() { | |
71 | let meta: Metadata = serde_json::from_str(JSON_OLD_MINIMAL).unwrap(); | |
3c0e092e XL |
72 | assert_eq!(meta.packages.len(), 1); |
73 | let pkg = &meta.packages[0]; | |
74 | assert_eq!(pkg.name, "foo"); | |
75 | assert_eq!(pkg.version, semver::Version::parse("0.1.0").unwrap()); | |
76 | assert_eq!(pkg.authors.len(), 0); | |
77 | assert_eq!(pkg.id.to_string(), "foo 0.1.0 (path+file:///foo)"); | |
78 | assert_eq!(pkg.description, None); | |
79 | assert_eq!(pkg.license, None); | |
80 | assert_eq!(pkg.license_file, None); | |
81 | assert_eq!(pkg.default_run, None); | |
82 | assert_eq!(pkg.rust_version, None); | |
83 | assert_eq!(pkg.dependencies.len(), 1); | |
84 | let dep = &pkg.dependencies[0]; | |
85 | assert_eq!(dep.name, "somedep"); | |
86 | assert_eq!(dep.source, None); | |
87 | assert_eq!(dep.req, semver::VersionReq::parse("^1.0").unwrap()); | |
88 | assert_eq!(dep.kind, DependencyKind::Normal); | |
064997fb FG |
89 | assert!(!dep.optional); |
90 | assert!(dep.uses_default_features); | |
3c0e092e XL |
91 | assert_eq!(dep.features.len(), 0); |
92 | assert!(dep.target.is_none()); | |
93 | assert_eq!(dep.rename, None); | |
94 | assert_eq!(dep.registry, None); | |
95 | assert_eq!(pkg.targets.len(), 1); | |
96 | let target = &pkg.targets[0]; | |
97 | assert_eq!(target.name, "foo"); | |
98 | assert_eq!(target.kind, vec!["bin"]); | |
99 | assert_eq!(target.crate_types, vec!["bin"]); | |
100 | assert_eq!(target.required_features.len(), 0); | |
101 | assert_eq!(target.src_path, "/foo/src/main.rs"); | |
064997fb FG |
102 | assert_eq!(target.edition, Edition::E2015); |
103 | assert!(target.doctest); | |
104 | assert!(target.test); | |
105 | assert!(target.doc); | |
3c0e092e XL |
106 | assert_eq!(pkg.features.len(), 0); |
107 | assert_eq!(pkg.manifest_path, "/foo/Cargo.toml"); | |
108 | assert_eq!(pkg.categories.len(), 0); | |
109 | assert_eq!(pkg.keywords.len(), 0); | |
110 | assert_eq!(pkg.readme, None); | |
111 | assert_eq!(pkg.repository, None); | |
112 | assert_eq!(pkg.homepage, None); | |
113 | assert_eq!(pkg.documentation, None); | |
064997fb | 114 | assert_eq!(pkg.edition, Edition::E2015); |
3c0e092e XL |
115 | assert_eq!(pkg.metadata, serde_json::Value::Null); |
116 | assert_eq!(pkg.links, None); | |
117 | assert_eq!(pkg.publish, None); | |
118 | assert_eq!(meta.workspace_members.len(), 1); | |
119 | assert_eq!( | |
120 | meta.workspace_members[0].to_string(), | |
121 | "foo 0.1.0 (path+file:///foo)" | |
122 | ); | |
123 | assert!(meta.resolve.is_none()); | |
124 | assert_eq!(meta.workspace_root, "/foo"); | |
125 | assert_eq!(meta.workspace_metadata, serde_json::Value::Null); | |
126 | assert_eq!(meta.target_directory, "/foo/target"); | |
ed00b5ec FG |
127 | |
128 | assert!(workspace_default_members_is_missing( | |
129 | &meta.workspace_default_members | |
130 | )); | |
131 | let serialized = serde_json::to_value(meta).unwrap(); | |
132 | assert!(!serialized | |
133 | .as_object() | |
134 | .unwrap() | |
135 | .contains_key("workspace_default_members")); | |
3c0e092e XL |
136 | } |
137 | ||
138 | macro_rules! sorted { | |
139 | ($e:expr) => {{ | |
140 | let mut v = $e.clone(); | |
141 | v.sort(); | |
142 | v | |
143 | }}; | |
144 | } | |
145 | ||
146 | fn cargo_version() -> semver::Version { | |
147 | let output = std::process::Command::new("cargo") | |
148 | .arg("-V") | |
149 | .output() | |
150 | .expect("Failed to exec cargo."); | |
151 | let out = std::str::from_utf8(&output.stdout) | |
152 | .expect("invalid utf8") | |
153 | .trim(); | |
154 | let split: Vec<&str> = out.split_whitespace().collect(); | |
155 | assert!(split.len() >= 2, "cargo -V output is unexpected: {}", out); | |
156 | let mut ver = semver::Version::parse(split[1]).expect("cargo -V semver could not be parsed"); | |
157 | // Don't care about metadata, it is awkward to compare. | |
158 | ver.pre = semver::Prerelease::EMPTY; | |
159 | ver.build = semver::BuildMetadata::EMPTY; | |
160 | ver | |
161 | } | |
162 | ||
163 | #[derive(serde::Deserialize, PartialEq, Eq, Debug)] | |
164 | struct WorkspaceMetadata { | |
165 | testobject: TestObject, | |
166 | } | |
167 | ||
168 | #[derive(serde::Deserialize, PartialEq, Eq, Debug)] | |
169 | struct TestObject { | |
170 | myvalue: String, | |
171 | } | |
172 | ||
173 | #[test] | |
174 | fn all_the_fields() { | |
5e7ed085 | 175 | // All the fields currently generated as of 1.60. This tries to exercise as |
3c0e092e XL |
176 | // much as possible. |
177 | let ver = cargo_version(); | |
178 | let minimum = semver::Version::parse("1.56.0").unwrap(); | |
179 | if ver < minimum { | |
180 | // edition added in 1.30 | |
181 | // rename added in 1.31 | |
182 | // links added in 1.33 | |
183 | // doctest added in 1.37 | |
184 | // publish added in 1.39 | |
185 | // dep_kinds added in 1.41 | |
186 | // test added in 1.47 | |
187 | // homepage added in 1.49 | |
188 | // documentation added in 1.49 | |
189 | // doc added in 1.50 | |
190 | // path added in 1.51 | |
191 | // default_run added in 1.55 | |
192 | // rust_version added in 1.58 | |
ed00b5ec | 193 | // workspace_default_members added in 1.71 |
3c0e092e XL |
194 | eprintln!("Skipping all_the_fields test, cargo {} is too old.", ver); |
195 | return; | |
196 | } | |
197 | let meta = MetadataCommand::new() | |
198 | .manifest_path("tests/all/Cargo.toml") | |
199 | .exec() | |
200 | .unwrap(); | |
201 | assert_eq!(meta.workspace_root.file_name().unwrap(), "all"); | |
202 | assert_eq!( | |
ed00b5ec | 203 | serde_json::from_value::<WorkspaceMetadata>(meta.workspace_metadata.clone()).unwrap(), |
3c0e092e XL |
204 | WorkspaceMetadata { |
205 | testobject: TestObject { | |
206 | myvalue: "abc".to_string() | |
207 | } | |
208 | } | |
209 | ); | |
210 | assert_eq!(meta.workspace_members.len(), 1); | |
211 | assert!(meta.workspace_members[0].to_string().starts_with("all")); | |
ed00b5ec FG |
212 | if ver >= semver::Version::parse("1.71.0").unwrap() { |
213 | assert_eq!(&*meta.workspace_default_members, &meta.workspace_members); | |
214 | } | |
3c0e092e XL |
215 | |
216 | assert_eq!(meta.packages.len(), 9); | |
217 | let all = meta.packages.iter().find(|p| p.name == "all").unwrap(); | |
218 | assert_eq!(all.version, semver::Version::parse("0.1.0").unwrap()); | |
219 | assert_eq!(all.authors, vec!["Jane Doe <user@example.com>"]); | |
220 | assert!(all.id.to_string().starts_with("all")); | |
221 | assert_eq!(all.description, Some("Package description.".to_string())); | |
222 | assert_eq!(all.license, Some("MIT/Apache-2.0".to_string())); | |
223 | assert_eq!(all.license_file, Some(Utf8PathBuf::from("LICENSE"))); | |
224 | assert!(all.license_file().unwrap().ends_with("tests/all/LICENSE")); | |
225 | assert_eq!(all.publish, Some(vec![])); | |
226 | assert_eq!(all.links, Some("foo".to_string())); | |
227 | assert_eq!(all.default_run, Some("otherbin".to_string())); | |
228 | if ver >= semver::Version::parse("1.58.0").unwrap() { | |
229 | assert_eq!( | |
230 | all.rust_version, | |
781aab86 | 231 | Some(semver::Version::parse("1.56.0").unwrap()) |
3c0e092e XL |
232 | ); |
233 | } | |
234 | ||
235 | assert_eq!(all.dependencies.len(), 8); | |
236 | let bitflags = all | |
237 | .dependencies | |
238 | .iter() | |
239 | .find(|d| d.name == "bitflags") | |
240 | .unwrap(); | |
241 | assert_eq!( | |
242 | bitflags.source, | |
243 | Some("registry+https://github.com/rust-lang/crates.io-index".to_string()) | |
244 | ); | |
064997fb | 245 | assert!(bitflags.optional); |
3c0e092e XL |
246 | assert_eq!(bitflags.req, semver::VersionReq::parse("^1.0").unwrap()); |
247 | ||
248 | let path_dep = all | |
249 | .dependencies | |
250 | .iter() | |
251 | .find(|d| d.name == "path-dep") | |
252 | .unwrap(); | |
253 | assert_eq!(path_dep.source, None); | |
254 | assert_eq!(path_dep.kind, DependencyKind::Normal); | |
255 | assert_eq!(path_dep.req, semver::VersionReq::parse("*").unwrap()); | |
256 | assert_eq!( | |
257 | path_dep.path.as_ref().map(|p| p.ends_with("path-dep")), | |
258 | Some(true), | |
259 | ); | |
260 | ||
261 | all.dependencies | |
262 | .iter() | |
263 | .find(|d| d.name == "namedep") | |
264 | .unwrap(); | |
265 | ||
266 | let featdep = all | |
267 | .dependencies | |
268 | .iter() | |
269 | .find(|d| d.name == "featdep") | |
270 | .unwrap(); | |
271 | assert_eq!(featdep.features, vec!["i128"]); | |
064997fb | 272 | assert!(!featdep.uses_default_features); |
3c0e092e XL |
273 | |
274 | let renamed = all | |
275 | .dependencies | |
276 | .iter() | |
277 | .find(|d| d.name == "oldname") | |
278 | .unwrap(); | |
279 | assert_eq!(renamed.rename, Some("newname".to_string())); | |
280 | ||
281 | let devdep = all | |
282 | .dependencies | |
283 | .iter() | |
284 | .find(|d| d.name == "devdep") | |
285 | .unwrap(); | |
286 | assert_eq!(devdep.kind, DependencyKind::Development); | |
287 | ||
288 | let bdep = all.dependencies.iter().find(|d| d.name == "bdep").unwrap(); | |
289 | assert_eq!(bdep.kind, DependencyKind::Build); | |
290 | ||
291 | let windep = all | |
292 | .dependencies | |
293 | .iter() | |
294 | .find(|d| d.name == "windep") | |
295 | .unwrap(); | |
296 | assert_eq!( | |
297 | windep.target.as_ref().map(|x| x.to_string()), | |
298 | Some("cfg(windows)".to_string()) | |
299 | ); | |
300 | ||
301 | macro_rules! get_file_name { | |
302 | ($v:expr) => { | |
303 | all.targets | |
304 | .iter() | |
305 | .find(|t| t.src_path.file_name().unwrap() == $v) | |
306 | .unwrap() | |
307 | }; | |
308 | } | |
309 | assert_eq!(all.targets.len(), 8); | |
310 | let lib = get_file_name!("lib.rs"); | |
311 | assert_eq!(lib.name, "all"); | |
5e7ed085 | 312 | assert_eq!(sorted!(lib.kind), vec!["cdylib", "rlib", "staticlib"]); |
3c0e092e XL |
313 | assert_eq!( |
314 | sorted!(lib.crate_types), | |
5e7ed085 | 315 | vec!["cdylib", "rlib", "staticlib"] |
3c0e092e XL |
316 | ); |
317 | assert_eq!(lib.required_features.len(), 0); | |
064997fb FG |
318 | assert_eq!(lib.edition, Edition::E2018); |
319 | assert!(lib.doctest); | |
320 | assert!(lib.test); | |
321 | assert!(lib.doc); | |
3c0e092e XL |
322 | |
323 | let main = get_file_name!("main.rs"); | |
324 | assert_eq!(main.crate_types, vec!["bin"]); | |
325 | assert_eq!(main.kind, vec!["bin"]); | |
064997fb FG |
326 | assert!(!main.doctest); |
327 | assert!(main.test); | |
328 | assert!(main.doc); | |
3c0e092e XL |
329 | |
330 | let otherbin = get_file_name!("otherbin.rs"); | |
064997fb FG |
331 | assert_eq!(otherbin.edition, Edition::E2015); |
332 | assert!(!otherbin.doc); | |
3c0e092e XL |
333 | |
334 | let reqfeat = get_file_name!("reqfeat.rs"); | |
335 | assert_eq!(reqfeat.required_features, vec!["feat2"]); | |
336 | ||
337 | let ex1 = get_file_name!("ex1.rs"); | |
338 | assert_eq!(ex1.kind, vec!["example"]); | |
064997fb | 339 | assert!(!ex1.test); |
3c0e092e XL |
340 | |
341 | let t1 = get_file_name!("t1.rs"); | |
342 | assert_eq!(t1.kind, vec!["test"]); | |
343 | ||
344 | let b1 = get_file_name!("b1.rs"); | |
345 | assert_eq!(b1.kind, vec!["bench"]); | |
346 | ||
347 | let build = get_file_name!("build.rs"); | |
348 | assert_eq!(build.kind, vec!["custom-build"]); | |
349 | ||
5e7ed085 FG |
350 | if ver >= semver::Version::parse("1.60.0").unwrap() { |
351 | // 1.60 now reports optional dependencies within the features table | |
352 | assert_eq!(all.features.len(), 4); | |
353 | assert_eq!(all.features["bitflags"], vec!["dep:bitflags"]); | |
354 | } else { | |
355 | assert_eq!(all.features.len(), 3); | |
356 | } | |
3c0e092e XL |
357 | assert_eq!(all.features["feat1"].len(), 0); |
358 | assert_eq!(all.features["feat2"].len(), 0); | |
359 | assert_eq!(sorted!(all.features["default"]), vec!["bitflags", "feat1"]); | |
360 | ||
361 | assert!(all.manifest_path.ends_with("all/Cargo.toml")); | |
362 | assert_eq!(all.categories, vec!["command-line-utilities"]); | |
363 | assert_eq!(all.keywords, vec!["cli"]); | |
364 | assert_eq!(all.readme, Some(Utf8PathBuf::from("README.md"))); | |
9ffffee4 | 365 | assert!(all.readme().unwrap().ends_with("tests/all/README.md")); |
3c0e092e XL |
366 | assert_eq!( |
367 | all.repository, | |
368 | Some("https://github.com/oli-obk/cargo_metadata/".to_string()) | |
369 | ); | |
370 | assert_eq!( | |
371 | all.homepage, | |
372 | Some("https://github.com/oli-obk/cargo_metadata/".to_string()) | |
373 | ); | |
374 | assert_eq!( | |
375 | all.documentation, | |
376 | Some("https://docs.rs/cargo_metadata/".to_string()) | |
377 | ); | |
064997fb | 378 | assert_eq!(all.edition, Edition::E2018); |
3c0e092e XL |
379 | assert_eq!( |
380 | all.metadata, | |
381 | json!({ | |
382 | "docs": { | |
383 | "rs": { | |
384 | "all-features": true, | |
385 | "default-target": "x86_64-unknown-linux-gnu", | |
386 | "rustc-args": ["--example-rustc-arg"] | |
387 | } | |
388 | } | |
389 | }) | |
390 | ); | |
391 | ||
392 | let resolve = meta.resolve.as_ref().unwrap(); | |
393 | assert!(resolve | |
394 | .root | |
395 | .as_ref() | |
396 | .unwrap() | |
397 | .to_string() | |
398 | .starts_with("all")); | |
399 | ||
400 | assert_eq!(resolve.nodes.len(), 9); | |
401 | let path_dep = resolve | |
402 | .nodes | |
403 | .iter() | |
404 | .find(|n| n.id.to_string().starts_with("path-dep")) | |
405 | .unwrap(); | |
406 | assert_eq!(path_dep.deps.len(), 0); | |
407 | assert_eq!(path_dep.dependencies.len(), 0); | |
408 | assert_eq!(path_dep.features.len(), 0); | |
409 | ||
410 | let bitflags = resolve | |
411 | .nodes | |
412 | .iter() | |
413 | .find(|n| n.id.to_string().starts_with("bitflags")) | |
414 | .unwrap(); | |
415 | assert_eq!(bitflags.features, vec!["default"]); | |
416 | ||
417 | let featdep = resolve | |
418 | .nodes | |
419 | .iter() | |
420 | .find(|n| n.id.to_string().starts_with("featdep")) | |
421 | .unwrap(); | |
422 | assert_eq!(featdep.features, vec!["i128"]); | |
423 | ||
424 | let all = resolve | |
425 | .nodes | |
426 | .iter() | |
427 | .find(|n| n.id.to_string().starts_with("all")) | |
428 | .unwrap(); | |
429 | assert_eq!(all.dependencies.len(), 8); | |
430 | assert_eq!(all.deps.len(), 8); | |
431 | let newname = all.deps.iter().find(|d| d.name == "newname").unwrap(); | |
432 | assert!(newname.pkg.to_string().starts_with("oldname")); | |
433 | // Note the underscore here. | |
434 | let path_dep = all.deps.iter().find(|d| d.name == "path_dep").unwrap(); | |
435 | assert!(path_dep.pkg.to_string().starts_with("path-dep")); | |
436 | assert_eq!(path_dep.dep_kinds.len(), 1); | |
437 | let kind = &path_dep.dep_kinds[0]; | |
438 | assert_eq!(kind.kind, DependencyKind::Normal); | |
439 | assert!(kind.target.is_none()); | |
440 | ||
441 | let namedep = all | |
442 | .deps | |
443 | .iter() | |
444 | .find(|d| d.name == "different_name") | |
445 | .unwrap(); | |
446 | assert!(namedep.pkg.to_string().starts_with("namedep")); | |
447 | assert_eq!(sorted!(all.features), vec!["bitflags", "default", "feat1"]); | |
448 | ||
449 | let bdep = all.deps.iter().find(|d| d.name == "bdep").unwrap(); | |
450 | assert_eq!(bdep.dep_kinds.len(), 1); | |
451 | let kind = &bdep.dep_kinds[0]; | |
452 | assert_eq!(kind.kind, DependencyKind::Build); | |
453 | assert!(kind.target.is_none()); | |
454 | ||
455 | let devdep = all.deps.iter().find(|d| d.name == "devdep").unwrap(); | |
456 | assert_eq!(devdep.dep_kinds.len(), 1); | |
457 | let kind = &devdep.dep_kinds[0]; | |
458 | assert_eq!(kind.kind, DependencyKind::Development); | |
459 | assert!(kind.target.is_none()); | |
460 | ||
461 | let windep = all.deps.iter().find(|d| d.name == "windep").unwrap(); | |
462 | assert_eq!(windep.dep_kinds.len(), 1); | |
463 | let kind = &windep.dep_kinds[0]; | |
464 | assert_eq!(kind.kind, DependencyKind::Normal); | |
465 | assert_eq!( | |
466 | kind.target.as_ref().map(|x| x.to_string()), | |
467 | Some("cfg(windows)".to_string()) | |
468 | ); | |
ed00b5ec FG |
469 | |
470 | let serialized = serde_json::to_value(meta).unwrap(); | |
471 | if ver >= semver::Version::parse("1.71.0").unwrap() { | |
472 | assert!(serialized.as_object().unwrap()["workspace_default_members"] | |
473 | .as_array() | |
474 | .is_some()); | |
475 | } else { | |
476 | assert!(!serialized | |
477 | .as_object() | |
478 | .unwrap() | |
479 | .contains_key("workspace_default_members")); | |
480 | } | |
3c0e092e XL |
481 | } |
482 | ||
483 | #[test] | |
484 | fn alt_registry() { | |
485 | // This is difficult to test (would need to set up a custom index). | |
486 | // Just manually check the JSON is handled. | |
487 | let json = r#" | |
488 | { | |
489 | "packages": [ | |
490 | { | |
491 | "name": "alt", | |
492 | "version": "0.1.0", | |
493 | "id": "alt 0.1.0 (path+file:///alt)", | |
494 | "source": null, | |
495 | "dependencies": [ | |
496 | { | |
497 | "name": "alt2", | |
498 | "source": "registry+https://example.com", | |
499 | "req": "^0.1", | |
500 | "kind": null, | |
501 | "rename": null, | |
502 | "optional": false, | |
503 | "uses_default_features": true, | |
504 | "features": [], | |
505 | "target": null, | |
506 | "registry": "https://example.com" | |
507 | } | |
508 | ], | |
509 | "targets": [ | |
510 | { | |
511 | "kind": [ | |
512 | "lib" | |
513 | ], | |
514 | "crate_types": [ | |
515 | "lib" | |
516 | ], | |
517 | "name": "alt", | |
518 | "src_path": "/alt/src/lib.rs", | |
519 | "edition": "2018" | |
520 | } | |
521 | ], | |
522 | "features": {}, | |
523 | "manifest_path": "/alt/Cargo.toml", | |
524 | "metadata": null, | |
525 | "authors": [], | |
526 | "categories": [], | |
527 | "keywords": [], | |
528 | "readme": null, | |
529 | "repository": null, | |
530 | "edition": "2018", | |
531 | "links": null | |
532 | } | |
533 | ], | |
534 | "workspace_members": [ | |
535 | "alt 0.1.0 (path+file:///alt)" | |
536 | ], | |
537 | "resolve": null, | |
538 | "target_directory": "/alt/target", | |
539 | "version": 1, | |
540 | "workspace_root": "/alt" | |
541 | } | |
542 | "#; | |
543 | let meta: Metadata = serde_json::from_str(json).unwrap(); | |
544 | assert_eq!(meta.packages.len(), 1); | |
545 | let alt = &meta.packages[0]; | |
546 | let deps = &alt.dependencies; | |
547 | assert_eq!(deps.len(), 1); | |
548 | let dep = &deps[0]; | |
549 | assert_eq!(dep.registry, Some("https://example.com".to_string())); | |
550 | } | |
551 | ||
552 | #[test] | |
553 | fn current_dir() { | |
554 | let meta = MetadataCommand::new() | |
555 | .current_dir("tests/all/namedep") | |
556 | .exec() | |
557 | .unwrap(); | |
558 | let namedep = meta.packages.iter().find(|p| p.name == "namedep").unwrap(); | |
559 | assert!(namedep.name.starts_with("namedep")); | |
560 | } | |
561 | ||
562 | #[test] | |
563 | fn parse_stream_is_robust() { | |
564 | // Proc macros can print stuff to stdout, which naturally breaks JSON messages. | |
565 | // Let's check that we don't die horribly in this case, and report an error. | |
9ffffee4 | 566 | let json_output = r##"{"reason":"compiler-artifact","package_id":"chatty 0.1.0 (path+file:///chatty-macro/chatty)","manifest_path":"chatty-macro/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"chatty","src_path":"/chatty-macro/chatty/src/lib.rs","edition":"2018","doctest":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/chatty-macro/target/debug/deps/libchatty-f2adcff24cdf3bb2.so"],"executable":null,"fresh":false} |
3c0e092e | 567 | Evil proc macro was here! |
9ffffee4 | 568 | {"reason":"compiler-artifact","package_id":"chatty-macro 0.1.0 (path+file:///chatty-macro)","manifest_path":"chatty-macro/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"chatty-macro","src_path":"/chatty-macro/src/lib.rs","edition":"2018","doctest":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/chatty-macro/target/debug/libchatty_macro.rlib","/chatty-macro/target/debug/deps/libchatty_macro-cb5956ed52a11fb6.rmeta"],"executable":null,"fresh":false} |
3c0e092e XL |
569 | "##; |
570 | let mut n_messages = 0; | |
571 | let mut text = String::new(); | |
572 | for message in cargo_metadata::Message::parse_stream(json_output.as_bytes()) { | |
573 | let message = message.unwrap(); | |
574 | match message { | |
575 | cargo_metadata::Message::TextLine(line) => text = line, | |
576 | _ => n_messages += 1, | |
577 | } | |
578 | } | |
579 | assert_eq!(n_messages, 2); | |
580 | assert_eq!(text, "Evil proc macro was here!"); | |
581 | } | |
582 | ||
583 | #[test] | |
584 | fn advanced_feature_configuration() { | |
585 | fn build_features<F: FnOnce(&mut MetadataCommand) -> &mut MetadataCommand>( | |
586 | func: F, | |
587 | ) -> Vec<String> { | |
588 | let mut meta = MetadataCommand::new(); | |
589 | let meta = meta.manifest_path("tests/all/Cargo.toml"); | |
590 | ||
591 | let meta = func(meta); | |
592 | let meta = meta.exec().unwrap(); | |
593 | ||
594 | let resolve = meta.resolve.as_ref().unwrap(); | |
595 | ||
596 | let all = resolve | |
597 | .nodes | |
598 | .iter() | |
599 | .find(|n| n.id.to_string().starts_with("all")) | |
600 | .unwrap(); | |
601 | ||
602 | all.features.clone() | |
603 | } | |
604 | ||
605 | // Default behavior; tested above | |
606 | let default_features = build_features(|meta| meta); | |
607 | assert_eq!( | |
608 | sorted!(default_features), | |
609 | vec!["bitflags", "default", "feat1"] | |
610 | ); | |
611 | ||
612 | // Manually specify the same default features | |
613 | let manual_features = build_features(|meta| { | |
614 | meta.features(CargoOpt::NoDefaultFeatures) | |
615 | .features(CargoOpt::SomeFeatures(vec![ | |
616 | "feat1".into(), | |
617 | "bitflags".into(), | |
618 | ])) | |
619 | }); | |
620 | assert_eq!(sorted!(manual_features), vec!["bitflags", "feat1"]); | |
621 | ||
622 | // Multiple SomeFeatures is same as one longer SomeFeatures | |
623 | let manual_features = build_features(|meta| { | |
624 | meta.features(CargoOpt::NoDefaultFeatures) | |
625 | .features(CargoOpt::SomeFeatures(vec!["feat1".into()])) | |
626 | .features(CargoOpt::SomeFeatures(vec!["feat2".into()])) | |
627 | }); | |
628 | assert_eq!(sorted!(manual_features), vec!["feat1", "feat2"]); | |
629 | ||
630 | // No features + All features == All features | |
631 | let all_features = build_features(|meta| { | |
632 | meta.features(CargoOpt::AllFeatures) | |
633 | .features(CargoOpt::NoDefaultFeatures) | |
634 | }); | |
635 | assert_eq!( | |
636 | sorted!(all_features), | |
637 | vec!["bitflags", "default", "feat1", "feat2"] | |
638 | ); | |
639 | ||
640 | // The '--all-features' flag supersedes other feature flags | |
641 | let all_flag_variants = build_features(|meta| { | |
642 | meta.features(CargoOpt::SomeFeatures(vec!["feat2".into()])) | |
643 | .features(CargoOpt::NoDefaultFeatures) | |
644 | .features(CargoOpt::AllFeatures) | |
645 | }); | |
646 | assert_eq!(sorted!(all_flag_variants), sorted!(all_features)); | |
647 | } | |
648 | ||
649 | #[test] | |
650 | fn depkind_to_string() { | |
651 | assert_eq!(DependencyKind::Normal.to_string(), "normal"); | |
652 | assert_eq!(DependencyKind::Development.to_string(), "dev"); | |
653 | assert_eq!(DependencyKind::Build.to_string(), "build"); | |
654 | assert_eq!(DependencyKind::Unknown.to_string(), "Unknown"); | |
655 | } | |
9ffffee4 FG |
656 | |
657 | #[test] | |
658 | fn basic_workspace_root_package_exists() { | |
659 | // First try with dependencies | |
660 | let meta = MetadataCommand::new() | |
661 | .manifest_path("tests/basic_workspace/Cargo.toml") | |
662 | .exec() | |
663 | .unwrap(); | |
664 | assert_eq!(meta.root_package().unwrap().name, "ex_bin"); | |
665 | // Now with no_deps, it should still work exactly the same | |
666 | let meta = MetadataCommand::new() | |
667 | .manifest_path("tests/basic_workspace/Cargo.toml") | |
668 | .no_deps() | |
669 | .exec() | |
670 | .unwrap(); | |
671 | assert_eq!( | |
672 | meta.root_package() | |
673 | .expect("workspace root still exists when no_deps used") | |
674 | .name, | |
675 | "ex_bin" | |
676 | ); | |
677 | } | |
781aab86 FG |
678 | |
679 | #[test] | |
680 | fn debuginfo_variants() { | |
681 | // Checks behavior for the different debuginfo variants. | |
682 | let variants = [ | |
683 | ("0", ArtifactDebuginfo::None), | |
684 | ("1", ArtifactDebuginfo::Limited), | |
685 | ("2", ArtifactDebuginfo::Full), | |
686 | ( | |
687 | "\"line-directives-only\"", | |
688 | ArtifactDebuginfo::LineDirectivesOnly, | |
689 | ), | |
690 | ("\"line-tables-only\"", ArtifactDebuginfo::LineTablesOnly), | |
691 | ("3", ArtifactDebuginfo::UnknownInt(3)), | |
692 | ( | |
693 | "\"abc\"", | |
694 | ArtifactDebuginfo::UnknownString("abc".to_string()), | |
695 | ), | |
696 | ("null", ArtifactDebuginfo::None), | |
697 | ]; | |
698 | for (value, expected) in variants { | |
699 | let s = r#"{"reason":"compiler-artifact","package_id":"cargo_metadata 0.16.0 (path+file:////cargo_metadata)","manifest_path":"/cargo_metadata/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"cargo_metadata","src_path":"/cargo_metadata/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":DEBUGINFO,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/cargo_metadata/target/debug/deps/libcargo_metadata-27f582f7187b9a2c.rmeta"],"executable":null,"fresh":false}"#; | |
700 | let message: Message = serde_json::from_str(&s.replace("DEBUGINFO", value)).unwrap(); | |
701 | match message { | |
702 | Message::CompilerArtifact(artifact) => { | |
703 | assert_eq!(artifact.profile.debuginfo, expected); | |
704 | let de_s = serde_json::to_string(&artifact.profile.debuginfo).unwrap(); | |
705 | // Note: Roundtrip does not retain null value. | |
706 | if value == "null" { | |
707 | assert_eq!(artifact.profile.debuginfo.to_string(), "0"); | |
708 | assert_eq!(de_s, "0"); | |
709 | } else { | |
710 | assert_eq!( | |
711 | artifact.profile.debuginfo.to_string(), | |
712 | value.trim_matches('"') | |
713 | ); | |
714 | assert_eq!(de_s, value); | |
715 | } | |
716 | } | |
717 | _ => panic!("unexpected {:?}", message), | |
718 | } | |
719 | } | |
720 | } | |
ed00b5ec FG |
721 | |
722 | #[test] | |
723 | #[should_panic = "WorkspaceDefaultMembers should only be dereferenced on Cargo versions >= 1.71"] | |
724 | fn missing_workspace_default_members() { | |
725 | let meta: Metadata = serde_json::from_str(JSON_OLD_MINIMAL).unwrap(); | |
726 | let _ = &*meta.workspace_default_members; | |
727 | } |