#![allow(dead_code)] #![allow(unused_imports)] use firedbg_rust_debugger::{ new_breakpoint, DebuggerInfo, DebuggerParams, Event, FireDbgForRust, InfoMessage, SourceFile, EVENT_STREAM, INFO_STREAM, }; use pretty_assertions::assert_eq; use sea_streamer::{ file::FileId, Error as SeaStreamerErr, Producer, SeaConnectOptions, SeaConsumer, SeaProducer, SeaStreamer, StreamKey, Streamer, Timestamp, }; use std::{ fs::{File, OpenOptions}, io::Write, path::Path, }; #[derive(Debug)] pub enum Expected { FnCall { name: String, args: Vec }, FnRet { name: String, value: String }, } pub fn debugger_params_from_file(testcase: &str) -> DebuggerParams { let path = format!("testcases/{testcase}.rs"); let modified = Path::new(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), path)) .metadata() .unwrap() .modified() .unwrap(); let mut files = vec![Default::default()]; files.push(SourceFile { id: 1, path: path.clone(), crate_name: testcase.into(), modified, }); let mut breakpoints = vec![Default::default()]; for func in firedbg_rust_parser::parse_file(&path).unwrap() { breakpoints.push(new_breakpoint(breakpoints.len() as u32, 1, func)); } rustc(&format!("testcases/{testcase}")); DebuggerParams { binary: format!("testcases/{testcase}.o"), files, breakpoints, arguments: vec![], } } pub fn generate_rust_program(testcase: &str, content: &str) -> DebuggerParams { let path = format!("testcases/generated/{testcase}"); let src = format!("{path}.rs"); let mut file = std::fs::File::create(&src).unwrap(); file.write_all(content.as_bytes()).unwrap(); rustc(&path); debugger_params_from_file(&format!("generated/{testcase}")) } pub async fn setup(testcase: &str) -> Result<(SeaProducer, SeaConsumer), SeaStreamerErr> { create_env_logger(); let file_id = temp_file(testcase).unwrap(); println!("{file_id:?}"); let mut options = SeaConnectOptions::default(); options.set_file_connect_options(|options| { options.set_end_with_eos(true); }); let streamer = SeaStreamer::connect(file_id.to_streamer_uri()?, options).await?; let producer = streamer.create_generic_producer(Default::default()).await?; let consumer = streamer .create_consumer(&[StreamKey::new(EVENT_STREAM)?], Default::default()) .await?; producer.send_to( &StreamKey::new(INFO_STREAM)?, serde_json::to_string(&InfoMessage::Debugger(DebuggerInfo { debugger: FireDbgForRust, version: env!("CARGO_PKG_VERSION").to_owned(), workspace_root: "".to_owned(), package_name: testcase.to_owned(), target: testcase.to_owned(), arguments: vec![], })) .unwrap() .as_str(), )?; Ok((producer, consumer)) } pub fn verify(testcase: &str, events: Vec, expected: Vec) { for (i, expect) in expected.into_iter().enumerate() { match &events[i] { Event::Breakpoint { .. } => unreachable!(), Event::FunctionCall { function_name, arguments, .. } => match expect { Expected::FnCall { name, args } => { if function_name.starts_with('<') { assert_eq!(function_name, &name); } else { assert_eq!(function_name, &format!("{testcase}::{name}")); } assert_eq!(arguments.len(), args.len()); for (j, arg) in args.iter().enumerate() { assert_wildcard(i, arg, &arguments[j].1.to_string()); } } e => panic!("Expected {e:?}"), }, Event::FunctionReturn { function_name, return_value, .. } => match expect { Expected::FnRet { name, value } => { if function_name.starts_with('<') { assert_eq!(function_name, &name); } else { assert_eq!(function_name, &format!("{testcase}::{name}")); } assert_wildcard(i, &value, &return_value.to_string()); } e => panic!("Expected {e:?}"), }, } } } fn temp_file(name: &str) -> Result { let name = format!("{}-{}", name, millis_of(&Timestamp::now_utc())); let path = format!("/tmp/{name}.firedbg.ss"); let _file = OpenOptions::new() .read(true) .write(true) .create_new(true) .open(&path)?; Ok(FileId::new(path)) } fn millis_of(ts: &Timestamp) -> i64 { (ts.unix_timestamp_nanos() / 1_000_000) as i64 } pub fn create_env_logger() { env_logger::Builder::from_default_env() .format_timestamp_nanos() .init(); } fn assert_wildcard(i: usize, template: &str, against: &str) { if !firedbg_rust_debugger::typename::wildcard_match(template, against) { print!("[{i}] "); assert_eq!(against, template); } } pub fn rustc(path: &str) { let src = format!("{path}.rs"); let obj = format!("{path}.o"); let result = rustc_cmd(&src, &obj) .spawn() .unwrap() .wait_with_output() .unwrap(); if result.status.code().unwrap() != 0 { panic!("Failed to compile {src}"); } } pub fn rustc_optimize(path: &str) { let src = format!("{path}.rs"); let obj = format!("{path}.o"); let mut cmd = rustc_cmd(&src, &obj); cmd.arg("-O"); let result = cmd.spawn().unwrap().wait_with_output().unwrap(); if result.status.code().unwrap() != 0 { panic!("Failed to compile {src}"); } } fn rustc_cmd(src: &str, obj: &str) -> std::process::Command { let mut cmd = std::process::Command::new("rustc"); cmd.arg("--cap-lints=allow") .arg("--edition=2021") .arg("-g") .arg(&src) .arg("-o") .arg(&obj); cmd }