| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! This is an integration test for the `gn_json` crate, ensuring that it is |
| //! able to parse the `gn desc` output from the in-tree GN implementation. |
| //! |
| //! The general shape of the test is: |
| //! |
| //! 1. Execute GN on a minimal fake build setup, generating json output, so that |
| //! we have a controlled set of inputs to work with, generating |
| //! |
| //! 2. Read in the json produced. |
| //! |
| //! 3. Run the various tests. |
| //! |
| |
| use anyhow::{bail, Context, Result}; |
| use argh::FromArgs; |
| use camino::Utf8PathBuf; |
| use gn_json::target::{AllTargets, ConfigValues, Public, TargetDescription}; |
| use pretty_assertions::assert_eq; |
| use std::ffi::OsStr; |
| use std::process::Command; |
| use tempfile::tempdir; |
| |
| /// Test arguments for the integration test |
| #[derive(Debug, FromArgs)] |
| struct TestArgs { |
| /// the path to the dir which contains the GN binary. |
| #[argh(option)] |
| gn_tool_dir: Utf8PathBuf, |
| |
| /// the path to the gn project. |
| #[argh(option)] |
| project_dir: Utf8PathBuf, |
| } |
| |
| fn main() -> Result<()> { |
| let args: TestArgs = argh::from_env(); |
| |
| let gn = GN { |
| tool_path: args.gn_tool_dir.join("gn"), |
| project_path: args.project_dir, |
| outdir: "//outdir".into(), |
| }; |
| |
| // Run GN on the test project. |
| gn.gen().context("Running GN 'gen' on the test project")?; |
| let raw_desc_json = gn.desc().context("Running GN 'desc' on the test project")?; |
| |
| // Write the GN desc output to a temporary file. |
| let temp_out_dir = tempdir()?; |
| let temp_file_path = temp_out_dir.path().join("gn_desc.json"); |
| let temp_file = temp_file_path |
| .to_str() |
| .ok_or(anyhow::anyhow!("Is not a valid UTF-8 path: {}", temp_file_path.display()))?; |
| std::fs::write(&temp_file, raw_desc_json)?; |
| |
| // Now parse that file using the function provided by the library |
| let all_targets: AllTargets = gn_json::parse_file(&String::try_from(temp_file)?) |
| .context("Parsing the GN desc json output")?; |
| |
| assert_eq!( |
| vec!["//:default", "//:tests", "//foo/bar:bar", "//foo:foo_action", "//foo:foo_binary",], |
| itertools::sorted(all_targets.keys().into_iter()).collect::<Vec<&String>>(), |
| "The expected targets were not found in the parsed json output" |
| ); |
| |
| assert_eq!( |
| &TargetDescription { |
| config_values: ConfigValues { |
| cflags: vec!["-Os".to_string()], |
| ldflags: vec!["-some_ld_flag".to_string()], |
| ..ConfigValues::default() |
| }, |
| config_targets: vec![ |
| "//build:compiler_defaults".to_string(), |
| "//build:executable_ldconfig".to_string(), |
| ], |
| crate_name: Some("foo_binary".to_string()), |
| crate_root: Some("//foo/src/main.rs".to_string()), |
| deps: vec!["//foo/bar:bar".to_string()], |
| outputs: vec!["//outdir/foo_binary".to_string()], |
| sources: vec!["//foo/src/main.rs".to_string()], |
| toolchain: "//build/toolchain:main".to_string(), |
| target_type: "executable".to_string(), |
| metadata: [ |
| ("simple_key".to_string(), serde_json::json!(["simple_value"])), |
| ( |
| "complex_key".to_string(), |
| serde_json::json!([{ |
| "arg": "arg_value", |
| "arg2": "arg2_value" |
| }]), |
| ), |
| ] |
| .into(), |
| ..TargetDescription::default() |
| }, |
| all_targets.get("//foo:foo_binary").unwrap(), |
| ); |
| |
| assert_eq!( |
| &TargetDescription { |
| args: vec!["--arg".to_string(), "value".to_string()], |
| deps: vec!["//foo:foo_binary".to_string()], |
| public: Public::StringVal("*".to_string()), |
| toolchain: "//build/toolchain:main".to_string(), |
| target_type: "action".to_string(), |
| script: Some("//foo/some_script.py".to_string()), |
| inputs: vec!["//foo/src/main.rs".to_string()], |
| outputs: vec!["//outdir/obj/foo/action_output.txt".to_string()], |
| ..TargetDescription::default() |
| }, |
| all_targets.get("//foo:foo_action").unwrap(), |
| ); |
| |
| assert_eq!( |
| &TargetDescription { |
| deps: vec!["//:tests".to_string(), "//foo:foo_binary".to_string()], |
| toolchain: "//build/toolchain:main".to_string(), |
| target_type: "group".to_string(), |
| ..TargetDescription::default() |
| }, |
| all_targets.get("//:default").unwrap(), |
| ); |
| |
| assert_eq!( |
| &TargetDescription { |
| toolchain: "//build/toolchain:main".to_string(), |
| target_type: "group".to_string(), |
| ..TargetDescription::default() |
| }, |
| all_targets.get("//:tests").unwrap(), |
| ); |
| |
| assert_eq!( |
| &TargetDescription { |
| crate_name: Some("bar".to_string()), |
| crate_root: Some("//foo/bar/src/lib.rs".to_string()), |
| outputs: vec!["//outdir/obj/foo/bar/libbar.rlib".to_string()], |
| sources: vec!["//foo/bar/src/lib.rs".to_string()], |
| toolchain: "//build/toolchain:main".to_string(), |
| target_type: "rust_library".to_string(), |
| ..TargetDescription::default() |
| }, |
| all_targets.get("//foo/bar:bar").unwrap(), |
| ); |
| |
| Ok(()) |
| } |
| |
| #[derive(Debug)] |
| struct GN { |
| tool_path: Utf8PathBuf, |
| project_path: Utf8PathBuf, |
| outdir: Utf8PathBuf, |
| } |
| |
| impl GN { |
| fn gen(&self) -> Result<String> { |
| self.run_cmd("gen", vec![self.outdir.as_str()]) |
| } |
| |
| /// Run `gn desc` |
| fn desc(&self) -> Result<String> { |
| self.run_cmd("desc", vec![self.outdir.as_str(), "*", "--format=json"]) |
| } |
| |
| /// Run GN with the given cmd and args. |
| /// |
| /// The Result is: |
| /// Ok(stdout) |
| /// Err(stdout if non-zero length, otherwise stderr) |
| fn run_cmd<I: IntoIterator<Item = S>, S: AsRef<OsStr>>( |
| &self, |
| cmd: &str, |
| args: I, |
| ) -> Result<String> { |
| let mut command = Command::new(&self.tool_path); |
| command.arg(cmd); |
| command.arg(format!("--root={}", &self.project_path)); |
| |
| for arg in args.into_iter() { |
| command.arg(arg); |
| } |
| |
| let result = command.output(); |
| |
| let output = result?; |
| let stdout = String::from_utf8(output.stdout.clone()) |
| .context("Converting cmd stdout to a string")?; |
| if !output.status.success() { |
| if stdout.len() != 0 { |
| bail!("GN failed (stdout):\n{}", stdout); |
| } else { |
| let stderr = String::from_utf8_lossy(&output.stderr); |
| bail!("GN failed (stderr): \n{}", stderr); |
| } |
| } |
| Ok(stdout) |
| } |
| } |