Run coverage analysis.
(self)
| 193 | return False, "", str(e), None |
| 194 | |
| 195 | def _run_coverage_analysis(self) -> Tuple[bool, str, Optional[str], Optional[float]]: |
| 196 | """Run coverage analysis.""" |
| 197 | try: |
| 198 | # Run Rust coverage with tarpaulin |
| 199 | rust_coverage = None |
| 200 | try: |
| 201 | cargo_path = shutil.which("cargo") |
| 202 | if not cargo_path: |
| 203 | raise FileNotFoundError("cargo executable not found in PATH") |
| 204 | result = subprocess.run( |
| 205 | [cargo_path, "tarpaulin", "--workspace", "--out", "Xml", "--output-dir", "target/coverage"], capture_output=True, text=True, cwd=self.root_path, timeout=900, shell=False |
| 206 | ) # nosec |
| 207 | |
| 208 | if result.returncode == 0: |
| 209 | # Try to extract coverage percentage |
| 210 | import re |
| 211 | |
| 212 | coverage_match = re.search(r"(\d+\.?\d*)%", result.stdout) |
| 213 | if coverage_match: |
| 214 | rust_coverage = float(coverage_match.group(1)) |
| 215 | |
| 216 | except (subprocess.TimeoutExpired, FileNotFoundError): |
| 217 | pass # tarpaulin might not be available |
| 218 | |
| 219 | # Run Python coverage |
| 220 | python_coverage = None |
| 221 | try: |
| 222 | test_paths = [] |
| 223 | if (self.root_path / "tests").exists(): |
| 224 | test_paths.append("tests/") |
| 225 | if (self.root_path / "python" / "tests").exists(): |
| 226 | test_paths.append("python/tests/") |
| 227 | |
| 228 | if test_paths: |
| 229 | result = subprocess.run( |
| 230 | ["python", "-m", "pytest"] + test_paths + ["--cov=graphbit", "--cov-report=xml", "--cov-report=term-missing"], |
| 231 | capture_output=True, |
| 232 | text=True, |
| 233 | cwd=self.root_path, |
| 234 | timeout=600, |
| 235 | shell=False, |
| 236 | ) # nosec |
| 237 | |
| 238 | if result.returncode == 0: |
| 239 | # Try to extract coverage percentage |
| 240 | import re |
| 241 | |
| 242 | coverage_match = re.search(r"TOTAL.*?(\d+)%", result.stdout) |
| 243 | if coverage_match: |
| 244 | python_coverage = float(coverage_match.group(1)) |
| 245 | |
| 246 | except (subprocess.TimeoutExpired, FileNotFoundError): |
| 247 | pass # pytest-cov might not be available |
| 248 | |
| 249 | # Determine overall result |
| 250 | if rust_coverage is not None or python_coverage is not None: |
| 251 | overall_coverage = max(filter(None, [rust_coverage, python_coverage])) |
| 252 | coverage_text = f"Rust: {rust_coverage or 'N/A'}%, Python: {python_coverage or 'N/A'}%" |