#![cfg(feature = "dap")]
use serde_json::{json, Value};
fn create_initialize_request() -> Value {
json!({
"seq": 1,
"type": "request",
"command": "initialize",
"arguments": {
"clientID": "vscode",
"adapterID": "pmat-debug",
"linesStartAt1": true,
"columnsStartAt1": true,
"pathFormat": "path",
"supportsVariableType": true,
"supportsVariablePaging": false
}
})
}
fn create_launch_request(program: &str) -> Value {
json!({
"seq": 2,
"type": "request",
"command": "launch",
"arguments": {
"program": program,
"stopOnEntry": true,
"cwd": "${workspaceFolder}"
}
})
}
fn create_invalid_request() -> Value {
json!({
"seq": 999,
"type": "request",
"command": "nonexistent_command",
"arguments": {}
})
}
#[test]
fn test_dap_server_initialization() {
let server = pmat::services::dap::DapServer::new();
let init_request = create_initialize_request();
let response = server.handle_request(init_request);
assert_eq!(response["type"], "response");
assert_eq!(response["command"], "initialize");
assert_eq!(response["success"], true);
assert_eq!(response["request_seq"], 1);
let body = &response["body"];
assert!(body.is_object(), "Response body should be an object");
assert!(body["supportsConfigurationDoneRequest"]
.as_bool()
.unwrap_or(false));
assert!(
server.is_initialized(),
"Server should be initialized after initialize request"
);
}
#[test]
fn test_dap_server_launch_request() {
let server = pmat::services::dap::DapServer::new();
let init_request = create_initialize_request();
server.handle_request(init_request);
let config_done = json!({
"seq": 2,
"type": "request",
"command": "configurationDone"
});
server.handle_request(config_done);
let launch_request = create_launch_request("tests/fixtures/sample.rs");
let response = server.handle_request(launch_request);
assert_eq!(response["type"], "response");
assert_eq!(response["command"], "launch");
assert_eq!(response["success"], true);
assert!(
server.has_program_loaded(),
"Server should have program loaded"
);
assert_eq!(
server.current_program().unwrap(),
"tests/fixtures/sample.rs",
"Server should track current program"
);
}
#[test]
fn test_dap_server_handles_invalid_request() {
let server = pmat::services::dap::DapServer::new();
let invalid_request = create_invalid_request();
let response = server.handle_request(invalid_request);
assert_eq!(response["type"], "response");
assert_eq!(response["command"], "nonexistent_command");
assert_eq!(response["success"], false);
let message = response["message"].as_str().unwrap_or("");
assert!(!message.is_empty(), "Error message should be present");
assert!(
message.contains("unknown") || message.contains("not supported"),
"Error message should indicate unknown command"
);
}
#[test]
fn test_dap_server_tracks_request_sequence() {
let server = pmat::services::dap::DapServer::new();
let req1 = json!({"seq": 1, "type": "request", "command": "initialize", "arguments": {}});
let req2 = json!({"seq": 2, "type": "request", "command": "disconnect", "arguments": {}});
let req3 = json!({"seq": 3, "type": "request", "command": "terminate", "arguments": {}});
let resp1 = server.handle_request(req1);
let resp2 = server.handle_request(req2);
let resp3 = server.handle_request(req3);
assert_eq!(resp1["request_seq"], 1);
assert_eq!(resp2["request_seq"], 2);
assert_eq!(resp3["request_seq"], 3);
assert!(resp1["seq"].as_i64().unwrap() > 0);
assert!(resp2["seq"].as_i64().unwrap() > resp1["seq"].as_i64().unwrap());
assert!(resp3["seq"].as_i64().unwrap() > resp2["seq"].as_i64().unwrap());
}
#[test]
fn test_dap_server_state_machine() {
let server = pmat::services::dap::DapServer::new();
assert!(
!server.is_initialized(),
"Server should start uninitialized"
);
assert!(
!server.is_running(),
"Server should not be running initially"
);
let init_req = create_initialize_request();
server.handle_request(init_req);
assert!(server.is_initialized(), "Server should be initialized");
let config_done = json!({"seq": 2, "type": "request", "command": "configurationDone"});
server.handle_request(config_done);
let launch_req = create_launch_request("tests/fixtures/sample.rs");
server.handle_request(launch_req);
assert!(server.is_running(), "Server should be running after launch");
let disconnect = json!({"seq": 4, "type": "request", "command": "disconnect"});
server.handle_request(disconnect);
assert!(
!server.is_running(),
"Server should not be running after disconnect"
);
}
#[test]
fn test_dap_protocol_compliance() {
let server = pmat::services::dap::DapServer::new();
let init_req = create_initialize_request();
let response = server.handle_request(init_req);
assert!(
response.get("seq").is_some(),
"Response must have 'seq' field"
);
assert!(
response.get("type").is_some(),
"Response must have 'type' field"
);
assert!(
response.get("request_seq").is_some(),
"Response must have 'request_seq' field"
);
assert!(
response.get("success").is_some(),
"Response must have 'success' field"
);
assert!(
response.get("command").is_some(),
"Response must have 'command' field"
);
assert_eq!(response["type"], "response", "Type must be 'response'");
assert!(response["success"].is_boolean(), "Success must be boolean");
}
#[test]
fn test_dap_server_handles_concurrent_requests() {
use std::sync::{Arc, Mutex};
use std::thread;
let server = Arc::new(Mutex::new(pmat::services::dap::DapServer::new()));
let mut handles = vec![];
for i in 0..10 {
let server_clone = Arc::clone(&server);
let handle = thread::spawn(move || {
let req = json!({
"seq": i,
"type": "request",
"command": "initialize",
"arguments": {}
});
let server = server_clone.lock().unwrap();
let response = server.handle_request(req);
assert_eq!(response["success"], true);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
#[test]
fn test_dap_server_memory_safety() {
let server = pmat::services::dap::DapServer::new();
for i in 0..1000 {
let req = json!({
"seq": i,
"type": "request",
"command": "initialize",
"arguments": {}
});
let _ = server.handle_request(req);
}
}
#[test]
fn test_dap_server_error_recovery() {
let server = pmat::services::dap::DapServer::new();
let malformed = json!({
"type": "request",
});
let response = server.handle_request(malformed);
assert_eq!(response["success"], false, "Malformed request should fail");
let valid_req = create_initialize_request();
let valid_response = server.handle_request(valid_req);
assert_eq!(
valid_response["success"], true,
"Server should recover from errors"
);
}
#[test]
fn test_dap_server_performance() {
use std::time::Instant;
let server = pmat::services::dap::DapServer::new();
let req = create_initialize_request();
let start = Instant::now();
let _ = server.handle_request(req);
let duration = start.elapsed();
assert!(
duration.as_millis() < 100,
"Request handling should take less than 100ms, took {:?}",
duration
);
}