1use crate::{
5 errors::{FromMessagesError, RustBuildMetaParseError, WriteTestListError},
6 helpers::convert_rel_path_to_forward_slash,
7 list::{BinaryListState, OutputFormat, RustBuildMeta, Styles},
8 platform::BuildPlatforms,
9 write_str::WriteStr,
10};
11use camino::{Utf8Path, Utf8PathBuf};
12use cargo_metadata::{Artifact, BuildScript, Message, PackageId, TargetKind};
13use guppy::graph::PackageGraph;
14use nextest_metadata::{
15 BinaryListSummary, BuildPlatform, RustBinaryId, RustNonTestBinaryKind,
16 RustNonTestBinarySummary, RustTestBinaryKind, RustTestBinarySummary,
17};
18use owo_colors::OwoColorize;
19use serde::Deserialize;
20use std::{
21 collections::{BTreeMap, HashSet},
22 io,
23};
24use tracing::{debug, warn};
25
26#[derive(Clone, Debug)]
28pub struct RustTestBinary {
29 pub id: RustBinaryId,
31 pub path: Utf8PathBuf,
33 pub package_id: String,
35 pub kind: RustTestBinaryKind,
37 pub name: String,
39 pub build_platform: BuildPlatform,
42}
43
44#[derive(Clone, Debug)]
46pub struct BinaryList {
47 pub rust_build_meta: RustBuildMeta<BinaryListState>,
49
50 pub rust_binaries: Vec<RustTestBinary>,
52}
53
54impl BinaryList {
55 pub fn from_messages(
57 reader: impl io::BufRead,
58 graph: &PackageGraph,
59 build_platforms: BuildPlatforms,
60 ) -> Result<Self, FromMessagesError> {
61 let mut builder = BinaryListBuilder::new(graph, build_platforms);
62
63 for message in Message::parse_stream(reader) {
64 let message = message.map_err(FromMessagesError::ReadMessages)?;
65 builder.process_message(message)?;
66 }
67
68 Ok(builder.finish())
69 }
70
71 pub fn from_summary(summary: BinaryListSummary) -> Result<Self, RustBuildMetaParseError> {
73 let rust_binaries = summary
74 .rust_binaries
75 .into_values()
76 .map(|bin| RustTestBinary {
77 name: bin.binary_name,
78 path: bin.binary_path,
79 package_id: bin.package_id,
80 kind: bin.kind,
81 id: bin.binary_id,
82 build_platform: bin.build_platform,
83 })
84 .collect();
85 Ok(Self {
86 rust_build_meta: RustBuildMeta::from_summary(summary.rust_build_meta)?,
87 rust_binaries,
88 })
89 }
90
91 pub fn write(
93 &self,
94 output_format: OutputFormat,
95 writer: &mut dyn WriteStr,
96 colorize: bool,
97 ) -> Result<(), WriteTestListError> {
98 match output_format {
99 OutputFormat::Human { verbose } => self
100 .write_human(writer, verbose, colorize)
101 .map_err(WriteTestListError::Io),
102 OutputFormat::Oneline { verbose } => self
103 .write_oneline(writer, verbose, colorize)
104 .map_err(WriteTestListError::Io),
105 OutputFormat::Serializable(format) => format.to_writer(&self.to_summary(), writer),
106 }
107 }
108
109 fn to_summary(&self) -> BinaryListSummary {
110 BinaryListSummary {
111 rust_build_meta: self.rust_build_meta.to_summary(),
112 rust_binaries: self.binary_summaries(),
113 }
114 }
115
116 pub(crate) fn to_archive_summary(&self) -> BinaryListSummary {
123 let target_dir = &self.rust_build_meta.target_directory;
124 let build_directory = &self.rust_build_meta.build_directory;
125
126 let rust_binaries = self
127 .rust_binaries
128 .iter()
129 .map(|bin| {
130 let binary_path = target_dir.join(
134 bin.path
135 .strip_prefix(build_directory)
136 .expect("test binary paths must be within the build directory"),
137 );
138 let summary = RustTestBinarySummary {
139 binary_name: bin.name.clone(),
140 package_id: bin.package_id.clone(),
141 kind: bin.kind.clone(),
142 binary_path,
143 binary_id: bin.id.clone(),
144 build_platform: bin.build_platform,
145 };
146 (bin.id.clone(), summary)
147 })
148 .collect();
149
150 BinaryListSummary {
151 rust_build_meta: self.rust_build_meta.to_archive_summary(),
152 rust_binaries,
153 }
154 }
155
156 fn binary_summaries(&self) -> BTreeMap<RustBinaryId, RustTestBinarySummary> {
157 self.rust_binaries
158 .iter()
159 .map(|bin| {
160 let summary = RustTestBinarySummary {
161 binary_name: bin.name.clone(),
162 package_id: bin.package_id.clone(),
163 kind: bin.kind.clone(),
164 binary_path: bin.path.clone(),
165 binary_id: bin.id.clone(),
166 build_platform: bin.build_platform,
167 };
168 (bin.id.clone(), summary)
169 })
170 .collect()
171 }
172
173 fn write_human(
174 &self,
175 writer: &mut dyn WriteStr,
176 verbose: bool,
177 colorize: bool,
178 ) -> io::Result<()> {
179 let mut styles = Styles::default();
180 if colorize {
181 styles.colorize();
182 }
183 for bin in &self.rust_binaries {
184 if verbose {
185 writeln!(writer, "{}:", bin.id.style(styles.binary_id))?;
186 writeln!(writer, " {} {}", "bin:".style(styles.field), bin.path)?;
187 writeln!(
188 writer,
189 " {} {}",
190 "build platform:".style(styles.field),
191 bin.build_platform,
192 )?;
193 } else {
194 writeln!(writer, "{}", bin.id.style(styles.binary_id))?;
195 }
196 }
197 Ok(())
198 }
199
200 fn write_oneline(
201 &self,
202 writer: &mut dyn WriteStr,
203 verbose: bool,
204 colorize: bool,
205 ) -> io::Result<()> {
206 let mut styles = Styles::default();
207 if colorize {
208 styles.colorize();
209 }
210 for bin in &self.rust_binaries {
211 write!(writer, "{}", bin.id.style(styles.binary_id))?;
212 if verbose {
213 write!(
214 writer,
215 " [{}{}] [{}{}]",
216 "bin: ".style(styles.field),
217 bin.path,
218 "build platform: ".style(styles.field),
219 bin.build_platform,
220 )?;
221 }
222 writeln!(writer)?;
223 }
224 Ok(())
225 }
226
227 pub fn to_string(&self, output_format: OutputFormat) -> Result<String, WriteTestListError> {
229 let mut s = String::with_capacity(1024);
230 self.write(output_format, &mut s, false)?;
231 Ok(s)
232 }
233}
234
235#[derive(Debug)]
237pub struct BinaryListBuilder<'g> {
238 state: BinaryListBuildState<'g>,
239}
240
241impl<'g> BinaryListBuilder<'g> {
242 pub fn new(graph: &'g PackageGraph, build_platforms: BuildPlatforms) -> Self {
244 Self {
245 state: BinaryListBuildState::new(graph, build_platforms),
246 }
247 }
248
249 pub fn process_message(&mut self, message: Message) -> Result<(), FromMessagesError> {
251 self.state.process_message(message)
252 }
253
254 pub fn process_message_line(&mut self, line: &str) -> Result<(), FromMessagesError> {
259 self.process_message(parse_message_line(line))
260 }
261
262 pub fn finish(self) -> BinaryList {
264 self.state.finish()
265 }
266}
267
268fn parse_message_line(line: &str) -> Message {
270 let mut deserializer = serde_json::Deserializer::from_str(line);
271 deserializer.disable_recursion_limit();
272 Message::deserialize(&mut deserializer).unwrap_or_else(|_| Message::TextLine(line.to_owned()))
273}
274
275#[derive(Debug)]
276struct BinaryListBuildState<'g> {
277 graph: &'g PackageGraph,
278 rust_binaries: Vec<RustTestBinary>,
279 rust_build_meta: RustBuildMeta<BinaryListState>,
280 alt_target_dir: Option<Utf8PathBuf>,
281}
282
283impl<'g> BinaryListBuildState<'g> {
284 fn new(graph: &'g PackageGraph, build_platforms: BuildPlatforms) -> Self {
285 let rust_target_dir = graph.workspace().target_directory().to_path_buf();
286 let build_directory = graph
289 .workspace()
290 .build_directory()
291 .unwrap_or_else(|| graph.workspace().target_directory())
292 .to_path_buf();
293 let alt_target_dir = std::env::var("__NEXTEST_ALT_TARGET_DIR")
295 .ok()
296 .map(Utf8PathBuf::from);
297
298 Self {
299 graph,
300 rust_binaries: vec![],
301 rust_build_meta: RustBuildMeta::new(rust_target_dir, build_directory, build_platforms),
302 alt_target_dir,
303 }
304 }
305
306 fn process_message(&mut self, message: Message) -> Result<(), FromMessagesError> {
307 match message {
308 Message::CompilerArtifact(artifact) => {
309 self.process_artifact(artifact)?;
310 }
311 Message::BuildScriptExecuted(build_script) => {
312 self.process_build_script(build_script)?;
313 }
314 _ => {
315 }
317 }
318
319 Ok(())
320 }
321
322 fn process_artifact(&mut self, artifact: Artifact) -> Result<(), FromMessagesError> {
323 if let Some(path) = artifact.executable {
324 self.detect_base_output_dir(&path);
325
326 if artifact.profile.test {
327 let package_id = artifact.package_id.repr;
328
329 let name = artifact.target.name;
332
333 let package = self
334 .graph
335 .metadata(&guppy::PackageId::new(package_id.clone()))
336 .map_err(FromMessagesError::PackageGraph)?;
337
338 let kind = artifact.target.kind;
339 if kind.is_empty() {
340 return Err(FromMessagesError::MissingTargetKind {
341 package_name: package.name().to_owned(),
342 binary_name: name.clone(),
343 });
344 }
345
346 let (computed_kind, platform) = if kind.iter().any(|k| {
347 matches!(
349 k,
350 TargetKind::Lib
351 | TargetKind::RLib
352 | TargetKind::DyLib
353 | TargetKind::CDyLib
354 | TargetKind::StaticLib
355 )
356 }) {
357 (RustTestBinaryKind::LIB, BuildPlatform::Target)
358 } else if let Some(TargetKind::ProcMacro) = kind.first() {
359 (RustTestBinaryKind::PROC_MACRO, BuildPlatform::Host)
360 } else {
361 (
363 RustTestBinaryKind::new(
364 kind.into_iter()
365 .next()
366 .expect("already checked that kind is non-empty")
367 .to_string(),
368 ),
369 BuildPlatform::Target,
370 )
371 };
372
373 let id = RustBinaryId::from_parts(package.name(), &computed_kind, &name);
375
376 self.rust_binaries.push(RustTestBinary {
377 path,
378 package_id,
379 kind: computed_kind,
380 name,
381 id,
382 build_platform: platform,
383 });
384 } else if artifact
385 .target
386 .kind
387 .iter()
388 .any(|x| matches!(x, TargetKind::Bin))
389 {
390 if let Ok(rel_path) = path.strip_prefix(&self.rust_build_meta.target_directory) {
394 let non_test_binary = RustNonTestBinarySummary {
395 name: artifact.target.name,
396 kind: RustNonTestBinaryKind::BIN_EXE,
397 path: convert_rel_path_to_forward_slash(rel_path),
398 };
399
400 self.rust_build_meta
401 .non_test_binaries
402 .entry(artifact.package_id.repr)
403 .or_default()
404 .insert(non_test_binary);
405 };
406 }
407 } else if artifact
408 .target
409 .kind
410 .iter()
411 .any(|x| matches!(x, TargetKind::DyLib | TargetKind::CDyLib))
412 {
413 for filename in artifact.filenames {
415 if let Ok(rel_path) = filename.strip_prefix(&self.rust_build_meta.target_directory)
416 {
417 let non_test_binary = RustNonTestBinarySummary {
418 name: artifact.target.name.clone(),
419 kind: RustNonTestBinaryKind::DYLIB,
420 path: convert_rel_path_to_forward_slash(rel_path),
421 };
422 self.rust_build_meta
423 .non_test_binaries
424 .entry(artifact.package_id.repr.clone())
425 .or_default()
426 .insert(non_test_binary);
427 }
428 }
429 }
430
431 Ok(())
432 }
433
434 fn detect_base_output_dir(&mut self, artifact_path: &Utf8Path) -> Option<()> {
446 let rel_path = match artifact_path.strip_prefix(&self.rust_build_meta.build_directory) {
450 Ok(rel) => rel,
451 Err(_) => {
452 debug!(
453 target: "nextest-runner::list",
454 "artifact path `{}` is not within the build directory `{}`, \
455 skipping base output directory detection",
456 artifact_path, self.rust_build_meta.build_directory,
457 );
458 return None;
459 }
460 };
461 let parent = rel_path.parent()?;
462 if parent.file_name() == Some("deps") {
463 let base = parent.parent()?;
464 if !self.rust_build_meta.base_output_directories.contains(base) {
465 self.rust_build_meta
466 .base_output_directories
467 .insert(convert_rel_path_to_forward_slash(base));
468 }
469 }
470 Some(())
471 }
472
473 fn process_build_script(&mut self, build_script: BuildScript) -> Result<(), FromMessagesError> {
474 for path in build_script.linked_paths {
475 self.detect_linked_path(&build_script.package_id, &path);
476 }
477
478 let package_id = guppy::PackageId::new(build_script.package_id.repr);
480 let in_workspace = self.graph.metadata(&package_id).map_or_else(
481 |_| {
482 warn!(
484 target: "nextest-runner::list",
485 "warning: saw package ID `{}` which wasn't produced by cargo metadata",
486 package_id
487 );
488 false
489 },
490 |p| p.in_workspace(),
491 );
492 if in_workspace {
493 match build_script
495 .out_dir
496 .strip_prefix(&self.rust_build_meta.build_directory)
497 {
498 Ok(rel_out_dir) => {
499 self.rust_build_meta.build_script_out_dirs.insert(
500 package_id.repr().to_owned(),
501 convert_rel_path_to_forward_slash(rel_out_dir),
502 );
503 }
504 Err(_) => {
505 debug!(
506 target: "nextest-runner::list",
507 "build script out_dir `{}` for package `{}` is not within \
508 the build directory `{}`, skipping",
509 build_script.out_dir, package_id,
510 self.rust_build_meta.build_directory,
511 );
512 }
513 }
514
515 if !build_script.env.is_empty() {
518 self.rust_build_meta
519 .build_script_info
520 .get_or_insert_with(BTreeMap::new)
521 .entry(package_id.repr().to_owned())
522 .or_default()
523 .envs = build_script.env.into_iter().collect();
524 }
525 }
526
527 Ok(())
528 }
529
530 fn detect_linked_path(&mut self, package_id: &PackageId, path: &Utf8Path) -> Option<()> {
532 let actual_path = match path.as_str().split_once('=') {
534 Some((_, p)) => p.into(),
535 None => path,
536 };
537
538 let rel_path = match actual_path.strip_prefix(&self.rust_build_meta.build_directory) {
539 Ok(rel) => rel,
540 Err(_) => {
541 if let Some(alt_target_dir) = &self.alt_target_dir {
553 actual_path.strip_prefix(alt_target_dir).ok()?
554 } else {
555 return None;
556 }
557 }
558 };
559
560 self.rust_build_meta
561 .linked_paths
562 .entry(convert_rel_path_to_forward_slash(rel_path))
563 .or_default()
564 .insert(package_id.repr.clone());
565
566 Some(())
567 }
568
569 fn finish(mut self) -> BinaryList {
570 self.rust_binaries.sort_by(|b1, b2| b1.id.cmp(&b2.id));
571
572 let relevant_package_ids = self
574 .rust_binaries
575 .iter()
576 .map(|bin| bin.package_id.clone())
577 .collect::<HashSet<_>>();
578
579 self.rust_build_meta
580 .build_script_out_dirs
581 .retain(|package_id, _| relevant_package_ids.contains(package_id));
582 if let Some(info) = &mut self.rust_build_meta.build_script_info {
583 info.retain(|package_id, _| relevant_package_ids.contains(package_id));
584 }
585
586 BinaryList {
587 rust_build_meta: self.rust_build_meta,
588 rust_binaries: self.rust_binaries,
589 }
590 }
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use crate::{
597 cargo_config::{TargetDefinitionLocation, TargetTriple, TargetTripleSource},
598 list::{
599 SerializableFormat,
600 test_helpers::{PACKAGE_GRAPH_FIXTURE, PACKAGE_METADATA_ID, package_metadata},
601 },
602 platform::{HostPlatform, PlatformLibdir, TargetPlatform},
603 };
604 use indoc::indoc;
605 use maplit::btreeset;
606 use nextest_metadata::PlatformLibdirUnavailable;
607 use pretty_assertions::assert_eq;
608 use serde_json::json;
609 use target_spec::{Platform, TargetFeatures};
610
611 #[test]
612 fn test_parse_binary_list() {
613 let fake_bin_test = RustTestBinary {
614 id: "fake-package::bin/fake-binary".into(),
615 path: "/fake/binary".into(),
616 package_id: "fake-package 0.1.0 (path+file:///Users/fakeuser/project/fake-package)"
617 .to_owned(),
618 kind: RustTestBinaryKind::LIB,
619 name: "fake-binary".to_owned(),
620 build_platform: BuildPlatform::Target,
621 };
622 let fake_macro_test = RustTestBinary {
623 id: "fake-macro::proc-macro/fake-macro".into(),
624 path: "/fake/macro".into(),
625 package_id: "fake-macro 0.1.0 (path+file:///Users/fakeuser/project/fake-macro)"
626 .to_owned(),
627 kind: RustTestBinaryKind::PROC_MACRO,
628 name: "fake-macro".to_owned(),
629 build_platform: BuildPlatform::Host,
630 };
631
632 let fake_triple = TargetTriple {
633 platform: Platform::new("aarch64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(),
634 source: TargetTripleSource::CliOption,
635 location: TargetDefinitionLocation::Builtin,
636 };
637 let fake_host_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib";
638 let build_platforms = BuildPlatforms {
639 host: HostPlatform {
640 platform: TargetTriple::x86_64_unknown_linux_gnu().platform,
641 libdir: PlatformLibdir::Available(Utf8PathBuf::from(fake_host_libdir)),
642 },
643 target: Some(TargetPlatform {
644 triple: fake_triple,
645 libdir: PlatformLibdir::Unavailable(PlatformLibdirUnavailable::RUSTC_OUTPUT_ERROR),
647 }),
648 };
649
650 let mut rust_build_meta =
651 RustBuildMeta::new("/fake/target", "/fake/target", build_platforms);
652 rust_build_meta
653 .base_output_directories
654 .insert("my-profile".into());
655 rust_build_meta.non_test_binaries.insert(
656 "my-package-id".into(),
657 btreeset! {
658 RustNonTestBinarySummary {
659 name: "my-name".into(),
660 kind: RustNonTestBinaryKind::BIN_EXE,
661 path: "my-profile/my-name".into(),
662 },
663 RustNonTestBinarySummary {
664 name: "your-name".into(),
665 kind: RustNonTestBinaryKind::DYLIB,
666 path: "my-profile/your-name.dll".into(),
667 },
668 RustNonTestBinarySummary {
669 name: "your-name".into(),
670 kind: RustNonTestBinaryKind::DYLIB,
671 path: "my-profile/your-name.exp".into(),
672 },
673 },
674 );
675
676 let binary_list = BinaryList {
677 rust_build_meta,
678 rust_binaries: vec![fake_bin_test, fake_macro_test],
679 };
680
681 static EXPECTED_HUMAN: &str = indoc! {"
683 fake-package::bin/fake-binary
684 fake-macro::proc-macro/fake-macro
685 "};
686 static EXPECTED_HUMAN_VERBOSE: &str = indoc! {r"
687 fake-package::bin/fake-binary:
688 bin: /fake/binary
689 build platform: target
690 fake-macro::proc-macro/fake-macro:
691 bin: /fake/macro
692 build platform: host
693 "};
694 static EXPECTED_JSON_PRETTY: &str = indoc! {r#"
695 {
696 "rust-build-meta": {
697 "target-directory": "/fake/target",
698 "build-directory": "/fake/target",
699 "base-output-directories": [
700 "my-profile"
701 ],
702 "non-test-binaries": {
703 "my-package-id": [
704 {
705 "name": "my-name",
706 "kind": "bin-exe",
707 "path": "my-profile/my-name"
708 },
709 {
710 "name": "your-name",
711 "kind": "dylib",
712 "path": "my-profile/your-name.dll"
713 },
714 {
715 "name": "your-name",
716 "kind": "dylib",
717 "path": "my-profile/your-name.exp"
718 }
719 ]
720 },
721 "build-script-out-dirs": {},
722 "build-script-info": {},
723 "linked-paths": [],
724 "platforms": {
725 "host": {
726 "platform": {
727 "triple": "x86_64-unknown-linux-gnu",
728 "target-features": "unknown"
729 },
730 "libdir": {
731 "status": "available",
732 "path": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib"
733 }
734 },
735 "targets": [
736 {
737 "platform": {
738 "triple": "aarch64-unknown-linux-gnu",
739 "target-features": "unknown"
740 },
741 "libdir": {
742 "status": "unavailable",
743 "reason": "rustc-output-error"
744 }
745 }
746 ]
747 },
748 "target-platforms": [
749 {
750 "triple": "aarch64-unknown-linux-gnu",
751 "target-features": "unknown"
752 }
753 ],
754 "target-platform": "aarch64-unknown-linux-gnu"
755 },
756 "rust-binaries": {
757 "fake-macro::proc-macro/fake-macro": {
758 "binary-id": "fake-macro::proc-macro/fake-macro",
759 "binary-name": "fake-macro",
760 "package-id": "fake-macro 0.1.0 (path+file:///Users/fakeuser/project/fake-macro)",
761 "kind": "proc-macro",
762 "binary-path": "/fake/macro",
763 "build-platform": "host"
764 },
765 "fake-package::bin/fake-binary": {
766 "binary-id": "fake-package::bin/fake-binary",
767 "binary-name": "fake-binary",
768 "package-id": "fake-package 0.1.0 (path+file:///Users/fakeuser/project/fake-package)",
769 "kind": "lib",
770 "binary-path": "/fake/binary",
771 "build-platform": "target"
772 }
773 }
774 }"#};
775 static EXPECTED_ONELINE: &str = indoc! {"
777 fake-package::bin/fake-binary
778 fake-macro::proc-macro/fake-macro
779 "};
780 static EXPECTED_ONELINE_VERBOSE: &str = indoc! {r"
781 fake-package::bin/fake-binary [bin: /fake/binary] [build platform: target]
782 fake-macro::proc-macro/fake-macro [bin: /fake/macro] [build platform: host]
783 "};
784
785 assert_eq!(
786 binary_list
787 .to_string(OutputFormat::Human { verbose: false })
788 .expect("human succeeded"),
789 EXPECTED_HUMAN
790 );
791 assert_eq!(
792 binary_list
793 .to_string(OutputFormat::Human { verbose: true })
794 .expect("human succeeded"),
795 EXPECTED_HUMAN_VERBOSE
796 );
797 assert_eq!(
798 binary_list
799 .to_string(OutputFormat::Serializable(SerializableFormat::JsonPretty))
800 .expect("json-pretty succeeded"),
801 EXPECTED_JSON_PRETTY
802 );
803 assert_eq!(
804 binary_list
805 .to_string(OutputFormat::Oneline { verbose: false })
806 .expect("oneline succeeded"),
807 EXPECTED_ONELINE
808 );
809 assert_eq!(
810 binary_list
811 .to_string(OutputFormat::Oneline { verbose: true })
812 .expect("oneline verbose succeeded"),
813 EXPECTED_ONELINE_VERBOSE
814 );
815 }
816
817 #[test]
818 fn test_parse_binary_list_from_message_lines() {
819 let build_platforms = BuildPlatforms {
820 host: HostPlatform {
821 platform: TargetTriple::x86_64_unknown_linux_gnu().platform,
822 libdir: PlatformLibdir::Available("/fake/libdir".into()),
823 },
824 target: None,
825 };
826 let package = package_metadata();
827 let artifact_path = PACKAGE_GRAPH_FIXTURE
828 .workspace()
829 .target_directory()
830 .join("debug/deps/metadata_helper-test");
831 let src_path = package
832 .manifest_path()
833 .parent()
834 .expect("manifest path has a parent")
835 .join("src/lib.rs");
836
837 let compiler_artifact = json!({
838 "reason": "compiler-artifact",
839 "package_id": PACKAGE_METADATA_ID,
840 "manifest_path": package.manifest_path(),
841 "target": {
842 "name": package.name(),
843 "kind": ["lib"],
844 "crate_types": ["lib"],
845 "required-features": [],
846 "src_path": src_path,
847 "edition": "2021",
848 "doctest": true,
849 "test": true,
850 "doc": true
851 },
852 "profile": {
853 "opt_level": "0",
854 "debuginfo": 0,
855 "debug_assertions": true,
856 "overflow_checks": true,
857 "test": true
858 },
859 "features": [],
860 "filenames": [artifact_path],
861 "executable": artifact_path,
862 "fresh": false
863 });
864 let input = format!("this is not JSON\n{}\n\n", compiler_artifact);
865
866 let from_messages = BinaryList::from_messages(
867 input.as_bytes(),
868 &PACKAGE_GRAPH_FIXTURE,
869 build_platforms.clone(),
870 )
871 .expect("parsing from messages succeeds");
872
873 let mut builder = BinaryListBuilder::new(&PACKAGE_GRAPH_FIXTURE, build_platforms);
874 for line in input.lines() {
875 builder
876 .process_message_line(line)
877 .expect("processing line succeeds");
878 }
879 let from_lines = builder.finish();
880
881 assert_eq!(
882 from_lines
883 .to_string(OutputFormat::Serializable(SerializableFormat::JsonPretty))
884 .expect("json-pretty succeeds"),
885 from_messages
886 .to_string(OutputFormat::Serializable(SerializableFormat::JsonPretty))
887 .expect("json-pretty succeeds")
888 );
889 }
890}