sim_lib_lang_prolog/
generated_coverage.rs1use 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
15pub struct PrologGeneratedCoverage {
17 pub report: GeneratedCoverageReport,
19 pub verdict: CoverageVerdict,
21}
22
23pub 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}