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"] # --------------------------------------------------------- # 3. LaTeX-Datei wird kompiliert # --------------------------------------------------------- @patch("subprocess.run") def test_run_tex_file_success(mock_run, engine, tmp_path): file = tmp_path / "doc.tex" file.write_text("\\documentclass{article}") mock_run.return_value = Mock( stdout="PDF created", stderr="", returncode=0 ) result = engine.run_code(file) assert result["rc"] == 0 assert "PDF created" in result["stdout"] # --------------------------------------------------------- # 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 # --------------------------------------------------------- # 16. .tex nutzt pdflatex # --------------------------------------------------------- @patch("subprocess.run") def test_tex_uses_pdflatex(mock_run, engine, tmp_path): file = tmp_path / "doc.tex" file.write_text("x") mock_run.return_value = Mock( stdout="", stderr="", returncode=0 ) engine.run_code(file) args, _ = mock_run.call_args assert args[0][0] == "pdflatex" # --------------------------------------------------------- # 17. .py nutzt py Interpreter # --------------------------------------------------------- @patch("subprocess.run") def test_python_uses_py_interpreter(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] == "py" # --------------------------------------------------------- # 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