//! Roundtrip gate for master/examples/. //! //! Mirrors crates/ailang-surface/tests/round_trip.rs but scoped to //! the master examples directory. For each .ail.json fixture: load, //! print via ailang_surface::print, reparse, canonicalise both sides, //! assert byte equality. use std::path::{Path, PathBuf}; fn examples_dir() -> PathBuf { let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); manifest.parent().unwrap().join("master").join("examples") } fn list_examples() -> Vec { let mut paths: Vec = std::fs::read_dir(examples_dir()) .expect("master/examples/ should exist") .filter_map(|entry| entry.ok()) .map(|entry| entry.path()) .filter(|path| path.extension().and_then(|s| s.to_str()) == Some("json")) .filter(|path| { path.file_name() .and_then(|s| s.to_str()) .map(|name| name.ends_with(".ail.json")) .unwrap_or(false) }) .collect(); paths.sort(); paths } fn round_trip_one(path: &Path) -> Result<(), String> { let original = ailang_core::load_module(path) .map_err(|e| format!("{}: load_module failed: {e}", path.display()))?; let text = ailang_surface::print(&original); let parsed = ailang_surface::parse(&text) .map_err(|e| format!("{}: surface::parse failed: {e}", path.display()))?; let bytes_original = ailang_core::canonical::to_bytes(&original); let bytes_parsed = ailang_core::canonical::to_bytes(&parsed); if bytes_original != bytes_parsed { return Err(format!( "{}: roundtrip diverged.\n--- printed AIL ---\n{}\n--- original bytes len {} vs parsed bytes len {} ---", path.display(), text, bytes_original.len(), bytes_parsed.len(), )); } Ok(()) } #[test] fn every_example_roundtrips_via_ail() { let examples = list_examples(); assert!( !examples.is_empty(), "master/examples/ should contain at least one .ail.json fixture" ); let mut failures: Vec = Vec::new(); for path in &examples { if let Err(msg) = round_trip_one(path) { failures.push(msg); } } if !failures.is_empty() { panic!( "{} of {} examples failed roundtrip:\n\n{}", failures.len(), examples.len(), failures.join("\n\n") ); } }