import json
import os
import subprocess
import sys
from io import StringIO
from pathlib import Path
from typing import Any
import pytest
from rich.console import Console
from rich.style import Style
from rich.text import Text
from rich.panel import Panel
from rich.table import Table
from rich.tree import Tree
RICHRS_BIN = Path(__file__).parent.parent / "target" / "debug" / "richrs_test_runner"
class RichrsTestRunner:
def __init__(self):
self.binary_path = RICHRS_BIN
self._process = None
def ensure_binary(self) -> bool:
if not self.binary_path.exists():
result = subprocess.run(
["cargo", "build", "--bin", "richrs_test_runner"],
cwd=self.binary_path.parent.parent.parent,
capture_output=True,
text=True,
)
return result.returncode == 0
return True
def run_test(self, test_name: str, test_data: dict[str, Any]) -> dict[str, Any]:
if not self.ensure_binary():
return {
"success": False,
"error": "Failed to build richrs_test_runner binary",
}
input_json = json.dumps({"test": test_name, "data": test_data})
try:
result = subprocess.run(
[str(self.binary_path)],
input=input_json + "\n",
capture_output=True,
text=True,
timeout=30,
)
if result.returncode != 0:
return {
"success": False,
"error": f"Binary failed: {result.stderr}",
}
output = result.stdout.strip()
if output:
return json.loads(output)
return {"success": False, "error": "No output from binary"}
except subprocess.TimeoutExpired:
return {"success": False, "error": "Test timed out"}
except json.JSONDecodeError as e:
return {"success": False, "error": f"Invalid JSON response: {e}"}
except Exception as e:
return {"success": False, "error": str(e)}
_test_runner = RichrsTestRunner()
def run_richrs_test(test_name: str, test_data: dict[str, Any]) -> dict[str, Any]:
return _test_runner.run_test(test_name, test_data)
def capture_rich_output(renderable: Any, width: int = 80) -> str:
console = Console(
file=None,
width=width,
force_terminal=True,
color_system="truecolor",
record=True,
)
console.print(renderable, end="")
return console.export_text()
def capture_rich_ansi(renderable: Any, width: int = 80) -> str:
output = StringIO()
console = Console(
file=output,
width=width,
force_terminal=True,
color_system="truecolor",
)
console.print(renderable, end="")
return output.getvalue()
def normalize_ansi(s: str) -> str:
return s.rstrip()
def compare_ansi_output(python_output: str, rust_output: str) -> bool:
if normalize_ansi(python_output) == normalize_ansi(rust_output):
return True
return False
@pytest.fixture
def rich_console():
return Console(
width=80,
force_terminal=True,
color_system="truecolor",
)
@pytest.fixture
def test_data_dir():
return Path(__file__).parent / "test_data"
@pytest.fixture
def richrs_runner():
return _test_runner