use super::parse::{LineReading, read_line};
use crate::muterprater::backend::roster::{collected, matched};
use crate::muterprater::backend::types::{
AdapterProfile, AnnouncedRoster, ArtifactManifestRefusal, BackendOutputId,
BackendVersionPosture, CompiledSuiteArtifactManifest, FamilyLookup, GrammarVersion,
MutationBackendInvocation, MutationSourceRevision, OwnerLookup, ReadingSource, UnparsedLine,
WrapOutcomeWord, WrapReading, WrapRefusal, WrappedBackend,
};
use crate::muterprater::types::{
ActivationDisposition, BaselineAxis, BaselineQualification, EquivalenceAxis, ExecutionAxis,
FamilyAttribution, InconclusiveCause, IntendedRejection, MappingPosture, MaterializationAxis,
MutantId, MutationIdentity, MutationReport, MutationRun, MutationSite, MutationTarget,
SourceCoordinate,
};
use crate::report::ForeignText;
use std::collections::BTreeSet;
const CONSOLE_GRAMMAR_VERSION: u32 = 1;
const WRAP_ACTIVATION: ActivationDisposition = ActivationDisposition::UnobservableUnderBackend;
const WRAP_EQUIVALENCE: EquivalenceAxis = EquivalenceAxis::NotAssessed;
#[must_use]
pub fn console_profile(version: BackendVersionPosture) -> AdapterProfile {
AdapterProfile::stated(
WrappedBackend::CargoMutants,
version,
ReadingSource::ConsoleStream,
GrammarVersion::adapter(CONSOLE_GRAMMAR_VERSION),
)
}
pub fn read_output(
text: &str,
version: BackendVersionPosture,
owner: OwnerLookup,
family_lookup: FamilyLookup,
) -> Result<WrapReading, WrapRefusal> {
let profile = console_profile(version);
let baseline = read_baseline(text)?;
let mut reports: Vec<MutationReport> = Vec::new();
let mut unparsed: Vec<UnparsedLine> = Vec::new();
let mut announced = AnnouncedRoster::Unstated;
for (ordinal, line) in text.lines().enumerate() {
match read_line(line) {
LineReading::Roster(found) => announced = AnnouncedRoster::Stated(found),
LineReading::Baseline(_) => {}
LineReading::Mutant {
word,
coordinate,
damage,
line: whole,
} => {
let target = targeted(&coordinate, damage.as_bytes(), owner, family_lookup);
reports.push(recorded(ordinal, word, target, whole, baseline)?);
}
LineReading::Unread => unparsed.push(UnparsedLine::unread(ordinal, line.as_bytes())),
}
}
WrapReading::read(
profile,
MutationRun::recorded(baseline, reports),
announced,
unparsed,
)
}
pub fn read_artifact(
text: &str,
invocation: MutationBackendInvocation,
sources: Vec<MutationSourceRevision>,
owner: OwnerLookup,
family_lookup: FamilyLookup,
) -> Result<CompiledSuiteArtifactManifest, ArtifactManifestRefusal> {
let reading = match invocation.backend() {
WrappedBackend::CargoMutants => read_output(
text,
BackendVersionPosture::Stated(invocation.version().clone()),
owner,
family_lookup,
),
}
.map_err(ArtifactManifestRefusal::Reading)?;
let supplied = collected(sources, ArtifactManifestRefusal::DuplicateSource)?;
let mut reported = BTreeSet::new();
for report in reading.run().reports() {
match report.target().site() {
MutationSite::Reported(coordinate) => {
reported.insert(coordinate.file());
}
MutationSite::Declared(_) => {
return Err(ArtifactManifestRefusal::MutationSiteNotReported);
}
}
}
matched(
&supplied,
&reported,
ArtifactManifestRefusal::ReportedSourceMissing,
ArtifactManifestRefusal::SourceNotReported,
)?;
Ok(CompiledSuiteArtifactManifest::recorded(
invocation,
BackendOutputId::derived(text.as_bytes()),
supplied.into_values().collect(),
reading,
))
}
fn read_baseline(text: &str) -> Result<BaselineQualification, WrapRefusal> {
for line in text.lines() {
if let LineReading::Baseline(axis) = read_line(line) {
return BaselineQualification::read(axis).map_err(WrapRefusal::BaselineNotQualified);
}
}
Err(WrapRefusal::BaselineNotStated)
}
fn recorded(
ordinal: usize,
word: WrapOutcomeWord,
target: MutationTarget,
line: &str,
baseline: BaselineQualification,
) -> Result<MutationReport, WrapRefusal> {
let axis = baseline.axis();
let cause = match word {
WrapOutcomeWord::Caught => {
return MutationReport::killed(
target,
axis,
MaterializationAxis::from(word),
WRAP_ACTIVATION,
ExecutionAxis::from(word),
IntendedRejection::ReportedByBackend {
stated: ForeignText::admitted(line.as_bytes()),
},
WRAP_EQUIVALENCE,
)
.map_err(|cause| WrapRefusal::KillNotLawful { ordinal, cause });
}
WrapOutcomeWord::Missed => InconclusiveCause::UnobservableAndUnrejected,
WrapOutcomeWord::Unviable | WrapOutcomeWord::ToolFailed => {
InconclusiveCause::NotMaterialized
}
WrapOutcomeWord::TimedOut => InconclusiveCause::WitnessIncomplete,
};
Ok(unlearned(target, axis, word, cause))
}
fn unlearned(
target: MutationTarget,
axis: BaselineAxis,
word: WrapOutcomeWord,
cause: InconclusiveCause,
) -> MutationReport {
MutationReport::inconclusive(
target,
axis,
MaterializationAxis::from(word),
WRAP_ACTIVATION,
ExecutionAxis::from(word),
cause,
WRAP_EQUIVALENCE,
)
}
fn targeted(
coordinate: &SourceCoordinate,
damage: &[u8],
owner: OwnerLookup,
family: FamilyLookup,
) -> MutationTarget {
let attribution = family(coordinate, damage).map_or(
FamilyAttribution::OutsideTheBank,
FamilyAttribution::Declared,
);
let posture = owner(coordinate).map_or(MappingPosture::OwnerUnmapped, MappingPosture::Mapped);
MutationTarget::pressed(
MutationIdentity::External(MutantId::over(coordinate, damage)),
attribution,
MutationSite::Reported(coordinate.clone()),
posture,
)
}