use std::collections::{BTreeMap, BTreeSet};
use supercov_contracts::{
AttributionPrecision, ExecutionModel, FrontendAttribution, FrontendLimitation,
FrontendLimitationScope, FrontendRunDeclaration, FrontendRunnerDeclaration,
LANGUAGE_FRONTEND_PROTOCOL_VERSION,
};
use crate::coverage_analysis::McdcVector;
use crate::coverage_report::{
CoverageManifest, CoverageModelDeclaration, CoveragePhase, CoverageReportRequest,
DecisionSnapshot, ExecutionScope, ExitCodeInput, PersistedCoverageModel, RawTestResult,
RuntimeEvent, RuntimeSnapshot, TestProvenance,
};
fn test_phase(test_id: &str) -> String {
format!("{test_id}#call")
}
use crate::evidence_archive::EvidenceArchiveEntry;
use crate::go_instrumenter::{GoProbe, GoProbeTarget};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OwnedEvidenceError {
Truncated(&'static str),
NoTests,
}
impl std::fmt::Display for OwnedEvidenceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OwnedEvidenceError::Truncated(part) => {
write!(f, "Coverage evidence ended in the middle of its {part}")
}
OwnedEvidenceError::NoTests => write!(
f,
"the run produced coverage evidence but no test announced itself"
),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PackedVector {
pub decision: u32,
pub key: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct OwnedTestEvidence {
pub name: String,
pub status: String,
pub runner: String,
pub probes: BTreeMap<u32, u32>,
pub vectors: Vec<PackedVector>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct OwnedEvidence {
pub global: Vec<u32>,
pub tests: Vec<OwnedTestEvidence>,
pub widths: Vec<u8>,
pub decision_vectors: Vec<Vec<u64>>,
}
struct Cursor<'a> {
bytes: &'a [u8],
offset: usize,
}
impl Cursor<'_> {
fn u64(&mut self, part: &'static str) -> Result<u64, OwnedEvidenceError> {
let end = self.offset + 8;
let slice = self
.bytes
.get(self.offset..end)
.ok_or(OwnedEvidenceError::Truncated(part))?;
self.offset = end;
Ok(u64::from_le_bytes(slice.try_into().expect("eight bytes")))
}
fn text(&mut self, length: usize, part: &'static str) -> Result<String, OwnedEvidenceError> {
let end = self.offset + length;
let slice = self
.bytes
.get(self.offset..end)
.ok_or(OwnedEvidenceError::Truncated(part))?;
self.offset = end;
String::from_utf8(slice.to_vec()).map_err(|_| OwnedEvidenceError::Truncated(part))
}
}
pub fn read_evidence(bytes: &[u8]) -> Result<OwnedEvidence, OwnedEvidenceError> {
let mut cursor = Cursor { bytes, offset: 0 };
let probe_count = cursor.u64("probe totals")? as usize;
let mut global = Vec::with_capacity(probe_count);
for _ in 0..probe_count {
global.push(cursor.u64("probe totals")? as u32);
}
let test_count = cursor.u64("test count")? as usize;
let mut tests = Vec::with_capacity(test_count);
for _ in 0..test_count {
let length = cursor.u64("test name")? as usize;
let name = cursor.text(length, "test name")?;
let status_length = cursor.u64("test status")? as usize;
let status = cursor.text(status_length, "test status")?;
let runner_length = cursor.u64("test runner")? as usize;
let runner = cursor.text(runner_length, "test runner")?;
let hits = cursor.u64("test probes")? as usize;
let mut probes = BTreeMap::new();
for _ in 0..hits {
let index = cursor.u64("test probes")? as u32;
probes.insert(index, cursor.u64("test probes")? as u32);
}
let vector_count = cursor.u64("test vectors")? as usize;
let mut vectors = Vec::with_capacity(vector_count);
for _ in 0..vector_count {
let decision = cursor.u64("test vectors")? as u32;
vectors.push(PackedVector {
decision,
key: cursor.u64("test vectors")?,
});
}
tests.push(OwnedTestEvidence {
name,
status,
runner,
probes,
vectors,
});
}
let decision_count = cursor.u64("decision table")? as usize;
let mut widths = Vec::with_capacity(decision_count);
let mut decision_vectors = Vec::with_capacity(decision_count);
for _ in 0..decision_count {
widths.push(cursor.u64("decision table")? as u8);
let keys = cursor.u64("decision table")? as usize;
let mut seen = Vec::with_capacity(keys);
for _ in 0..keys {
seen.push(cursor.u64("decision table")?);
}
decision_vectors.push(seen);
}
Ok(OwnedEvidence {
global,
tests,
widths,
decision_vectors,
})
}
const PACKED_VALUE_SHIFT: u32 = 24;
const PACKED_OUTCOME_SHIFT: u32 = 48;
pub fn unpack_vector(key: u64, width: u8) -> McdcVector {
let evaluated = key & ((1 << PACKED_VALUE_SHIFT) - 1);
let values = (key >> PACKED_VALUE_SHIFT) & ((1 << PACKED_VALUE_SHIFT) - 1);
McdcVector {
values: (0..width)
.map(|index| {
let bit = 1_u64 << index;
(evaluated & bit != 0).then_some(values & bit != 0)
})
.collect(),
outcome: (key >> PACKED_OUTCOME_SHIFT) & 1 == 1,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OwnedTestOutcome {
pub name: String,
pub package: String,
pub file: Option<String>,
pub status: String,
pub runner: String,
}
pub fn go_declaration() -> FrontendRunDeclaration {
FrontendRunDeclaration {
protocol_version: LANGUAGE_FRONTEND_PROTOCOL_VERSION,
frontend_id: "supercov-go".into(),
frontend_version: "go-owned-v1".into(),
language: "go".into(),
structural_source: supercov_contracts::StructuralSource::OwnedProbes,
runners: vec![FrontendRunnerDeclaration {
runner: "go-test".into(),
execution_model: ExecutionModel::SerialInProcess,
attribution: exact_per_test(),
limitations: {
let mut limitations = owned_attribution_limitations("go");
limitations.push(FrontendLimitation {
id: "go-parallel-tests".into(),
scopes: vec![FrontendLimitationScope::Test],
reason:
"a test that calls t.Parallel() runs alongside others, so work it does after that call is recorded run-wide rather than against that test"
.into(),
});
limitations
},
}],
structural_limitations: Vec::new(),
}
}
pub fn go_coverage_model() -> CoverageModelDeclaration {
CoverageModelDeclaration {
language: "go".into(),
variant: "go-owned-probes-v1".into(),
name: "supercov-go-owned-v1".into(),
completeness_meaning: "Every obligation Supercov derived from the module's own Go sources was observed; explicit manifest limitations identify unmeasured Go surfaces.".into(),
measured: vec![
"owned Go statements and function entries".into(),
"owned atomic condition vectors and decision outcomes".into(),
"exact per-test attribution for tests that do not call t.Parallel()".into(),
],
not_measured: vec![
"generated code, vendored packages and testdata".into(),
"work a test does after calling t.Parallel(), which counts run-wide".into(),
"causal linkage to individual actions".into(),
"all input values, semantic partitions, paths, or concurrency interleavings".into(),
"mutation score or assertion fault-detection strength".into(),
],
}
}
pub fn jvm_declaration() -> FrontendRunDeclaration {
let runner = |name: &str, concurrency: &str| FrontendRunnerDeclaration {
runner: name.into(),
execution_model: ExecutionModel::SerialInProcess,
attribution: exact_per_test(),
limitations: {
let mut limitations = owned_attribution_limitations(name);
limitations.push(FrontendLimitation {
id: format!("{name}-parallel-execution"),
scopes: vec![FrontendLimitationScope::Test],
reason: format!(
"with {concurrency}, tests overlap in one process; work they do concurrently is recorded run-wide rather than against a single test, and condition coverage is dropped because concurrent evaluations corrupt it"
),
});
limitations
},
};
FrontendRunDeclaration {
protocol_version: LANGUAGE_FRONTEND_PROTOCOL_VERSION,
frontend_id: "supercov-jvm".into(),
frontend_version: "jvm-owned-v1".into(),
language: "jvm".into(),
structural_source: supercov_contracts::StructuralSource::OwnedProbes,
runners: vec![
runner("junit-platform", "JUnit parallel execution enabled"),
runner("testng", "TestNG's parallel suites or methods"),
],
structural_limitations: Vec::new(),
}
}
pub fn jvm_coverage_model() -> CoverageModelDeclaration {
CoverageModelDeclaration {
language: "jvm".into(),
variant: "jvm-owned-probes-v1".into(),
name: "supercov-jvm-owned-v1".into(),
completeness_meaning: "Every obligation Supercov derived from the project's own Java and Kotlin sources was observed; explicit manifest limitations identify unmeasured JVM surfaces.".into(),
measured: vec![
"owned Java and Kotlin statements and method entries".into(),
"owned atomic condition vectors and decision outcomes".into(),
"exact per-test attribution through the JUnit Platform's own lifecycle".into(),
],
not_measured: vec![
"generated sources, and bytecode with no source in the project".into(),
"tests run concurrently, which count run-wide and drop condition coverage".into(),
"causal linkage to individual actions".into(),
"all input values, semantic partitions, paths, or concurrency interleavings".into(),
"mutation score or assertion fault-detection strength".into(),
],
}
}
fn owned_attribution_limitations(language: &str) -> Vec<FrontendLimitation> {
vec![
FrontendLimitation {
id: format!("{language}-phase-linkage-aggregate"),
scopes: vec![FrontendLimitationScope::Phase],
reason:
"probes record what a test reached, not which of its setup, body or teardown phases reached it"
.into(),
},
FrontendLimitation {
id: format!("{language}-action-linkage-unavailable"),
scopes: vec![FrontendLimitationScope::Action],
reason: "there is no general application-action lifecycle to link coverage to".into(),
},
FrontendLimitation {
id: format!("{language}-assertion-linkage-unavailable"),
scopes: vec![FrontendLimitationScope::Assertion],
reason:
"coverage is attributed to the test that reached the code, not to the assertion that checked it"
.into(),
},
]
}
fn exact_per_test() -> FrontendAttribution {
FrontendAttribution {
run: AttributionPrecision::Exact,
worker: AttributionPrecision::Exact,
test: AttributionPrecision::Exact,
retry: AttributionPrecision::Exact,
phase: AttributionPrecision::Aggregate,
action: AttributionPrecision::Unavailable,
assertion: AttributionPrecision::Unavailable,
}
}
pub fn merge_evidence(parts: Vec<OwnedEvidence>) -> OwnedEvidence {
let mut merged = OwnedEvidence::default();
for part in parts {
if merged.global.len() < part.global.len() {
merged.global.resize(part.global.len(), 0);
}
for (slot, value) in merged.global.iter_mut().zip(part.global) {
*slot |= value;
}
if merged.widths.len() < part.widths.len() {
merged.widths.resize(part.widths.len(), 0);
merged
.decision_vectors
.resize(part.widths.len(), Vec::new());
}
for (id, width) in part.widths.into_iter().enumerate() {
merged.widths[id] = merged.widths[id].max(width);
}
for (id, keys) in part.decision_vectors.into_iter().enumerate() {
let seen = &mut merged.decision_vectors[id];
for key in keys {
if !seen.contains(&key) {
seen.push(key);
}
}
}
merged.tests.extend(part.tests);
}
merged
}
fn stable_id(prefix: &str, values: &[&str]) -> String {
use sha2::{Digest, Sha256};
let mut hash = Sha256::new();
for value in values {
hash.update(value.as_bytes());
hash.update([0]);
}
let digest = hash.finalize();
let mut encoded = String::with_capacity(prefix.len() + 25);
encoded.push_str(prefix);
encoded.push(':');
for byte in &digest[..12] {
use std::fmt::Write as _;
write!(&mut encoded, "{byte:02x}").expect("string formatting");
}
encoded
}
fn obligations(probes: &BTreeMap<u64, GoProbe>) -> BTreeMap<u32, String> {
probes
.iter()
.map(|(id, probe)| {
let obligation = match &probe.target {
GoProbeTarget::Statement { id } | GoProbeTarget::Function { id } => id.clone(),
GoProbeTarget::Alternative { alternative, .. } => alternative.clone(),
};
(*id as u32, obligation)
})
.collect()
}
fn snapshot(
environment: &str,
evidence: &OwnedTestEvidence,
manifest: &CoverageManifest,
by_probe: &BTreeMap<u32, String>,
phase: &str,
) -> RuntimeSnapshot {
let mut events = Vec::new();
let mut clock = 0_i64;
let hits = evidence
.probes
.keys()
.filter_map(|probe| by_probe.get(probe).cloned())
.collect::<BTreeSet<_>>();
for id in &hits {
events.push(RuntimeEvent {
event_type: "hit".into(),
id: id.clone(),
vector: None,
timestamp_ms: clock,
phase_id: Some(phase.to_owned()),
statement_id: None,
environment: environment.into(),
});
clock += 1;
}
let mut by_decision: BTreeMap<u32, Vec<u64>> = BTreeMap::new();
for vector in &evidence.vectors {
by_decision
.entry(vector.decision)
.or_default()
.push(vector.key);
}
let mut decisions = Vec::new();
for (index, meta) in manifest.decisions.iter().enumerate() {
let Some(keys) = by_decision.get(&(index as u32)) else {
continue;
};
let width = meta.conditions.len() as u8;
let observed = keys
.iter()
.map(|key| unpack_vector(*key, width))
.collect::<Vec<_>>();
for vector in &observed {
events.push(RuntimeEvent {
event_type: "decision".into(),
id: meta.id.clone(),
vector: Some(vector.clone()),
timestamp_ms: clock,
phase_id: Some(phase.to_owned()),
statement_id: None,
environment: environment.into(),
});
clock += 1;
}
decisions.push(DecisionSnapshot {
meta: meta.clone(),
vectors: observed,
});
}
RuntimeSnapshot {
decisions,
hits: hits.into_iter().collect(),
events,
logicals: Vec::new(),
}
}
pub struct OwnedFrontendRun {
pub declaration: FrontendRunDeclaration,
pub request: CoverageReportRequest,
pub tests: usize,
}
impl OwnedFrontendRun {
pub fn archive_entries(&self) -> Result<Vec<EvidenceArchiveEntry>, serde_json::Error> {
let model = PersistedCoverageModel::from_declaration(
self.request
.coverage_model
.as_ref()
.expect("an owned frontend always declares a coverage model"),
)
.expect("owned coverage models are contract-valid");
let mut entries = vec![
EvidenceArchiveEntry {
path: "coverage-model.json".into(),
contents: serde_json::to_vec(&model)?,
},
EvidenceArchiveEntry {
path: "frontend.json".into(),
contents: serde_json::to_vec(&self.declaration)?,
},
EvidenceArchiveEntry {
path: "manifest.json".into(),
contents: serde_json::to_vec(&self.request.manifest)?,
},
];
for (index, result) in self.request.raw_results.iter().enumerate() {
entries.push(EvidenceArchiveEntry {
path: format!("results/{index:08}/mcdc.json"),
contents: serde_json::to_vec(result)?,
});
}
Ok(entries)
}
}
pub struct OwnedRunInputs<'a> {
pub declaration: FrontendRunDeclaration,
pub environment: &'a str,
pub manifest: &'a CoverageManifest,
pub probes: &'a BTreeMap<u64, GoProbe>,
pub evidence: &'a OwnedEvidence,
pub outcomes: &'a [OwnedTestOutcome],
pub run_id: &'a str,
pub generated_at: &'a str,
pub test_exit_code: i32,
pub coverage_model: CoverageModelDeclaration,
}
pub fn build_frontend_run(inputs: OwnedRunInputs) -> Result<OwnedFrontendRun, OwnedEvidenceError> {
let OwnedRunInputs {
declaration,
environment,
manifest,
probes,
evidence,
outcomes,
run_id,
generated_at,
test_exit_code,
coverage_model,
} = inputs;
if outcomes.is_empty() {
return Err(OwnedEvidenceError::NoTests);
}
let default_runner = declaration
.runners
.first()
.map(|runner| runner.runner.clone())
.ok_or(OwnedEvidenceError::NoTests)?;
let declared = declaration
.runners
.iter()
.map(|runner| runner.runner.as_str())
.collect::<BTreeSet<_>>();
let source = declaration.frontend_version.clone();
let by_probe = obligations(probes);
let recorded = evidence
.tests
.iter()
.map(|test| (test.name.clone(), test))
.collect::<BTreeMap<_, _>>();
let empty = OwnedTestEvidence::default();
let mut raw_results = outcomes
.iter()
.map(|outcome| {
let test = recorded.get(&outcome.name).copied().unwrap_or(&empty);
let test_id = format!("{}::{}", outcome.package, outcome.name);
let phase = test_phase(&test_id);
let provenance = TestProvenance {
runner: if declared.contains(outcome.runner.as_str()) {
outcome.runner.clone()
} else {
default_runner.clone()
},
kind: "unit".into(),
project: Some(outcome.package.clone()),
source: source.clone(),
};
RawTestResult {
scope: Some(ExecutionScope {
version: 1,
run_id: run_id.to_owned(),
worker_id: outcome.package.clone(),
test_id: test_id.clone(),
test_key: stable_id("owned-test", &[&outcome.package, &outcome.name]),
retry: 0,
attempt_id: stable_id(
"owned-attempt",
&[run_id, &outcome.package, &outcome.name, "0"],
),
}),
test_id: Some(test_id),
test: outcome.name.clone(),
test_file: outcome.file.clone(),
title: None,
retry: Some(0),
status: Some(outcome.status.clone()),
expected_status: None,
flaky: false,
provenance: provenance.clone(),
role: "test".into(),
phases: vec![CoveragePhase {
id: phase.clone(),
kind: "test".into(),
operation: format!("{} {}", provenance.runner, outcome.name),
source: outcome.file.clone(),
caused_by_phase_id: None,
started_at_ms: 0,
ended_at_ms: None,
status: Some(outcome.status.clone()),
error: None,
}],
runtime: vec![snapshot(environment, test, manifest, &by_probe, &phase)],
browser: Vec::new(),
server: Vec::new(),
}
})
.collect::<Vec<_>>();
let claimed = evidence
.tests
.iter()
.flat_map(|test| test.probes.keys().copied())
.collect::<BTreeSet<_>>();
let unclaimed = evidence
.global
.iter()
.enumerate()
.filter(|(index, mask)| **mask != 0 && !claimed.contains(&(*index as u32)))
.map(|(index, mask)| (index as u32, *mask))
.collect::<BTreeMap<_, _>>();
if !unclaimed.is_empty() {
let status = if test_exit_code == 0 {
"passed"
} else {
"failed"
};
let test_id = format!("{}::background", declaration.language);
let phase = test_phase(&test_id);
let background = OwnedTestEvidence {
name: "background".into(),
status: "unknown".into(),
runner: String::new(),
probes: unclaimed,
vectors: Vec::new(),
};
raw_results.push(RawTestResult {
scope: Some(ExecutionScope {
version: 1,
run_id: run_id.to_owned(),
worker_id: "background".into(),
test_id: test_id.clone(),
test_key: stable_id("owned-background", &[run_id]),
retry: 0,
attempt_id: stable_id("owned-background-attempt", &[run_id]),
}),
test_id: Some(test_id),
test: "execution no test could be credited with".into(),
test_file: None,
title: None,
retry: Some(0),
status: Some(status.into()),
expected_status: None,
flaky: false,
provenance: TestProvenance {
runner: default_runner.clone(),
kind: "background".into(),
project: None,
source: source.clone(),
},
role: "background".into(),
phases: vec![CoveragePhase {
id: phase.clone(),
kind: "background".into(),
operation: "execution outside any test".into(),
source: None,
caused_by_phase_id: None,
started_at_ms: 0,
ended_at_ms: Some(0),
status: Some(status.into()),
error: None,
}],
runtime: vec![snapshot(
environment,
&background,
manifest,
&by_probe,
&phase,
)],
browser: Vec::new(),
server: Vec::new(),
});
}
let observed = raw_results
.iter()
.map(|result| result.provenance.runner.as_str())
.collect::<BTreeSet<_>>();
let mut declaration = declaration;
declaration.structural_limitations = manifest
.limitations
.iter()
.filter_map(|limitation| limitation.get("id")?.as_str().map(str::to_owned))
.collect::<BTreeSet<_>>()
.into_iter()
.collect();
if declaration
.runners
.iter()
.any(|runner| observed.contains(runner.runner.as_str()))
{
declaration
.runners
.retain(|runner| observed.contains(runner.runner.as_str()));
}
Ok(OwnedFrontendRun {
declaration,
tests: raw_results.len(),
request: CoverageReportRequest {
run_id: run_id.to_owned(),
manifest: manifest.clone(),
raw_results,
generated_at: generated_at.to_owned(),
coverage_model: Some(coverage_model),
integrity: None,
test_exit_code: ExitCodeInput::Present(Some(test_exit_code)),
},
})
}
#[cfg(test)]
mod tests {
use super::*;
fn write(values: &[u64]) -> Vec<u8> {
values.iter().flat_map(|v| v.to_le_bytes()).collect()
}
#[test]
fn an_unevaluated_condition_is_absent_rather_than_false() {
let short_circuited = unpack_vector(0b01, 2);
assert_eq!(short_circuited.values, [Some(false), None]);
assert!(!short_circuited.outcome);
let both = unpack_vector(0b11 | (0b11 << 24) | (1 << 48), 2);
assert_eq!(both.values, [Some(true), Some(true)]);
assert!(both.outcome);
let first_false = unpack_vector(0b11 | (0b01 << 24), 2);
assert_eq!(first_false.values, [Some(true), Some(false)]);
}
#[test]
fn a_truncated_file_is_refused_rather_than_read_short() {
let full = write(&[2, 0, 2, 1, 4, u64::from_le_bytes(*b"Test\0\0\0\0"), 0, 0, 0]);
for cut in [0, 8, 16, 24, 32] {
assert!(
read_evidence(&full[..cut.min(full.len())]).is_err(),
"a file cut at {cut} bytes must not decode"
);
}
assert!(matches!(
read_evidence(&write(&[5, 1])),
Err(OwnedEvidenceError::Truncated("probe totals"))
));
}
#[test]
fn a_run_with_no_tests_is_an_error_not_an_empty_report() {
let evidence = OwnedEvidence::default();
let manifest = CoverageManifest {
decisions: Vec::new(),
points: Vec::new(),
branches: Vec::new(),
limitations: Vec::new(),
unmeasured: Vec::new(),
scope: None,
};
assert_eq!(
build_frontend_run(OwnedRunInputs {
declaration: go_declaration(),
environment: "go",
manifest: &manifest,
probes: &BTreeMap::new(),
evidence: &evidence,
outcomes: &[],
run_id: "run",
generated_at: "now",
test_exit_code: 0,
coverage_model: go_coverage_model(),
})
.err(),
Some(OwnedEvidenceError::NoTests)
);
}
#[test]
fn each_declaration_says_what_its_runner_can_and_cannot_attribute() {
let jvm = jvm_declaration();
let jvm_runner = &jvm.runners[0];
assert_eq!(jvm_runner.runner, "junit-platform");
let gaps = jvm_runner
.limitations
.iter()
.map(|limitation| limitation.id.as_str())
.collect::<Vec<_>>();
assert!(
gaps.contains(&"junit-platform-parallel-execution"),
"{gaps:?}"
);
let named = jvm
.runners
.iter()
.map(|runner| runner.runner.as_str())
.collect::<Vec<_>>();
assert_eq!(named, ["junit-platform", "testng"]);
}
#[test]
fn the_declaration_says_what_go_can_and_cannot_attribute() {
let declared = go_declaration();
let runner = &declared.runners[0];
assert_eq!(runner.execution_model, ExecutionModel::SerialInProcess);
assert_eq!(runner.attribution.test, AttributionPrecision::Exact);
assert_eq!(
runner.attribution.assertion,
AttributionPrecision::Unavailable
);
let gaps = runner
.limitations
.iter()
.map(|limitation| limitation.id.as_str())
.collect::<Vec<_>>();
assert!(gaps.contains(&"go-parallel-tests"), "{gaps:?}");
for gap in [
"go-phase-linkage-aggregate",
"go-action-linkage-unavailable",
"go-assertion-linkage-unavailable",
] {
assert!(gaps.contains(&gap), "{gaps:?}");
}
}
#[test]
fn a_test_the_runner_saw_but_that_recorded_nothing_still_appears() {
let manifest = CoverageManifest {
decisions: Vec::new(),
points: Vec::new(),
branches: Vec::new(),
limitations: Vec::new(),
unmeasured: Vec::new(),
scope: None,
};
let run = build_frontend_run(OwnedRunInputs {
declaration: go_declaration(),
environment: "go",
manifest: &manifest,
probes: &BTreeMap::new(),
evidence: &OwnedEvidence::default(),
outcomes: &[OwnedTestOutcome {
name: "TestSilent".into(),
runner: String::new(),
package: "example.com/p".into(),
file: Some("p/x_test.go".into()),
status: "passed".into(),
}],
run_id: "run",
generated_at: "now",
test_exit_code: 0,
coverage_model: go_coverage_model(),
})
.expect("run");
assert_eq!(run.tests, 1);
let result = &run.request.raw_results[0];
assert_eq!(result.test, "TestSilent");
assert_eq!(result.test_id.as_deref(), Some("example.com/p::TestSilent"));
assert!(result.runtime[0].hits.is_empty());
}
#[test]
fn both_owned_declarations_satisfy_the_contract_they_are_read_back_through() {
for declaration in [go_declaration(), jvm_declaration()] {
let language = declaration.language.clone();
supercov_contracts::validate_frontend_run_declaration(&declaration)
.unwrap_or_else(|error| panic!("{language} declaration is unreadable: {error}"));
}
}
#[test]
fn merging_processes_unions_the_run_and_keeps_every_test() {
let left = OwnedEvidence {
global: vec![0b01, 0b00],
tests: vec![OwnedTestEvidence {
name: "TestA".into(),
status: "passed".into(),
..Default::default()
}],
widths: vec![2],
decision_vectors: vec![vec![0b01]],
};
let right = OwnedEvidence {
global: vec![0b10, 0b10],
tests: vec![OwnedTestEvidence {
name: "TestB".into(),
status: "failed".into(),
..Default::default()
}],
widths: vec![2],
decision_vectors: vec![vec![0b01, 0b11]],
};
let merged = merge_evidence(vec![left, right]);
assert_eq!(merged.global, [0b11, 0b10]);
assert_eq!(
merged
.tests
.iter()
.map(|test| test.name.as_str())
.collect::<Vec<_>>(),
["TestA", "TestB"]
);
assert_eq!(merged.decision_vectors, [vec![0b01, 0b11]]);
}
}