use anyhow::Result;
use serde_json::{json, Value};
use std::io::{BufRead, BufReader, Write};
use std::process::{Command, Stdio};
struct ComprehensiveTestSuite {
server_process: std::process::Child,
stdin: std::process::ChildStdin,
stdout: BufReader<std::process::ChildStdout>,
request_id: u64,
}
impl ComprehensiveTestSuite {
fn new() -> Result<Self> {
println!("🧪 FERROSCOPE COMPREHENSIVE TEST SUITE");
println!("🎯 Testing all critical functionality that was previously broken");
println!();
let mut server_process = Command::new("cargo")
.args(["run", "--bin", "ferroscope"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let stdin = server_process.stdin.take().unwrap();
let stdout = BufReader::new(server_process.stdout.take().unwrap());
Ok(ComprehensiveTestSuite {
server_process,
stdin,
stdout,
request_id: 0,
})
}
fn send_request(&mut self, method: &str, params: Value) -> Result<Value> {
self.request_id += 1;
let request = json!({
"jsonrpc": "2.0",
"id": self.request_id,
"method": method,
"params": params
});
writeln!(self.stdin, "{}", serde_json::to_string(&request)?)?;
self.stdin.flush()?;
let mut response_line = String::new();
self.stdout.read_line(&mut response_line)?;
let response: Value = serde_json::from_str(response_line.trim())?;
Ok(response)
}
fn debug_command(&mut self, tool_name: &str, args: Value) -> Result<Value> {
let params = json!({
"name": tool_name,
"arguments": args
});
let response = self.send_request("tools/call", params)?;
if let Some(result) = response.get("result") {
if let Some(content) = result.get("content").and_then(|c| c.as_array()) {
if let Some(text) = content[0].get("text").and_then(|t| t.as_str()) {
let parsed: Value = serde_json::from_str(text)?;
return Ok(parsed);
}
}
}
if let Some(error) = response.get("error") {
anyhow::bail!("Command failed: {}", error);
}
anyhow::bail!("Unexpected response: {:?}", response);
}
fn run_test(&mut self, test_name: &str, test_fn: impl FnOnce(&mut Self) -> Result<()>) -> bool {
print!("🔍 Testing {}: ", test_name);
std::io::stdout().flush().unwrap();
match test_fn(self) {
Ok(()) => {
println!("✅ PASSED");
true
}
Err(e) => {
println!("❌ FAILED - {}", e);
false
}
}
}
fn test_initialization(&mut self) -> Result<()> {
let response = self.send_request(
"initialize",
json!({
"protocolVersion": "2024-11-05",
"capabilities": {}
}),
)?;
let server_info = response
.get("result")
.and_then(|r| r.get("serverInfo"))
.ok_or_else(|| anyhow::anyhow!("No server info"))?;
let name = server_info
.get("name")
.and_then(|n| n.as_str())
.ok_or_else(|| anyhow::anyhow!("No server name"))?;
if name != "ferroscope" {
anyhow::bail!("Expected v2 server, got: {}", name);
}
Ok(())
}
fn test_program_loading(&mut self) -> Result<()> {
let result = self.debug_command(
"debug_run",
json!({
"binary_path": "./examples/simple_counter"
}),
)?;
let success = result
.get("success")
.and_then(|s| s.as_bool())
.ok_or_else(|| anyhow::anyhow!("No success field"))?;
if !success {
anyhow::bail!("Program loading failed");
}
let state = result
.get("state")
.and_then(|s| s.as_str())
.ok_or_else(|| anyhow::anyhow!("No state field"))?;
if state != "loaded" {
anyhow::bail!("Expected 'loaded' state, got: {}", state);
}
let output = result
.get("output")
.and_then(|o| o.as_str())
.ok_or_else(|| anyhow::anyhow!("No output field"))?;
if !output.contains("Current executable set to") {
anyhow::bail!("No LLDB output found");
}
Ok(())
}
fn test_breakpoint_setting(&mut self) -> Result<()> {
let result = self.debug_command(
"debug_break",
json!({
"location": "main"
}),
)?;
let success = result
.get("success")
.and_then(|s| s.as_bool())
.ok_or_else(|| anyhow::anyhow!("No success field"))?;
if !success {
anyhow::bail!("Breakpoint setting failed");
}
let output = result
.get("output")
.and_then(|o| o.as_str())
.ok_or_else(|| anyhow::anyhow!("No output field"))?;
if !output.contains("Breakpoint 1:") {
anyhow::bail!("No breakpoint output found");
}
Ok(())
}
fn test_process_launch(&mut self) -> Result<()> {
let result = self.debug_command("debug_continue", json!({}))?;
let success = result
.get("success")
.and_then(|s| s.as_bool())
.ok_or_else(|| anyhow::anyhow!("No success field"))?;
if !success {
anyhow::bail!("Process launch failed");
}
let output = result
.get("output")
.and_then(|o| o.as_str())
.ok_or_else(|| anyhow::anyhow!("No output field"))?;
if !output.contains("process launch") {
anyhow::bail!("No process launch command found");
}
Ok(())
}
fn test_state_management(&mut self) -> Result<()> {
let result = self.debug_command("debug_state", json!({}))?;
let state = result
.get("state")
.and_then(|s| s.as_str())
.ok_or_else(|| anyhow::anyhow!("No state field"))?;
let binary_path = result
.get("binary_path")
.and_then(|b| b.as_str())
.ok_or_else(|| anyhow::anyhow!("No binary_path field"))?;
if !binary_path.contains("simple-counter") {
anyhow::bail!("Wrong binary path: {}", binary_path);
}
if state != "loaded" && state != "running" && state != "stopped" {
anyhow::bail!("Invalid state: {}", state);
}
Ok(())
}
fn test_error_handling(&mut self) -> Result<()> {
let result = self.debug_command(
"debug_run",
json!({
"binary_path": "./nonexistent_program"
}),
);
if result.is_ok() {
anyhow::bail!("Should have failed with nonexistent program");
}
Ok(())
}
fn test_invalid_breakpoint(&mut self) -> Result<()> {
self.debug_command(
"debug_run",
json!({
"binary_path": "./examples/simple_counter"
}),
)?;
let result = self.debug_command(
"debug_break",
json!({
"location": "nonexistent_function"
}),
)?;
let output = result
.get("output")
.and_then(|o| o.as_str())
.ok_or_else(|| anyhow::anyhow!("No output field"))?;
if !output.contains("no locations") {
anyhow::bail!("Should warn about no locations for invalid function");
}
Ok(())
}
fn run_comprehensive_test_suite(&mut self) -> bool {
println!("🧪 FERROSCOPE COMPREHENSIVE VALIDATION");
println!("{}", "=".repeat(60));
println!("Testing all functionality that was previously broken:");
println!();
let mut passed = 0;
let mut total = 0;
macro_rules! test {
($name:expr, $method:ident) => {
total += 1;
if self.run_test($name, |suite| suite.$method()) {
passed += 1;
}
};
}
test!("Server initialization (v2.0)", test_initialization);
test!("Program loading with binary", test_program_loading);
test!("Breakpoint setting with LLDB", test_breakpoint_setting);
test!("Process launch (not just continue)", test_process_launch);
test!("State management and tracking", test_state_management);
test!("Error handling for invalid inputs", test_error_handling);
test!(
"Invalid breakpoint graceful handling",
test_invalid_breakpoint
);
println!();
println!("🏆 TEST RESULTS:");
println!(" ✅ Passed: {}/{}", passed, total);
println!(" ❌ Failed: {}/{}", total - passed, total);
if passed == total {
println!(" 🎉 ALL TESTS PASSED! Ferroscope functionality verified!");
true
} else {
println!(" ⚠️ Some tests failed. Ferroscope needs more fixes.");
false
}
}
}
impl Drop for ComprehensiveTestSuite {
fn drop(&mut self) {
let _ = self.server_process.kill();
let _ = self.server_process.wait();
}
}
fn main() -> Result<()> {
println!("🔨 Building test programs...");
let build_output = Command::new("cargo")
.args(["build"])
.current_dir("examples/simple_counter")
.output()?;
if !build_output.status.success() {
anyhow::bail!("Failed to build test program");
}
println!("✅ Test programs built");
println!();
let mut test_suite = ComprehensiveTestSuite::new()?;
std::thread::sleep(std::time::Duration::from_millis(1000));
let all_passed = test_suite.run_comprehensive_test_suite();
println!();
if all_passed {
println!("🚀 FERROSCOPE VALIDATION: SUCCESS");
println!("Ferroscope now provides full debugging functionality!");
println!("Ready for Gemini validation.");
} else {
println!("🔧 FERROSCOPE VALIDATION: NEEDS MORE WORK");
println!("Some critical functionality is still broken.");
}
Ok(())
}