blob: d93c152ef165333bbfec5d38ea424cd60339085e [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 {
anyhow::{format_err, Error},
fuchsia_async as fasync,
futures::{channel::mpsc, prelude::*},
std::collections::HashSet,
std::fmt,
std::io::Write,
test_executor::TestEvent,
};
#[derive(PartialEq, Debug)]
pub enum TestOutcome {
Passed,
Failed,
Inconclusive,
Error,
}
impl fmt::Display for TestOutcome {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TestOutcome::Passed => write!(f, "PASSED"),
TestOutcome::Failed => write!(f, "FAILED"),
TestOutcome::Inconclusive => write!(f, "INCONCLUSIVE"),
TestOutcome::Error => write!(f, "ERROR"),
}
}
}
// Runs test defined by |url|, and writes logs to writer.
// Returns (Outcome, Names of tests executed, Names of tests passed)
pub async fn run_test<W: Write>(
url: String,
writer: &mut W,
) -> Result<(TestOutcome, Vec<String>, Vec<String>), Error> {
let launcher =
match fuchsia_component::client::connect_to_service::<fidl_fuchsia_sys::LauncherMarker>() {
Ok(l) => l,
Err(e) => return Err(e),
};
let (sender, mut recv) = mpsc::channel(1);
let (remote, test_fut) =
test_executor::run_test_component(launcher, url, sender).remote_handle();
fasync::spawn(remote);
let mut test_outcome = TestOutcome::Passed;
let mut test_cases_in_progress = HashSet::new();
let mut test_cases_executed = HashSet::new();
let mut test_cases_passed = HashSet::new();
while let Some(test_event) = recv.next().await {
match test_event {
TestEvent::TestCaseStarted { test_case_name } => {
if test_cases_executed.contains(&test_case_name) {
return Err(format_err!("test case: '{}' started twice", test_case_name));
}
writeln!(writer, "[RUNNING]\t{}", test_case_name).expect("Cannot write logs");
test_cases_in_progress.insert(test_case_name.clone());
test_cases_executed.insert(test_case_name);
}
TestEvent::TestCaseFinished { test_case_name, outcome } => {
if !test_cases_in_progress.contains(&test_case_name) {
return Err(format_err!(
"test case: '{}' was never started, still got a finish event",
test_case_name
));
}
test_cases_in_progress.remove(&test_case_name);
let outcome_str = match outcome {
test_executor::Outcome::Passed => {
test_cases_passed.insert(test_case_name.clone());
"PASSED".to_string()
}
test_executor::Outcome::Failed => {
if test_outcome == TestOutcome::Passed {
test_outcome = TestOutcome::Failed;
}
"FAILED".to_string()
}
test_executor::Outcome::Skipped => "SKIPPED".to_string(),
test_executor::Outcome::Error => {
test_outcome = TestOutcome::Error;
"ERROR".to_string()
}
test_executor::Outcome::Inconclusive => "INCONCLUSIVE".to_string(),
};
writeln!(writer, "[{}]\t{}", outcome_str, test_case_name)
.expect("Cannot write logs");
}
TestEvent::LogMessage { test_case_name, msg } => {
if !test_cases_executed.contains(&test_case_name) {
return Err(format_err!(
"test case: '{}' was never started, still got a log",
test_case_name
));
}
let msgs = msg.trim().split("\n");
for msg in msgs {
writeln!(writer, "[{}]\t{}", test_case_name, msg).expect("Cannot write logs");
}
}
}
}
test_fut.await.map_err(|e| format_err!("Error running test: {}", e))?;
let mut test_cases_in_progress: Vec<String> = test_cases_in_progress.into_iter().collect();
test_cases_in_progress.sort();
if test_cases_in_progress.len() != 0 {
match test_outcome {
TestOutcome::Passed | TestOutcome::Failed => {
test_outcome = TestOutcome::Inconclusive;
}
_ => {}
}
writeln!(writer, "\nThe following test(s) never completed:").expect("Cannot write logs");
for t in test_cases_in_progress {
writeln!(writer, "{}", t).expect("Cannot write logs");
}
}
let mut test_cases_executed: Vec<String> = test_cases_executed.into_iter().collect();
let mut test_cases_passed: Vec<String> = test_cases_passed.into_iter().collect();
test_cases_executed.sort();
test_cases_passed.sort();
Ok((test_outcome, test_cases_executed, test_cases_passed))
}