use crate::query::json::J;
use super::profile::REGISTERED_PROFILES;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CapabilityAxis {
ContextAcquisition,
SemanticConformance,
FrameworkModeling,
RepositoryEvidence,
RuntimeEvidence,
}
impl CapabilityAxis {
pub const ALL: [CapabilityAxis; 5] = [
CapabilityAxis::ContextAcquisition,
CapabilityAxis::SemanticConformance,
CapabilityAxis::FrameworkModeling,
CapabilityAxis::RepositoryEvidence,
CapabilityAxis::RuntimeEvidence,
];
pub fn label(self) -> &'static str {
match self {
CapabilityAxis::ContextAcquisition => "context-acquisition",
CapabilityAxis::SemanticConformance => "semantic-conformance",
CapabilityAxis::FrameworkModeling => "framework-modeling",
CapabilityAxis::RepositoryEvidence => "repository-evidence",
CapabilityAxis::RuntimeEvidence => "runtime-evidence",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExecutedObligation {
pub executed: u32,
}
impl ExecutedObligation {
pub fn record(executed: u32) -> Option<ExecutedObligation> {
if executed == 0 {
None
} else {
Some(ExecutedObligation { executed })
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CapabilityState {
Planned,
Unavailable,
SmokeTested,
Passing(ExecutedObligation),
Failing,
}
impl CapabilityState {
pub fn label(self) -> &'static str {
match self {
CapabilityState::Planned => "planned",
CapabilityState::Unavailable => "unavailable",
CapabilityState::SmokeTested => "smoke-tested",
CapabilityState::Passing(_) => "passing",
CapabilityState::Failing => "failing",
}
}
}
#[derive(Debug, Clone)]
pub struct MatrixEntry {
pub profile_id: &'static str,
pub axis: CapabilityAxis,
pub state: CapabilityState,
pub denominator: u32,
pub excluded: u32,
}
#[derive(Debug, Clone)]
pub struct CapabilityMatrix {
pub entries: Vec<MatrixEntry>,
}
fn axis_ceiling(ceiling: CapabilityState, axis: CapabilityAxis) -> CapabilityState {
match axis {
CapabilityAxis::RuntimeEvidence | CapabilityAxis::FrameworkModeling => match ceiling {
CapabilityState::Unavailable => CapabilityState::Unavailable,
_ => CapabilityState::Planned,
},
CapabilityAxis::ContextAcquisition
| CapabilityAxis::SemanticConformance
| CapabilityAxis::RepositoryEvidence => ceiling,
}
}
pub fn build_matrix() -> CapabilityMatrix {
let mut entries = Vec::new();
for profile in REGISTERED_PROFILES {
for axis in CapabilityAxis::ALL {
entries.push(MatrixEntry {
profile_id: profile.id,
axis,
state: axis_ceiling(profile.ceiling, axis),
denominator: 0,
excluded: 0,
});
}
}
CapabilityMatrix { entries }
}
pub fn matrix_to_json(matrix: &CapabilityMatrix) -> String {
let mut sorted = matrix.entries.clone();
sorted.sort_by(|a, b| {
a.profile_id
.cmp(b.profile_id)
.then_with(|| axis_order(a.axis).cmp(&axis_order(b.axis)))
});
J::Obj(vec![(
"entries",
J::Arr(
sorted
.iter()
.map(|e| {
J::Obj(vec![
("profile", J::Str(e.profile_id.to_string())),
("axis", J::Str(e.axis.label().to_string())),
("state", J::Str(e.state.label().to_string())),
("denominator", J::UInt(u64::from(e.denominator))),
("excluded", J::UInt(u64::from(e.excluded))),
])
})
.collect(),
),
)])
.to_json_string()
}
fn axis_order(axis: CapabilityAxis) -> usize {
CapabilityAxis::ALL
.iter()
.position(|a| *a == axis)
.unwrap_or(usize::MAX)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_executions_cannot_produce_a_witness() {
assert_eq!(ExecutedObligation::record(0), None);
assert!(ExecutedObligation::record(1).is_some());
}
#[test]
fn the_registry_only_matrix_holds_no_unexecuted_passing_entry() {
let matrix = build_matrix();
assert!(!matrix.entries.is_empty());
for entry in &matrix.entries {
assert!(
!matches!(entry.state, CapabilityState::Passing(_)),
"{} / {} claims passing with nothing executed",
entry.profile_id,
entry.axis.label()
);
}
}
#[test]
fn matrix_json_is_byte_identical_across_two_builds() {
let a = matrix_to_json(&build_matrix());
let b = matrix_to_json(&build_matrix());
assert_eq!(a, b);
}
#[test]
fn runtime_and_framework_axes_never_claim_more_than_planned_from_a_static_ceiling() {
let matrix = build_matrix();
for entry in matrix
.entries
.iter()
.filter(|e| e.profile_id == "csharp-net8.0-sdk")
{
match entry.axis {
CapabilityAxis::RuntimeEvidence | CapabilityAxis::FrameworkModeling => {
assert_eq!(
entry.state,
CapabilityState::Planned,
"{:?} must not inherit the profile's smoke-tested ceiling",
entry.axis
);
}
CapabilityAxis::ContextAcquisition
| CapabilityAxis::SemanticConformance
| CapabilityAxis::RepositoryEvidence => {
assert_eq!(entry.state, CapabilityState::SmokeTested);
}
}
}
}
#[test]
fn an_unavailable_profile_stays_unavailable_on_every_axis() {
let matrix = build_matrix();
for entry in matrix
.entries
.iter()
.filter(|e| e.profile_id == "csharp-netcoreapp2.x-sdk")
{
assert_eq!(entry.state, CapabilityState::Unavailable);
}
}
#[test]
fn matrix_json_is_sorted_by_profile_then_axis() {
let json = matrix_to_json(&build_matrix());
let first = json.find("csharp-net40-framework").unwrap();
let later = json.find("csharp-netstandard2.1-library").unwrap();
assert!(first < later, "profiles must sort lexically");
}
}