blob: 78b0a076e718d853eb90f40063c920071f9afd1c [file] [log] [blame]
// Copyright 2022 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.
//! test_list_tool generates test-list.json.
use {
anyhow::{format_err, Error},
camino::{Utf8Path, Utf8PathBuf},
fidl::unpersist,
fidl_fuchsia_component_decl::Component,
fidl_fuchsia_data as fdata, fuchsia_archive, fuchsia_pkg,
fuchsia_url::AbsoluteComponentUrl,
maplit::btreeset,
serde::{Deserialize, Serialize},
serde_json,
std::{
cmp::{Eq, PartialEq},
collections::HashMap,
fmt::Debug,
fs,
io::Read,
},
structopt::StructOpt,
test_list::{ExecutionEntry, FuchsiaComponentExecutionEntry, TestList, TestListEntry, TestTag},
};
const META_FAR_PREFIX: &'static str = "meta/";
const TEST_REALM_FACET_NAME: &'static str = "fuchsia.test.type";
const TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY: &'static str =
"fuchsia.test.deprecated-allowed-packages";
const HERMETIC_TEST_REALM: &'static str = "hermetic";
mod error;
mod opts;
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct TestsJsonEntry {
test: TestEntry,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
struct TestEntry {
name: String,
label: String,
cpu: String,
os: String,
package_url: Option<String>,
component_label: Option<String>,
package_manifests: Option<Vec<String>>,
log_settings: Option<LogSettings>,
build_rule: Option<String>,
has_generated_manifest: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct LogSettings {
max_severity: Option<diagnostics_data::Severity>,
min_severity: Option<diagnostics_data::Severity>,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)]
struct TestComponentsJsonEntry {
test_component: TestComponentEntry,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Default, Clone)]
struct TestComponentEntry {
label: String,
moniker: Option<String>,
}
impl TestComponentsJsonEntry {
fn convert_to_map(
value: Vec<TestComponentsJsonEntry>,
) -> Result<HashMap<String, TestComponentsJsonEntry>, Error> {
let mut map = HashMap::<String, TestComponentsJsonEntry>::default();
for entry in value {
if let Some(old_entry) = map.get(&entry.test_component.label) {
if !old_entry.eq(&entry) {
return Err(format_err!(
"Conflicting test components: {:?}, {:?}",
old_entry,
entry
));
}
} else {
map.insert(entry.test_component.label.clone(), entry);
}
}
Ok(map)
}
}
/// This struct contains the set of tags that can be added to tests in the fuchsia.git build.
#[derive(Default, Debug, PartialEq, Eq)]
struct FuchsiaTestTags {
os: Option<String>,
cpu: Option<String>,
scope: Option<String>,
realm: Option<String>,
hermetic: Option<bool>,
legacy_test: Option<bool>,
}
impl FuchsiaTestTags {
pub fn new() -> Self {
Self::default()
}
pub fn into_vec(self) -> Vec<TestTag> {
let string_to_val = |v: Option<String>| v.unwrap_or_else(|| "".to_string());
let bool_to_val = |v: Option<bool>| match v {
None => "".to_string(),
Some(true) => "true".to_string(),
Some(false) => "false".to_string(),
};
btreeset![
TestTag { key: "os".to_string(), value: string_to_val(self.os) },
TestTag { key: "cpu".to_string(), value: string_to_val(self.cpu) },
TestTag { key: "scope".to_string(), value: string_to_val(self.scope) },
TestTag { key: "realm".to_string(), value: string_to_val(self.realm) },
TestTag { key: "hermetic".to_string(), value: bool_to_val(self.hermetic) },
TestTag { key: "legacy_test".to_string(), value: bool_to_val(self.legacy_test) },
]
.into_iter()
.collect()
}
}
impl TestEntry {
fn get_max_log_severity(&self) -> Option<diagnostics_data::Severity> {
self.log_settings.as_ref().map(|settings| settings.max_severity).unwrap_or(None)
}
fn get_min_log_severity(&self) -> Option<diagnostics_data::Severity> {
self.log_settings.as_ref().map(|settings| settings.min_severity).unwrap_or(None)
}
}
fn find_meta_far(build_dir: &Utf8Path, manifest_path: String) -> Result<Utf8PathBuf, Error> {
let package_manifest =
fuchsia_pkg::PackageManifest::try_load_from(build_dir.join(&manifest_path))?;
for blob in package_manifest.blobs() {
if blob.path.eq(META_FAR_PREFIX) {
return Ok(build_dir.join(&blob.source_path));
}
}
Err(error::TestListToolError::MissingMetaBlob(manifest_path).into())
}
fn cm_decl_from_meta_far(meta_far_path: &Utf8PathBuf, cm_path: &str) -> Result<Component, Error> {
let mut meta_far = fs::File::open(meta_far_path)?;
let mut far_reader = fuchsia_archive::Utf8Reader::new(&mut meta_far)?;
let cm_contents = far_reader.read_file(cm_path)?;
let decl: Component = unpersist(&cm_contents)?;
Ok(decl)
}
fn update_tags_from_facets(
test_tags: &mut FuchsiaTestTags,
facets: &fdata::Dictionary,
) -> Result<(), Error> {
let mut runs_in_hermetic_realm = test_tags.realm.is_none();
let mut depends_on_system_packages = false;
for facet in facets.entries.as_ref().unwrap_or(&vec![]) {
// TODO(rudymathu): CFv1 tests should not have a hermetic tag.
if facet.key.eq(TEST_REALM_FACET_NAME) && test_tags.realm.is_none() {
let val = facet
.value
.as_ref()
.ok_or(error::TestListToolError::NullFacet(facet.key.clone()))?;
match &**val {
fdata::DictionaryValue::Str(s) => {
test_tags.realm = Some(s.to_string());
runs_in_hermetic_realm = s.eq(HERMETIC_TEST_REALM);
}
_ => {
return Err(error::TestListToolError::InvalidFacetValue(
facet.key.clone(),
format!("{:?}", val),
)
.into());
}
}
} else if facet.key.eq(TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY) {
let val = facet
.value
.as_ref()
.ok_or(error::TestListToolError::NullFacet(facet.key.clone()))?;
match &**val {
fdata::DictionaryValue::StrVec(s) => {
depends_on_system_packages = !s.is_empty();
}
_ => {
return Err(error::TestListToolError::InvalidFacetValue(
facet.key.clone(),
format!("{:?}", val),
)
.into());
}
}
}
}
if test_tags.realm.is_none() {
test_tags.realm = Some(HERMETIC_TEST_REALM.to_string());
}
// Only hermetic if the test is executed in `hermetic` realm and does not depend on any system
// packages.
test_tags.hermetic = Some(!depends_on_system_packages && runs_in_hermetic_realm);
Ok(())
}
fn to_test_list_entry(test_entry: &TestEntry, realm: Option<String>) -> TestListEntry {
let execution = match &test_entry.package_url {
Some(url) => Some(ExecutionEntry::FuchsiaComponent(FuchsiaComponentExecutionEntry {
component_url: url.to_string(),
test_args: vec![],
timeout_seconds: None,
test_filters: None,
also_run_disabled_tests: false,
parallel: None,
max_severity_logs: test_entry.get_max_log_severity(),
min_severity_logs: test_entry.get_min_log_severity(),
realm: realm,
})),
None => None,
};
TestListEntry {
name: test_entry.name.clone(),
labels: vec![test_entry.label.clone()],
tags: vec![],
execution,
}
}
fn update_tags_with_test_entry(tags: &mut FuchsiaTestTags, test_entry: &TestEntry) {
let realm = &tags.realm;
let (build_rule, has_generated_manifest) = (
test_entry.build_rule.as_ref().map(|s| s.as_str()),
test_entry.has_generated_manifest.unwrap_or_default(),
);
// Determine the type of a test as follows:
// - A "host" test is run from a host binary.
// - A "unit" test is in a hermetic realm with a generated manifest.
// - An "integration" test is in a hermetic realm with a custom manifest.
// - A "system" test is any test in a non-hermetic realm.
// - "unknown" means we do not have knowledge of the build rule being used.
// - "uncategorized" means we otherwise could not match the test.
let test_type = if test_entry.name.starts_with("host_") || test_entry.name.starts_with("linux_")
{
"host"
} else if test_entry.name.ends_with("_host_test.sh") {
"host_shell"
} else {
match (build_rule, has_generated_manifest, realm) {
(_, _, Some(realm)) if realm != HERMETIC_TEST_REALM => "system",
(Some("fuchsia_unittest_package"), true, _) => "unit",
(Some("fuchsia_unittest_package"), false, _) => "integration",
(Some("fuchsia_test_package"), true, _) => "unit",
(Some("fuchsia_test_package"), false, _) => "integration",
(Some("fuchsia_test"), true, _) => "unit",
(Some("fuchsia_test"), false, _) => "integration",
(Some("prebuilt_test_package"), _, _) => "prebuilt",
(Some("fuchsia_fuzzer_package"), _, _) => "fuzzer",
(Some("fuzzer_package"), _, _) => "fuzzer",
(Some("bootfs_test"), _, _) => "bootfs",
(None, _, _) => "unknown",
_ => "uncategorized",
}
};
tags.os = Some(test_entry.os.clone());
tags.cpu = Some(test_entry.cpu.clone());
tags.scope = Some(test_type.to_string());
}
fn main() -> Result<(), Error> {
run_tool()
}
fn read_tests_json(file: &Utf8PathBuf) -> Result<Vec<TestsJsonEntry>, Error> {
let mut buffer = String::new();
fs::File::open(&file)?.read_to_string(&mut buffer)?;
let t: Vec<TestsJsonEntry> = serde_json::from_str(&buffer)?;
Ok(t)
}
fn read_test_components_json(file: &Utf8PathBuf) -> Result<Vec<TestComponentsJsonEntry>, Error> {
let mut buffer = String::new();
fs::File::open(&file)?.read_to_string(&mut buffer)?;
let t: Vec<TestComponentsJsonEntry> = serde_json::from_str(&buffer)?;
Ok(t)
}
fn update_tags_from_manifest(
test_tags: &mut FuchsiaTestTags,
package_url: String,
meta_far_path: &Utf8PathBuf,
) -> Result<(), Error> {
let pkg_url = AbsoluteComponentUrl::parse(&package_url)?;
let cm_path = pkg_url.resource();
// CFv1 manifests don't generate the same FIDL declarations, so just skip generating tags
// from them.
if &cm_path[cm_path.len() - 3..] == "cmx" {
test_tags.legacy_test = Some(true);
Ok(())
} else {
let decl = cm_decl_from_meta_far(&meta_far_path, cm_path)?;
update_tags_from_facets(test_tags, &decl.facets.unwrap_or(fdata::Dictionary::default()))?;
Ok(())
}
}
fn write_depfile(
depfile: &Utf8PathBuf,
output: &Utf8PathBuf,
inputs: &Vec<Utf8PathBuf>,
) -> Result<(), Error> {
if inputs.len() == 0 {
return Ok(());
}
let contents =
format!("{}: {}\n", output, &inputs.iter().map(|i| format!(" {}", i)).collect::<String>(),);
fs::write(depfile, contents)?;
Ok(())
}
fn run_tool() -> Result<(), Error> {
let opt = opts::Opt::from_args();
opt.validate()?;
let tests_json = read_tests_json(&opt.input)?;
let test_components_json = read_test_components_json(&opt.test_components_list)?;
let test_components_map = TestComponentsJsonEntry::convert_to_map(test_components_json)?;
use rayon::prelude::*;
let (data, inputs): (Vec<Option<TestListEntry>>, Vec<Vec<Utf8PathBuf>>) = tests_json
.par_iter()
.map(|entry| {
let realm = match &entry.test.component_label {
Some(component_label) => match test_components_map.get(component_label) {
Some(test_component) => test_component.test_component.moniker.clone(),
None => None,
},
None => None,
};
// Construct the base TestListEntry.
let mut test_list_entry = to_test_list_entry(&entry.test, realm.clone());
let mut test_tags = FuchsiaTestTags::new();
let pkg_manifests = entry.test.package_manifests.clone().unwrap_or(vec![]);
// Aggregate any tags from the component manifest of the test.
let mut inputs: Vec<Utf8PathBuf> = vec![];
if entry.test.package_url.is_some() && pkg_manifests.len() > 0 {
let pkg_url = entry.test.package_url.clone().unwrap();
let pkg_manifest = pkg_manifests[0].clone();
inputs.push(pkg_manifest.clone().into());
let res = find_meta_far(&opt.build_dir, pkg_manifest.clone());
if res.is_err() {
println!(
"error finding meta.far file in package manifest {}: {:?}",
&pkg_manifest,
res.unwrap_err()
);
return (None, inputs);
}
let meta_far_path = res.unwrap();
inputs.push(meta_far_path.clone());
test_tags.realm = realm;
// Find additional tags. Note that this can override existing tags (e.g. to set the test type based on realm)
match update_tags_from_manifest(&mut test_tags, pkg_url.clone(), &meta_far_path) {
Err(e) => {
println!("error processing manifest for package URL {}: {:?}", &pkg_url, e)
}
_ => {}
}
}
update_tags_with_test_entry(&mut test_tags, &entry.test);
test_list_entry.tags = test_tags.into_vec();
(Some(test_list_entry), inputs)
})
.unzip();
let data = data.into_par_iter().flatten().collect();
let inputs = inputs.into_par_iter().flatten().collect();
let test_list = TestList::Experimental { data };
let test_list_json = serde_json::to_string_pretty(&test_list)?;
fs::write(&opt.output, test_list_json)?;
if let Some(depfile) = opt.depfile {
write_depfile(&depfile, &opt.output, &inputs)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use {super::*, tempfile::tempdir};
#[test]
fn test_find_meta_far() {
let tmp = tempdir().expect("failed to get tempdir");
let build_dir = Utf8Path::from_path(tmp.path()).unwrap();
let package_manifest_path = "package_manifest.json";
// Test the working case.
let mut contents = r#"
{
"version": "1",
"repository": "fuchsia.com",
"package": {
"name": "echo-integration-test",
"version": "0"
},
"blobs": [
{
"source_path": "obj/build/components/tests/echo-integration-test/meta.far",
"path": "meta/",
"merkle": "0ec72cdf55fec3e0cc3dd47e86b95ee62c974ebaebea1d05769fea3fc4edca0b",
"size": 36864
}
]
}"#;
fs::write(build_dir.join(package_manifest_path), contents)
.expect("failed to write fake package manifest");
assert_eq!(
find_meta_far(&build_dir.to_path_buf(), package_manifest_path.into()).unwrap(),
build_dir.join("obj/build/components/tests/echo-integration-test/meta.far"),
);
// Test the error case.
contents = r#"
{
"version": "1",
"repository": "fuchsia.com",
"package": {
"name": "echo-integration-test",
"version": "0"
},
"blobs": []
}"#;
fs::write(build_dir.join(package_manifest_path), contents)
.expect("failed to write fake package manifest");
let err = find_meta_far(&build_dir.to_path_buf(), package_manifest_path.into())
.expect_err("find_meta_far failed unexpectedly");
match err.downcast_ref::<error::TestListToolError>() {
Some(error::TestListToolError::MissingMetaBlob(path)) => {
assert_eq!(package_manifest_path.to_string(), *path)
}
Some(e) => panic!("find_meta_far returned incorrect TestListToolError: {:?}", e),
None => panic!("find_meta_far returned non TestListToolError: {:?}", err),
}
}
#[test]
fn test_update_tags_from_facets() {
// Test that empty facets return the hermetic tags.
let mut facets = fdata::Dictionary::default();
let mut tags = FuchsiaTestTags::default();
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
let hermetic_tags = FuchsiaTestTags {
realm: Some(HERMETIC_TEST_REALM.to_string()),
hermetic: Some(true),
..FuchsiaTestTags::default()
};
assert_eq!(tags, hermetic_tags);
// Test that a facet of fuchsia.test: tests returns hermetic tags.
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str(HERMETIC_TEST_REALM.to_string()))),
}]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
assert_eq!(tags, hermetic_tags);
// Test that a null fuchsia.test facet returns a NullFacet error.
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: None,
}]);
let err = update_tags_from_facets(&mut tags, &facets)
.expect_err("tags_from_facets succeeded on null facet value");
match err.downcast_ref::<error::TestListToolError>() {
Some(error::TestListToolError::NullFacet(key)) => {
assert_eq!(*key, TEST_REALM_FACET_NAME.to_string());
}
Some(e) => panic!("tags_from_facets returned incorrect TestListToolError: {:?}", e),
None => panic!("tags_from_facets returned non-TestListToolError: {:?}", err),
}
// Test that an invalid fuchsia.test facet returns an InvalidFacetValue error.
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![
HERMETIC_TEST_REALM.to_string()
]))),
}]);
let err = update_tags_from_facets(&mut tags, &facets)
.expect_err("tags_from_facets succeeded on null facet value");
match err.downcast_ref::<error::TestListToolError>() {
Some(error::TestListToolError::InvalidFacetValue(k, _)) => {
assert_eq!(*k, TEST_REALM_FACET_NAME.to_string());
}
Some(e) => panic!("tags_from_facets returned incorrect TestListToolError: {:?}", e),
None => panic!("tags_from_facets returned non-TestListToolError: {:?}", err),
}
// test that hermetic tag depends on allowed_package_list
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![fdata::DictionaryEntry {
key: TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![]))),
}]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
assert_eq!(tags, hermetic_tags);
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![fdata::DictionaryEntry {
key: TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["some-pkg".to_string()]))),
}]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
let non_hermetic_tags = FuchsiaTestTags {
realm: Some(HERMETIC_TEST_REALM.to_string()),
hermetic: Some(false),
..FuchsiaTestTags::default()
};
assert_eq!(tags, non_hermetic_tags);
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![
fdata::DictionaryEntry {
key: TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["some-pkg".to_string()]))),
},
fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str(HERMETIC_TEST_REALM.to_string()))),
},
]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
assert_eq!(tags, non_hermetic_tags);
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![
fdata::DictionaryEntry {
key: TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![]))),
},
fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str(HERMETIC_TEST_REALM.to_string()))),
},
]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
assert_eq!(tags, hermetic_tags);
// test with other collections
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("other_collection".to_string()))),
}]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
let non_hermetic_tags = FuchsiaTestTags {
realm: Some("other_collection".to_string()),
hermetic: Some(false),
..FuchsiaTestTags::default()
};
assert_eq!(tags, non_hermetic_tags);
let mut tags = FuchsiaTestTags::default();
facets.entries = Some(vec![
fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("other_collection".to_string()))),
},
fdata::DictionaryEntry {
key: TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![]))),
},
]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
assert_eq!(tags, non_hermetic_tags);
let mut tags = FuchsiaTestTags::default();
tags.realm = Some("/some/moniker".into());
facets.entries = Some(vec![
fdata::DictionaryEntry {
key: TEST_REALM_FACET_NAME.to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("other_collection".to_string()))),
},
fdata::DictionaryEntry {
key: TEST_DEPRECATED_ALLOWED_PACKAGES_FACET_KEY.to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![]))),
},
]);
update_tags_from_facets(&mut tags, &facets).expect("failed get tags in tags_from_facets");
assert_eq!(tags.hermetic, Some(false));
assert_eq!(tags.realm, Some("/some/moniker".into()));
}
#[test]
fn test_to_test_list_entry() {
let make_test_entry = |log_settings| TestEntry {
name: "test-name".to_string(),
label: "test-label".to_string(),
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
package_url: Some(
"fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm"
.to_string(),
),
package_manifests: Some(vec![
"obj/build/components/tests/echo-integration-test/package_manifest.json"
.to_string(),
]),
log_settings,
..TestEntry::default()
};
let host_test_entry = TestEntry {
name: "test-name".to_string(),
label: "test-label".to_string(),
cpu: "x64".to_string(),
os: "linux".to_string(),
log_settings: None,
package_url: None,
package_manifests: None,
..TestEntry::default()
};
let make_expected_test_list_entry = |max_severity_logs, min_severity_logs| TestListEntry {
name: "test-name".to_string(),
labels: vec!["test-label".to_string()],
tags: vec![],
execution: Some(ExecutionEntry::FuchsiaComponent(FuchsiaComponentExecutionEntry {
component_url:
"fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm"
.to_string(),
test_args: vec![],
timeout_seconds: None,
test_filters: None,
also_run_disabled_tests: false,
parallel: None,
max_severity_logs,
min_severity_logs,
realm: None,
})),
};
// Default severity.
let test_list_entry = to_test_list_entry(&make_test_entry(None), None);
assert_eq!(test_list_entry, make_expected_test_list_entry(None, None),);
// Inner default severity.
let test_list_entry = to_test_list_entry(
&make_test_entry(Some(LogSettings { max_severity: None, min_severity: None })),
None,
);
assert_eq!(test_list_entry, make_expected_test_list_entry(None, None),);
// Explicit severity
let test_list_entry = to_test_list_entry(
&make_test_entry(Some(LogSettings {
max_severity: Some(diagnostics_data::Severity::Error),
min_severity: None,
})),
None,
);
assert_eq!(
test_list_entry,
make_expected_test_list_entry(Some(diagnostics_data::Severity::Error), None)
);
// pass in realm
let test_list_entry =
to_test_list_entry(&make_test_entry(None), Some("/some/moniker".into()));
let expected_list = TestListEntry {
name: "test-name".to_string(),
labels: vec!["test-label".to_string()],
tags: vec![],
execution: Some(ExecutionEntry::FuchsiaComponent(FuchsiaComponentExecutionEntry {
component_url:
"fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm"
.to_string(),
test_args: vec![],
timeout_seconds: None,
test_filters: None,
also_run_disabled_tests: false,
parallel: None,
max_severity_logs: None,
min_severity_logs: None,
realm: Some("/some/moniker".into()),
})),
};
assert_eq!(test_list_entry, expected_list);
// Host test
let test_list_entry = to_test_list_entry(&host_test_entry, None);
assert_eq!(
test_list_entry,
TestListEntry {
name: "test-name".to_string(),
labels: vec!["test-label".to_string()],
tags: vec![],
execution: None,
}
)
}
#[test]
fn test_update_tags_from_test_entry() {
let cases = vec![
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "unknown".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_unittest_package".to_string()),
has_generated_manifest: Some(true),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "unit".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_unittest_package".to_string()),
has_generated_manifest: Some(false),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "integration".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_test_package".to_string()),
has_generated_manifest: Some(true),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "unit".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_test_package".to_string()),
has_generated_manifest: Some(false),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "integration".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_test_package".to_string()),
has_generated_manifest: Some(false),
..TestEntry::default()
},
FuchsiaTestTags { realm: Some("vulkan".to_string()), ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "vulkan".to_string() },
TestTag { key: "scope".to_string(), value: "system".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_test".to_string()),
has_generated_manifest: Some(true),
..TestEntry::default()
},
FuchsiaTestTags {
realm: Some("hermetic".to_string()),
..FuchsiaTestTags::default()
},
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "hermetic".to_string() },
TestTag { key: "scope".to_string(), value: "unit".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuchsia_fuzzer_package".to_string()),
has_generated_manifest: Some(false),
..TestEntry::default()
},
FuchsiaTestTags {
realm: Some("hermetic".to_string()),
..FuchsiaTestTags::default()
},
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "hermetic".to_string() },
TestTag { key: "scope".to_string(), value: "fuzzer".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("fuzzer_package".to_string()),
has_generated_manifest: Some(false),
..TestEntry::default()
},
FuchsiaTestTags {
realm: Some("hermetic".to_string()),
..FuchsiaTestTags::default()
},
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "hermetic".to_string() },
TestTag { key: "scope".to_string(), value: "fuzzer".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("prebuilt_test_package".to_string()),
has_generated_manifest: Some(false),
..TestEntry::default()
},
FuchsiaTestTags {
realm: Some("hermetic".to_string()),
..FuchsiaTestTags::default()
},
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "hermetic".to_string() },
TestTag { key: "scope".to_string(), value: "prebuilt".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
build_rule: Some("bootfs_test".to_string()),
has_generated_manifest: None,
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "bootfs".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "linux".to_string(),
name: "some_host_test.sh".to_string(),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "linux".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "host_shell".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "linux".to_string(),
name: "host_x64/test".to_string(),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "linux".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "host".to_string() },
],
),
(
TestEntry {
cpu: "x64".to_string(),
os: "linux".to_string(),
name: "linux_x64/test".to_string(),
..TestEntry::default()
},
FuchsiaTestTags { ..FuchsiaTestTags::default() },
vec![
TestTag { key: "cpu".to_string(), value: "x64".to_string() },
TestTag { key: "hermetic".to_string(), value: "".to_string() },
TestTag { key: "legacy_test".to_string(), value: "".to_string() },
TestTag { key: "os".to_string(), value: "linux".to_string() },
TestTag { key: "realm".to_string(), value: "".to_string() },
TestTag { key: "scope".to_string(), value: "host".to_string() },
],
),
];
for (entry, mut tags, expected) in cases.into_iter() {
update_tags_with_test_entry(&mut tags, &entry);
assert_eq!(tags.into_vec(), expected, "entry was {:?}", entry);
}
}
#[test]
fn test_read_tests_json() {
let data = r#"
[
{
"test": {
"cpu": "x64",
"label": "//build/components/tests:echo-integration-test_test_echo-client-test(//build/toolchain/fuchsia:x64)",
"log_settings": {
"max_severity": "WARN",
"min_severity": "DEBUG"
},
"name": "fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm",
"os": "fuchsia",
"package_label": "//build/components/tests:echo-integration-test(//build/toolchain/fuchsia:x64)",
"package_manifests": [
"obj/build/components/tests/echo-integration-test/package_manifest.json"
],
"package_url": "fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm"
}
}
]"#;
let tmp = tempdir().expect("failed to get tempdir");
let dir = Utf8Path::from_path(tmp.path()).unwrap();
let tests_json_path = dir.join("tests.json");
fs::write(&tests_json_path, data).expect("failed to write tests.json to tempfile");
let tests_json = read_tests_json(&tests_json_path).expect("read_tests_json() failed");
assert_eq!(
tests_json,
vec![
TestsJsonEntry{
test: TestEntry{
name: "fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm".to_string(),
label: "//build/components/tests:echo-integration-test_test_echo-client-test(//build/toolchain/fuchsia:x64)".to_string(),
cpu: "x64".to_string(),
os: "fuchsia".to_string(),
package_url: Some("fuchsia-pkg://fuchsia.com/echo-integration-test#meta/echo-client-test.cm".to_string()),
package_manifests: Some(vec![
"obj/build/components/tests/echo-integration-test/package_manifest.json".to_string(),
]),
log_settings: Some(LogSettings {
max_severity: Some(diagnostics_data::Severity::Warn),
min_severity: Some(diagnostics_data::Severity::Debug),
}),
..TestEntry::default()
},
}
],
);
}
#[test]
fn test_read_test_components_json() {
let data = r#"
[
{
"test_component": {
"label": "//build/components/tests:echo-integration-test(//build/toolchain/fuchsia:x64)"
}
},
{
"test_component": {
"label": "//build/components/tests:echo-system-integration-test(//build/toolchain/fuchsia:x64)",
"moniker": "/some/moniker"
}
}
]"#;
let tmp = tempdir().expect("failed to get tempdir");
let dir = Utf8Path::from_path(tmp.path()).unwrap();
let json_path = dir.join("test_components.json");
fs::write(&json_path, data).expect("failed to write tests.json to tempfile");
let tests_json =
read_test_components_json(&json_path).expect("read_test_components_json() failed");
assert_eq!(
tests_json,
vec![
TestComponentsJsonEntry{
test_component: TestComponentEntry {
label: "//build/components/tests:echo-integration-test(//build/toolchain/fuchsia:x64)".into(),
moniker: None
}
},
TestComponentsJsonEntry{
test_component: TestComponentEntry {
label: "//build/components/tests:echo-system-integration-test(//build/toolchain/fuchsia:x64)".into(),
moniker: Some("/some/moniker".into())
}
}
],
);
}
#[test]
fn convert_test_components_to_map() {
let entry1 = TestComponentsJsonEntry {
test_component: TestComponentEntry {
label:
"//build/components/tests:echo-integration-test(//build/toolchain/fuchsia:x64)"
.into(),
moniker: None,
},
};
let entry2 = TestComponentsJsonEntry{
test_component: TestComponentEntry {
label: "//build/components/tests:echo-system-integration-test(//build/toolchain/fuchsia:x64)".into(),
moniker: Some("/some/moniker".into())
}
};
let entries = vec![entry1.clone(), entry2.clone()];
let map = TestComponentsJsonEntry::convert_to_map(entries).unwrap();
let mut expected_map = HashMap::new();
expected_map.insert(entry1.test_component.label.clone(), entry1.clone());
expected_map.insert(entry2.test_component.label.clone(), entry2.clone());
assert_eq!(map, expected_map);
// test non-conflicting entries
// duplicate entries.
let entries = vec![entry1.clone(), entry2.clone(), entry1.clone(), entry2.clone()];
let map = TestComponentsJsonEntry::convert_to_map(entries).unwrap();
let mut expected_map = HashMap::new();
expected_map.insert(entry1.test_component.label.clone(), entry1.clone());
expected_map.insert(entry2.test_component.label.clone(), entry2.clone());
assert_eq!(map, expected_map);
// test non-conflicting entries
let mut entry3 = entry1.clone();
entry3.test_component.moniker = Some("moniker".into());
let entries = vec![entry1, entry2, entry3];
let _ = TestComponentsJsonEntry::convert_to_map(entries)
.expect_err("This should error out due to conflicting entries");
}
}