blob: d379713f190126a340075c66663574c6b3dfb8f5 [file] [log] [blame]
// Copyright 2020 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 file contains "golden" tests, which compare the output of known sample
//! `Cargo.toml` files with known fixed reference output files.
use {
anyhow::Context,
// Without this, the test diffs are impractical to debug.
pretty_assertions::assert_eq,
std::path::{Path, PathBuf},
tempfile,
};
/// All the paths to runfiles and tools which are used in this test.
///
/// All paths are absolute, and are resolved based on knowing that they are all
/// beneath the directory in which this test binary is stored. See the `BUILD.gn`
/// file for this test target and the corresponding `host_test_data` targets.
///
/// Note that it is not possible to refer to paths inside the source tree, because
/// the source infra runners only have access to the output artifacts (i.e. contents
/// of the "out" directory).
#[derive(Debug)]
struct Paths {
/// `.../host_x64`
test_root_dir: PathBuf,
/// `.../host_x64/test_data`, this is the root of the runfilfes tree, a
/// path //foo/bar will be copied at `.../host_x64/test_data/foo/bar` for
/// this test.
test_data_dir: PathBuf,
/// `.../host_x64/test_data/tools/cargo-gnaw/tests`: this is the directory
/// where golden tests are placed. Corresponds to `//tools/cargo-gnaw/tests`.
test_base_dir: PathBuf,
/// `.../host_x64/test_data/tools/cargo-gnaw/runfiles`: this is the directory
/// where the binary runfiles live.
runfiles_dir: PathBuf,
/// `.../runfiles/rust/bin/rustc`: the path to the rustc binary. rustc is
/// used by cargo.
rustc_binary_path: PathBuf,
/// `.../runfiles/gn`: the absolute path to the gn binary. gn is used for
/// formatting.
gn_binary_path: PathBuf,
/// `.../runfiles/gn`: the absolute path to the cargo binary.
cargo_binary_path: PathBuf,
/// `.../runfiles/rust/lib`: the absolute path to the directory where the
/// shared libraries are stored.
lib_path: PathBuf,
}
/// Gets the hermetic test paths for the runfiles and tools used in this test.
///
/// The hermetic test paths are computed based on the parent directory of this
/// binary.
fn get_paths() -> Paths {
let args: Vec<String> = std::env::args().collect();
eprintln!("test args: {:?}", &args);
let test_binary_path =
std::fs::canonicalize([&args[0]].iter().collect::<PathBuf>()).expect("existing path");
let test_root_dir = test_binary_path.parent().unwrap();
let test_data_dir: PathBuf = [test_root_dir.to_str().unwrap(), "test_data"].iter().collect();
let test_base_dir: PathBuf =
[test_data_dir.to_str().unwrap(), "tools", "cargo-gnaw", "tests"].iter().collect();
let runfiles_dir: PathBuf =
[test_root_dir.to_str().unwrap(), "test_data", "tools", "cargo-gnaw", "runfiles"]
.iter()
.collect();
// Cargo needs rustc under the hoods, provide it.
let rustc_binary_path: PathBuf =
[runfiles_dir.to_str().unwrap(), "rust", "bin", "rustc"].iter().collect();
let gn_binary_path: PathBuf = [runfiles_dir.to_str().unwrap(), "gn", "gn"].iter().collect();
let cargo_binary_path: PathBuf =
[runfiles_dir.to_str().unwrap(), "rust", "bin", "cargo"].iter().collect();
// Set the shared library path to use for loading libraries that rustc needs.
let lib_path: PathBuf = [runfiles_dir.to_str().unwrap(), "rust", "lib"].iter().collect();
eprintln!("lib_path: {:?}", &lib_path);
Paths {
test_root_dir: test_root_dir.to_path_buf(),
test_data_dir,
test_base_dir,
runfiles_dir,
rustc_binary_path,
gn_binary_path,
cargo_binary_path,
lib_path,
}
}
#[test]
fn build_file_generation_test() {
let paths = get_paths();
eprintln!("paths: {:?}", &paths);
// Shared library setup for Linux and Mac. Systems will ignore the settings
// that don't apply to them.
std::env::set_var("LD_LIBRARY_PATH", &paths.lib_path);
std::env::set_var("DYLD_LIBRARY_PATH", &paths.lib_path);
// Cargo internally invokes rustc; but we must tell it to use the one from
// our sandbox, and this is configured using the env variable "RUSTC".
// See:
// https://doc.rust-lang.org/cargo/reference/environment-variables.html
std::env::set_var("RUSTC", &paths.rustc_binary_path);
// Test are executing from the test base directory.
std::env::set_current_dir(&paths.test_base_dir).unwrap();
eprintln!("current dir: {:?}", std::env::current_dir());
#[derive(Debug)]
struct TestCase {
/// Manifest file path (`Cargo.toml`); relative to the base test directory.
manifest_path: Vec<&'static str>,
/// Expected file (`BUILD.gn`); relative to the base test directory.
golden_expected_filename: Vec<&'static str>,
/// If set, the flag `--skip-root` is added to `cargo_gnaw` invocation.
skip_root: bool,
}
let tests = vec![
TestCase {
manifest_path: vec!["simple", "Cargo.toml"],
golden_expected_filename: vec!["simple", "BUILD.gn"],
skip_root: false,
},
TestCase {
manifest_path: vec!["simple_deps", "Cargo.toml"],
golden_expected_filename: vec!["simple_deps", "BUILD.gn"],
skip_root: false,
},
TestCase {
manifest_path: vec!["simple_deps", "Cargo.toml"],
golden_expected_filename: vec!["simple_deps", "BUILD_WITH_NO_ROOT.gn"],
skip_root: true,
},
TestCase {
manifest_path: vec!["platform_deps", "Cargo.toml"],
golden_expected_filename: vec!["platform_deps", "BUILD.gn"],
skip_root: true,
},
TestCase {
manifest_path: vec!["binary", "Cargo.toml"],
golden_expected_filename: vec!["binary", "BUILD.gn"],
skip_root: false,
},
];
for test in tests {
let test_dir = tempfile::TempDir::new().unwrap();
let mut manifest_path: PathBuf = test_dir.path().to_owned();
manifest_path.extend(&test.manifest_path);
let output = test_dir.path().join("BUILD.gn");
// we need the emitted file to be under the same path as the gn targets it references
copy_contents(&paths.test_base_dir, test_dir.path());
let project_root = test_dir.path().to_str().unwrap().to_owned();
// Note: argh does not support "--flag=value" or "--bool-flag false".
let mut args: Vec<&str> = vec![
// args[0] is not used in arg parsing, so this can be any string.
"fake_binary_name",
"--manifest-path",
manifest_path.to_str().unwrap(),
"--project-root",
&project_root,
"--output",
output.to_str().unwrap(),
"--gn-bin",
paths.gn_binary_path.to_str().unwrap(),
"--cargo",
paths.cargo_binary_path.to_str().unwrap(),
];
if test.skip_root {
args.push("--skip-root");
}
gnaw_lib::run(&args)
.with_context(|| format!("\n\targs were: {:?}\n\ttest was: {:?}", &args, &test))
.expect("gnaw_lib::run should succeed");
let output = std::fs::read_to_string(&output)
.with_context(|| format!("while reading tempfile: {}", output.display()))
.expect("tempfile read success");
let expected_path: PathBuf = test.golden_expected_filename.iter().collect();
let expected = std::fs::read_to_string(expected_path.to_string_lossy().to_string())
.with_context(|| {
format!("while reading expected: {:?}", &test.golden_expected_filename)
})
.expect("expected file read success");
assert_eq!(expected, output, "left: expected; right: actual: {:?}", &test);
}
}
fn copy_contents(original_test_dir: &Path, test_dir_path: &Path) {
// copy the contents of original test dir to test_dir
for entry in walkdir::WalkDir::new(&original_test_dir) {
let entry = entry.expect("walking original test directory to copy files to /tmp");
if !entry.file_type().is_file() {
continue;
}
let to_copy = entry.path();
let destination = test_dir_path.join(to_copy.strip_prefix(&original_test_dir).unwrap());
std::fs::create_dir_all(destination.parent().unwrap())
.expect("making parent of file to copy");
std::fs::copy(to_copy, destination).expect("copying file");
}
println!("done copying files");
}