blob: d5c9f5c6c2ac3d729452013ff2ed442c5146b92a [file] [log] [blame]
// Copyright 2019 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.
use {
super::{
data::Data,
puppet, results,
trials::{self, Step},
PUPPET_MONIKER,
},
anyhow::{bail, Error},
diagnostics_reader::{ArchiveReader, ComponentSelector, Inspect},
fidl_diagnostics_validate as validate,
};
pub async fn run_all_trials() -> results::Results {
let trial_set = trials::real_trials();
let mut results = results::Results::new();
for mut trial in trial_set {
match puppet::Puppet::connect().await {
Ok(mut puppet) => {
results.diff_type = puppet.config.diff_type;
if puppet.config.test_archive {
match puppet.publish().await {
Ok(validate::TestResult::Ok) => {}
Ok(result) => {
results.error(format!("Publish reported {:?}", result));
return results;
}
Err(e) => {
results.error(format!("Publish error: {:?}", e));
return results;
}
}
}
let mut data = Data::new();
if let Err(e) = run_trial(&mut puppet, &mut data, &mut trial, &mut results).await {
results.error(format!("Running trial {}, got failure:\n{}", trial.name, e));
} else {
results.log(format!("Trial {} succeeds", trial.name));
}
// The puppet has to be shut down (it will restart) so the VMO fetch will succeed
// on the next trial. This also makes sure the puppet is in a clean state for
// each trial.
puppet.shutdown().await;
}
Err(e) => {
results.error(format!("Failed to form Puppet - error {e:?}."));
}
}
}
results
}
#[derive(PartialEq)]
enum StepResult {
// Signals that we should continue with the next step.
Continue,
// Signals that the state after executing this step is inconsistent, we should stop running
// further steps.
Stop,
}
async fn run_trial(
puppet: &mut puppet::Puppet,
data: &mut Data,
trial: &mut trials::Trial,
results: &mut results::Results,
) -> Result<(), Error> {
let trial_name = format!("{}:{}", puppet.printable_name(), trial.name);
// We have to give explicit type here because compiler can't deduce it from None option value.
try_compare::<validate::Action>(data, puppet, &trial_name, -1, None, -1, results).await?;
for (step_index, step) in trial.steps.iter_mut().enumerate() {
let step_result = match step {
Step::Actions(actions) => {
run_actions(actions, data, puppet, &trial.name, step_index, results).await?
}
Step::WithMetrics(actions, step_name) => {
let r =
run_actions(actions, data, puppet, &trial.name, step_index, results).await?;
results.remember_metrics(puppet.metrics()?, &trial.name, step_index, step_name);
r
}
Step::LazyActions(actions) => {
run_lazy_actions(actions, data, puppet, &trial.name, step_index, results).await?
}
};
if step_result == StepResult::Stop {
break;
}
}
Ok(())
}
async fn run_actions(
actions: &mut Vec<validate::Action>,
data: &mut Data,
puppet: &mut puppet::Puppet,
trial_name: &str,
step_index: usize,
results: &mut results::Results,
) -> Result<StepResult, Error> {
for (action_number, action) in actions.iter_mut().enumerate() {
if let Err(e) = data.apply(action) {
bail!(
"Local-apply error in trial {}, step {}, action {}: {:?} ",
trial_name,
step_index,
action_number,
e
);
}
match puppet.apply(action).await {
Err(e) => {
bail!(
"Puppet-apply error in trial {}, step {}, action {}: {:?} ",
trial_name,
step_index,
action_number,
e
);
}
Ok(validate::TestResult::Ok) => {}
Ok(validate::TestResult::Unimplemented) => {
results.unimplemented(puppet.printable_name(), action);
return Ok(StepResult::Stop);
}
Ok(bad_result) => {
bail!(
"In trial {}, puppet {} reported action {:?} was {:?}",
trial_name,
puppet.printable_name(),
action,
bad_result
);
}
}
try_compare(
data,
puppet,
&trial_name,
step_index as i32,
Some(action),
action_number as i32,
results,
)
.await?;
}
Ok(StepResult::Continue)
}
async fn run_lazy_actions(
actions: &mut Vec<validate::LazyAction>,
data: &mut Data,
puppet: &mut puppet::Puppet,
trial_name: &str,
step_index: usize,
results: &mut results::Results,
) -> Result<StepResult, Error> {
for (action_number, action) in actions.iter_mut().enumerate() {
if let Err(e) = data.apply_lazy(action) {
bail!(
"Local-apply_lazy error in trial {}, step {}, action {}: {:?} ",
trial_name,
step_index,
action_number,
e
);
}
match puppet.apply_lazy(action).await {
Err(e) => {
bail!(
"Puppet-apply_lazy error in trial {}, step {}, action {}: {:?} ",
trial_name,
step_index,
action_number,
e
);
}
Ok(validate::TestResult::Ok) => {}
Ok(validate::TestResult::Unimplemented) => {
results.unimplemented(puppet.printable_name(), action);
return Ok(StepResult::Stop);
}
Ok(bad_result) => {
bail!(
"In trial {}, puppet {} reported action {:?} was {:?}",
trial_name,
puppet.printable_name(),
action,
bad_result
);
}
}
try_compare(
data,
puppet,
&trial_name,
step_index as i32,
Some(action),
action_number as i32,
results,
)
.await?;
}
Ok(StepResult::Continue)
}
async fn try_compare<ActionType: std::fmt::Debug>(
data: &mut Data,
puppet: &puppet::Puppet,
trial_name: &str,
step_index: i32,
action: Option<&ActionType>,
action_number: i32,
results: &results::Results,
) -> Result<(), Error> {
if !data.is_empty() {
match puppet.read_data().await {
Err(e) => {
bail!(
"Puppet-read error in trial {}, step {}, action {} {:?}: {:?} ",
trial_name,
step_index,
action_number,
action,
e
);
}
Ok(mut puppet_data) => {
if puppet.config.has_runner_node {
puppet_data.remove_tree("runner");
}
if let Err(e) = data.compare(&puppet_data, results.diff_type) {
bail!(
"Compare error in trial {}, step {}, action {}:\n{:?}:\n{} ",
trial_name,
step_index,
action_number,
action,
e
);
}
}
}
if puppet.config.test_archive {
let archive_data = match ArchiveReader::new()
.add_selector(ComponentSelector::new(vec![PUPPET_MONIKER.to_string()]))
.snapshot::<Inspect>()
.await
{
Ok(archive_data) => archive_data,
Err(e) => {
bail!(
"Archive read error in trial {}, step {}, action {}:\n{:?}:\n{} ",
trial_name,
step_index,
action_number,
action,
e
);
}
};
if archive_data.len() != 1 {
bail!(
"Expected 1 component in trial {}, step {}, action {}:\n{:?}:\nfound {} ",
trial_name,
step_index,
action_number,
action,
archive_data.len()
);
}
let mut hierarchy_data: Data = archive_data[0].payload.as_ref().unwrap().clone().into();
if puppet.config.has_runner_node {
hierarchy_data.remove_tree("runner");
}
if let Err(e) = data.compare_to_json(&hierarchy_data, results.diff_type) {
bail!(
"Archive compare error in trial {}, step {}, action {}:\n{:?}:\n{} ",
trial_name,
step_index,
action_number,
action,
e
);
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use {
super::*, crate::trials::tests::trial_with_action, crate::trials::Trial, crate::*,
fidl_diagnostics_validate::*,
};
#[fuchsia::test]
async fn unimplemented_works() {
let mut int_maker = trial_with_action(
"foo",
create_numeric_property!(
parent: ROOT_ID, id: 1, name: "int", value: Value::IntT(0)),
);
let mut uint_maker = trial_with_action(
"foo",
create_numeric_property!(
parent: ROOT_ID, id: 2, name: "uint", value: Value::UintT(0)),
);
let mut uint_create_delete = Trial {
name: "foo".to_string(),
steps: vec![
Step::Actions(vec![
create_numeric_property!(parent: ROOT_ID, id: 2, name: "uint", value: Value::UintT(0)),
]),
Step::Actions(vec![delete_property!(id: 2)]),
],
};
let mut results = results::Results::new();
let mut puppet = puppet::tests::local_incomplete_puppet().await.unwrap();
// results contains a list of the _un_implemented actions. local_incomplete_puppet()
// implements Int creation, but not Uint. So results should not include Uint but should
// include Int.
{
let mut data = Data::new();
run_trial(&mut puppet, &mut data, &mut int_maker, &mut results).await.unwrap();
}
{
let mut data = Data::new();
run_trial(&mut puppet, &mut data, &mut uint_maker, &mut results).await.unwrap();
}
{
let mut data = Data::new();
run_trial(&mut puppet, &mut data, &mut uint_create_delete, &mut results).await.unwrap();
}
assert!(!results
.to_json()
.contains(&format!("{}: CreateProperty(Int)", puppet.printable_name())));
assert!(results
.to_json()
.contains(&format!("{}: CreateProperty(Uint)", puppet.printable_name())));
}
}