use std::fmt;
use std::fs;
use std::path::PathBuf;
use std::time::Duration;
use anyhow::Context;
use serde::Serialize;
use crate::exit_code;
use crate::log_file::LogFile;
use crate::*;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize)]
pub enum Phase {
Check,
Build,
Test,
}
impl Phase {
pub fn name(&self) -> &'static str {
match self {
Phase::Check => "check",
Phase::Build => "build",
Phase::Test => "test",
}
}
pub const ALL: &'static [Phase] = &[Phase::Check, Phase::Build, Phase::Test];
}
impl fmt::Display for Phase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Default, Serialize)]
pub struct LabOutcome {
outcomes: Vec<Outcome>,
}
impl LabOutcome {
pub fn add(&mut self, outcome: &Outcome) {
self.outcomes.push(outcome.clone());
}
pub fn exit_code(&self) -> i32 {
if self
.outcomes
.iter()
.any(|o| !o.scenario.is_mutant() && !o.success())
{
exit_code::CLEAN_TESTS_FAILED
} else if self.outcomes.iter().any(|o| o.has_timeout()) {
exit_code::TIMEOUT
} else if self.outcomes.iter().any(|o| o.mutant_missed()) {
exit_code::FOUND_PROBLEMS
} else {
exit_code::SUCCESS
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
#[must_use]
pub struct Outcome {
log_path: PathBuf,
pub scenario: Scenario,
phase_results: Vec<PhaseResult>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
struct PhaseResult {
phase: Phase,
duration: Duration,
cargo_result: CargoResult,
}
impl Outcome {
pub fn new(log_file: &LogFile, scenario: Scenario) -> Outcome {
Outcome {
log_path: log_file.path().to_owned(),
scenario,
phase_results: Vec::new(),
}
}
pub fn add_phase_result(
&mut self,
phase: Phase,
duration: Duration,
cargo_result: CargoResult,
) {
self.phase_results.push(PhaseResult {
phase,
duration,
cargo_result,
});
}
pub fn get_log_content(&self) -> Result<String> {
fs::read_to_string(&self.log_path).context("read log file")
}
pub fn last_phase(&self) -> Phase {
self.phase_results.last().unwrap().phase
}
pub fn last_phase_result(&self) -> CargoResult {
self.phase_results.last().unwrap().cargo_result
}
pub fn should_show_logs(&self) -> bool {
!self.scenario.is_mutant() && !self.success()
}
pub fn success(&self) -> bool {
self.last_phase_result().success()
}
pub fn has_timeout(&self) -> bool {
self.phase_results
.iter()
.any(|pr| pr.cargo_result == CargoResult::Timeout)
}
pub fn check_or_build_failed(&self) -> bool {
self.phase_results
.iter()
.any(|pr| pr.phase != Phase::Test && pr.cargo_result == CargoResult::Failure)
}
pub fn mutant_caught(&self) -> bool {
self.scenario.is_mutant()
&& self.last_phase() == Phase::Test
&& self.last_phase_result() == CargoResult::Failure
}
pub fn mutant_missed(&self) -> bool {
self.scenario.is_mutant()
&& self.last_phase() == Phase::Test
&& self.last_phase_result().success()
}
pub fn test_duration(&self) -> Option<Duration> {
if let Some(phase_result) = self.phase_results.last() {
if phase_result.phase == Phase::Test {
return Some(phase_result.duration);
}
}
None
}
}