use crate::codegen::{self, CodegenError, ExecutionResult, MachineCode}; use crate::emit::{self, EmitError}; use crate::l0_ir::L0Program; use crate::l1_ir::L1Program; use crate::parser::{self, ParseError}; use crate::runtime::{InvariantKind, InvariantResult, RuntimeTrace, check_output_determinism, RuntimeEngine}; use crate::translate_validate::{self, VerificationReport}; use crate::verify_l0; use crate::verify_l1::{self, VerifyError}; use std::collections::HashMap; use std::fmt; /// Errors from any stage of the pipeline. #[derive(Debug)] pub enum PipelineError { Parse(ParseError), VerifyL1(VerifyError), Emit(EmitError), VerifyL0(Vec), Codegen(CodegenError), TranslationValidation(String), Runtime(String), } impl fmt::Display for PipelineError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PipelineError::Parse(e) => write!(f, "parse: {}", e), PipelineError::VerifyL1(e) => write!(f, "L1 verify: {}", e), PipelineError::Emit(e) => write!(f, "emit: {}", e), PipelineError::VerifyL0(errs) => { write!(f, "L0 verify:")?; for e in errs { write!(f, " {}", e)?; } Ok(()) } PipelineError::Codegen(e) => write!(f, "codegen: {}", e), PipelineError::TranslationValidation(msg) => write!(f, "translation validation: {}", msg), PipelineError::Runtime(msg) => write!(f, "runtime: {}", msg), } } } impl std::error::Error for PipelineError {} /// All intermediate representations captured during pipeline execution. pub struct PipelineIR { pub l1: L1Program, pub l0: L0Program, pub machine_code: MachineCode, } /// Run the full pipeline: DSL text -> parse -> L1 IR -> verify -> emit L0 IR /// -> verify -> codegen -> execute. /// /// Returns both the execution result and captured intermediate representations. pub fn run(source: &str) -> Result<(ExecutionResult, PipelineIR), PipelineError> { // Parse let l1 = parser::parse(source).map_err(PipelineError::Parse)?; // Verify L1 verify_l1::verify(&l1).map_err(PipelineError::VerifyL1)?; // Emit L0 IR let l0 = emit::emit(&l1).map_err(PipelineError::Emit)?; // Verify L0 verify_l0::verify(&l0).map_err(PipelineError::VerifyL0)?; // Codegen let mc = codegen::codegen(&l0).map_err(PipelineError::Codegen)?; // Execute let result = codegen::execute(&mc).map_err(PipelineError::Codegen)?; let ir = PipelineIR { l1, l0, machine_code: mc, }; Ok((result, ir)) } /// Dump all intermediate representations for a given DSL input. /// Returns a human-readable string showing L1 IR, L0 IR, and x86-64 hex. pub fn dump_ir(source: &str) -> Result { let l1 = parser::parse(source).map_err(PipelineError::Parse)?; verify_l1::verify(&l1).map_err(PipelineError::VerifyL1)?; let l0 = emit::emit(&l1).map_err(PipelineError::Emit)?; verify_l0::verify(&l0).map_err(PipelineError::VerifyL0)?; let mc = codegen::codegen(&l0).map_err(PipelineError::Codegen)?; let mut out = String::new(); out.push_str("========== L1 IR ==========\n"); out.push_str(&format!("{}", l1)); out.push('\n'); out.push_str("========== L0 IR ==========\n"); out.push_str(&format!("{}", l0)); out.push('\n'); out.push_str("========== x86-64 Machine Code ==========\n"); out.push_str(&mc.hex_dump()); out.push('\n'); Ok(out) } /// Run the full verification pipeline: parse → L1 verify → emit → L0 verify → translate validate → report. /// /// Returns the verification report containing results from all three validation tools. pub fn verify_translation(source: &str) -> Result { // Parse let l1 = parser::parse(source).map_err(PipelineError::Parse)?; // Verify L1 verify_l1::verify(&l1).map_err(PipelineError::VerifyL1)?; // Emit L0 IR let l0 = emit::emit(&l1).map_err(PipelineError::Emit)?; // Verify L0 verify_l0::verify(&l0).map_err(PipelineError::VerifyL0)?; // Translation validation let report = translate_validate::validate(&l1, &l0); if !report.all_passed() { return Err(PipelineError::TranslationValidation(format!("{}", report))); } Ok(report) } /// Per-step summary of invariant check results. #[derive(Debug, Clone)] pub struct StepInvariantSummary { pub step: u64, pub checks: Vec<(InvariantKind, InvariantResult)>, } /// Comprehensive report from the verified execution pipeline. #[derive(Debug)] pub struct VerifiedExecutionReport { /// Full runtime execution trace (phase transitions, messages, snapshots, outputs). pub trace: RuntimeTrace, /// Invariant check results grouped by step. pub step_invariants: Vec, /// Final actor states as (name, fields) pairs. pub final_states: Vec<(String, Vec<(String, u64)>)>, /// Output values from the program. pub outputs: Vec, /// Translation validation report. pub verification_report: VerificationReport, /// Human-readable summary line. pub summary: String, } impl fmt::Display for VerifiedExecutionReport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== Verified Execution Report ===")?; writeln!(f)?; // Translation validation summary writeln!(f, "-- Translation Validation --")?; for result in &self.verification_report.results { let status = if result.passed { "PASS" } else { "FAIL" }; writeln!(f, " [{}] {}", status, result.tool)?; } writeln!(f)?; // Runtime invariants summary writeln!(f, "-- Runtime Invariants --")?; let total_checks: usize = self.step_invariants.iter() .map(|s| s.checks.len()) .sum(); let total_passed: usize = self.step_invariants.iter() .flat_map(|s| &s.checks) .filter(|(_, r)| *r == InvariantResult::Pass) .count(); writeln!(f, " {} invariant checks across {} steps: {} passed, {} failed", total_checks, self.step_invariants.len(), total_passed, total_checks - total_passed)?; // Per-kind summary let mut kind_counts: HashMap = HashMap::new(); for step in &self.step_invariants { for (kind, result) in &step.checks { let entry = kind_counts.entry(*kind).or_insert((0, 0)); entry.0 += 1; if *result == InvariantResult::Pass { entry.1 += 1; } } } for kind in &[ InvariantKind::Termination, InvariantKind::ActorSerialization, InvariantKind::MessageIntegrity, InvariantKind::StateIsolation, InvariantKind::WindowConsistency, InvariantKind::OutputDeterminism, ] { if let Some((total, passed)) = kind_counts.get(kind) { let status = if total == passed { "PASS" } else { "FAIL" }; writeln!(f, " [{}] {} ({}/{})", status, kind, passed, total)?; } } writeln!(f)?; // Final states writeln!(f, "-- Final States --")?; for (name, fields) in &self.final_states { let flds: Vec = fields.iter().map(|(n, v)| format!("{}={}", n, v)).collect(); writeln!(f, " {} {{ {} }}", name, flds.join(", "))?; } // Outputs if !self.outputs.is_empty() { writeln!(f, " outputs: {:?}", self.outputs)?; } writeln!(f)?; writeln!(f, "-- Summary --")?; writeln!(f, " {}", self.summary)?; Ok(()) } } /// Run the full verified execution pipeline: parse → L1 verify → emit → L0 verify /// → translation validation → runtime execution with all invariants → report. pub fn run_verified(source: &str) -> Result { // Parse let l1 = parser::parse(source).map_err(PipelineError::Parse)?; // Verify L1 verify_l1::verify(&l1).map_err(PipelineError::VerifyL1)?; // Emit L0 IR let l0 = emit::emit(&l1).map_err(PipelineError::Emit)?; // Verify L0 verify_l0::verify(&l0).map_err(PipelineError::VerifyL0)?; // Translation validation let verification_report = translate_validate::validate(&l1, &l0); if !verification_report.all_passed() { return Err(PipelineError::TranslationValidation(format!("{}", verification_report))); } // Runtime execution with invariants let mut engine = RuntimeEngine::new(&l1); let trace = engine.run_with_invariants(true) .map_err(|e| PipelineError::Runtime(e.message))?; // Output determinism: run a second time and compare let mut engine2 = RuntimeEngine::new(&l1); let trace2 = engine2.run_with_invariants(false) .map_err(|e| PipelineError::Runtime(e.message))?; let determinism_result = check_output_determinism(&trace, &trace2); // Build step invariant summaries let mut step_invariants: Vec = Vec::new(); for check in &trace.invariant_checks { let step_summary = step_invariants.iter_mut() .find(|s| s.step == check.step); match step_summary { Some(s) => s.checks.push((check.kind, check.result.clone())), None => step_invariants.push(StepInvariantSummary { step: check.step, checks: vec![(check.kind, check.result.clone())], }), } } // Add output determinism to the last step's checks if let Some(last) = step_invariants.last_mut() { last.checks.push((InvariantKind::OutputDeterminism, determinism_result.clone())); } // Final states let final_states: Vec<(String, Vec<(String, u64)>)> = trace.final_actor_states.iter() .map(|snap| (snap.actor_name.clone(), snap.fields.clone())) .collect(); let outputs = trace.all_outputs.clone(); // Build summary let total_checks: usize = step_invariants.iter().map(|s| s.checks.len()).sum(); let all_invariants_passed = step_invariants.iter() .flat_map(|s| &s.checks) .all(|(_, r)| *r == InvariantResult::Pass); let all_translation_passed = verification_report.all_passed(); let summary = if all_invariants_passed && all_translation_passed { format!( "all {} invariant checks passed across {} steps; translation validation passed", total_checks, trace.steps.len() ) } else { let failed: Vec = step_invariants.iter() .flat_map(|s| &s.checks) .filter(|(_, r)| *r != InvariantResult::Pass) .map(|(k, r)| match r { InvariantResult::Fail(msg) => format!("{}: {}", k, msg), InvariantResult::Pass => unreachable!(), }) .collect(); format!("FAILURES: {}", failed.join("; ")) }; Ok(VerifiedExecutionReport { trace, step_invariants, final_states, outputs, verification_report, summary, }) } #[cfg(test)] mod tests { use super::*; #[test] fn counter_end_to_end() { let source = include_str!("../examples/counter.l1"); let (result, _ir) = run(source).expect("counter pipeline should succeed"); let count = result.read_u64("counter_state", 0).expect("should read counter_state"); assert_eq!(count, 5, "counter should be 5 after 5 steps"); } #[test] fn window_end_to_end() { let source = include_str!("../examples/window.l1"); let (result, _ir) = run(source).expect("window pipeline should succeed"); let total = result.read_u64("accumulator_state", 0).expect("should read accumulator_state"); assert_eq!(total, 30, "total should be 30 after 3 steps of Add(10)"); let observed = result.read_output_values(); assert_eq!(observed, vec![10, 20, 30], "observed values should be [10, 20, 30]"); } #[test] fn counter_ir_dump() { let source = include_str!("../examples/counter.l1"); let dump = dump_ir(source).expect("dump should succeed"); assert!(dump.contains("========== L1 IR ==========")); assert!(dump.contains("========== L0 IR ==========")); assert!(dump.contains("========== x86-64 Machine Code ==========")); assert!(dump.contains("actor counter")); assert!(dump.contains("=== Regions ===")); assert!(dump.contains("counter_state")); } #[test] fn window_ir_dump() { let source = include_str!("../examples/window.l1"); let dump = dump_ir(source).expect("dump should succeed"); assert!(dump.contains("========== L1 IR ==========")); assert!(dump.contains("accumulator")); assert!(dump.contains("output")); } #[test] fn counter_test_vector_full_pipeline() { // Full test vector: DSL source -> L1 IR -> L0 IR -> x86-64 -> result let source = include_str!("../examples/counter.l1"); let (result, ir) = run(source).expect("pipeline should succeed"); // 1. DSL source is the counter.l1 file (inlined via include_str!) assert!(source.contains("actor counter")); assert!(source.contains("on Increment(amount: u64)")); assert!(source.contains("steps: 5")); // 2. Expected L1 IR text let l1_text = format!("{}", ir.l1); assert!(l1_text.contains("actor counter {")); assert!(l1_text.contains("count: u64 = 0")); assert!(l1_text.contains("window count_view : (count) readers(display)")); assert!(l1_text.contains("on Increment(amount: u64) { count = (count + amount) }")); assert!(l1_text.contains("leaf ticker {")); assert!(l1_text.contains("forward(counter, Increment(1))")); assert!(l1_text.contains("leaf display {")); assert!(l1_text.contains("pipeline main { ticker -> counter -> display }")); assert!(l1_text.contains("steps: 5")); // 3. Expected L0 IR text let l0_text = format!("{}", ir.l0); assert!(l0_text.contains("=== Regions ===")); assert!(l0_text.contains("region counter_state : 8 bytes, rw, state")); assert!(l0_text.contains("queue")); assert!(l0_text.contains("region step_counter : 8 bytes, rw, control")); assert!(l0_text.contains("=== Blocks ===")); assert!(l0_text.contains("entry:")); assert!(l0_text.contains("loop_check:")); assert!(l0_text.contains("step:")); assert!(l0_text.contains("exit:")); assert!(l0_text.contains("load.64")); assert!(l0_text.contains("store.64")); assert!(l0_text.contains("cmp.lt")); assert!(l0_text.contains("branch")); assert!(l0_text.contains("queue_push")); assert!(l0_text.contains("queue_pop")); assert!(l0_text.contains("terminate")); // 4. Expected x86-64 bytes (non-empty hex string) let hex = ir.machine_code.hex_dump(); assert!(!hex.is_empty(), "machine code should not be empty"); // Verify it ends with ret (c3) preceded by the epilogue pops assert!(hex.contains("c3"), "machine code should contain ret instruction"); // 5. Expected execution result let count = result.read_u64("counter_state", 0).unwrap(); assert_eq!(count, 5, "counter should equal step count (5)"); } #[test] fn window_test_vector_full_pipeline() { // Full test vector: DSL source -> L1 IR -> L0 IR -> x86-64 -> result let source = include_str!("../examples/window.l1"); let (result, ir) = run(source).expect("pipeline should succeed"); // 1. DSL source assert!(source.contains("actor accumulator")); assert!(source.contains("on Add(value: u64)")); assert!(source.contains("steps: 3")); // 2. Expected L1 IR text let l1_text = format!("{}", ir.l1); assert!(l1_text.contains("actor accumulator {")); assert!(l1_text.contains("total: u64 = 0")); assert!(l1_text.contains("window total_view : (total) readers(observe)")); assert!(l1_text.contains("on Add(value: u64) { total = (total + value) }")); assert!(l1_text.contains("leaf source {")); assert!(l1_text.contains("forward(accumulator, Add(10))")); assert!(l1_text.contains("leaf observe {")); assert!(l1_text.contains("emit(total)")); assert!(l1_text.contains("pipeline main { source -> accumulator -> observe }")); assert!(l1_text.contains("steps: 3")); // 3. Expected L0 IR text let l0_text = format!("{}", ir.l0); assert!(l0_text.contains("region accumulator_state : 8 bytes, rw, state")); assert!(l0_text.contains("region output :")); assert!(l0_text.contains("output")); assert!(l0_text.contains("entry:")); assert!(l0_text.contains("queue_push output")); // 4. x86-64 bytes let hex = ir.machine_code.hex_dump(); assert!(!hex.is_empty()); assert!(hex.contains("c3")); // 5. Expected execution result let total = result.read_u64("accumulator_state", 0).unwrap(); assert_eq!(total, 30, "total should be 30"); let observed = result.read_output_values(); assert_eq!(observed, vec![10, 20, 30], "observed values should be [10, 20, 30]"); } #[test] fn pipeline_error_on_invalid_input() { let result = run("invalid garbage input"); assert!(result.is_err()); } #[test] fn verify_translation_counter() { let source = include_str!("../examples/counter.l1"); let report = verify_translation(source).expect("counter verification should pass"); assert!(report.all_passed()); assert_eq!(report.results.len(), 3); } #[test] fn verify_translation_window() { let source = include_str!("../examples/window.l1"); let report = verify_translation(source).expect("window verification should pass"); assert!(report.all_passed()); } #[test] fn verify_translation_product() { let source = include_str!("../examples/product.l1"); let report = verify_translation(source).expect("product verification should pass"); assert!(report.all_passed()); } #[test] fn verify_translation_invalid_input() { let result = verify_translation("invalid garbage"); assert!(result.is_err()); } // ── Stage 4: Verified Execution Pipeline Tests ────────────── #[test] fn run_verified_counter_all_invariants_pass() { let source = include_str!("../examples/counter.l1"); let report = run_verified(source).expect("run_verified should succeed for counter"); // All six invariant kinds should be present let all_kinds: std::collections::HashSet<_> = report.step_invariants.iter() .flat_map(|s| s.checks.iter().map(|(k, _)| *k)) .collect(); assert!(all_kinds.contains(&crate::runtime::InvariantKind::Termination)); assert!(all_kinds.contains(&crate::runtime::InvariantKind::ActorSerialization)); assert!(all_kinds.contains(&crate::runtime::InvariantKind::MessageIntegrity)); assert!(all_kinds.contains(&crate::runtime::InvariantKind::StateIsolation)); assert!(all_kinds.contains(&crate::runtime::InvariantKind::WindowConsistency)); assert!(all_kinds.contains(&crate::runtime::InvariantKind::OutputDeterminism)); // All should pass let all_pass = report.step_invariants.iter() .flat_map(|s| &s.checks) .all(|(_, r)| *r == crate::runtime::InvariantResult::Pass); assert!(all_pass, "all invariants should pass for counter"); // Final state should match assert_eq!(report.final_states.len(), 1); assert_eq!(report.final_states[0].0, "counter"); assert_eq!(report.final_states[0].1, vec![("count".to_string(), 5)]); // Summary should be positive assert!(report.summary.contains("passed"), "summary should indicate success: {}", report.summary); } #[test] fn run_verified_window_all_invariants_pass() { let source = include_str!("../examples/window.l1"); let report = run_verified(source).expect("run_verified should succeed for window"); let all_pass = report.step_invariants.iter() .flat_map(|s| &s.checks) .all(|(_, r)| *r == crate::runtime::InvariantResult::Pass); assert!(all_pass, "all invariants should pass for window"); assert_eq!(report.outputs, vec![10, 20, 30]); assert!(report.summary.contains("passed")); } #[test] fn run_verified_product_all_invariants_pass() { let source = include_str!("../examples/product.l1"); let report = run_verified(source).expect("run_verified should succeed for product"); let all_pass = report.step_invariants.iter() .flat_map(|s| &s.checks) .all(|(_, r)| *r == crate::runtime::InvariantResult::Pass); assert!(all_pass, "all invariants should pass for product"); assert_eq!(report.outputs, vec![21, 42, 63, 84]); } #[test] fn run_verified_report_final_states_match_jit() { let source = include_str!("../examples/counter.l1"); let report = run_verified(source).expect("run_verified should succeed"); let (jit_result, _) = run(source).expect("JIT should succeed"); let jit_count = jit_result.read_u64("counter_state", 0).unwrap(); let verified_count = report.final_states[0].1.iter() .find(|(n, _)| n == "count").unwrap().1; assert_eq!(verified_count, jit_count, "verified and JIT final states must agree"); } #[test] fn run_verified_report_final_states_match_interpreter() { let source = include_str!("../examples/window.l1"); let report = run_verified(source).expect("run_verified should succeed"); let program = crate::parser::parse(source).unwrap(); let interp_trace = crate::interpret_l1::interpret(&program).unwrap(); let verified_total = report.final_states[0].1.iter() .find(|(n, _)| n == "total").unwrap().1; let interp_total = interp_trace.final_actor_states[0].fields.iter() .find(|(n, _)| n == "total").unwrap().1; assert_eq!(verified_total, interp_total, "verified and interpreter final states must agree"); assert_eq!(report.outputs, interp_trace.all_outputs, "verified and interpreter outputs must agree"); } #[test] fn run_verified_report_contains_translation_validation() { let source = include_str!("../examples/counter.l1"); let report = run_verified(source).expect("run_verified should succeed"); assert_eq!(report.verification_report.results.len(), 3, "should have 3 translation validation tool results"); assert!(report.verification_report.all_passed()); } #[test] fn run_verified_report_display_is_readable() { let source = include_str!("../examples/counter.l1"); let report = run_verified(source).expect("run_verified should succeed"); let text = format!("{}", report); assert!(text.contains("Verified Execution Report")); assert!(text.contains("Translation Validation")); assert!(text.contains("Runtime Invariants")); assert!(text.contains("Final States")); assert!(text.contains("Summary")); assert!(text.contains("counter")); assert!(text.contains("count=5")); } #[test] fn run_verified_invalid_input() { let result = run_verified("invalid garbage"); assert!(result.is_err()); } // ── Stage 5: Cross-Validation and Negative Testing ────────── // --- Mutated L0 IR tests: corrupt emitted L0, verify translation validation catches it --- fn emit_l0_for(source: &str) -> (crate::l1_ir::L1Program, crate::l0_ir::L0Program) { let l1 = crate::parser::parse(source).expect("parse"); crate::verify_l1::verify(&l1).expect("verify L1"); let l0 = crate::emit::emit(&l1).expect("emit"); crate::verify_l0::verify(&l0).expect("verify L0"); (l1, l0) } #[test] fn mutated_l0_counter_add_to_sub_caught() { let source = include_str!("../examples/counter.l1"); let (l1, mut l0) = emit_l0_for(source); // Corrupt: change Add to Sub in the step block let step = l0.blocks.iter_mut().find(|b| b.name == "step").unwrap(); let mut mutated = false; for op in step.ops.iter_mut() { if let crate::l0_ir::Op::Arith { kind, .. } = op { if *kind == crate::l0_ir::ArithKind::Add { *kind = crate::l0_ir::ArithKind::Sub; mutated = true; break; } } } assert!(mutated, "should have found an Add to corrupt"); let report = crate::translate_validate::validate(&l1, &l0); let trace_result = report.results.iter() .find(|r| r.tool == crate::translate_validate::ToolName::TraceEquivalence) .unwrap(); assert!(!trace_result.passed, "trace checker should catch Add→Sub corruption"); } #[test] fn mutated_l0_window_add_to_sub_caught() { let source = include_str!("../examples/window.l1"); let (l1, mut l0) = emit_l0_for(source); let step = l0.blocks.iter_mut().find(|b| b.name == "step").unwrap(); for op in step.ops.iter_mut() { if let crate::l0_ir::Op::Arith { kind, .. } = op { if *kind == crate::l0_ir::ArithKind::Add { *kind = crate::l0_ir::ArithKind::Sub; break; } } } let report = crate::translate_validate::validate(&l1, &l0); assert!(!report.all_passed(), "should catch Add→Sub mutation in window example"); } #[test] fn mutated_l0_product_add_to_mul_caught() { let source = include_str!("../examples/product.l1"); let (l1, mut l0) = emit_l0_for(source); // Product uses Add for count and Mul for total — corrupt the Mul to Add let step = l0.blocks.iter_mut().find(|b| b.name == "step").unwrap(); let mut mutated = false; for op in step.ops.iter_mut() { if let crate::l0_ir::Op::Arith { kind, .. } = op { if *kind == crate::l0_ir::ArithKind::Mul { *kind = crate::l0_ir::ArithKind::Add; mutated = true; break; } } } assert!(mutated, "should have found a Mul to corrupt"); let report = crate::translate_validate::validate(&l1, &l0); assert!(!report.all_passed(), "should catch Mul→Add mutation in product example"); } #[test] fn mutated_l0_remove_queue_pop_caught() { let source = include_str!("../examples/counter.l1"); let (l1, mut l0) = emit_l0_for(source); // Remove all queue_pop ops from step block let step = l0.blocks.iter_mut().find(|b| b.name == "step").unwrap(); let before = step.ops.len(); step.ops.retain(|op| !matches!(op, crate::l0_ir::Op::QueuePop { .. })); assert!(step.ops.len() < before, "should have removed queue_pop ops"); let report = crate::translate_validate::validate(&l1, &l0); // Structural checker should catch missing queue_pop let structural = report.results.iter() .find(|r| r.tool == crate::translate_validate::ToolName::StructuralCorrespondence) .unwrap(); assert!(!structural.passed, "structural checker should catch missing queue_pop"); } #[test] fn mutated_l0_wrong_loop_limit_caught() { let source = include_str!("../examples/counter.l1"); let (l1, mut l0) = emit_l0_for(source); // Change the loop limit from 5 to 3 let loop_check = l0.blocks.iter_mut().find(|b| b.name == "loop_check").unwrap(); for op in loop_check.ops.iter_mut() { if let crate::l0_ir::Op::LoadImm { value, .. } = op { if *value == 5 { *value = 3; break; } } } let report = crate::translate_validate::validate(&l1, &l0); // Both trace checker (wrong final state) and BMC (wrong loop limit) should catch this assert!(!report.all_passed(), "should catch wrong loop limit"); } #[test] fn mutated_l0_remove_state_region_caught() { let source = include_str!("../examples/window.l1"); let (l1, mut l0) = emit_l0_for(source); l0.regions.retain(|r| r.name != "accumulator_state"); let report = crate::translate_validate::validate(&l1, &l0); let structural = report.results.iter() .find(|r| r.tool == crate::translate_validate::ToolName::StructuralCorrespondence) .unwrap(); assert!(!structural.passed, "structural checker should catch missing state region"); } // --- Round-trip agreement: three-way cross-validation for all examples --- fn three_way_agreement(source: &str, actor_name: &str, region_name: &str) { // 1. JIT pipeline let (jit_result, _) = run(source).expect("JIT pipeline should succeed"); // 2. Verified runtime let report = run_verified(source).expect("verified pipeline should succeed"); // 3. L1 interpreter let program = crate::parser::parse(source).unwrap(); let interp_trace = crate::interpret_l1::interpret(&program).unwrap(); // Compare final actor states let verified_fields = &report.final_states.iter() .find(|(n, _)| n == actor_name).unwrap().1; let interp_fields = &interp_trace.final_actor_states.iter() .find(|s| s.actor_name == actor_name).unwrap().fields; // Verified == Interpreter assert_eq!(verified_fields, interp_fields, "verified runtime and interpreter must agree on {} final state", actor_name); // Verified == JIT (compare each field) let mut jit_offset = 0usize; for (name, verified_val) in verified_fields { let jit_val = jit_result.read_u64(region_name, jit_offset) .unwrap_or_else(|| panic!("JIT should have field {} at offset {}", name, jit_offset)); assert_eq!(*verified_val, jit_val, "verified runtime and JIT must agree on {}.{}", actor_name, name); jit_offset += 8; } // Compare outputs assert_eq!(report.outputs, interp_trace.all_outputs, "verified and interpreter outputs must agree"); let jit_outputs = jit_result.read_output_values(); assert_eq!(report.outputs, jit_outputs, "verified and JIT outputs must agree"); } #[test] fn round_trip_counter_three_way_agreement() { three_way_agreement( include_str!("../examples/counter.l1"), "counter", "counter_state", ); } #[test] fn round_trip_window_three_way_agreement() { three_way_agreement( include_str!("../examples/window.l1"), "accumulator", "accumulator_state", ); } #[test] fn round_trip_product_three_way_agreement() { three_way_agreement( include_str!("../examples/product.l1"), "ledger", "ledger_state", ); } }