use crate::discovered::{DiscoveryLayer, compose, deep_merge, deep_merge_attributed};
use crate::source::ConfigSource;
use figment::value::Dict;
use figment::{Figment, providers::Serialized};
use serde::{Serialize, de::DeserializeOwned};
use std::collections::BTreeMap;
use std::env;
use std::path::PathBuf;
#[non_exhaustive]
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
gen_platform::TypedDispatcher,
gen_platform::Discriminant,
gen_platform::IsVariant,
gen_platform::FromStrKind,
)]
#[discriminant(also_display)]
pub enum ConfigTierKind {
Bare,
Discovered,
#[allow(clippy::module_name_repetitions)]
Default,
Custom,
}
gen_platform::register_dispatcher!("shikumi.config-tier-kind", ConfigTierKind);
impl ConfigTierKind {
pub const ALL: &'static [Self] = &[Self::Bare, Self::Discovered, Self::Default, Self::Custom];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Bare => "bare",
Self::Discovered => "discovered",
Self::Default => "default",
Self::Custom => "custom",
}
}
#[allow(clippy::should_implement_trait)]
#[must_use]
pub fn from_str(s: &str) -> Option<Self> {
<Self as crate::ClosedAxisLabel>::from_canonical_str(s)
}
}
impl crate::ClosedAxis for ConfigTierKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for ConfigTierKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigTier {
Bare,
Discovered,
#[allow(clippy::module_name_repetitions)]
Default,
Custom(std::path::PathBuf),
}
impl Default for ConfigTier {
fn default() -> Self {
Self::Default
}
}
impl ConfigTier {
#[must_use]
pub fn from_env(env_var: &str) -> Self {
env::var(env_var)
.map(|raw| Self::from_str_or_default(&raw))
.unwrap_or_default()
}
#[must_use]
pub fn from_str_or_default(s: &str) -> Self {
let normalized = s.trim().to_ascii_lowercase();
if normalized.is_empty() {
return Self::default();
}
match ConfigTierKind::from_str(&normalized) {
Some(ConfigTierKind::Bare) => Self::Bare,
Some(ConfigTierKind::Discovered) => Self::Discovered,
Some(ConfigTierKind::Default) => Self::Default,
Some(ConfigTierKind::Custom) | None => {
Self::Custom(std::path::PathBuf::from(normalized))
}
}
}
#[must_use]
pub fn name(&self) -> &'static str {
self.kind().as_str()
}
#[must_use]
pub const fn kind(&self) -> ConfigTierKind {
match self {
Self::Bare => ConfigTierKind::Bare,
Self::Discovered => ConfigTierKind::Discovered,
Self::Default => ConfigTierKind::Default,
Self::Custom(_) => ConfigTierKind::Custom,
}
}
}
pub trait TieredConfig: Sized + Clone + Serialize + DeserializeOwned {
fn bare() -> Self;
fn discovered() -> Self {
Self::bare()
}
fn prescribed_default() -> Self;
fn extend(self, _base: &Self) -> Self {
self
}
fn resolve_tier(tier: ConfigTier) -> Self {
match tier {
ConfigTier::Bare => Self::bare(),
ConfigTier::Discovered => Self::discovered(),
ConfigTier::Default => Self::prescribed_default(),
ConfigTier::Custom(path) => {
let base = Self::prescribed_default();
match std::fs::read_to_string(&path) {
Ok(s) => match serde_yaml::from_str::<Self>(&s) {
Ok(overlay) => overlay.extend(&base),
Err(e) => {
tracing::warn!(
target: "shikumi::tiered",
error = %e,
path = %path.display(),
"custom tier YAML failed to deserialize — falling back to prescribed_default"
);
base
}
},
Err(e) => {
tracing::warn!(
target: "shikumi::tiered",
error = %e,
path = %path.display(),
"custom tier YAML not readable — falling back to prescribed_default"
);
base
}
}
}
}
}
fn resolve_from_env(env_var: &str) -> Self {
Self::resolve_tier(ConfigTier::from_env(env_var))
}
#[must_use]
fn resolve_progressive() -> ProgressiveResolution<Self> {
Self::resolve_progressive_with(&[])
}
#[must_use]
fn resolve_progressive_with(overlays: &[ProgressiveLayer]) -> ProgressiveResolution<Self> {
let mut layers: Vec<(Provenance, Dict)> = vec![
(
Provenance::computed(ConfigTierKind::Bare),
tiered_to_dict(&Self::bare()),
),
(
Provenance::computed(ConfigTierKind::Discovered),
tiered_to_dict(&Self::discovered()),
),
(
Provenance::computed(ConfigTierKind::Default),
tiered_to_dict(&Self::prescribed_default()),
),
];
layers.extend(
overlays
.iter()
.map(|ov| (ov.provenance().clone(), ov.dict().clone())),
);
layers.sort_by_key(|(prov, _)| prov.tier_ordinal());
let mut merged = Dict::new();
let mut attribution: BTreeMap<Vec<String>, Provenance> = BTreeMap::new();
for (prov, dict) in layers {
deep_merge_attributed(&mut merged, dict, &[], &prov, &mut attribution, true);
}
let value = Figment::new()
.merge(Serialized::defaults(&merged))
.extract::<Self>()
.unwrap_or_else(|_| Self::prescribed_default());
ProgressiveResolution {
value,
provenance: ProvenanceMap { inner: attribution },
}
}
#[must_use]
fn discovered_from_layers(layers: &[&dyn DiscoveryLayer]) -> Self {
let mut merged = tiered_to_dict(&Self::bare());
deep_merge(&mut merged, compose(layers));
Figment::new()
.merge(Serialized::defaults(&merged))
.extract::<Self>()
.unwrap_or_else(|_| Self::bare())
}
fn diff_against(&self, baseline: &Self) -> ConfigDiff {
let a = serde_yaml::to_string(baseline).unwrap_or_default();
let b = serde_yaml::to_string(self).unwrap_or_default();
ConfigDiff::from_yaml_pair(&a, &b)
}
}
fn tiered_to_dict<T: Serialize>(value: &T) -> Dict {
Figment::new()
.merge(Serialized::defaults(value))
.extract::<Dict>()
.unwrap_or_default()
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Provenance {
tier: ConfigTierKind,
source: ConfigSource,
}
impl Provenance {
#[must_use]
pub fn new(tier: ConfigTierKind, source: ConfigSource) -> Self {
Self { tier, source }
}
#[must_use]
pub fn computed(tier: ConfigTierKind) -> Self {
Self {
tier,
source: ConfigSource::Defaults,
}
}
#[must_use]
pub fn file(path: impl Into<PathBuf>) -> Self {
Self {
tier: ConfigTierKind::Custom,
source: ConfigSource::File(path.into()),
}
}
#[must_use]
pub fn env(prefix: impl Into<String>) -> Self {
Self {
tier: ConfigTierKind::Custom,
source: ConfigSource::Env(prefix.into()),
}
}
#[must_use]
pub fn tier(&self) -> ConfigTierKind {
self.tier
}
#[must_use]
pub fn source(&self) -> &ConfigSource {
&self.source
}
#[must_use]
pub fn tier_ordinal(&self) -> usize {
crate::axis_ordinal(self.tier)
}
}
impl std::fmt::Display for Provenance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.tier.as_str())?;
match &self.source {
ConfigSource::Defaults => Ok(()),
ConfigSource::Env(prefix) => write!(f, " (env: {prefix})"),
ConfigSource::File(path) => write!(f, " (file: {})", path.display()),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ProvenanceMap {
inner: BTreeMap<Vec<String>, Provenance>,
}
impl ProvenanceMap {
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[must_use]
pub fn provenance_of(&self, path: &[&str]) -> Option<&Provenance> {
self.provenance_of_owned(&path.iter().map(|&s| s.to_owned()).collect::<Vec<String>>())
}
#[must_use]
pub fn provenance_of_owned(&self, path: &[String]) -> Option<&Provenance> {
self.inner.get(path)
}
#[must_use]
pub fn entries(&self) -> ProvenanceMapEntries<'_> {
ProvenanceMapEntries {
inner: self.inner.iter(),
}
}
#[must_use]
pub fn iter(&self) -> ProvenanceMapEntries<'_> {
self.entries()
}
#[must_use]
pub fn tier_histogram(&self) -> crate::AxisHistogram<ConfigTierKind> {
crate::axis_histogram(self.inner.values().map(|prov| prov.tier))
}
#[must_use]
pub fn contributing_tiers(&self) -> Vec<ConfigTierKind> {
self.tier_histogram().observed().collect()
}
#[must_use]
pub fn absent_tiers(&self) -> Vec<ConfigTierKind> {
self.tier_histogram().unobserved().collect()
}
#[must_use]
pub fn dominant_tier(&self) -> Option<ConfigTierKind> {
self.tier_histogram().dominant_cell()
}
#[must_use]
pub fn peak_tier_count(&self) -> usize {
self.tier_histogram().peak_count()
}
#[must_use]
pub fn trough_tier_count(&self) -> usize {
self.tier_histogram().trough_count()
}
#[must_use]
pub fn recessive_tier(&self) -> Option<ConfigTierKind> {
self.tier_histogram().recessive_cell()
}
}
#[derive(Clone, Debug)]
pub struct ProvenanceMapEntries<'a> {
inner: std::collections::btree_map::Iter<'a, Vec<String>, Provenance>,
}
impl<'a> Iterator for ProvenanceMapEntries<'a> {
type Item = (&'a [String], &'a Provenance);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, v)| (k.as_slice(), v))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl DoubleEndedIterator for ProvenanceMapEntries<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(k, v)| (k.as_slice(), v))
}
}
impl ExactSizeIterator for ProvenanceMapEntries<'_> {
fn len(&self) -> usize {
self.inner.len()
}
}
impl std::iter::FusedIterator for ProvenanceMapEntries<'_> {}
impl<'a> IntoIterator for &'a ProvenanceMap {
type Item = (&'a [String], &'a Provenance);
type IntoIter = ProvenanceMapEntries<'a>;
fn into_iter(self) -> Self::IntoIter {
self.entries()
}
}
#[derive(Debug)]
pub struct ProvenanceMapIntoIter {
inner: std::collections::btree_map::IntoIter<Vec<String>, Provenance>,
}
impl Iterator for ProvenanceMapIntoIter {
type Item = (Vec<String>, Provenance);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
fn count(self) -> usize {
self.inner.count()
}
fn last(mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
impl DoubleEndedIterator for ProvenanceMapIntoIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
impl ExactSizeIterator for ProvenanceMapIntoIter {
fn len(&self) -> usize {
self.inner.len()
}
}
impl std::iter::FusedIterator for ProvenanceMapIntoIter {}
impl IntoIterator for ProvenanceMap {
type Item = (Vec<String>, Provenance);
type IntoIter = ProvenanceMapIntoIter;
fn into_iter(self) -> Self::IntoIter {
ProvenanceMapIntoIter {
inner: self.inner.into_iter(),
}
}
}
impl FromIterator<(Vec<String>, Provenance)> for ProvenanceMap {
fn from_iter<I: IntoIterator<Item = (Vec<String>, Provenance)>>(iter: I) -> Self {
ProvenanceMap {
inner: iter.into_iter().collect(),
}
}
}
impl Extend<(Vec<String>, Provenance)> for ProvenanceMap {
fn extend<I: IntoIterator<Item = (Vec<String>, Provenance)>>(&mut self, iter: I) {
self.inner.extend(iter);
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProgressiveLayer {
provenance: Provenance,
dict: Dict,
}
impl ProgressiveLayer {
#[must_use]
pub fn new(provenance: Provenance, dict: Dict) -> Self {
Self { provenance, dict }
}
#[must_use]
pub fn file(path: impl Into<PathBuf>, dict: Dict) -> Self {
Self {
provenance: Provenance::file(path),
dict,
}
}
#[must_use]
pub fn env(prefix: impl Into<String>, dict: Dict) -> Self {
Self {
provenance: Provenance::env(prefix),
dict,
}
}
#[must_use]
pub fn provenance(&self) -> &Provenance {
&self.provenance
}
#[must_use]
pub fn dict(&self) -> &Dict {
&self.dict
}
}
#[derive(Debug, Clone)]
pub struct ProgressiveResolution<T> {
value: T,
provenance: ProvenanceMap,
}
impl<T> ProgressiveResolution<T> {
#[must_use]
pub fn value(&self) -> &T {
&self.value
}
#[must_use]
pub fn provenance(&self) -> &ProvenanceMap {
&self.provenance
}
#[must_use]
pub fn into_value(self) -> T {
self.value
}
#[must_use]
pub fn into_parts(self) -> (T, ProvenanceMap) {
(self.value, self.provenance)
}
}
impl<T: PartialEq> PartialEq for ProgressiveResolution<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value && self.provenance == other.provenance
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ConfigDiff {
pub lines: Vec<DiffLine>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DiffLine {
Removed(String),
Added(String),
Context(String),
}
impl DiffLine {
#[must_use]
pub const fn kind(&self) -> DiffLineKind {
match self {
Self::Removed(_) => DiffLineKind::Removed,
Self::Added(_) => DiffLineKind::Added,
Self::Context(_) => DiffLineKind::Context,
}
}
#[must_use]
pub fn text(&self) -> &str {
match self {
Self::Removed(s) | Self::Added(s) | Self::Context(s) => s.as_str(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum DiffLineKind {
Removed,
Added,
Context,
}
impl DiffLineKind {
pub const ALL: &'static [Self] = &[Self::Removed, Self::Added, Self::Context];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Removed => "removed",
Self::Added => "added",
Self::Context => "context",
}
}
#[must_use]
pub const fn glyph(self) -> char {
match self {
Self::Removed => '-',
Self::Added => '+',
Self::Context => ' ',
}
}
#[must_use]
pub const fn is_changed(self) -> bool {
matches!(self, Self::Added | Self::Removed)
}
#[must_use]
pub const fn is_removed(self) -> bool {
matches!(self, Self::Removed)
}
#[must_use]
pub const fn is_added(self) -> bool {
matches!(self, Self::Added)
}
#[must_use]
pub const fn is_context(self) -> bool {
matches!(self, Self::Context)
}
}
impl crate::ClosedAxis for DiffLineKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for DiffLineKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
closed_axis_label_string_surface! {
type = DiffLineKind,
parse_error = "unknown diff line kind",
expecting = "a canonical DiffLineKind lowercase label \
(`removed`, `added`, `context`; case-insensitive)",
}
impl ConfigDiff {
#[must_use]
pub fn from_yaml_pair(baseline: &str, candidate: &str) -> Self {
let a: Vec<&str> = baseline.lines().collect();
let b: Vec<&str> = candidate.lines().collect();
let mut lines = Vec::with_capacity(a.len().max(b.len()));
let mut i = 0;
let mut j = 0;
while i < a.len() || j < b.len() {
match (a.get(i), b.get(j)) {
(Some(la), Some(lb)) if la == lb => {
lines.push(DiffLine::Context((*la).to_string()));
i += 1;
j += 1;
}
(Some(la), Some(lb)) => {
lines.push(DiffLine::Removed((*la).to_string()));
lines.push(DiffLine::Added((*lb).to_string()));
i += 1;
j += 1;
}
(Some(la), None) => {
lines.push(DiffLine::Removed((*la).to_string()));
i += 1;
}
(None, Some(lb)) => {
lines.push(DiffLine::Added((*lb).to_string()));
j += 1;
}
(None, None) => break,
}
}
Self { lines }
}
#[must_use]
pub fn render_unified(&self) -> String {
let mut out = String::new();
for line in &self.lines {
out.push(line.kind().glyph());
out.push_str(line.text());
out.push('\n');
}
out
}
#[must_use]
pub fn is_empty_diff(&self) -> bool {
!self.lines.iter().any(|l| l.kind().is_changed())
}
#[must_use]
pub fn kind_histogram(&self) -> crate::AxisHistogram<DiffLineKind> {
crate::axis_histogram(self.lines.iter().map(DiffLine::kind))
}
#[must_use]
pub fn present_kinds(&self) -> Vec<DiffLineKind> {
self.kind_histogram().observed().collect()
}
#[must_use]
pub fn absent_kinds(&self) -> Vec<DiffLineKind> {
self.kind_histogram().unobserved().collect()
}
#[must_use]
pub fn dominant_kind(&self) -> Option<DiffLineKind> {
self.kind_histogram().dominant_cell()
}
#[must_use]
pub fn peak_kind_count(&self) -> usize {
self.kind_histogram().peak_count()
}
#[must_use]
pub fn recessive_kind(&self) -> Option<DiffLineKind> {
self.kind_histogram().recessive_cell()
}
#[must_use]
pub fn trough_kind_count(&self) -> usize {
self.kind_histogram().trough_count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Toy {
name: String,
size: u32,
flag: bool,
}
impl TieredConfig for Toy {
fn bare() -> Self {
Self {
name: String::new(),
size: 0,
flag: false,
}
}
fn prescribed_default() -> Self {
Self {
name: "default-name".into(),
size: 42,
flag: true,
}
}
}
#[test]
fn bare_returns_floor_values() {
let b = Toy::bare();
assert_eq!(b.name, "");
assert_eq!(b.size, 0);
assert!(!b.flag);
}
#[test]
fn prescribed_default_is_different_from_bare() {
let b = Toy::bare();
let p = Toy::prescribed_default();
assert_ne!(b, p);
}
#[test]
fn discovered_default_impl_returns_bare() {
let d = Toy::discovered();
let b = Toy::bare();
assert_eq!(d, b);
}
#[test]
fn diff_against_self_is_empty() {
let p = Toy::prescribed_default();
let diff = p.diff_against(&p);
assert!(diff.is_empty_diff());
}
#[test]
fn diff_bare_vs_default_yields_added_and_removed_lines() {
let b = Toy::bare();
let p = Toy::prescribed_default();
let diff = p.diff_against(&b);
assert!(!diff.is_empty_diff());
let has_added = diff
.lines
.iter()
.any(|l| matches!(l, DiffLine::Added(s) if s.contains("default-name")));
let has_removed = diff
.lines
.iter()
.any(|l| matches!(l, DiffLine::Removed(s) if s.contains("name: ''")));
assert!(has_added, "diff should add the prescribed name");
assert!(has_removed, "diff should remove the bare empty name");
}
#[test]
fn render_unified_uses_diff_prefixes() {
let b = Toy::bare();
let p = Toy::prescribed_default();
let rendered = p.diff_against(&b).render_unified();
assert!(rendered.contains("-name: ''"));
assert!(rendered.contains("+name: default-name"));
}
#[test]
fn extend_default_impl_full_replaces_base() {
let b = Toy::bare();
let p = Toy::prescribed_default();
let merged = p.clone().extend(&b);
assert_eq!(merged, p);
}
#[test]
fn config_tier_default_is_default_variant() {
assert_eq!(ConfigTier::default(), ConfigTier::Default);
}
#[test]
fn config_tier_from_str_recognizes_named_tiers() {
assert_eq!(ConfigTier::from_str_or_default("bare"), ConfigTier::Bare);
assert_eq!(
ConfigTier::from_str_or_default("DISCOVERED"),
ConfigTier::Discovered
);
assert_eq!(
ConfigTier::from_str_or_default("default"),
ConfigTier::Default
);
assert_eq!(ConfigTier::from_str_or_default(""), ConfigTier::Default);
match ConfigTier::from_str_or_default("/etc/foo.yaml") {
ConfigTier::Custom(p) => {
assert_eq!(p, std::path::PathBuf::from("/etc/foo.yaml"));
}
other => panic!("expected Custom, got {other:?}"),
}
}
#[test]
fn config_tier_names_are_stable() {
assert_eq!(ConfigTier::Bare.name(), "bare");
assert_eq!(ConfigTier::Discovered.name(), "discovered");
assert_eq!(ConfigTier::Default.name(), "default");
assert_eq!(
ConfigTier::Custom(std::path::PathBuf::from("/x")).name(),
"custom"
);
}
#[test]
fn config_tier_from_env_resolves_correctly() {
let key = "SHIKUMI_TIERED_TEST_TIER_X";
unsafe {
std::env::set_var(key, "bare");
}
assert_eq!(ConfigTier::from_env(key), ConfigTier::Bare);
unsafe {
std::env::set_var(key, "");
}
assert_eq!(ConfigTier::from_env(key), ConfigTier::Default);
unsafe {
std::env::remove_var(key);
}
assert_eq!(ConfigTier::from_env(key), ConfigTier::Default);
}
#[test]
fn resolve_tier_dispatches_to_each_method() {
assert_eq!(Toy::resolve_tier(ConfigTier::Bare), Toy::bare());
assert_eq!(Toy::resolve_tier(ConfigTier::Discovered), Toy::discovered());
assert_eq!(
Toy::resolve_tier(ConfigTier::Default),
Toy::prescribed_default()
);
}
#[test]
fn resolve_tier_custom_missing_file_falls_back_to_default() {
let phantom = std::path::PathBuf::from("/nonexistent/path/shikumi-tier-fallback-test.yaml");
let resolved = Toy::resolve_tier(ConfigTier::Custom(phantom));
assert_eq!(resolved, Toy::prescribed_default());
}
#[test]
fn config_tier_kind_all_has_four_entries() {
assert_eq!(ConfigTierKind::ALL.len(), 4);
assert_eq!(ConfigTierKind::ALL[0], ConfigTierKind::Bare);
assert_eq!(ConfigTierKind::ALL[1], ConfigTierKind::Discovered);
assert_eq!(ConfigTierKind::ALL[2], ConfigTierKind::Default);
assert_eq!(ConfigTierKind::ALL[3], ConfigTierKind::Custom);
}
#[test]
fn config_tier_kind_trait_all_matches_inherent_all() {
assert_eq!(
<ConfigTierKind as crate::ClosedAxis>::ALL.len(),
ConfigTierKind::ALL.len(),
);
for (i, (trait_kind, inherent_kind)) in <ConfigTierKind as crate::ClosedAxis>::ALL
.iter()
.zip(ConfigTierKind::ALL.iter())
.enumerate()
{
assert_eq!(
trait_kind, inherent_kind,
"trait ALL[{i}] must equal inherent ALL[{i}]",
);
}
}
#[test]
fn config_tier_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(ConfigTierKind::Bare.as_str(), "bare");
assert_eq!(ConfigTierKind::Discovered.as_str(), "discovered");
assert_eq!(ConfigTierKind::Default.as_str(), "default");
assert_eq!(ConfigTierKind::Custom.as_str(), "custom");
}
#[test]
fn config_tier_kind_from_str_round_trips_with_as_str() {
for &kind in ConfigTierKind::ALL {
assert_eq!(
ConfigTierKind::from_str(kind.as_str()),
Some(kind),
"round-trip failed for kind {kind:?}",
);
}
}
#[test]
fn config_tier_kind_from_str_is_case_insensitive() {
assert_eq!(ConfigTierKind::from_str("BARE"), Some(ConfigTierKind::Bare),);
assert_eq!(
ConfigTierKind::from_str("Discovered"),
Some(ConfigTierKind::Discovered),
);
assert_eq!(
ConfigTierKind::from_str("DeFaUlT"),
Some(ConfigTierKind::Default),
);
assert_eq!(
ConfigTierKind::from_str("CUSTOM"),
Some(ConfigTierKind::Custom),
);
}
#[test]
fn config_tier_kind_from_str_returns_none_on_unknown() {
assert_eq!(ConfigTierKind::from_str(""), None);
assert_eq!(ConfigTierKind::from_str("nonexistent"), None);
assert_eq!(ConfigTierKind::from_str("/etc/foo.yaml"), None);
assert_eq!(ConfigTierKind::from_str(" bare "), None);
}
#[test]
fn config_tier_kind_projection_matches_config_tier_name() {
let pairs: [(ConfigTier, ConfigTierKind); 4] = [
(ConfigTier::Bare, ConfigTierKind::Bare),
(ConfigTier::Discovered, ConfigTierKind::Discovered),
(ConfigTier::Default, ConfigTierKind::Default),
(
ConfigTier::Custom(std::path::PathBuf::from("/x")),
ConfigTierKind::Custom,
),
];
for (tier, expected_kind) in pairs {
assert_eq!(tier.kind(), expected_kind);
assert_eq!(tier.name(), expected_kind.as_str());
}
}
#[test]
fn config_tier_from_env_still_lowercases_unknown_paths() {
let key = "SHIKUMI_TIERED_TEST_TIER_PATH";
unsafe {
std::env::set_var(key, "/Foo/Bar.YAML");
}
let tier = ConfigTier::from_env(key);
match tier {
ConfigTier::Custom(p) => assert_eq!(
p,
std::path::PathBuf::from("/foo/bar.yaml"),
"from_env preserves the pre-lift lowercase behavior",
),
other => panic!("expected Custom, got {other:?}"),
}
unsafe {
std::env::remove_var(key);
}
}
fn canonical_diff_line_kind_samples() -> Vec<(DiffLine, DiffLineKind)> {
vec![
(DiffLine::Removed("name: ''".into()), DiffLineKind::Removed),
(
DiffLine::Added("name: default-name".into()),
DiffLineKind::Added,
),
(DiffLine::Context("size: 42".into()), DiffLineKind::Context),
(DiffLine::Removed(String::new()), DiffLineKind::Removed),
(DiffLine::Added(String::new()), DiffLineKind::Added),
(DiffLine::Context(String::new()), DiffLineKind::Context),
]
}
#[test]
fn diff_line_kind_classifies_each_variant() {
assert_eq!(DiffLine::Removed("x".into()).kind(), DiffLineKind::Removed,);
assert_eq!(DiffLine::Added("y".into()).kind(), DiffLineKind::Added);
assert_eq!(DiffLine::Context("z".into()).kind(), DiffLineKind::Context,);
}
#[test]
fn diff_line_kind_is_data_free() {
for payload in ["", "a", "name: 'long value' ", "\n", "\u{1F600}"] {
assert_eq!(
DiffLine::Removed(payload.to_string()).kind(),
DiffLineKind::Removed,
);
assert_eq!(
DiffLine::Added(payload.to_string()).kind(),
DiffLineKind::Added,
);
assert_eq!(
DiffLine::Context(payload.to_string()).kind(),
DiffLineKind::Context,
);
}
}
#[test]
fn diff_line_kind_agrees_with_predicates_pointwise() {
for (line, expected) in canonical_diff_line_kind_samples() {
let k = line.kind();
assert_eq!(k, expected);
assert_eq!(k.is_removed(), k == DiffLineKind::Removed);
assert_eq!(k.is_added(), k == DiffLineKind::Added);
assert_eq!(k.is_context(), k == DiffLineKind::Context);
}
}
#[test]
fn diff_line_kind_is_changed_partitions_added_or_removed() {
assert!(DiffLineKind::Removed.is_changed());
assert!(DiffLineKind::Added.is_changed());
assert!(!DiffLineKind::Context.is_changed());
}
#[test]
fn diff_line_kind_is_static_and_copy_and_hashable() {
use std::collections::HashSet;
fn assert_static<T: 'static>() {}
assert_static::<DiffLineKind>();
let mut set: HashSet<DiffLineKind> = DiffLineKind::ALL.iter().copied().collect();
set.insert(DiffLineKind::Removed); assert_eq!(set.len(), DiffLineKind::ALL.len());
let k = DiffLineKind::Added;
let k2 = k;
assert_eq!(k, k2);
}
#[test]
fn diff_line_kind_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<DiffLineKind> = DiffLineKind::ALL.iter().copied().collect();
assert_eq!(unique.len(), DiffLineKind::ALL.len());
}
#[test]
fn diff_line_kind_all_covers_every_constructible_line() {
for (line, _) in canonical_diff_line_kind_samples() {
assert!(
DiffLineKind::ALL.contains(&line.kind()),
"DiffLineKind::ALL must contain the kind of every constructible DiffLine",
);
}
}
#[test]
fn diff_line_kind_all_equals_diff_line_kind_image() {
use std::collections::HashSet;
let image: HashSet<DiffLineKind> = canonical_diff_line_kind_samples()
.into_iter()
.map(|(l, _)| l.kind())
.collect();
let all: HashSet<DiffLineKind> = DiffLineKind::ALL.iter().copied().collect();
assert_eq!(image, all);
}
#[test]
fn diff_line_kind_all_declaration_order_is_removed_added_context() {
assert_eq!(DiffLineKind::ALL.len(), 3);
assert_eq!(DiffLineKind::ALL[0], DiffLineKind::Removed);
assert_eq!(DiffLineKind::ALL[1], DiffLineKind::Added);
assert_eq!(DiffLineKind::ALL[2], DiffLineKind::Context);
}
#[test]
fn diff_line_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(DiffLineKind::Removed.as_str(), "removed");
assert_eq!(DiffLineKind::Added.as_str(), "added");
assert_eq!(DiffLineKind::Context.as_str(), "context");
}
#[test]
fn diff_line_kind_glyph_yields_canonical_unified_diff_prefixes() {
assert_eq!(DiffLineKind::Removed.glyph(), '-');
assert_eq!(DiffLineKind::Added.glyph(), '+');
assert_eq!(DiffLineKind::Context.glyph(), ' ');
}
#[test]
fn diff_line_text_returns_inner_payload_pointwise() {
for payload in ["", "a", "name: value", " leading spaces"] {
assert_eq!(DiffLine::Removed(payload.to_string()).text(), payload);
assert_eq!(DiffLine::Added(payload.to_string()).text(), payload);
assert_eq!(DiffLine::Context(payload.to_string()).text(), payload);
}
}
#[test]
fn diff_line_kind_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for &k in DiffLineKind::ALL {
let lower = k.as_str();
assert_eq!(DiffLineKind::from_canonical_str(lower), Some(k));
let upper = lower.to_ascii_uppercase();
assert_eq!(DiffLineKind::from_canonical_str(&upper), Some(k));
let mut mixed = String::new();
for (i, c) in lower.chars().enumerate() {
if i == 0 {
mixed.extend(c.to_uppercase());
} else {
mixed.push(c);
}
}
assert_eq!(DiffLineKind::from_canonical_str(&mixed), Some(k));
}
}
#[test]
fn config_diff_is_empty_diff_routes_through_diff_line_kind_is_changed() {
let only_context = ConfigDiff {
lines: vec![DiffLine::Context("a".into()), DiffLine::Context("b".into())],
};
assert!(only_context.is_empty_diff());
let with_added = ConfigDiff {
lines: vec![
DiffLine::Context("a".into()),
DiffLine::Added("c".into()),
DiffLine::Context("b".into()),
],
};
assert!(!with_added.is_empty_diff());
let with_removed = ConfigDiff {
lines: vec![DiffLine::Removed("x".into())],
};
assert!(!with_removed.is_empty_diff());
let empty_lines = ConfigDiff { lines: vec![] };
assert!(empty_lines.is_empty_diff());
}
#[test]
fn config_diff_render_unified_emits_one_glyph_per_kind() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("name: ''".into()),
DiffLine::Added("name: default-name".into()),
DiffLine::Context("size: 42".into()),
],
};
let rendered = diff.render_unified();
assert_eq!(
rendered, "-name: ''\n+name: default-name\n size: 42\n",
"render_unified must emit the canonical glyph per kind",
);
for (i, line) in diff.lines.iter().enumerate() {
let expected_glyph = line.kind().glyph();
let actual_first = rendered
.lines()
.nth(i)
.and_then(|s| s.chars().next())
.expect("rendered output must have at least i+1 lines");
assert_eq!(
actual_first, expected_glyph,
"rendered line {i} must start with its kind's glyph",
);
}
}
#[test]
fn config_diff_render_unified_byte_identical_to_pre_lift_form() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context(String::new()),
DiffLine::Removed("a".into()),
DiffLine::Added("b".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(diff.render_unified(), " \n-a\n+b\n c\n");
}
#[test]
fn config_tier_from_str_or_default_via_kind_dispatch() {
assert_eq!(ConfigTier::from_str_or_default("bare"), ConfigTier::Bare);
assert_eq!(
ConfigTier::from_str_or_default("DISCOVERED"),
ConfigTier::Discovered,
);
assert_eq!(
ConfigTier::from_str_or_default("default"),
ConfigTier::Default,
);
assert_eq!(ConfigTier::from_str_or_default(""), ConfigTier::Default,);
match ConfigTier::from_str_or_default("custom") {
ConfigTier::Custom(p) => {
assert_eq!(p, std::path::PathBuf::from("custom"));
}
other => panic!("expected Custom, got {other:?}"),
}
match ConfigTier::from_str_or_default("/etc/foo.yaml") {
ConfigTier::Custom(p) => {
assert_eq!(p, std::path::PathBuf::from("/etc/foo.yaml"));
}
other => panic!("expected Custom, got {other:?}"),
}
}
#[test]
fn kind_histogram_counts_each_kind_pointwise() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
],
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Removed), 1);
assert_eq!(hist.count(DiffLineKind::Added), 2);
assert_eq!(hist.count(DiffLineKind::Context), 3);
assert_eq!(hist.total(), diff.lines.len());
}
#[test]
fn kind_histogram_empty_diff_is_zero_on_every_cell() {
let diff = ConfigDiff::default();
let hist = diff.kind_histogram();
assert_eq!(hist.total(), 0);
assert!(hist.is_empty());
for cell in [
DiffLineKind::Removed,
DiffLineKind::Added,
DiffLineKind::Context,
] {
assert_eq!(hist.count(cell), 0);
}
}
#[test]
fn kind_histogram_changed_cells_match_is_empty_diff() {
let context_only = ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
};
let h1 = context_only.kind_histogram();
assert!(context_only.is_empty_diff());
assert_eq!(
h1.count(DiffLineKind::Added) + h1.count(DiffLineKind::Removed),
0
);
let with_change = ConfigDiff {
lines: vec![DiffLine::Context("c".into()), DiffLine::Added("a".into())],
};
let h2 = with_change.kind_histogram();
assert!(!with_change.is_empty_diff());
assert!(h2.count(DiffLineKind::Added) + h2.count(DiffLineKind::Removed) > 0);
}
#[test]
fn kind_histogram_iter_yields_declaration_order() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c".into()),
DiffLine::Added("a".into()),
DiffLine::Removed("r".into()),
],
};
let pairs: Vec<(DiffLineKind, usize)> = diff.kind_histogram().iter().collect();
assert_eq!(
pairs,
vec![
(DiffLineKind::Removed, 1),
(DiffLineKind::Added, 1),
(DiffLineKind::Context, 1),
],
);
}
#[test]
fn present_kinds_matches_kind_histogram_observed_pointwise() {
let fixtures: [ConfigDiff; 4] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
],
},
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Removed("r".into())],
},
];
for diff in fixtures {
let via_direct = diff.present_kinds();
let via_histogram: Vec<DiffLineKind> = diff.kind_histogram().observed().collect();
assert_eq!(
via_direct, via_histogram,
"present_kinds must equal kind_histogram().observed().collect() pointwise",
);
}
}
#[test]
fn present_kinds_empty_diff_is_empty() {
let empty = ConfigDiff::default();
assert!(empty.lines.is_empty());
assert!(empty.present_kinds().is_empty());
assert_eq!(empty.present_kinds(), Vec::<DiffLineKind>::new());
let one_line = ConfigDiff {
lines: vec![DiffLine::Context("x".into())],
};
assert!(!one_line.lines.is_empty());
assert!(!one_line.present_kinds().is_empty());
}
#[test]
fn present_kinds_iterates_in_declaration_order() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c".into()),
DiffLine::Added("a".into()),
DiffLine::Removed("r".into()),
],
};
assert_eq!(
diff.present_kinds(),
vec![
DiffLineKind::Removed,
DiffLineKind::Added,
DiffLineKind::Context,
],
);
}
#[test]
fn present_kinds_dedups_across_repeated_observations() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Removed("r2".into()),
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Added("a3".into()),
DiffLine::Context("c1".into()),
],
};
assert_eq!(
diff.present_kinds(),
vec![
DiffLineKind::Removed,
DiffLineKind::Added,
DiffLineKind::Context,
],
);
}
#[test]
fn present_kinds_context_only_diff_yields_context() {
let ctx_only = ConfigDiff {
lines: vec![
DiffLine::Context("a".into()),
DiffLine::Context("b".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(ctx_only.present_kinds(), vec![DiffLineKind::Context]);
assert!(ctx_only.is_empty_diff());
}
#[test]
fn present_kinds_distinct_cells_matches_histogram() {
let fixtures: [ConfigDiff; 4] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into()), DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
},
];
for diff in fixtures {
assert_eq!(
diff.present_kinds().len(),
diff.kind_histogram().distinct_cells(),
"present_kinds().len() must equal kind_histogram().distinct_cells()",
);
}
}
#[test]
fn present_kinds_changed_subset_agrees_with_is_empty_diff() {
let context_only = ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
};
assert!(context_only.is_empty_diff());
let changed_in_ctx_only: Vec<DiffLineKind> = context_only
.present_kinds()
.into_iter()
.filter(|k| k.is_changed())
.collect();
assert!(changed_in_ctx_only.is_empty());
let with_change = ConfigDiff {
lines: vec![DiffLine::Context("c".into()), DiffLine::Added("a".into())],
};
assert!(!with_change.is_empty_diff());
let changed_in_mixed: Vec<DiffLineKind> = with_change
.present_kinds()
.into_iter()
.filter(|k| k.is_changed())
.collect();
assert!(!changed_in_mixed.is_empty());
assert!(changed_in_mixed.contains(&DiffLineKind::Added));
}
#[test]
fn present_kinds_is_strictly_ascending_by_axis_ordinal() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c".into()),
DiffLine::Removed("r".into()),
DiffLine::Context("c2".into()),
DiffLine::Added("a".into()),
DiffLine::Removed("r2".into()),
],
};
let present = diff.present_kinds();
for window in present.windows(2) {
let a = crate::axis_ordinal(window[0]);
let b = crate::axis_ordinal(window[1]);
assert!(
a < b,
"present_kinds must be strictly ascending by axis_ordinal, \
but ord({:?})={a} >= ord({:?})={b}",
window[0],
window[1],
);
}
}
#[test]
fn absent_kinds_matches_kind_histogram_unobserved_pointwise() {
let fixtures: [ConfigDiff; 4] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
],
},
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Removed("r".into())],
},
];
for diff in fixtures {
let via_direct = diff.absent_kinds();
let via_histogram: Vec<DiffLineKind> = diff.kind_histogram().unobserved().collect();
assert_eq!(
via_direct, via_histogram,
"absent_kinds must equal kind_histogram().unobserved().collect() pointwise",
);
}
}
#[test]
fn absent_kinds_empty_diff_is_full_axis() {
let empty = ConfigDiff::default();
assert_eq!(empty.absent_kinds(), DiffLineKind::ALL.to_vec());
}
#[test]
fn absent_kinds_iterates_in_declaration_order() {
let empty = ConfigDiff::default();
assert_eq!(
empty.absent_kinds(),
vec![
DiffLineKind::Removed,
DiffLineKind::Added,
DiffLineKind::Context,
],
);
}
#[test]
fn absent_kinds_context_only_diff_is_added_and_removed() {
let ctx_only = ConfigDiff {
lines: vec![
DiffLine::Context("a".into()),
DiffLine::Context("b".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(
ctx_only.absent_kinds(),
vec![DiffLineKind::Removed, DiffLineKind::Added],
);
assert!(ctx_only.is_empty_diff());
}
#[test]
fn absent_kinds_len_matches_unobserved_cells() {
let fixtures: [ConfigDiff; 5] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into()), DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Added("b".into())],
},
];
for diff in fixtures {
assert_eq!(
diff.absent_kinds().len(),
diff.kind_histogram().unobserved_cells(),
"absent_kinds().len() must equal kind_histogram().unobserved_cells()",
);
}
}
#[test]
fn absent_kinds_and_present_kinds_partition_axis() {
let axis_size = crate::axis_cardinality::<DiffLineKind>();
let fixtures: [ConfigDiff; 5] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into()), DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Added("b".into())],
},
];
for diff in fixtures {
let observed = diff.present_kinds();
let absent = diff.absent_kinds();
assert_eq!(observed.len() + absent.len(), axis_size);
for kind in &observed {
assert!(
!absent.contains(kind),
"kind {kind:?} appears in both present and absent",
);
}
for cell in DiffLineKind::ALL {
assert!(
observed.contains(cell) || absent.contains(cell),
"kind {cell:?} appears in neither present nor absent",
);
}
}
}
#[test]
fn absent_kinds_is_empty_iff_is_full_cover() {
let fixtures: [ConfigDiff; 5] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into()), DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Added("b".into())],
},
];
for diff in fixtures {
assert_eq!(
diff.absent_kinds().is_empty(),
diff.kind_histogram().is_full_cover(),
);
}
let full_cover = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
};
assert!(full_cover.kind_histogram().is_full_cover());
assert_eq!(full_cover.absent_kinds(), Vec::<DiffLineKind>::new());
assert_eq!(full_cover.present_kinds(), DiffLineKind::ALL.to_vec());
}
#[test]
fn absent_kinds_is_strictly_ascending_by_axis_ordinal() {
let fixtures: [ConfigDiff; 5] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into())],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Added("b".into())],
},
];
for diff in fixtures {
let absent = diff.absent_kinds();
for pair in absent.windows(2) {
assert!(
crate::axis_ordinal(pair[0]) < crate::axis_ordinal(pair[1]),
"absent_kinds must be strictly ascending: {absent:?}",
);
}
}
}
#[test]
fn absent_kinds_full_cover_yields_empty() {
let full_cover = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
};
assert!(full_cover.kind_histogram().is_full_cover());
assert_eq!(full_cover.absent_kinds(), Vec::<DiffLineKind>::new());
assert_eq!(full_cover.present_kinds(), DiffLineKind::ALL.to_vec());
}
#[test]
fn absent_kinds_singleton_diff_yields_two_absent() {
let axis_size = crate::axis_cardinality::<DiffLineKind>();
for (line, present_kind) in [
(DiffLine::Removed("r".into()), DiffLineKind::Removed),
(DiffLine::Added("a".into()), DiffLineKind::Added),
(DiffLine::Context("c".into()), DiffLineKind::Context),
] {
let diff = ConfigDiff { lines: vec![line] };
let absent = diff.absent_kinds();
assert_eq!(absent.len(), axis_size - 1);
assert!(
!absent.contains(&present_kind),
"the observed kind {present_kind:?} must not appear in the coverage gap",
);
for cell in DiffLineKind::ALL {
if *cell != present_kind {
assert!(
absent.contains(cell),
"the singleton diff's coverage gap must contain \
every non-observed axis cell — missing {cell:?}",
);
}
}
}
}
#[test]
fn absent_kinds_agrees_with_open_coded_coverage_gap_walk() {
let fixtures: [ConfigDiff; 6] = [
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into()), DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into()), DiffLine::Added("b".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into())],
},
];
for diff in fixtures {
let via_seam = diff.absent_kinds();
let present = diff.present_kinds();
let hand_rolled: Vec<DiffLineKind> = DiffLineKind::ALL
.iter()
.copied()
.filter(|k| !present.contains(k))
.collect();
assert_eq!(via_seam, hand_rolled);
}
}
fn dominant_kind_fixtures() -> [ConfigDiff; 8] {
[
ConfigDiff::default(),
ConfigDiff {
lines: vec![DiffLine::Removed("r".into())],
},
ConfigDiff {
lines: vec![DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![DiffLine::Context("c".into())],
},
ConfigDiff {
lines: vec![DiffLine::Removed("r".into()), DiffLine::Added("a".into())],
},
ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
},
ConfigDiff {
lines: vec![
DiffLine::Added("a".into()),
DiffLine::Added("b".into()),
DiffLine::Context("c".into()),
],
},
ConfigDiff {
lines: vec![
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
DiffLine::Removed("r".into()),
],
},
]
}
#[test]
fn dominant_kind_matches_kind_histogram_dominant_cell_pointwise() {
for diff in dominant_kind_fixtures() {
let via_histogram = diff.kind_histogram().dominant_cell();
assert_eq!(diff.dominant_kind(), via_histogram);
}
}
#[test]
fn dominant_kind_context_dominated_fixture_is_context() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
DiffLine::Removed("r".into()),
],
};
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Context));
}
#[test]
fn dominant_kind_added_dominated_fixture_is_added() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Added));
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Added), 2);
assert_eq!(hist.peak_count(), 2);
}
#[test]
fn dominant_kind_empty_diff_is_none() {
let empty = ConfigDiff::default();
assert_eq!(empty.dominant_kind(), None);
assert!(empty.lines.is_empty());
}
#[test]
fn dominant_kind_is_some_iff_diff_is_nonempty() {
for diff in dominant_kind_fixtures() {
assert_eq!(diff.dominant_kind().is_some(), !diff.lines.is_empty());
}
}
#[test]
fn dominant_kind_is_member_of_present_kinds() {
for diff in dominant_kind_fixtures() {
let Some(dominant) = diff.dominant_kind() else {
continue;
};
assert!(
diff.present_kinds().contains(&dominant),
"dominant kind {dominant:?} must appear in present_kinds",
);
}
}
#[test]
fn dominant_kind_is_not_member_of_absent_kinds() {
for diff in dominant_kind_fixtures() {
let Some(dominant) = diff.dominant_kind() else {
continue;
};
assert!(
!diff.absent_kinds().contains(&dominant),
"dominant kind {dominant:?} must not appear in absent_kinds",
);
}
}
#[test]
fn dominant_kind_count_equals_peak_count_on_nonempty_diff() {
for diff in dominant_kind_fixtures() {
let Some(dominant) = diff.dominant_kind() else {
continue;
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(dominant), hist.peak_count());
}
}
#[test]
fn dominant_kind_ties_broken_by_declaration_order() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Removed), 1);
assert_eq!(hist.count(DiffLineKind::Added), 1);
assert_eq!(hist.count(DiffLineKind::Context), 1);
assert!(hist.is_full_cover());
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Removed));
}
#[test]
fn dominant_kind_two_way_tie_picks_declaration_order_first() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
],
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Removed), 0);
assert_eq!(hist.count(DiffLineKind::Added), 2);
assert_eq!(hist.count(DiffLineKind::Context), 2);
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Added));
}
#[test]
fn dominant_kind_agrees_with_open_coded_argmax_walk() {
for diff in dominant_kind_fixtures() {
let via_seam = diff.dominant_kind();
let hist = diff.kind_histogram();
let mut iter = hist.iter().filter(|&(_, c)| c > 0);
let hand_rolled = iter.next().map(|first| {
iter.fold(
first,
|best, current| {
if current.1 > best.1 { current } else { best }
},
)
.0
});
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn dominant_kind_uniform_cover_picks_first_cell() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Removed("r2".into()),
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
],
};
assert!(diff.kind_histogram().is_full_cover());
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Removed));
}
#[test]
fn peak_kind_count_matches_kind_histogram_peak_count_pointwise() {
for diff in dominant_kind_fixtures() {
let via_histogram = diff.kind_histogram().peak_count();
assert_eq!(diff.peak_kind_count(), via_histogram);
}
}
#[test]
fn peak_kind_count_context_dominated_fixture_is_three() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
DiffLine::Removed("r".into()),
],
};
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Context));
assert_eq!(diff.peak_kind_count(), 3);
}
#[test]
fn peak_kind_count_added_dominated_fixture_is_two() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(diff.dominant_kind(), Some(DiffLineKind::Added));
assert_eq!(diff.peak_kind_count(), 2);
}
#[test]
fn peak_kind_count_empty_diff_is_zero() {
let empty = ConfigDiff::default();
assert_eq!(empty.dominant_kind(), None);
assert_eq!(empty.peak_kind_count(), 0);
assert!(empty.lines.is_empty());
}
#[test]
fn peak_kind_count_is_zero_iff_diff_is_empty() {
for diff in dominant_kind_fixtures() {
assert_eq!(diff.peak_kind_count() == 0, diff.lines.is_empty());
}
}
#[test]
fn peak_kind_count_equals_count_at_dominant_kind_on_nonempty_diff() {
for diff in dominant_kind_fixtures() {
let Some(dominant) = diff.dominant_kind() else {
continue;
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(dominant), diff.peak_kind_count());
}
}
#[test]
fn peak_kind_count_equals_dominant_kind_map_or_count() {
for diff in dominant_kind_fixtures() {
let hist = diff.kind_histogram();
let via_pair = diff.dominant_kind().map_or(0, |k| hist.count(k));
assert_eq!(diff.peak_kind_count(), via_pair);
}
}
#[test]
fn peak_kind_count_bounded_above_by_lines_len() {
for diff in dominant_kind_fixtures() {
assert!(
diff.peak_kind_count() <= diff.lines.len(),
"peak_kind_count {} must not exceed lines.len() {}",
diff.peak_kind_count(),
diff.lines.len(),
);
}
}
#[test]
fn peak_kind_count_equals_lines_len_iff_at_most_one_present_kind() {
for diff in dominant_kind_fixtures() {
let peak_eq_len = diff.peak_kind_count() == diff.lines.len();
let support_le_one = diff.present_kinds().len() <= 1;
assert_eq!(
peak_eq_len,
support_le_one,
"peak_kind_count == lines.len() must agree with present_kinds().len() <= 1 \
for diff with peak={peak_kind}, len={line_count}, present={present:?}",
peak_kind = diff.peak_kind_count(),
line_count = diff.lines.len(),
present = diff.present_kinds(),
);
}
}
#[test]
fn peak_kind_count_is_at_least_one_on_nonempty_diff() {
for diff in dominant_kind_fixtures() {
if diff.lines.is_empty() {
continue;
}
assert!(
diff.peak_kind_count() >= 1,
"non-empty diff must have peak_kind_count >= 1, got {}",
diff.peak_kind_count(),
);
}
}
#[test]
fn peak_kind_count_uniform_cover_is_one() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
};
assert!(diff.kind_histogram().is_full_cover());
assert_eq!(diff.peak_kind_count(), 1);
}
#[test]
fn peak_kind_count_singleton_support_equals_lines_len() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Removed("r2".into()),
DiffLine::Removed("r3".into()),
DiffLine::Removed("r4".into()),
],
};
assert_eq!(diff.present_kinds().len(), 1);
assert_eq!(diff.peak_kind_count(), 4);
assert_eq!(diff.peak_kind_count(), diff.lines.len());
}
#[test]
fn peak_kind_count_agrees_with_open_coded_max_over_axis_walk() {
for diff in dominant_kind_fixtures() {
let via_seam = diff.peak_kind_count();
let hist = diff.kind_histogram();
let hand_rolled = hist.iter().map(|(_, c)| c).max().unwrap_or(0);
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn recessive_kind_matches_kind_histogram_recessive_cell_pointwise() {
for diff in dominant_kind_fixtures() {
let via_histogram = diff.kind_histogram().recessive_cell();
assert_eq!(diff.recessive_kind(), via_histogram);
}
}
#[test]
fn recessive_kind_context_dominated_fixture_is_removed() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
DiffLine::Removed("r".into()),
],
};
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Removed));
}
#[test]
fn recessive_kind_added_dominated_fixture_is_context() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Context));
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Context), 1);
assert_eq!(hist.trough_count(), 1);
}
#[test]
fn recessive_kind_empty_diff_is_none() {
let empty = ConfigDiff::default();
assert_eq!(empty.recessive_kind(), None);
assert!(empty.lines.is_empty());
}
#[test]
fn recessive_kind_is_some_iff_diff_is_nonempty() {
for diff in dominant_kind_fixtures() {
assert_eq!(diff.recessive_kind().is_some(), !diff.lines.is_empty());
}
}
#[test]
fn recessive_kind_is_some_iff_dominant_kind_is_some() {
for diff in dominant_kind_fixtures() {
assert_eq!(
diff.recessive_kind().is_some(),
diff.dominant_kind().is_some(),
);
}
}
#[test]
fn recessive_kind_is_member_of_present_kinds() {
for diff in dominant_kind_fixtures() {
let Some(recessive) = diff.recessive_kind() else {
continue;
};
assert!(
diff.present_kinds().contains(&recessive),
"recessive kind {recessive:?} must appear in present_kinds",
);
}
}
#[test]
fn recessive_kind_is_not_member_of_absent_kinds() {
for diff in dominant_kind_fixtures() {
let Some(recessive) = diff.recessive_kind() else {
continue;
};
assert!(
!diff.absent_kinds().contains(&recessive),
"recessive kind {recessive:?} must not appear in absent_kinds",
);
}
}
#[test]
fn recessive_kind_count_equals_trough_count_on_nonempty_diff() {
for diff in dominant_kind_fixtures() {
let Some(recessive) = diff.recessive_kind() else {
continue;
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(recessive), hist.trough_count());
}
}
#[test]
fn recessive_kind_count_bounded_by_dominant_kind_count() {
for diff in dominant_kind_fixtures() {
let Some(recessive) = diff.recessive_kind() else {
continue;
};
let Some(dominant) = diff.dominant_kind() else {
unreachable!("presence of recessive kind implies presence of dominant kind");
};
let hist = diff.kind_histogram();
assert!(
hist.count(recessive) <= hist.count(dominant),
"count(recessive={recessive:?})={r} must be <= count(dominant={dominant:?})={d}",
r = hist.count(recessive),
d = hist.count(dominant),
);
}
}
#[test]
fn recessive_kind_ties_broken_by_declaration_order() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Removed), 1);
assert_eq!(hist.count(DiffLineKind::Added), 1);
assert_eq!(hist.count(DiffLineKind::Context), 1);
assert!(hist.is_full_cover());
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Removed));
assert_eq!(diff.recessive_kind(), diff.dominant_kind());
}
#[test]
fn recessive_kind_two_way_tie_picks_declaration_order_first() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
],
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(DiffLineKind::Removed), 1);
assert_eq!(hist.count(DiffLineKind::Added), 1);
assert_eq!(hist.count(DiffLineKind::Context), 3);
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Removed));
}
#[test]
fn recessive_kind_singleton_support_agrees_with_dominant_kind() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Added("a3".into()),
],
};
assert_eq!(diff.present_kinds().len(), 1);
assert_eq!(diff.recessive_kind(), diff.dominant_kind());
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Added));
}
#[test]
fn recessive_kind_agrees_with_open_coded_argmin_walk() {
for diff in dominant_kind_fixtures() {
let via_seam = diff.recessive_kind();
let hist = diff.kind_histogram();
let mut iter = hist.iter().filter(|&(_, c)| c > 0);
let hand_rolled = iter.next().map(|first| {
iter.fold(
first,
|best, current| {
if current.1 < best.1 { current } else { best }
},
)
.0
});
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn recessive_kind_uniform_cover_picks_first_cell() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Removed("r2".into()),
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
],
};
assert!(diff.kind_histogram().is_full_cover());
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Removed));
assert_eq!(diff.recessive_kind(), diff.dominant_kind());
}
#[test]
fn trough_kind_count_matches_kind_histogram_trough_count_pointwise() {
for diff in dominant_kind_fixtures() {
let via_histogram = diff.kind_histogram().trough_count();
assert_eq!(diff.trough_kind_count(), via_histogram);
}
}
#[test]
fn trough_kind_count_context_dominated_fixture_is_one() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Context("c1".into()),
DiffLine::Context("c2".into()),
DiffLine::Context("c3".into()),
DiffLine::Removed("r".into()),
],
};
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Removed));
assert_eq!(diff.trough_kind_count(), 1);
}
#[test]
fn trough_kind_count_added_dominated_fixture_is_one() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Added("a1".into()),
DiffLine::Added("a2".into()),
DiffLine::Context("c".into()),
],
};
assert_eq!(diff.recessive_kind(), Some(DiffLineKind::Context));
assert_eq!(diff.trough_kind_count(), 1);
}
#[test]
fn trough_kind_count_empty_diff_is_zero() {
let empty = ConfigDiff::default();
assert_eq!(empty.recessive_kind(), None);
assert_eq!(empty.trough_kind_count(), 0);
assert!(empty.lines.is_empty());
}
#[test]
fn trough_kind_count_is_zero_iff_diff_is_empty() {
for diff in dominant_kind_fixtures() {
assert_eq!(diff.trough_kind_count() == 0, diff.lines.is_empty());
}
}
#[test]
fn trough_kind_count_equals_count_at_recessive_kind_on_nonempty_diff() {
for diff in dominant_kind_fixtures() {
let Some(recessive) = diff.recessive_kind() else {
continue;
};
let hist = diff.kind_histogram();
assert_eq!(hist.count(recessive), diff.trough_kind_count());
}
}
#[test]
fn trough_kind_count_equals_recessive_kind_map_or_count() {
for diff in dominant_kind_fixtures() {
let hist = diff.kind_histogram();
let via_pair = diff.recessive_kind().map_or(0, |k| hist.count(k));
assert_eq!(diff.trough_kind_count(), via_pair);
}
}
#[test]
fn trough_kind_count_bounded_above_by_peak_kind_count() {
for diff in dominant_kind_fixtures() {
assert!(
diff.trough_kind_count() <= diff.peak_kind_count(),
"trough_kind_count()={t} must be <= peak_kind_count()={p}",
t = diff.trough_kind_count(),
p = diff.peak_kind_count(),
);
}
}
#[test]
fn trough_kind_count_equals_peak_kind_count_iff_at_most_one_present_kind() {
for diff in dominant_kind_fixtures() {
let equal = diff.trough_kind_count() == diff.peak_kind_count();
let support_le_one = diff.present_kinds().len() <= 1;
if support_le_one {
assert!(
equal,
"at_most_one_present_kind → trough == peak \
(trough={t}, peak={p}, present={present:?})",
t = diff.trough_kind_count(),
p = diff.peak_kind_count(),
present = diff.present_kinds(),
);
}
}
}
#[test]
fn trough_kind_count_is_at_least_one_on_nonempty_diff() {
for diff in dominant_kind_fixtures() {
if diff.lines.is_empty() {
continue;
}
assert!(
diff.trough_kind_count() >= 1,
"non-empty diff must have trough_kind_count >= 1, got {}",
diff.trough_kind_count(),
);
}
}
#[test]
fn trough_kind_count_uniform_cover_is_one() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r".into()),
DiffLine::Added("a".into()),
DiffLine::Context("c".into()),
],
};
assert!(diff.kind_histogram().is_full_cover());
assert_eq!(diff.trough_kind_count(), 1);
assert_eq!(diff.trough_kind_count(), diff.peak_kind_count());
}
#[test]
fn trough_kind_count_singleton_support_equals_lines_len() {
let diff = ConfigDiff {
lines: vec![
DiffLine::Removed("r1".into()),
DiffLine::Removed("r2".into()),
DiffLine::Removed("r3".into()),
DiffLine::Removed("r4".into()),
],
};
assert_eq!(diff.present_kinds().len(), 1);
assert_eq!(diff.trough_kind_count(), 4);
assert_eq!(diff.trough_kind_count(), diff.lines.len());
assert_eq!(diff.trough_kind_count(), diff.peak_kind_count());
}
#[test]
fn trough_kind_count_agrees_with_open_coded_min_over_support_walk() {
for diff in dominant_kind_fixtures() {
let via_seam = diff.trough_kind_count();
let hand_rolled = diff
.kind_histogram()
.iter()
.filter(|&(_, c)| c > 0)
.map(|(_, c)| c)
.min()
.unwrap_or(0);
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn diff_line_kind_ord_matches_all_declaration_order() {
use std::cmp::Ordering;
for window in DiffLineKind::ALL.windows(2) {
assert!(
window[0] < window[1],
"DiffLineKind::ALL must be strictly increasing under Ord, \
but {:?} >= {:?}",
window[0],
window[1],
);
}
for (i, &a) in DiffLineKind::ALL.iter().enumerate() {
for (j, &b) in DiffLineKind::ALL.iter().enumerate() {
let expected = i.cmp(&j);
assert_eq!(
a.cmp(&b),
expected,
"DiffLineKind::cmp must match ALL-index lex for ({a:?}, {b:?})",
);
assert_eq!(
a.partial_cmp(&b),
Some(expected),
"DiffLineKind::partial_cmp must agree with cmp for ({a:?}, {b:?})",
);
if i == j {
assert_eq!(a.cmp(&b), Ordering::Equal, "Ord must be reflexive on {a:?}",);
}
}
}
}
#[test]
fn diff_line_kind_btreemap_emits_in_declaration_order() {
use std::collections::BTreeMap;
let mut counts: BTreeMap<DiffLineKind, u32> = BTreeMap::new();
counts.insert(DiffLineKind::Context, 3);
counts.insert(DiffLineKind::Removed, 1);
counts.insert(DiffLineKind::Added, 2);
let observed: Vec<DiffLineKind> = counts.keys().copied().collect();
assert_eq!(
observed,
DiffLineKind::ALL.to_vec(),
"BTreeMap<DiffLineKind, _> must emit keys in ALL declaration order",
);
}
#[test]
fn diff_line_kind_display_matches_as_str() {
for k in DiffLineKind::ALL.iter().copied() {
assert_eq!(
format!("{k}"),
k.as_str(),
"Display must agree with as_str for {k:?}",
);
}
}
#[test]
fn diff_line_kind_from_str_round_trips_over_every_variant() {
for k in DiffLineKind::ALL {
let rendered = k.to_string();
let parsed: DiffLineKind = rendered
.parse()
.expect("FromStr must round-trip Display output");
assert_eq!(parsed, *k, "FromStr must round-trip {k:?}");
}
}
#[test]
fn diff_line_kind_from_str_is_case_insensitive() {
assert_eq!(
"REMOVED".parse::<DiffLineKind>().unwrap(),
DiffLineKind::Removed,
);
assert_eq!(
"Added".parse::<DiffLineKind>().unwrap(),
DiffLineKind::Added,
);
assert_eq!(
"cOnTeXt".parse::<DiffLineKind>().unwrap(),
DiffLineKind::Context,
);
assert_eq!(
"rEmOvEd".parse::<DiffLineKind>().unwrap(),
DiffLineKind::Removed,
);
}
#[test]
fn diff_line_kind_from_str_unknown_kind_error_carries_label_verbatim() {
for bad in &["changed", "deleted", "modified", "", " removed"] {
let err = bad
.parse::<DiffLineKind>()
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn diff_line_kind_serde_yaml_round_trips_over_every_variant() {
for k in DiffLineKind::ALL {
let yaml = serde_yaml::to_string(k).expect("Serialize must succeed");
let parsed: DiffLineKind =
serde_yaml::from_str(&yaml).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_yaml round-trip must preserve {k:?}");
}
}
#[test]
fn diff_line_kind_serde_json_round_trips_over_every_variant() {
for k in DiffLineKind::ALL {
let json = serde_json::to_string(k).expect("Serialize must succeed");
let parsed: DiffLineKind =
serde_json::from_str(&json).expect("Deserialize must accept Serialize output");
assert_eq!(parsed, *k, "serde_json round-trip must preserve {k:?}");
}
}
#[test]
fn diff_line_kind_serde_yaml_is_case_insensitive() {
let cases: &[(&str, DiffLineKind)] = &[
("Removed", DiffLineKind::Removed),
("ADDED", DiffLineKind::Added),
("CoNtExT", DiffLineKind::Context),
("rEmOvEd", DiffLineKind::Removed),
];
for (input, expected) in cases {
let parsed: DiffLineKind =
serde_yaml::from_str(input).expect("case-insensitive Deserialize must succeed");
assert_eq!(
parsed, *expected,
"serde_yaml must parse case-insensitively for input {input:?}",
);
}
}
#[test]
fn diff_line_kind_serde_yaml_unknown_kind_error_carries_label_verbatim() {
for bad in &["changed", "deleted", "modified", "noop"] {
let err = serde_yaml::from_str::<DiffLineKind>(bad)
.expect_err("non-canonical label must reject");
let rendered = err.to_string();
assert!(
rendered.contains(bad),
"rendered serde error must contain the offending label verbatim: \
input={bad:?}, rendered={rendered:?}",
);
}
}
#[test]
fn diff_line_kind_serde_yaml_emission_is_bare_scalar() {
assert_eq!(
serde_yaml::to_string(&DiffLineKind::Removed).unwrap(),
"removed\n",
);
assert_eq!(
serde_yaml::to_string(&DiffLineKind::Added).unwrap(),
"added\n",
);
assert_eq!(
serde_yaml::to_string(&DiffLineKind::Context).unwrap(),
"context\n",
);
}
}
#[cfg(test)]
mod progressive_tests {
use super::*;
use crate::ConfigSource;
use figment::value::{Dict, Value};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Prog {
a: u32,
b: u32,
c: u32,
d: u32,
}
impl TieredConfig for Prog {
fn bare() -> Self {
Self {
a: 0,
b: 0,
c: 0,
d: 0,
}
}
fn discovered() -> Self {
Self {
a: 10,
b: 0,
c: 0,
d: 5,
}
}
fn prescribed_default() -> Self {
Self {
a: 10,
b: 20,
c: 0,
d: 7,
}
}
}
#[test]
fn progressive_value_folds_all_tiers() {
let r = Prog::resolve_progressive();
assert_eq!(
*r.value(),
Prog {
a: 10,
b: 20,
c: 0,
d: 7
}
);
}
#[test]
fn progressive_provenance_credits_each_leaf_to_its_producing_tier() {
let r = Prog::resolve_progressive();
let p = r.provenance();
assert_eq!(
p.provenance_of(&["a"]).unwrap().tier(),
ConfigTierKind::Discovered
);
assert_eq!(
p.provenance_of(&["b"]).unwrap().tier(),
ConfigTierKind::Default
);
assert_eq!(
p.provenance_of(&["c"]).unwrap().tier(),
ConfigTierKind::Bare
);
assert_eq!(
p.provenance_of(&["d"]).unwrap().tier(),
ConfigTierKind::Default
);
}
#[test]
fn progressive_discovery_shows_through_where_prescribed_does_not_override() {
let r = Prog::resolve_progressive();
assert_eq!(r.value().a, 10, "discovered a=10 shows through");
assert_eq!(
r.provenance().provenance_of(&["a"]).unwrap().tier(),
ConfigTierKind::Discovered,
);
assert_eq!(
Prog::resolve_tier(ConfigTier::Default),
Prog::prescribed_default()
);
}
#[test]
fn progressive_provenance_is_complete_over_every_leaf() {
let r = Prog::resolve_progressive();
assert_eq!(r.provenance().len(), 4);
for leaf in [["a"], ["b"], ["c"], ["d"]] {
assert!(
r.provenance().provenance_of(&leaf).is_some(),
"leaf {leaf:?} must have provenance"
);
}
assert!(!r.provenance().is_empty());
}
#[test]
fn progressive_higher_tier_beats_lower_on_override() {
let r = Prog::resolve_progressive();
assert_eq!(r.value().d, 7);
assert_eq!(
r.provenance().provenance_of(&["d"]).unwrap().tier(),
ConfigTierKind::Default
);
}
#[test]
fn progressive_overlay_file_beats_prescribed_and_carries_file_provenance() {
let mut d = Dict::new();
d.insert("b".to_owned(), Value::from(99_u32));
let r = Prog::resolve_progressive_with(&[ProgressiveLayer::file("/etc/prog.yaml", d)]);
assert_eq!(r.value().b, 99, "file overlay beats prescribed b=20");
let prov = r.provenance().provenance_of(&["b"]).unwrap();
assert_eq!(prov.tier(), ConfigTierKind::Custom);
assert_eq!(prov.source(), &ConfigSource::File("/etc/prog.yaml".into()));
assert_eq!(
r.provenance().provenance_of(&["a"]).unwrap().tier(),
ConfigTierKind::Discovered
);
}
#[test]
fn progressive_fold_reorders_a_misordered_low_tier_overlay() {
let mut d = Dict::new();
d.insert("a".to_owned(), Value::from(999_u32));
let sneaky = ProgressiveLayer::new(
Provenance::new(ConfigTierKind::Bare, ConfigSource::Defaults),
d,
);
let r = Prog::resolve_progressive_with(&[sneaky]);
assert_eq!(
r.value().a,
10,
"a low-tier overlay cannot beat the Discovered tier"
);
assert_eq!(
r.provenance().provenance_of(&["a"]).unwrap().tier(),
ConfigTierKind::Discovered
);
}
#[test]
fn progressive_contributing_tiers_in_precedence_order() {
let r = Prog::resolve_progressive();
assert_eq!(
r.provenance().contributing_tiers(),
vec![
ConfigTierKind::Bare,
ConfigTierKind::Discovered,
ConfigTierKind::Default
],
);
}
#[test]
fn progressive_entries_iterate_lexicographically() {
let r = Prog::resolve_progressive();
let paths: Vec<Vec<String>> = r.provenance().entries().map(|(p, _)| p.to_vec()).collect();
assert_eq!(
paths,
vec![
vec!["a".to_string()],
vec!["b".to_string()],
vec!["c".to_string()],
vec!["d".to_string()],
],
);
}
#[test]
fn provenance_map_entries_return_type_is_nameable_provenance_map_entries() {
struct Held<'a> {
walker: ProvenanceMapEntries<'a>,
}
fn hold(map: &ProvenanceMap) -> Held<'_> {
Held {
walker: map.entries(),
}
}
let r = Prog::resolve_progressive();
let mut h = hold(r.provenance());
assert!(h.walker.next().is_some());
}
#[test]
fn provenance_map_entries_clone_preserves_static_traits() {
fn assert_algebra<'a, I>(_: &I)
where
I: Iterator<Item = (&'a [String], &'a Provenance)>
+ DoubleEndedIterator
+ ExactSizeIterator
+ std::iter::FusedIterator
+ Clone,
{
}
let r = Prog::resolve_progressive();
let it = r.provenance().entries();
assert_algebra(&it);
let cloned = it.clone();
let a: Vec<Vec<String>> = it.map(|(p, _)| p.to_vec()).collect();
let b: Vec<Vec<String>> = cloned.map(|(p, _)| p.to_vec()).collect();
assert_eq!(a, b);
}
#[test]
fn provenance_map_entries_next_back_walks_specific_to_coarse() {
let r = Prog::resolve_progressive();
let mut it = r.provenance().entries();
let (last, _) = it.next_back().unwrap();
assert_eq!(last, &["d".to_string()][..]);
let (before_last, _) = it.next_back().unwrap();
assert_eq!(before_last, &["c".to_string()][..]);
let (head, _) = it.next().unwrap();
assert_eq!(head, &["a".to_string()][..]);
let (mid, _) = it.next_back().unwrap();
assert_eq!(mid, &["b".to_string()][..]);
assert!(it.next().is_none());
assert!(it.next_back().is_none());
}
#[test]
fn provenance_map_entries_len_matches_remaining_pulls() {
let r = Prog::resolve_progressive();
let mut it = r.provenance().entries();
assert_eq!(it.len(), 4);
it.next();
assert_eq!(it.len(), 3);
it.next_back();
assert_eq!(it.len(), 2);
it.next();
it.next_back();
assert_eq!(it.len(), 0);
assert!(it.next().is_none());
}
#[test]
fn provenance_map_entries_debug_impl_names_the_struct() {
let r = Prog::resolve_progressive();
let it = r.provenance().entries();
let s = format!("{it:?}");
assert!(
s.contains("ProvenanceMapEntries"),
"Debug output should name the struct type, got: {s}"
);
}
#[test]
fn progressive_pair_is_atomic_via_into_parts() {
let (value, prov) = Prog::resolve_progressive().into_parts();
assert_eq!(value.a, 10);
assert_eq!(
prov.provenance_of(&["a"]).unwrap().tier(),
ConfigTierKind::Discovered
);
}
#[test]
fn into_iter_ref_forwards_to_entries_pointwise() {
let r = Prog::resolve_progressive();
let via_entries: Vec<Vec<String>> =
r.provenance().entries().map(|(p, _)| p.to_vec()).collect();
let via_into_iter_ref: Vec<Vec<String>> = r
.provenance()
.into_iter()
.map(|(p, _)| p.to_vec())
.collect();
assert_eq!(via_entries, via_into_iter_ref);
}
#[test]
fn into_iter_owned_yields_same_paths_and_provenance_as_entries() {
let r = Prog::resolve_progressive();
let borrowed: Vec<(Vec<String>, Provenance)> = r
.provenance()
.entries()
.map(|(p, prov)| (p.to_vec(), prov.clone()))
.collect();
let owned: Vec<(Vec<String>, Provenance)> = r.provenance().clone().into_iter().collect();
assert_eq!(borrowed, owned);
}
#[test]
fn into_iter_owned_len_matches_provenance_map_len() {
let r = Prog::resolve_progressive();
let n = r.provenance().len();
let it = r.provenance().clone().into_iter();
assert_eq!(it.len(), n);
}
#[test]
fn into_iter_owned_next_back_walks_specific_to_coarse() {
let r = Prog::resolve_progressive();
let mut it = r.provenance().clone().into_iter();
let (last, _) = it.next_back().unwrap();
assert_eq!(last, vec!["d".to_string()]);
let (before_last, _) = it.next_back().unwrap();
assert_eq!(before_last, vec!["c".to_string()]);
let (head, _) = it.next().unwrap();
assert_eq!(head, vec!["a".to_string()]);
let (mid, _) = it.next_back().unwrap();
assert_eq!(mid, vec!["b".to_string()]);
assert!(it.next().is_none());
assert!(it.next_back().is_none());
}
#[test]
fn from_iter_collect_recovers_provenance_map() {
let r = Prog::resolve_progressive();
let source: ProvenanceMap = r.provenance().clone();
let round: ProvenanceMap = source
.entries()
.map(|(p, prov)| (p.to_vec(), prov.clone()))
.collect();
assert_eq!(source, round);
}
#[test]
fn from_iter_empty_source_is_default() {
let empty: ProvenanceMap = std::iter::empty::<(Vec<String>, Provenance)>().collect();
assert_eq!(empty, ProvenanceMap::default());
assert!(empty.is_empty());
assert_eq!(empty.len(), 0);
}
#[test]
fn from_iter_last_write_wins_on_duplicate_paths() {
let path = vec!["k".to_string()];
let first = Provenance::computed(ConfigTierKind::Bare);
let second = Provenance::computed(ConfigTierKind::Discovered);
let map: ProvenanceMap = vec![(path.clone(), first), (path.clone(), second.clone())]
.into_iter()
.collect();
assert_eq!(map.len(), 1);
assert_eq!(map.provenance_of_owned(&path), Some(&second));
}
#[test]
fn extend_adds_new_paths_and_overwrites_at_conflicts() {
let a = vec!["a".to_string()];
let b = vec!["b".to_string()];
let bare = Provenance::computed(ConfigTierKind::Bare);
let disc = Provenance::computed(ConfigTierKind::Discovered);
let mut map: ProvenanceMap = vec![(a.clone(), bare.clone())].into_iter().collect();
assert_eq!(map.len(), 1);
map.extend(vec![(b.clone(), disc.clone()), (a.clone(), disc.clone())]);
assert_eq!(map.len(), 2);
assert_eq!(map.provenance_of_owned(&a), Some(&disc));
assert_eq!(map.provenance_of_owned(&b), Some(&disc));
map.extend(std::iter::empty::<(Vec<String>, Provenance)>());
assert_eq!(map.len(), 2);
}
#[test]
fn into_iter_owned_debug_impl_names_the_struct() {
let r = Prog::resolve_progressive();
let it = r.provenance().clone().into_iter();
let s = format!("{it:?}");
assert!(
s.contains("ProvenanceMapIntoIter"),
"Debug output should name the struct type, got: {s}"
);
}
#[test]
fn into_iter_owned_return_type_is_nameable_provenance_map_into_iter() {
struct Held {
walker: ProvenanceMapIntoIter,
}
fn hold(map: ProvenanceMap) -> Held {
Held {
walker: map.into_iter(),
}
}
let r = Prog::resolve_progressive();
let mut h = hold(r.provenance().clone());
assert!(h.walker.next().is_some());
}
#[test]
fn owned_into_iter_last_returns_trailing_entry() {
let r = Prog::resolve_progressive();
let it = r.provenance().clone().into_iter();
let (last_path, _) = it.last().unwrap();
assert_eq!(last_path, vec!["d".to_string()]);
}
#[test]
fn tier_histogram_total_matches_provenance_map_len() {
let r = Prog::resolve_progressive();
let hist = r.provenance().tier_histogram();
assert_eq!(hist.total(), r.provenance().len());
assert_eq!(hist.total(), 4); }
#[test]
fn tier_histogram_per_tier_count_matches_entries_walk() {
let r = Prog::resolve_progressive();
let hist = r.provenance().tier_histogram();
assert_eq!(hist.count(ConfigTierKind::Bare), 1); assert_eq!(hist.count(ConfigTierKind::Discovered), 1); assert_eq!(hist.count(ConfigTierKind::Default), 2); assert_eq!(hist.count(ConfigTierKind::Custom), 0); for tier in ConfigTierKind::ALL.iter().copied() {
let manual = r
.provenance()
.entries()
.filter(|(_, p)| p.tier() == tier)
.count();
assert_eq!(
hist.count(tier),
manual,
"per-tier bucket must equal manual entries-walk tally on {tier:?}",
);
}
}
#[test]
fn tier_histogram_observed_matches_contributing_tiers_in_precedence_order() {
let r = Prog::resolve_progressive();
let observed: Vec<ConfigTierKind> = r.provenance().tier_histogram().observed().collect();
assert_eq!(observed, r.provenance().contributing_tiers());
assert_eq!(
observed,
vec![
ConfigTierKind::Bare,
ConfigTierKind::Discovered,
ConfigTierKind::Default,
],
);
}
#[test]
fn contributing_tiers_matches_tier_histogram_observed() {
for provenance_map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_histogram: Vec<ConfigTierKind> =
provenance_map.tier_histogram().observed().collect();
assert_eq!(provenance_map.contributing_tiers(), via_histogram);
}
}
#[test]
fn tier_histogram_dominant_cell_names_widest_tier() {
let r = Prog::resolve_progressive();
assert_eq!(
r.provenance().tier_histogram().dominant_cell(),
Some(ConfigTierKind::Default),
);
}
#[test]
fn tier_histogram_unobserved_names_the_absent_tiers() {
let r = Prog::resolve_progressive();
let unobserved: Vec<ConfigTierKind> =
r.provenance().tier_histogram().unobserved().collect();
assert_eq!(unobserved, vec![ConfigTierKind::Custom]);
}
#[test]
fn tier_histogram_is_empty_iff_provenance_map_is_empty() {
let empty = ProvenanceMap::default();
assert!(empty.is_empty());
assert!(empty.tier_histogram().is_empty());
assert_eq!(empty.tier_histogram().total(), 0);
assert_eq!(empty.contributing_tiers(), Vec::<ConfigTierKind>::new());
let r = Prog::resolve_progressive();
assert!(!r.provenance().is_empty());
assert!(!r.provenance().tier_histogram().is_empty());
}
#[test]
fn tier_histogram_distinct_cells_matches_contributing_tiers_len() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(
map.tier_histogram().distinct_cells(),
map.contributing_tiers().len(),
);
}
}
#[test]
fn absent_tiers_matches_tier_histogram_unobserved_pointwise() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_histogram: Vec<ConfigTierKind> = map.tier_histogram().unobserved().collect();
assert_eq!(map.absent_tiers(), via_histogram);
}
}
#[test]
fn absent_tiers_prog_fixture_is_custom_only() {
let r = Prog::resolve_progressive();
assert_eq!(r.provenance().absent_tiers(), vec![ConfigTierKind::Custom]);
}
#[test]
fn absent_tiers_empty_map_is_full_axis() {
let empty = ProvenanceMap::default();
assert_eq!(empty.absent_tiers(), ConfigTierKind::ALL.to_vec());
}
#[test]
fn absent_tiers_iterates_in_declaration_order() {
let empty = ProvenanceMap::default();
assert_eq!(
empty.absent_tiers(),
vec![
ConfigTierKind::Bare,
ConfigTierKind::Discovered,
ConfigTierKind::Default,
ConfigTierKind::Custom,
],
);
}
#[test]
fn absent_tiers_len_matches_unobserved_cells() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(
map.absent_tiers().len(),
map.tier_histogram().unobserved_cells(),
);
}
}
#[test]
fn absent_tiers_and_contributing_tiers_partition_axis() {
let axis_size = crate::axis_cardinality::<ConfigTierKind>();
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let observed = map.contributing_tiers();
let absent = map.absent_tiers();
assert_eq!(observed.len() + absent.len(), axis_size);
for tier in &observed {
assert!(
!absent.contains(tier),
"tier {tier:?} appears in both contributing and absent",
);
}
for cell in ConfigTierKind::ALL {
assert!(
observed.contains(cell) || absent.contains(cell),
"tier {cell:?} appears in neither contributing nor absent",
);
}
}
}
#[test]
fn absent_tiers_is_empty_iff_is_full_cover() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(
map.absent_tiers().is_empty(),
map.tier_histogram().is_full_cover(),
);
}
let prog = Prog::resolve_progressive();
assert!(!prog.provenance().tier_histogram().is_full_cover());
assert!(!prog.provenance().absent_tiers().is_empty());
}
#[test]
fn absent_tiers_is_strictly_ascending_by_axis_ordinal() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let absent = map.absent_tiers();
for pair in absent.windows(2) {
assert!(
crate::axis_ordinal(pair[0]) < crate::axis_ordinal(pair[1]),
"absent_tiers must be strictly ascending: {absent:?}",
);
}
}
}
#[test]
fn absent_tiers_full_cover_yields_empty() {
let mut d = Dict::new();
d.insert("b".to_owned(), Value::from(99_u32));
let r = Prog::resolve_progressive_with(&[ProgressiveLayer::file("/etc/prog.yaml", d)]);
assert!(r.provenance().tier_histogram().is_full_cover());
assert_eq!(r.provenance().absent_tiers(), Vec::<ConfigTierKind>::new());
assert_eq!(
r.provenance().contributing_tiers(),
ConfigTierKind::ALL.to_vec(),
);
}
#[test]
fn absent_tiers_agrees_with_open_coded_coverage_gap_walk() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_seam = map.absent_tiers();
let contributing = map.contributing_tiers();
let hand_rolled: Vec<ConfigTierKind> = ConfigTierKind::ALL
.iter()
.copied()
.filter(|t| !contributing.contains(t))
.collect();
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn dominant_tier_matches_tier_histogram_dominant_cell_pointwise() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_histogram = map.tier_histogram().dominant_cell();
assert_eq!(map.dominant_tier(), via_histogram);
}
}
#[test]
fn dominant_tier_prog_fixture_is_default() {
let r = Prog::resolve_progressive();
assert_eq!(
r.provenance().dominant_tier(),
Some(ConfigTierKind::Default)
);
}
#[test]
fn dominant_tier_nested_fixture_is_default() {
let r = Nested::resolve_progressive();
assert_eq!(
r.provenance().dominant_tier(),
Some(ConfigTierKind::Default)
);
}
#[test]
fn dominant_tier_empty_map_is_none() {
let empty = ProvenanceMap::default();
assert_eq!(empty.dominant_tier(), None);
}
#[test]
fn dominant_tier_is_some_iff_map_is_nonempty() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(map.dominant_tier().is_some(), !map.is_empty());
}
}
#[test]
fn dominant_tier_is_member_of_contributing_tiers() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let dominant = map
.dominant_tier()
.expect("non-empty map has dominant tier");
assert!(
map.contributing_tiers().contains(&dominant),
"dominant tier {dominant:?} must appear in contributing_tiers",
);
}
}
#[test]
fn dominant_tier_is_not_member_of_absent_tiers() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let dominant = map
.dominant_tier()
.expect("non-empty map has dominant tier");
assert!(
!map.absent_tiers().contains(&dominant),
"dominant tier {dominant:?} must not appear in absent_tiers",
);
}
}
#[test]
fn dominant_tier_count_equals_peak_count_on_nonempty_map() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let hist = map.tier_histogram();
let dominant = map
.dominant_tier()
.expect("non-empty map has dominant tier");
assert_eq!(hist.count(dominant), hist.peak_count());
}
}
#[test]
fn dominant_tier_ties_broken_by_declaration_order() {
let mut d = Dict::new();
d.insert("b".to_owned(), Value::from(99_u32));
let r = Prog::resolve_progressive_with(&[ProgressiveLayer::file("/etc/prog.yaml", d)]);
let hist = r.provenance().tier_histogram();
assert_eq!(hist.count(ConfigTierKind::Bare), 1);
assert_eq!(hist.count(ConfigTierKind::Discovered), 1);
assert_eq!(hist.count(ConfigTierKind::Default), 1);
assert_eq!(hist.count(ConfigTierKind::Custom), 1);
assert!(hist.is_full_cover());
assert_eq!(r.provenance().dominant_tier(), Some(ConfigTierKind::Bare));
}
#[test]
fn dominant_tier_agrees_with_open_coded_argmax_walk() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_seam = map.dominant_tier();
let hist = map.tier_histogram();
let mut iter = hist.iter().filter(|&(_, c)| c > 0);
let hand_rolled = iter.next().map(|first| {
iter.fold(
first,
|best, current| {
if current.1 > best.1 { current } else { best }
},
)
.0
});
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn dominant_tier_uniform_cover_picks_first_cell() {
let m: ProvenanceMap = ConfigTierKind::ALL
.iter()
.copied()
.map(|t| (vec![t.as_str().to_owned()], Provenance::computed(t)))
.collect();
assert!(m.tier_histogram().is_full_cover());
assert_eq!(m.dominant_tier(), Some(ConfigTierKind::Bare));
}
#[test]
fn peak_tier_count_matches_tier_histogram_peak_count_pointwise() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_histogram = map.tier_histogram().peak_count();
assert_eq!(map.peak_tier_count(), via_histogram);
}
}
#[test]
fn peak_tier_count_prog_fixture_is_two() {
let r = Prog::resolve_progressive();
assert_eq!(r.provenance().peak_tier_count(), 2);
}
#[test]
fn peak_tier_count_nested_fixture_is_two() {
let r = Nested::resolve_progressive();
assert_eq!(r.provenance().peak_tier_count(), 2);
}
#[test]
fn peak_tier_count_empty_map_is_zero() {
let empty = ProvenanceMap::default();
assert_eq!(empty.peak_tier_count(), 0);
}
#[test]
fn peak_tier_count_is_zero_iff_map_is_empty() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(map.peak_tier_count() == 0, map.is_empty());
}
}
#[test]
fn peak_tier_count_equals_count_at_dominant_tier_on_nonempty_map() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let hist = map.tier_histogram();
let dominant = map
.dominant_tier()
.expect("non-empty map has dominant tier");
assert_eq!(hist.count(dominant), map.peak_tier_count());
}
}
#[test]
fn peak_tier_count_equals_dominant_tier_map_or_count() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let hist = map.tier_histogram();
let via_fused_pair = map.dominant_tier().map_or(0, |t| hist.count(t));
assert_eq!(map.peak_tier_count(), via_fused_pair);
}
}
#[test]
fn peak_tier_count_is_bounded_by_len() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert!(
map.peak_tier_count() <= map.len(),
"peak_tier_count()={p} must be <= len()={n}",
p = map.peak_tier_count(),
n = map.len(),
);
}
}
#[test]
fn peak_tier_count_equals_len_iff_at_most_one_contributing_tier() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(
map.peak_tier_count() == map.len(),
map.contributing_tiers().len() <= 1,
"peak_tier_count == len iff contributing_tiers.len() <= 1 (peak={p}, len={n}, contribs={c})",
p = map.peak_tier_count(),
n = map.len(),
c = map.contributing_tiers().len(),
);
}
}
#[test]
fn peak_tier_count_is_at_least_one_on_nonempty_map() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
assert!(
map.peak_tier_count() >= 1,
"non-empty map must have peak_tier_count >= 1 (peak={p})",
p = map.peak_tier_count(),
);
}
}
#[test]
fn peak_tier_count_uniform_cover_is_one() {
let m: ProvenanceMap = ConfigTierKind::ALL
.iter()
.copied()
.map(|t| (vec![t.as_str().to_owned()], Provenance::computed(t)))
.collect();
assert!(m.tier_histogram().is_full_cover());
assert_eq!(m.peak_tier_count(), 1);
}
#[test]
fn peak_tier_count_singleton_support_equals_len() {
let m: ProvenanceMap = ["a", "b", "c"]
.iter()
.copied()
.map(|k| {
(
vec![k.to_owned()],
Provenance::computed(ConfigTierKind::Default),
)
})
.collect();
assert_eq!(m.contributing_tiers().len(), 1);
assert_eq!(m.peak_tier_count(), m.len());
assert_eq!(m.peak_tier_count(), 3);
}
#[test]
fn peak_tier_count_agrees_with_open_coded_max_over_axis_walk() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_seam = map.peak_tier_count();
let hand_rolled = map
.tier_histogram()
.iter()
.map(|(_, c)| c)
.max()
.unwrap_or(0);
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn recessive_tier_matches_tier_histogram_recessive_cell_pointwise() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_histogram = map.tier_histogram().recessive_cell();
assert_eq!(map.recessive_tier(), via_histogram);
}
}
#[test]
fn recessive_tier_prog_fixture_is_bare() {
let r = Prog::resolve_progressive();
assert_eq!(r.provenance().recessive_tier(), Some(ConfigTierKind::Bare));
}
#[test]
fn recessive_tier_nested_fixture_is_discovered() {
let r = Nested::resolve_progressive();
assert_eq!(
r.provenance().recessive_tier(),
Some(ConfigTierKind::Discovered)
);
}
#[test]
fn recessive_tier_empty_map_is_none() {
let empty = ProvenanceMap::default();
assert_eq!(empty.recessive_tier(), None);
}
#[test]
fn recessive_tier_is_some_iff_map_is_nonempty() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(map.recessive_tier().is_some(), !map.is_empty());
}
}
#[test]
fn recessive_tier_is_some_iff_dominant_tier_is_some() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(
map.recessive_tier().is_some(),
map.dominant_tier().is_some(),
);
}
}
#[test]
fn recessive_tier_is_member_of_contributing_tiers() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let recessive = map
.recessive_tier()
.expect("non-empty map has recessive tier");
assert!(
map.contributing_tiers().contains(&recessive),
"recessive tier {recessive:?} must appear in contributing_tiers",
);
}
}
#[test]
fn recessive_tier_is_not_member_of_absent_tiers() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let recessive = map
.recessive_tier()
.expect("non-empty map has recessive tier");
assert!(
!map.absent_tiers().contains(&recessive),
"recessive tier {recessive:?} must not appear in absent_tiers",
);
}
}
#[test]
fn recessive_tier_count_equals_trough_count_on_nonempty_map() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let hist = map.tier_histogram();
let recessive = map
.recessive_tier()
.expect("non-empty map has recessive tier");
assert_eq!(hist.count(recessive), hist.trough_count());
}
}
#[test]
fn recessive_tier_count_bounded_by_dominant_tier_count() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let hist = map.tier_histogram();
let recessive = map
.recessive_tier()
.expect("non-empty map has recessive tier");
let dominant = map
.dominant_tier()
.expect("non-empty map has dominant tier");
assert!(
hist.count(recessive) <= hist.count(dominant),
"count(recessive={recessive:?})={r} must be <= count(dominant={dominant:?})={d}",
r = hist.count(recessive),
d = hist.count(dominant),
);
}
}
#[test]
fn recessive_tier_ties_broken_by_declaration_order() {
let mut d = Dict::new();
d.insert("b".to_owned(), Value::from(99_u32));
let r = Prog::resolve_progressive_with(&[ProgressiveLayer::file("/etc/prog.yaml", d)]);
let hist = r.provenance().tier_histogram();
assert_eq!(hist.count(ConfigTierKind::Bare), 1);
assert_eq!(hist.count(ConfigTierKind::Discovered), 1);
assert_eq!(hist.count(ConfigTierKind::Default), 1);
assert_eq!(hist.count(ConfigTierKind::Custom), 1);
assert!(hist.is_full_cover());
assert_eq!(r.provenance().recessive_tier(), Some(ConfigTierKind::Bare));
assert_eq!(
r.provenance().recessive_tier(),
r.provenance().dominant_tier()
);
}
#[test]
fn recessive_tier_singleton_support_agrees_with_dominant_tier() {
let m: ProvenanceMap = ["a", "b", "c"]
.iter()
.copied()
.map(|k| {
(
vec![k.to_owned()],
Provenance::computed(ConfigTierKind::Default),
)
})
.collect();
assert_eq!(m.contributing_tiers().len(), 1);
assert_eq!(m.recessive_tier(), m.dominant_tier());
assert_eq!(m.recessive_tier(), Some(ConfigTierKind::Default));
}
#[test]
fn recessive_tier_agrees_with_open_coded_argmin_walk() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_seam = map.recessive_tier();
let hist = map.tier_histogram();
let mut iter = hist.iter().filter(|&(_, c)| c > 0);
let hand_rolled = iter.next().map(|first| {
iter.fold(
first,
|best, current| {
if current.1 < best.1 { current } else { best }
},
)
.0
});
assert_eq!(via_seam, hand_rolled);
}
}
#[test]
fn trough_tier_count_matches_tier_histogram_trough_count_pointwise() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_histogram = map.tier_histogram().trough_count();
assert_eq!(map.trough_tier_count(), via_histogram);
}
}
#[test]
fn trough_tier_count_prog_fixture_is_one() {
let r = Prog::resolve_progressive();
assert_eq!(r.provenance().trough_tier_count(), 1);
}
#[test]
fn trough_tier_count_nested_fixture_is_one() {
let r = Nested::resolve_progressive();
assert_eq!(r.provenance().trough_tier_count(), 1);
}
#[test]
fn trough_tier_count_empty_map_is_zero() {
let empty = ProvenanceMap::default();
assert_eq!(empty.trough_tier_count(), 0);
}
#[test]
fn trough_tier_count_is_zero_iff_map_is_empty() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert_eq!(map.trough_tier_count() == 0, map.is_empty());
}
}
#[test]
fn trough_tier_count_equals_count_at_recessive_tier_on_nonempty_map() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
let hist = map.tier_histogram();
let recessive = map
.recessive_tier()
.expect("non-empty map has recessive tier");
assert_eq!(hist.count(recessive), map.trough_tier_count());
}
}
#[test]
fn trough_tier_count_equals_recessive_tier_map_or_count() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let hist = map.tier_histogram();
let via_fused_pair = map.recessive_tier().map_or(0, |t| hist.count(t));
assert_eq!(map.trough_tier_count(), via_fused_pair);
}
}
#[test]
fn trough_tier_count_is_bounded_by_peak_tier_count() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
assert!(
map.trough_tier_count() <= map.peak_tier_count(),
"trough_tier_count()={t} must be <= peak_tier_count()={p}",
t = map.trough_tier_count(),
p = map.peak_tier_count(),
);
}
}
#[test]
fn trough_tier_count_equals_peak_tier_count_iff_at_most_one_contributing_tier() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let equal_when_at_most_one = map.trough_tier_count() == map.peak_tier_count();
let at_most_one_contributing = map.contributing_tiers().len() <= 1;
if at_most_one_contributing {
assert!(
equal_when_at_most_one,
"at_most_one_contributing → trough == peak (trough={t}, peak={p})",
t = map.trough_tier_count(),
p = map.peak_tier_count(),
);
}
}
}
#[test]
fn trough_tier_count_is_at_least_one_on_nonempty_map() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
] {
assert!(
map.trough_tier_count() >= 1,
"non-empty map must have trough_tier_count >= 1 (trough={t})",
t = map.trough_tier_count(),
);
}
}
#[test]
fn trough_tier_count_uniform_cover_is_one() {
let m: ProvenanceMap = ConfigTierKind::ALL
.iter()
.copied()
.map(|t| (vec![t.as_str().to_owned()], Provenance::computed(t)))
.collect();
assert!(m.tier_histogram().is_full_cover());
assert_eq!(m.trough_tier_count(), 1);
assert_eq!(m.trough_tier_count(), m.peak_tier_count());
}
#[test]
fn trough_tier_count_singleton_support_equals_len() {
let m: ProvenanceMap = ["a", "b", "c"]
.iter()
.copied()
.map(|k| {
(
vec![k.to_owned()],
Provenance::computed(ConfigTierKind::Default),
)
})
.collect();
assert_eq!(m.contributing_tiers().len(), 1);
assert_eq!(m.trough_tier_count(), m.len());
assert_eq!(m.trough_tier_count(), 3);
assert_eq!(m.trough_tier_count(), m.peak_tier_count());
}
#[test]
fn trough_tier_count_agrees_with_open_coded_min_over_support_walk() {
for map in [
Prog::resolve_progressive().provenance().clone(),
Nested::resolve_progressive().provenance().clone(),
ProvenanceMap::default(),
] {
let via_seam = map.trough_tier_count();
let hand_rolled = map
.tier_histogram()
.iter()
.filter(|&(_, c)| c > 0)
.map(|(_, c)| c)
.min()
.unwrap_or(0);
assert_eq!(via_seam, hand_rolled);
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Win {
w: u32,
h: u32,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Nested {
win: Win,
theme: u32,
}
impl TieredConfig for Nested {
fn bare() -> Self {
Self {
win: Win { w: 0, h: 0 },
theme: 0,
}
}
fn discovered() -> Self {
Self {
win: Win { w: 100, h: 0 },
theme: 0,
}
}
fn prescribed_default() -> Self {
Self {
win: Win { w: 100, h: 50 },
theme: 7,
}
}
}
#[test]
fn progressive_attributes_nested_leaves_independently() {
let r = Nested::resolve_progressive();
assert_eq!(r.value().win.w, 100);
assert_eq!(r.value().win.h, 50);
assert_eq!(
r.provenance().provenance_of(&["win", "w"]).unwrap().tier(),
ConfigTierKind::Discovered,
);
assert_eq!(
r.provenance().provenance_of(&["win", "h"]).unwrap().tier(),
ConfigTierKind::Default,
);
assert_eq!(
r.provenance().provenance_of(&["theme"]).unwrap().tier(),
ConfigTierKind::Default,
);
}
struct AxisLayer {
key: &'static str,
val: u32,
}
impl DiscoveryLayer for AxisLayer {
fn name(&self) -> &'static str {
"axis"
}
fn discover(&self) -> Dict {
let mut d = Dict::new();
d.insert(self.key.to_owned(), Value::from(self.val));
d
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Seam {
a: u32,
b: u32,
}
impl TieredConfig for Seam {
fn bare() -> Self {
Self { a: 0, b: 0 }
}
fn discovered() -> Self {
Self::discovered_from_layers(&[&AxisLayer { key: "a", val: 42 }])
}
fn prescribed_default() -> Self {
let mut s = Self::discovered();
s.b = 2;
s
}
}
#[test]
fn discovered_from_layers_overlays_detected_axes_on_bare() {
let d = Seam::discovered();
assert_eq!(d.a, 42, "detected axis a overlays bare");
assert_eq!(d.b, 0, "an axis no layer set keeps the bare floor");
}
#[test]
fn discovered_from_layers_empty_stack_is_bare() {
assert_eq!(Seam::discovered_from_layers(&[]), Seam::bare());
}
#[test]
fn seam_progressive_shows_detected_axis_through_prescribed() {
let r = Seam::resolve_progressive();
assert_eq!(*r.value(), Seam { a: 42, b: 2 });
assert_eq!(
r.provenance().provenance_of(&["a"]).unwrap().tier(),
ConfigTierKind::Discovered,
);
assert_eq!(
r.provenance().provenance_of(&["b"]).unwrap().tier(),
ConfigTierKind::Default,
);
}
#[test]
fn provenance_display_is_typed() {
assert_eq!(
Provenance::computed(ConfigTierKind::Discovered).to_string(),
"discovered"
);
assert_eq!(
Provenance::file("/x.yaml").to_string(),
"custom (file: /x.yaml)"
);
assert_eq!(Provenance::env("APP_").to_string(), "custom (env: APP_)");
}
#[test]
fn provenance_tier_ordinal_reuses_closed_axis_order() {
assert!(
Provenance::computed(ConfigTierKind::Bare).tier_ordinal()
< Provenance::computed(ConfigTierKind::Discovered).tier_ordinal()
);
assert!(
Provenance::computed(ConfigTierKind::Discovered).tier_ordinal()
< Provenance::computed(ConfigTierKind::Default).tier_ordinal()
);
assert!(
Provenance::computed(ConfigTierKind::Default).tier_ordinal()
< Provenance::file("/x").tier_ordinal()
);
}
}