#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod coverage_tests_io {
use super::*;
use std::io::Cursor;
#[test]
fn test_convert_to_recording_snapshot() {
let mut variables = HashMap::new();
variables.insert("x".to_string(), serde_json::json!(42));
let exec_snapshot = ExecutionSnapshot {
timestamp: 1_000_000_000, sequence: 5,
variables: variables.clone(),
call_stack: vec![StackFrame {
id: 1,
name: "main".to_string(),
source: Some(super::super::types::Source {
name: Some("test.rs".to_string()),
path: Some("/path/to/test.rs".to_string()),
}),
line: 42,
column: 0,
}],
location: SourceLocation {
file: "test.rs".to_string(),
line: 42,
column: Some(0),
},
delta: None,
};
let recording_snapshot =
ExecutionRecorder::<std::io::Sink>::convert_to_recording_snapshot(&exec_snapshot);
assert_eq!(recording_snapshot.frame_id, 5); assert_eq!(recording_snapshot.timestamp_relative_ms, 1000); assert_eq!(recording_snapshot.variables, variables);
assert_eq!(recording_snapshot.stack_frames.len(), 1);
assert_eq!(recording_snapshot.stack_frames[0].name, "main");
assert_eq!(
recording_snapshot.stack_frames[0].file,
Some("/path/to/test.rs".to_string())
);
assert_eq!(recording_snapshot.stack_frames[0].line, Some(42));
}
#[test]
fn test_convert_snapshot_negative_line() {
let exec_snapshot = ExecutionSnapshot {
timestamp: 0,
sequence: 0,
variables: HashMap::new(),
call_stack: vec![StackFrame {
id: 1,
name: "test".to_string(),
source: None,
line: -1, column: 0,
}],
location: SourceLocation {
file: "test.rs".to_string(),
line: 1,
column: None,
},
delta: None,
};
let recording_snapshot =
ExecutionRecorder::<std::io::Sink>::convert_to_recording_snapshot(&exec_snapshot);
assert_eq!(recording_snapshot.stack_frames[0].line, None);
}
#[test]
fn test_convert_snapshot_empty_call_stack() {
let exec_snapshot = ExecutionSnapshot {
timestamp: 0,
sequence: 0,
variables: HashMap::new(),
call_stack: vec![],
location: SourceLocation {
file: "test.rs".to_string(),
line: 1,
column: None,
},
delta: None,
};
let recording_snapshot =
ExecutionRecorder::<std::io::Sink>::convert_to_recording_snapshot(&exec_snapshot);
assert!(recording_snapshot.stack_frames.is_empty());
}
#[test]
fn test_convert_snapshot_no_source() {
let exec_snapshot = ExecutionSnapshot {
timestamp: 0,
sequence: 0,
variables: HashMap::new(),
call_stack: vec![StackFrame {
id: 1,
name: "anonymous".to_string(),
source: None,
line: 10,
column: 0,
}],
location: SourceLocation {
file: "test.rs".to_string(),
line: 1,
column: None,
},
delta: None,
};
let recording_snapshot =
ExecutionRecorder::<std::io::Sink>::convert_to_recording_snapshot(&exec_snapshot);
assert_eq!(recording_snapshot.stack_frames[0].file, None);
}
#[test]
fn test_save_to_file_empty() {
let dap = Arc::new(Mutex::new(DapServer::new()));
let recorder = ExecutionRecorder::new(dap);
let temp_dir = std::env::temp_dir();
let temp_file = temp_dir.join("test_recorder_empty.json");
let result = recorder.save_to_file(temp_file.to_str().unwrap());
assert!(result.is_ok());
let _ = std::fs::remove_file(&temp_file);
}
#[test]
fn test_save_to_file_invalid_path() {
let dap = Arc::new(Mutex::new(DapServer::new()));
let recorder = ExecutionRecorder::new(dap);
let result = recorder.save_to_file("/nonexistent/directory/file.json");
assert!(result.is_err());
assert!(result.unwrap_err().contains("Failed to write file"));
}
#[test]
fn test_load_from_file_nonexistent() {
let result = ExecutionRecorder::load_from_file("/nonexistent/file.json");
assert!(result.is_err());
assert!(result.err().unwrap().contains("Failed to read file"));
}
#[test]
fn test_load_from_file_invalid_json() {
let temp_dir = std::env::temp_dir();
let temp_file = temp_dir.join("test_recorder_invalid.json");
std::fs::write(&temp_file, "not valid json").unwrap();
let result = ExecutionRecorder::load_from_file(temp_file.to_str().unwrap());
assert!(result.is_err());
assert!(result.err().unwrap().contains("Failed to deserialize"));
let _ = std::fs::remove_file(&temp_file);
}
#[test]
fn test_save_and_load_roundtrip() {
let dap = Arc::new(Mutex::new(DapServer::new()));
let recorder = ExecutionRecorder::new(dap);
let temp_dir = std::env::temp_dir();
let temp_file = temp_dir.join("test_recorder_roundtrip.json");
recorder.save_to_file(temp_file.to_str().unwrap()).unwrap();
let loaded = ExecutionRecorder::load_from_file(temp_file.to_str().unwrap()).unwrap();
assert_eq!(loaded.snapshot_count(), 0);
assert!(!loaded.is_recording());
let _ = std::fs::remove_file(&temp_file);
}
#[test]
fn test_recorder_lifecycle_without_writer() {
let dap = Arc::new(Mutex::new(DapServer::new()));
let mut recorder = ExecutionRecorder::new(dap);
recorder.start_recording();
assert!(recorder.is_recording());
let _ = recorder.capture_snapshot();
recorder.stop_recording();
assert!(!recorder.is_recording());
let result = recorder.finalize();
assert!(result.is_ok());
}
#[test]
fn test_recorder_lifecycle_with_writer() {
let buffer = Cursor::new(Vec::new());
let dap = Arc::new(Mutex::new(DapServer::new()));
let mut recorder = ExecutionRecorder::with_writer(
buffer,
"test_program".to_string(),
vec!["--verbose".to_string()],
dap,
)
.unwrap();
recorder.add_environment("DEBUG", "1");
recorder.start_recording();
assert!(recorder.is_recording());
recorder.stop_recording();
assert!(!recorder.is_recording());
let result = recorder.finalize();
assert!(result.is_ok());
}
#[test]
fn test_multiple_environment_variables() {
let buffer = Cursor::new(Vec::new());
let dap = Arc::new(Mutex::new(DapServer::new()));
let mut recorder =
ExecutionRecorder::with_writer(buffer, "program".to_string(), vec![], dap).unwrap();
recorder.add_environment("PATH", "/usr/bin:/usr/local/bin");
recorder.add_environment("HOME", "/home/user");
recorder.add_environment("SHELL", "/bin/bash");
recorder.add_environment("LANG", "en_US.UTF-8");
recorder.add_environment("TERM", "xterm-256color");
let result = recorder.finalize();
assert!(result.is_ok());
}
}