Skip to main content

sim_lib_lang_prolog/
generated_coverage.rs

1//! Generated expression coverage for the Prolog language row.
2
3use sim_codec_lisp::LispCodecLib;
4use sim_kernel::{Cx, Result, Symbol};
5use sim_lib_lang_genconf::{
6    CoverageVerdict, ExprSpace, GeneratedCoverageReport, LandmarkCorpus, coverage_card_fields,
7    publish_coverage_claims, run_generated_row,
8};
9use sim_lib_standard_core::standard_test_capability;
10
11use crate::prolog_reader_symbol;
12
13const PROLOG_GENERATED_BUDGET: usize = 256;
14
15/// Published generated expression coverage for the Prolog row.
16pub struct PrologGeneratedCoverage {
17    /// Measured generated round-trip report.
18    pub report: GeneratedCoverageReport,
19    /// Anchor verdict controlling public coverage fields and claims.
20    pub verdict: CoverageVerdict,
21}
22
23/// Runs the Prolog row through the shared generated expression conformance path.
24///
25/// The generated claim is supplementary evidence under a generated-coverage
26/// profile. It does not alter the curated Prolog conformance badge.
27pub fn run_prolog_generated_coverage(cx: &mut Cx) -> Result<PrologGeneratedCoverage> {
28    cx.require(&standard_test_capability())?;
29    ensure_lisp_codec(cx)?;
30
31    let language = prolog_language_symbol();
32    let codec = prolog_reader_symbol();
33    let space = ExprSpace::core_round_trip_space(3);
34    let report = run_generated_row(cx, &language, &codec, &space, PROLOG_GENERATED_BUDGET);
35    let verdict = prolog_generated_corpus(&space).reconcile(&report);
36    publish_coverage_claims(cx, &report, &verdict)?;
37
38    Ok(PrologGeneratedCoverage { report, verdict })
39}
40
41pub(crate) fn prolog_generated_coverage_card_fields(
42    cx: &mut Cx,
43) -> Result<Vec<(Symbol, sim_kernel::Value)>> {
44    let coverage = run_prolog_generated_coverage(cx)?;
45    coverage_card_fields(cx, &coverage.report, &coverage.verdict)
46}
47
48fn ensure_lisp_codec(cx: &mut Cx) -> Result<()> {
49    if cx.resolve_codec(&prolog_reader_symbol()).is_ok() {
50        return Ok(());
51    }
52    let lib = LispCodecLib::new(cx.registry_mut().fresh_codec_id())?;
53    cx.load_lib(&lib).map(|_| ())
54}
55
56fn prolog_generated_corpus(space: &ExprSpace) -> LandmarkCorpus {
57    LandmarkCorpus::new(
58        prolog_language_symbol(),
59        Symbol::qualified("expr-space", "core"),
60        1,
61        space.seed_corpus(),
62    )
63}
64
65fn prolog_language_symbol() -> Symbol {
66    Symbol::new("prolog")
67}
68
69#[cfg(test)]
70mod tests {
71    use sim_kernel::{ClaimKind, ClaimPattern, Expr, NumberLiteral, Ref, testing::bare_cx as cx};
72    use sim_lib_lang_genconf::generated_coverage_profile_symbol;
73    use sim_lib_standard_core::{standard_test_capability, standard_test_result_predicate};
74
75    use super::*;
76    use crate::run_prolog_matrix_row;
77
78    #[test]
79    fn generated_coverage_publishes_claim_without_changing_curated_counts() {
80        let mut cx = cx();
81        cx.grant(standard_test_capability());
82
83        let curated = run_prolog_matrix_row(&mut cx).unwrap();
84        let coverage = run_prolog_generated_coverage(&mut cx).unwrap();
85
86        assert_eq!(curated.pass_count(), 16);
87        assert_eq!(curated.gap_count(), 3);
88        assert_eq!(curated.fail_count(), 0);
89        assert!(coverage.report.sampled > 0);
90        assert!(coverage.report.round_tripped > 0);
91        assert!(coverage.report.coverage().is_some_and(|value| value > 0.0));
92        let claims = cx
93            .query_facts(ClaimPattern {
94                subject: Some(Ref::Symbol(generated_coverage_profile_symbol(
95                    &coverage.report.language,
96                ))),
97                predicate: Some(standard_test_result_predicate()),
98                object: None,
99                include_revoked: false,
100            })
101            .unwrap();
102        assert_eq!(claims.len(), 1);
103        assert_eq!(claims[0].kind, ClaimKind::Observed);
104    }
105
106    #[test]
107    fn generated_card_fields_are_separate_from_curated_fields() {
108        let mut cx = cx();
109        cx.grant(standard_test_capability());
110
111        let fields = prolog_generated_coverage_card_fields(&mut cx).unwrap();
112
113        assert!(missing_field(&fields, "conformance.fidelity"));
114        assert_ne!(
115            number_field(&mut cx, &fields, "coverage.generated.sampled"),
116            "0"
117        );
118        assert_ne!(
119            number_field(&mut cx, &fields, "coverage.generated.round-trip"),
120            "0"
121        );
122        let percent = table_value(&mut cx, &fields, "coverage.generated.percent");
123        assert_ne!(percent, Expr::String("unanchored".to_owned()));
124        assert_eq!(
125            table_value(&mut cx, &fields, "coverage.generated.citation"),
126            Expr::String("expr-space/core".to_owned())
127        );
128    }
129
130    fn missing_field(fields: &[(Symbol, sim_kernel::Value)], name: &str) -> bool {
131        fields.iter().all(|(field, _)| field != &Symbol::new(name))
132    }
133
134    fn number_field(cx: &mut Cx, fields: &[(Symbol, sim_kernel::Value)], name: &str) -> String {
135        let Expr::Number(NumberLiteral { domain, canonical }) = table_value(cx, fields, name)
136        else {
137            panic!("expected number field {name}");
138        };
139        assert_eq!(domain, Symbol::qualified("numbers", "u64"));
140        canonical
141    }
142
143    fn table_value(cx: &mut Cx, fields: &[(Symbol, sim_kernel::Value)], name: &str) -> Expr {
144        fields
145            .iter()
146            .find_map(|(field, value)| {
147                (field == &Symbol::new(name)).then(|| value.object().as_expr(cx).unwrap())
148            })
149            .unwrap_or_else(|| panic!("missing field {name}"))
150    }
151}