use std::path::PathBuf; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; static NEXT_FILE: AtomicUsize = AtomicUsize::new(0); struct SourceFile(PathBuf); impl SourceFile { fn new(content: &[u8]) -> Self { let path = std::env::temp_dir().join(format!( "aster-parse-{}-{}.endor", std::process::id(), NEXT_FILE.fetch_add(1, Ordering::Relaxed) )); std::fs::write(&path, content).unwrap(); Self(path) } } impl Drop for SourceFile { fn drop(&mut self) { let _ = std::fs::remove_file(&self.0); } } #[test] fn parse_command_prints_ast_without_executing_source() { let source = SourceFile::new("function 表示(): String { return 未定義() }".as_bytes()); let output = Command::new(env!("CARGO_BIN_EXE_cli")) .arg("parse") .arg(&source.0) .output() .unwrap(); assert!( output.status.success(), "{}", String::from_utf8_lossy(&output.stderr) ); let stdout = String::from_utf8(output.stdout).unwrap(); assert!(stdout.contains("表示")); assert!(stdout.contains("未定義")); assert!(stdout.contains("Call")); } #[test] fn parse_command_reports_source_locations_and_rejects_invalid_utf8() { let source = SourceFile::new(b"function f(): Number {\n return )\n}"); let output = Command::new(env!("CARGO_BIN_EXE_cli")) .arg("parse") .arg(&source.0) .output() .unwrap(); assert!(!output.status.success()); assert!(output.stdout.is_empty()); let stderr = String::from_utf8(output.stderr).unwrap(); assert!(stderr.contains(":2:10:"), "{stderr}"); assert!(stderr.contains("expected")); let source = SourceFile::new(&[0xff]); let output = Command::new(env!("CARGO_BIN_EXE_cli")) .arg("parse") .arg(&source.0) .output() .unwrap(); assert!(!output.status.success()); assert!(output.stdout.is_empty()); }