test_edge_cases.py•12.5 kB
"""Edge case tests for DAP debugging.
Tests cover unusual scenarios and boundary conditions.
"""
from pathlib import Path
import sys
import pytest
from mcp_debug_tool.schemas import BreakpointRequest, StartSessionRequest
from mcp_debug_tool.sessions import SessionManager
@pytest.fixture
def workspace_root(tmp_path: Path) -> Path:
"""Create a temporary workspace for testing."""
return tmp_path
@pytest.fixture
def manager(workspace_root: Path) -> SessionManager:
"""Create a session manager."""
return SessionManager(workspace_root=workspace_root)
class TestEdgeCases:
"""Edge case scenarios."""
def test_script_with_unicode_characters(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging script with Unicode characters."""
script = workspace_root / "unicode_test.py"
script.write_text(
"""
# 日本語のコメント
message = "こんにちは世界"
emoji = "🐍 Python 🚀"
length = len(message)
print(f"{emoji}: {message}")
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=5)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should hit breakpoint in Unicode script"
assert 'message' in response.locals
assert 'emoji' in response.locals
manager.end_session(session_id)
def test_script_with_very_long_lines(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging script with very long lines."""
long_value = "x" * 1000
script = workspace_root / "long_lines.py"
script.write_text(
f"""
very_long_string = "{long_value}"
length = len(very_long_string)
doubled = very_long_string + very_long_string
result = len(doubled)
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=4)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle very long lines"
assert 'doubled' in response.locals
manager.end_session(session_id)
def test_empty_script(self, manager: SessionManager, workspace_root: Path):
"""Test debugging an empty script."""
script = workspace_root / "empty.py"
script.write_text("")
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=1)
# Should fail gracefully
response = manager.run_to_breakpoint(session_id, request)
assert not response.hit or response.error, "Empty script should not hit breakpoint"
manager.end_session(session_id)
def test_script_with_only_comments(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging script with only comments."""
script = workspace_root / "comments_only.py"
script.write_text(
"""
# This is a comment
# Another comment
# Yet another comment
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=2)
# Should fail gracefully (no executable code)
with pytest.raises((ValueError, AssertionError)):
manager.run_to_breakpoint(session_id, request)
manager.end_session(session_id)
def test_recursive_function_deep_stack(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging with deep recursion."""
script = workspace_root / "recursion.py"
script.write_text(
"""
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
result = factorial(10)
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=7)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should hit breakpoint after recursion"
assert 'result' in response.locals
manager.end_session(session_id)
def test_exception_in_finally_block(
self, manager: SessionManager, workspace_root: Path
):
"""Test breakpoint after exception in finally block."""
script = workspace_root / "finally_exception.py"
script.write_text(
"""
try:
x = 10
finally:
y = 20
z = x + y
result = z
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=8)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle finally block"
assert 'result' in response.locals
manager.end_session(session_id)
def test_generator_function(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging generator functions."""
script = workspace_root / "generator.py"
script.write_text(
"""
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
gen = count_up_to(5)
result = list(gen)
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=9)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should debug generator usage"
assert 'result' in response.locals
manager.end_session(session_id)
def test_async_function_not_awaited(
self, manager: SessionManager, workspace_root: Path
):
"""Test async function definition (not execution)."""
script = workspace_root / "async_def.py"
script.write_text(
"""
async def async_function():
return 42
# Not calling it, just defining
result = "defined"
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=6)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle async definition"
assert 'result' in response.locals
manager.end_session(session_id)
def test_class_with_property_decorator(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging class with @property."""
script = workspace_root / "property_class.py"
script.write_text(
"""
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
rect = Rectangle(5, 10)
area_value = rect.area
print(area_value)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=12)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should debug class with @property"
assert 'area_value' in response.locals
manager.end_session(session_id)
def test_multiple_decorators(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging function with multiple decorators."""
script = workspace_root / "decorators.py"
script.write_text(
"""
def decorator1(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def my_function(x):
return x * 2
result = my_function(5)
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=17)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle multiple decorators"
assert 'result' in response.locals
manager.end_session(session_id)
def test_script_name_with_special_characters(
self, manager: SessionManager, workspace_root: Path
):
"""Test script with special characters in name."""
# Use hyphen and underscore (common in real projects)
script = workspace_root / "test-script_v1.py"
script.write_text(
"""
x = 10
y = 20
result = x + y
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=4)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle special chars in filename"
assert 'result' in response.locals
manager.end_session(session_id)
def test_breakpoint_on_multiline_statement(
self, manager: SessionManager, workspace_root: Path
):
"""Test breakpoint on multiline statement."""
script = workspace_root / "multiline.py"
script.write_text(
"""
data = {
'key1': 'value1',
'key2': 'value2',
'key3': 'value3'
}
result = len(data)
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=7)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle multiline statement"
assert 'data' in response.locals
assert 'result' in response.locals
manager.end_session(session_id)
def test_global_and_local_same_name(
self, manager: SessionManager, workspace_root: Path
):
"""Test variable shadowing (global and local with same name)."""
script = workspace_root / "shadowing.py"
script.write_text(
"""
x = "global"
def my_function():
x = "local"
return x
result = my_function()
final = x
print(result, final)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=9)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle variable shadowing"
assert 'final' in response.locals
manager.end_session(session_id)
def test_list_comprehension_with_condition(
self, manager: SessionManager, workspace_root: Path
):
"""Test debugging list comprehension."""
script = workspace_root / "comprehension.py"
script.write_text(
"""
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = [x for x in numbers if x % 2 == 0]
result = sum(evens)
print(result)
"""
)
response = manager.create_session(StartSessionRequest(entry=str(script, pythonPath=sys.executable)))
session_id = response.sessionId
request = BreakpointRequest(file=str(script), line=4)
response = manager.run_to_breakpoint(session_id, request)
assert response.hit, "Should handle list comprehension"
assert 'evens' in response.locals
assert 'result' in response.locals
manager.end_session(session_id)