387 lines
9.3 KiB
Python
387 lines
9.3 KiB
Python
import sys
|
||
import pytest
|
||
import subprocess
|
||
from unittest.mock import Mock, patch
|
||
|
||
from backend.managers.execution_engine import ExecutionEngine
|
||
|
||
|
||
# =========================================================
|
||
# FIXTURE
|
||
# =========================================================
|
||
|
||
@pytest.fixture()
|
||
def engine():
|
||
return ExecutionEngine()
|
||
|
||
|
||
# =========================================================
|
||
# BASIC TESTS (1–10)
|
||
# =========================================================
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 1. Python-Datei wird korrekt ausgeführt
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_python_file_success(mock_run, engine, tmp_path):
|
||
file = tmp_path / "test.py"
|
||
file.write_text("print('hello')")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="hello\n",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 0
|
||
assert "hello" in result["stdout"]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 2. Python-Datei mit Fehler
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_python_file_error(mock_run, engine, tmp_path):
|
||
file = tmp_path / "broken.py"
|
||
file.write_text("1/0")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="ZeroDivisionError",
|
||
returncode=1
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 1
|
||
assert "ZeroDivisionError" in result["stderr"]
|
||
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 4. Unsupported File Type
|
||
# ---------------------------------------------------------
|
||
|
||
def test_run_unsupported_file(engine, tmp_path):
|
||
file = tmp_path / "test.js"
|
||
file.write_text("console.log('x')")
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 1
|
||
assert "Unsupported file type" in result["stderr"]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 5. Timeout wird behandelt
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_timeout(mock_run, engine, tmp_path):
|
||
file = tmp_path / "slow.py"
|
||
file.write_text("while True: pass")
|
||
|
||
mock_run.side_effect = subprocess.TimeoutExpired(
|
||
cmd=["py"],
|
||
timeout=30
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == -1
|
||
assert "Timed out" in result["stderr"]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 6. Fehlender Interpreter
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_missing_interpreter(mock_run, engine, tmp_path):
|
||
file = tmp_path / "test.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.side_effect = FileNotFoundError("py not found")
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == -1
|
||
assert "py not found" in result["stderr"]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 7. Allgemeine Exception
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_general_exception(mock_run, engine, tmp_path):
|
||
file = tmp_path / "test.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.side_effect = RuntimeError("unexpected")
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == -1
|
||
assert "unexpected" in result["stderr"]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 8. subprocess.run wird mit cwd ausgeführt
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_uses_correct_cwd(mock_run, engine, tmp_path):
|
||
folder = tmp_path / "project"
|
||
folder.mkdir()
|
||
|
||
file = folder / "main.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
engine.run_code(file)
|
||
|
||
_, kwargs = mock_run.call_args
|
||
|
||
assert kwargs["cwd"] == folder.resolve()
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 9. subprocess.run nutzt capture_output
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_capture_output_enabled(mock_run, engine, tmp_path):
|
||
file = tmp_path / "test.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
engine.run_code(file)
|
||
|
||
_, kwargs = mock_run.call_args
|
||
|
||
assert kwargs["capture_output"] is True
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 10. subprocess.run nutzt text=True
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_text_mode_enabled(mock_run, engine, tmp_path):
|
||
file = tmp_path / "test.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
engine.run_code(file)
|
||
|
||
_, kwargs = mock_run.call_args
|
||
|
||
assert kwargs["text"] is True
|
||
|
||
|
||
# =========================================================
|
||
# EDGE CASE TESTS (11–20)
|
||
# =========================================================
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 11. Unicode Output
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_unicode_output(mock_run, engine, tmp_path):
|
||
file = tmp_path / "unicode.py"
|
||
file.write_text("print('🔥 Grüezi 世界')", encoding="utf-8")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="🔥 Grüezi 世界\n",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert "🔥 Grüezi 世界" in result["stdout"]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 12. Leerer stdout/stderr
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_empty_output(mock_run, engine, tmp_path):
|
||
file = tmp_path / "empty.py"
|
||
file.write_text("x = 1")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["stdout"] == ""
|
||
assert result["stderr"] == ""
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 13. Sehr langer stdout
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_large_output(mock_run, engine, tmp_path):
|
||
file = tmp_path / "large.py"
|
||
file.write_text("print('A')")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="A" * 100000,
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert len(result["stdout"]) == 100000
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 14. Dateiname mit Leerzeichen
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_filename_with_spaces(mock_run, engine, tmp_path):
|
||
file = tmp_path / "my script.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="ok",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
engine.run_code(file)
|
||
|
||
args, _ = mock_run.call_args
|
||
|
||
assert "my script.py" in args[0]
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 15. Dateiname mit Unicode
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_unicode_filename(mock_run, engine, tmp_path):
|
||
file = tmp_path / "🔥_test.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="ok",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 0
|
||
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 17. .py nutzt sys.executable als Interpreter
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_python_uses_sys_executable(mock_run, engine, tmp_path):
|
||
file = tmp_path / "main.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
engine.run_code(file)
|
||
|
||
args, _ = mock_run.call_args
|
||
|
||
assert args[0][0] == sys.executable
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 18. Relative Pfade funktionieren
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_relative_paths(mock_run, engine, tmp_path):
|
||
sub = tmp_path / "src"
|
||
sub.mkdir()
|
||
|
||
file = sub / "main.py"
|
||
file.write_text("print(1)")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="ok",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 0
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 19. Großgeschriebenes Suffix blockiert
|
||
# ---------------------------------------------------------
|
||
|
||
def test_uppercase_suffix_not_supported(engine, tmp_path):
|
||
file = tmp_path / "SCRIPT.PY"
|
||
file.write_text("print(1)")
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 1
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# 20. Leere Datei ausführen
|
||
# ---------------------------------------------------------
|
||
|
||
@patch("subprocess.run")
|
||
def test_run_empty_file(mock_run, engine, tmp_path):
|
||
file = tmp_path / "empty.py"
|
||
file.write_text("")
|
||
|
||
mock_run.return_value = Mock(
|
||
stdout="",
|
||
stderr="",
|
||
returncode=0
|
||
)
|
||
|
||
result = engine.run_code(file)
|
||
|
||
assert result["rc"] == 0 |