create_coverage_report
Generate HTML and XML coverage reports for log analysis, with options to rebuild reports as needed, for detailed insights into test run data.
Instructions
Run the coverage report script and generate HTML and XML reports.
Args:
force_rebuild: Whether to force rebuilding the report even if it exists
Returns:
Dictionary containing execution results and report paths
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| force_rebuild | No |
Implementation Reference
- The main handler function for the 'create_coverage_report' tool, decorated with @mcp.tool() for registration. It delegates the core logic to the run_coverage_script helper.@mcp.tool() async def create_coverage_report(force_rebuild: bool = False) -> dict[str, Any]: """ Run the coverage report script and generate HTML and XML reports. Args: force_rebuild: Whether to force rebuilding the report even if it exists Returns: Dictionary containing execution results and report paths """ return await run_coverage_script(force_rebuild)
- Pydantic BaseModel defining the input schema for the create_coverage_report tool, including the force_rebuild parameter.class CreateCoverageReportInput(BaseModel): """Parameters for creating coverage report.""" force_rebuild: bool = Field( default=False, description="Whether to force rebuilding the coverage report even if it already exists" )
- Helper function containing the core implementation: executes subprocess calls to hatch scripts for running tests with coverage and generating HTML/XML reports, handles timeouts and errors.async def run_coverage_script(force_rebuild: bool = False) -> dict[str, Any]: """ Run the coverage report script and generate HTML and XML reports. Now uses hatch scripts for better integration. """ logger.info("Running coverage script...") # Correctly reference PROJECT_ROOT from the logger_setup module from log_analyzer_mcp.common import logger_setup as common_logger_setup # Ensure this import is here or global current_project_root = common_logger_setup.PROJECT_ROOT # Define different timeouts for different steps timeout_run_cov = 300 # Longer timeout for running tests with coverage timeout_cov_report = 120 # Shorter timeout for generating the report # Command parts for running the coverage script via hatch # This assumes 'run-cov' and 'cov-report' are defined in hatch envs. # Step 1: Run tests with coverage enabled cmd_parts_run_cov = ["hatch", "run", "hatch-test.py3.12:run-cov"] # Example: Target specific py version # Step 2: Generate combined report (HTML and XML) cmd_parts_report = ["hatch", "run", "hatch-test.py3.12:cov-report"] # Example outputs = [] errors_encountered = [] steps_with_timeouts = [ ("run-cov", cmd_parts_run_cov, timeout_run_cov), ("cov-report", cmd_parts_report, timeout_cov_report), ] for step_name, cmd_parts, current_timeout_seconds in steps_with_timeouts: logger.info( "Executing coverage step '%s': %s (timeout: %ss)", step_name, " ".join(cmd_parts), current_timeout_seconds ) try: # Use functools.partial for subprocess.run configured_subprocess_run_step = functools.partial( subprocess.run, cmd_parts, cwd=current_project_root, capture_output=True, text=True, # Decode stdout/stderr as text check=False, # Handle non-zero exit manually timeout=current_timeout_seconds, # Use current step's timeout ) process = await anyio.to_thread.run_sync(configured_subprocess_run_step) # type: ignore[attr-defined] stdout_output: str = process.stdout stderr_output: str = process.stderr rc = process.returncode outputs.append(f"--- {step_name} STDOUT ---\n{stdout_output}") if stderr_output: outputs.append(f"--- {step_name} STDERR ---\n{stderr_output}") if rc != 0: error_msg = f"Coverage step '{step_name}' failed with return code {rc}." logger.error("%s\nSTDERR:\n%s", error_msg, stderr_output) errors_encountered.append(error_msg) # Optionally break if a step fails, or collect all errors # break except subprocess.TimeoutExpired as e: stdout_output = e.stdout.decode("utf-8", errors="replace") if e.stdout else "" stderr_output = e.stderr.decode("utf-8", errors="replace") if e.stderr else "" error_msg = f"Coverage step '{step_name}' timed out after {current_timeout_seconds} seconds." logger.error("%s: %s", error_msg, e) errors_encountered.append(error_msg) outputs.append(f"--- {step_name} TIMEOUT STDOUT ---\n{stdout_output}") outputs.append(f"--- {step_name} TIMEOUT STDERR ---\n{stderr_output}") # break except Exception as e: # pylint: disable=broad-exception-caught error_msg = f"Error during coverage step '{step_name}': {e}" logger.error(error_msg, exc_info=True) errors_encountered.append(error_msg) # break # Ensure a dictionary is always returned, even if errors occurred. final_success = not errors_encountered overall_message = ( "Coverage script steps completed." if final_success else "Errors encountered during coverage script execution." ) # Placeholder for actual report paths, adapt as needed coverage_xml_report_path = os.path.join(logs_base_dir, "tests", "coverage", "coverage.xml") coverage_html_index_path = os.path.join(logs_base_dir, "tests", "coverage", "html", "index.html") return { "success": final_success, "message": overall_message, "details": "\n".join(outputs), "errors": errors_encountered, "coverage_xml_path": coverage_xml_report_path if final_success else None, # Example path "coverage_html_index": coverage_html_index_path if final_success else None, # Example path "timestamp": datetime.now().isoformat(), }
- src/log_analyzer_mcp/log_analyzer_mcp_server.py:519-519 (registration)The @mcp.tool() decorator registers the create_coverage_report function as an MCP tool.@mcp.tool()