blob: 081adcf52b53b79471605726fa5f7685d96560fe [file]
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import asyncio
import io
import json
import unittest
from pydap.client import DapClient, DapError
from pydap.dap_types import Source, SourceBreakpoint
from pydap.models import *
class MockWriter:
def __init__(self) -> None:
self.buffer = io.BytesIO()
def write(self, data: bytes) -> None:
self.buffer.write(data)
async def drain(self) -> None:
pass
class TestDapClient(unittest.IsolatedAsyncioTestCase):
async def test_read_message(self) -> None:
data = b'Content-Length: 26\r\n\r\n{"seq":1,"type":"request"}'
reader = asyncio.StreamReader()
reader.feed_data(data)
reader.feed_eof()
client = DapClient()
msg = await client._read_message(reader)
self.assertIsNotNone(msg)
assert msg is not None
self.assertEqual(msg["seq"], 1)
self.assertEqual(msg["type"], "request")
async def test_write_message(self) -> None:
client = DapClient()
value = {"seq": 1, "type": "request"}
writer = MockWriter()
await client._write_message(writer, value) # type: ignore
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
self.assertEqual(headers, b"Content-Length: 26")
self.assertEqual(json.loads(body.decode("utf-8")), value)
async def test__send_request(self) -> None:
client = DapClient()
writer = MockWriter()
send_task = asyncio.create_task(
client._send_request(writer, "initialize") # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "initialize",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp["success"])
self.assertEqual(resp["request_seq"], seq)
async def test_initialize(self) -> None:
client = DapClient()
writer = MockWriter()
args = InitializeArguments(adapter_id="test")
send_task = asyncio.create_task(
client.initialize(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "initialize",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
async def test_disconnect(self) -> None:
client = DapClient()
writer = MockWriter()
args = DisconnectArguments(terminate_debuggee=True)
send_task = asyncio.create_task(
client.disconnect(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "disconnect",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
async def test_stack_trace(self) -> None:
client = DapClient()
writer = MockWriter()
args = StackTraceArguments(thread_id=1)
send_task = asyncio.create_task(
client.stack_trace(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "stackTrace",
"body": {"stackFrames": []},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertEqual(resp.body.stack_frames, [])
async def test_continue_thread(self) -> None:
client = DapClient()
writer = MockWriter()
args = ContinueArguments(thread_id=1)
send_task = asyncio.create_task(
client.continue_thread(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "continue",
"body": {"allThreadsContinued": True},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertIsInstance(resp, ContinueResponse)
self.assertTrue(resp.success)
self.assertIsNotNone(resp.body)
self.assertTrue(resp.body.all_threads_continued)
def test_continue_response_empty_body(self) -> None:
response = {
"seq": 10,
"type": "response",
"request_seq": 1,
"success": True,
"command": "continue",
"body": {},
}
resp = ContinueResponse.model_validate(response)
self.assertIsInstance(resp, ContinueResponse)
self.assertIsNotNone(resp.body)
self.assertTrue(resp.body.all_threads_continued)
def test_continue_response_missing_body(self) -> None:
response = {
"seq": 10,
"type": "response",
"request_seq": 1,
"success": True,
"command": "continue",
}
resp = ContinueResponse.model_validate(response)
self.assertIsInstance(resp, ContinueResponse)
self.assertIsNotNone(resp.body)
self.assertTrue(resp.body.all_threads_continued)
async def test_pause_thread(self) -> None:
client = DapClient()
writer = MockWriter()
args = PauseArguments(thread_id=1)
send_task = asyncio.create_task(
client.pause_thread(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "pause",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
async def test_threads(self) -> None:
client = DapClient()
writer = MockWriter()
send_task = asyncio.create_task(client.threads(writer)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "threads",
"body": {
"threads": [
{"id": 1234, "name": "main"},
{"id": 5678, "name": "worker"},
]
},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertEqual(len(resp.body.threads), 2)
self.assertEqual(resp.body.threads[0].id, 1234)
self.assertEqual(resp.body.threads[0].name, "main")
async def test_attach(self) -> None:
client = DapClient()
writer = MockWriter()
args = AttachRequestArguments(
restart=True, extra_fields={"process": "my_process"}
)
send_task = asyncio.create_task(client.attach(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "attach",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertTrue(req_val["arguments"]["__restart"])
self.assertEqual(req_val["arguments"]["process"], "my_process")
async def test_launch(self) -> None:
client = DapClient()
writer = MockWriter()
args = LaunchArguments(process="my_process", launch_command="run")
send_task = asyncio.create_task(client.launch(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "launch",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(req_val["arguments"]["process"], "my_process")
self.assertEqual(req_val["arguments"]["launchCommand"], "run")
async def test_evaluate(self) -> None:
client = DapClient()
writer = MockWriter()
args = EvaluateArguments(
expression="1 + 1", context="repl", frame_id=42
)
send_task = asyncio.create_task(client.evaluate(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "evaluate",
"body": {
"result": "2",
"type": "int",
"variablesReference": 0,
},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(resp.body.result, "2")
self.assertEqual(resp.body.type, "int")
self.assertEqual(resp.body.variables_reference, 0)
self.assertEqual(req_val["arguments"]["expression"], "1 + 1")
self.assertEqual(req_val["arguments"]["context"], "repl")
self.assertEqual(req_val["arguments"]["frameId"], 42)
async def test_scopes(self) -> None:
client = DapClient()
writer = MockWriter()
args = ScopesArguments(frame_id=42)
send_task = asyncio.create_task(client.scopes(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "scopes",
"body": {
"scopes": [
{
"name": "Locals",
"variablesReference": 100,
"expensive": False,
}
]
},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(len(resp.body.scopes), 1)
self.assertEqual(resp.body.scopes[0].name, "Locals")
self.assertEqual(resp.body.scopes[0].variables_reference, 100)
self.assertFalse(resp.body.scopes[0].expensive)
self.assertEqual(req_val["arguments"]["frameId"], 42)
async def test_variables(self) -> None:
client = DapClient()
writer = MockWriter()
args = VariablesArguments(variables_reference=100)
send_task = asyncio.create_task(client.variables(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "variables",
"body": {
"variables": [
{
"name": "foo",
"value": "bar",
"variablesReference": 0,
"type": "str",
}
]
},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(len(resp.body.variables), 1)
self.assertEqual(resp.body.variables[0].name, "foo")
self.assertEqual(resp.body.variables[0].value, "bar")
self.assertEqual(resp.body.variables[0].variables_reference, 0)
self.assertEqual(resp.body.variables[0].type, "str")
self.assertEqual(req_val["arguments"]["variablesReference"], 100)
self.assertNotIn("start", req_val["arguments"])
self.assertNotIn("count", req_val["arguments"])
async def test_variables_with_start_only(self) -> None:
client = DapClient()
writer = MockWriter()
args = VariablesArguments(variables_reference=100, start=5)
send_task = asyncio.create_task(client.variables(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "variables",
"body": {"variables": []},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(req_val["arguments"]["variablesReference"], 100)
self.assertEqual(req_val["arguments"]["start"], 5)
self.assertNotIn("count", req_val["arguments"])
async def test_variables_with_count_only(self) -> None:
client = DapClient()
writer = MockWriter()
args = VariablesArguments(variables_reference=100, count=10)
send_task = asyncio.create_task(client.variables(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "variables",
"body": {"variables": []},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(req_val["arguments"]["variablesReference"], 100)
self.assertNotIn("start", req_val["arguments"])
self.assertEqual(req_val["arguments"]["count"], 10)
async def test_variables_with_start_and_count(self) -> None:
client = DapClient()
writer = MockWriter()
args = VariablesArguments(variables_reference=100, start=5, count=10)
send_task = asyncio.create_task(client.variables(writer, args)) # type: ignore
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "variables",
"body": {"variables": []},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(req_val["arguments"]["variablesReference"], 100)
self.assertEqual(req_val["arguments"]["start"], 5)
self.assertEqual(req_val["arguments"]["count"], 10)
async def test_set_breakpoints(self) -> None:
client = DapClient()
writer = MockWriter()
args = SetBreakpointsArguments(
source=Source(path="/path/to/file.rs"),
breakpoints=[SourceBreakpoint(line=12)],
)
send_task = asyncio.create_task(
client.set_breakpoints(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": True,
"command": "setBreakpoints",
"body": {
"breakpoints": [
{
"id": 1,
"verified": True,
"source": {"path": "/path/to/file.rs"},
"line": 12,
}
]
},
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
resp = await send_task
self.assertTrue(resp.success)
self.assertEqual(len(resp.body.breakpoints), 1)
self.assertEqual(resp.body.breakpoints[0].id, 1)
self.assertTrue(resp.body.breakpoints[0].verified)
self.assertEqual(resp.body.breakpoints[0].line, 12)
self.assertEqual(
req_val["arguments"]["source"]["path"], "/path/to/file.rs"
)
async def test_error_response(self) -> None:
client = DapClient()
writer = MockWriter()
args = SetBreakpointsArguments(source=Source(path="relative.rs"))
send_task = asyncio.create_task(
client.set_breakpoints(writer, args) # type: ignore
)
await asyncio.sleep(0.1)
buffer_val = writer.buffer.getvalue()
headers, body = buffer_val.split(b"\r\n\r\n", 1)
req_val = json.loads(body.decode("utf-8"))
seq = req_val["seq"]
response = {
"seq": 10,
"type": "response",
"request_seq": seq,
"success": False,
"command": "setBreakpoints",
"message": "SetBreakpointsRequest path must be absolute!",
}
if seq in client._pending_requests:
client._pending_requests[seq].set_result(response)
with self.assertRaises(DapError) as cm:
await send_task
self.assertIn(
"SetBreakpointsRequest path must be absolute!", str(cm.exception)
)