coverage-tool 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. coverage_tool/__init__.py +7 -0
  2. coverage_tool/analyzers/__init__.py +14 -0
  3. coverage_tool/analyzers/dependency.py +513 -0
  4. coverage_tool/converters/__init__.py +60 -0
  5. coverage_tool/converters/base.py +83 -0
  6. coverage_tool/converters/cunit_converter.py +47 -0
  7. coverage_tool/converters/factory.py +36 -0
  8. coverage_tool/converters/generic_converter.py +62 -0
  9. coverage_tool/converters/go_converter.py +178 -0
  10. coverage_tool/converters/gtest_converter.py +63 -0
  11. coverage_tool/core/__init__.py +18 -0
  12. coverage_tool/core/config.py +66 -0
  13. coverage_tool/core/reporter.py +270 -0
  14. coverage_tool/example.py +102 -0
  15. coverage_tool/handlers/__init__.py +13 -0
  16. coverage_tool/handlers/compilation.py +322 -0
  17. coverage_tool/handlers/env_checker.py +312 -0
  18. coverage_tool/main.py +559 -0
  19. coverage_tool/parsers/__init__.py +15 -0
  20. coverage_tool/parsers/junit.py +172 -0
  21. coverage_tool/runners/__init__.py +26 -0
  22. coverage_tool/runners/base.py +249 -0
  23. coverage_tool/runners/c_runner.py +66 -0
  24. coverage_tool/runners/cpp_runner.py +65 -0
  25. coverage_tool/runners/factory.py +41 -0
  26. coverage_tool/runners/go_runner.py +158 -0
  27. coverage_tool/runners/java_runner.py +54 -0
  28. coverage_tool/runners/python_runner.py +99 -0
  29. coverage_tool/test_removers/__init__.py +38 -0
  30. coverage_tool/test_removers/base.py +233 -0
  31. coverage_tool/test_removers/c_remover.py +210 -0
  32. coverage_tool/test_removers/cpp_remover.py +272 -0
  33. coverage_tool/test_removers/factory.py +45 -0
  34. coverage_tool/test_removers/go_remover.py +112 -0
  35. coverage_tool/test_removers/java_remover.py +216 -0
  36. coverage_tool/test_removers/python_remover.py +172 -0
  37. coverage_tool/utils/__init__.py +23 -0
  38. coverage_tool/utils/dependency_graph.py +52 -0
  39. coverage_tool/utils/file_backup.py +112 -0
  40. coverage_tool/utils/helpers.py +89 -0
  41. coverage_tool/utils/logger.py +54 -0
  42. coverage_tool/utils/progress.py +41 -0
  43. coverage_tool-1.0.0.dist-info/METADATA +484 -0
  44. coverage_tool-1.0.0.dist-info/RECORD +47 -0
  45. coverage_tool-1.0.0.dist-info/WHEEL +5 -0
  46. coverage_tool-1.0.0.dist-info/entry_points.txt +2 -0
  47. coverage_tool-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 覆盖率统计报告生成器
4
+ 生成格式化的测试覆盖率报告
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import List, Dict, Optional
9
+ from datetime import datetime
10
+ from enum import Enum
11
+ import json
12
+ import csv
13
+ import os
14
+
15
+ class ReportFormat(Enum):
16
+ TEXT = "text"
17
+ JSON = "json"
18
+ CSV = "csv"
19
+ HTML = "html"
20
+
21
+ @dataclass
22
+ class CoverageStats:
23
+ """覆盖率统计"""
24
+ language: str
25
+ total_tests: int = 0
26
+ passed_tests: int = 0
27
+ failed_tests: int = 0
28
+ error_tests: int = 0
29
+ skipped_tests: int = 0
30
+ pass_rate: float = 0.0
31
+ coverage_rate: float = 0.0
32
+ duration: float = 0.0
33
+ test_files: int = 0
34
+ source_files: int = 0
35
+ deleted_files: int = 0
36
+
37
+ @dataclass
38
+ class CoverageReport:
39
+ """完整覆盖率报告"""
40
+ project_name: str
41
+ generated_at: datetime = field(default_factory=datetime.now)
42
+ stats: List[CoverageStats] = field(default_factory=list)
43
+ total_duration: float = 0.0
44
+ overall_pass_rate: float = 0.0
45
+ overall_coverage: float = 0.0
46
+ deleted_files: List[str] = field(default_factory=list)
47
+
48
+ def calculate_overall_stats(self):
49
+ """计算总体统计"""
50
+ if not self.stats:
51
+ return
52
+
53
+ total_tests = sum(s.total_tests for s in self.stats)
54
+ total_passed = sum(s.passed_tests for s in self.stats)
55
+ total_failed = sum(s.failed_tests for s in self.stats)
56
+ total_errors = sum(s.error_tests for s in self.stats)
57
+ total_skipped = sum(s.skipped_tests for s in self.stats)
58
+ total_coverage = sum(s.coverage_rate for s in self.stats)
59
+ self.total_duration = sum(s.duration for s in self.stats)
60
+
61
+ if total_tests > 0:
62
+ self.overall_pass_rate = ((total_passed / total_tests) * 100) if total_tests > 0 else 0.0
63
+ else:
64
+ self.overall_pass_rate = 0.0
65
+
66
+ self.overall_coverage = (total_coverage / len(self.stats)) if self.stats else 0.0
67
+
68
+ for stat in self.stats:
69
+ self.deleted_files.extend([stat.language] * stat.deleted_files)
70
+
71
+ class CoverageReporter:
72
+ """覆盖率报告生成器"""
73
+
74
+ def __init__(self, report: CoverageReport):
75
+ self.report = report
76
+
77
+ def generate(self, output_file: str, format: ReportFormat = ReportFormat.TEXT) -> str:
78
+ """生成报告"""
79
+ if format == ReportFormat.JSON:
80
+ return self._generate_json(output_file)
81
+ elif format == ReportFormat.CSV:
82
+ return self._generate_csv(output_file)
83
+ elif format == ReportFormat.HTML:
84
+ return self._generate_html(output_file)
85
+ else:
86
+ return self._generate_text(output_file)
87
+
88
+ def _generate_text(self, output_file: str) -> str:
89
+ """生成文本报告"""
90
+ lines = []
91
+ lines.append("=" * 80)
92
+ lines.append(f"单测覆盖率报告")
93
+ lines.append(f"项目: {self.report.project_name}")
94
+ lines.append(f"生成时间: {self.report.generated_at.strftime('%Y-%m-%d %H:%M:%S')}")
95
+ lines.append("=" * 80)
96
+ lines.append("")
97
+
98
+ for stat in self.report.stats:
99
+ lines.append(f"语言: {stat.language}")
100
+ lines.append("-" * 40)
101
+ lines.append(f" 测试用例数: {stat.total_tests}")
102
+ lines.append(f" 通过: {stat.passed_tests}")
103
+ lines.append(f" 失败: {stat.failed_tests}")
104
+ lines.append(f" 错误: {stat.error_tests}")
105
+ lines.append(f" 跳过: {stat.skipped_tests}")
106
+ lines.append(f" 通过率: {stat.pass_rate:.2f}%")
107
+ lines.append(f" 覆盖率: {stat.coverage_rate:.2f}%")
108
+ lines.append(f" 执行时间: {stat.duration:.2f}s")
109
+ lines.append(f" 测试文件数: {stat.test_files}")
110
+ lines.append(f" 源文件数: {stat.source_files}")
111
+ lines.append(f" 删除文件数: {stat.deleted_files}")
112
+ lines.append("")
113
+
114
+ lines.append("=" * 80)
115
+ lines.append("总体统计")
116
+ lines.append("=" * 80)
117
+ lines.append(f"总执行时间: {self.report.total_duration:.2f}s")
118
+ lines.append(f"总体通过率: {self.report.overall_pass_rate:.2f}%")
119
+ lines.append(f"总体覆盖率: {self.report.overall_coverage:.2f}%")
120
+ lines.append("")
121
+
122
+ if self.report.deleted_files:
123
+ lines.append(f"已删除文件: {len(self.report.deleted_files)}个")
124
+
125
+ report_content = '\n'.join(lines)
126
+
127
+ if output_file:
128
+ with open(output_file, 'w', encoding='utf-8') as f:
129
+ f.write(report_content)
130
+
131
+ return report_content
132
+
133
+ def _generate_json(self, output_file: str) -> str:
134
+ """生成JSON报告"""
135
+ report_data = {
136
+ "project_name": self.report.project_name,
137
+ "generated_at": self.report.generated_at.isoformat(),
138
+ "total_duration": self.report.total_duration,
139
+ "overall_pass_rate": self.report.overall_pass_rate,
140
+ "overall_coverage": self.report.overall_coverage,
141
+ "stats": [
142
+ {
143
+ "language": stat.language,
144
+ "total_tests": stat.total_tests,
145
+ "passed_tests": stat.passed_tests,
146
+ "failed_tests": stat.failed_tests,
147
+ "error_tests": stat.error_tests,
148
+ "skipped_tests": stat.skipped_tests,
149
+ "pass_rate": stat.pass_rate,
150
+ "coverage_rate": stat.coverage_rate,
151
+ "duration": stat.duration,
152
+ "test_files": stat.test_files,
153
+ "source_files": stat.source_files,
154
+ "deleted_files": stat.deleted_files
155
+ }
156
+ for stat in self.report.stats
157
+ ]
158
+ }
159
+
160
+ report_content = json.dumps(report_data, ensure_ascii=False, indent=2)
161
+
162
+ if output_file:
163
+ with open(output_file, 'w', encoding='utf-8') as f:
164
+ f.write(report_content)
165
+
166
+ return report_content
167
+
168
+ def _generate_csv(self, output_file: str) -> str:
169
+ """生成CSV报告"""
170
+ lines = []
171
+ headers = [
172
+ "语言", "测试用例数", "通过", "失败", "错误", "跳过",
173
+ "通过率", "覆盖率", "执行时间", "测试文件数", "源文件数", "删除文件数"
174
+ ]
175
+ lines.append(','.join(headers))
176
+
177
+ for stat in self.report.stats:
178
+ row = [
179
+ stat.language,
180
+ str(stat.total_tests),
181
+ str(stat.passed_tests),
182
+ str(stat.failed_tests),
183
+ str(stat.error_tests),
184
+ str(stat.skipped_tests),
185
+ f"{stat.pass_rate:.2f}",
186
+ f"{stat.coverage_rate:.2f}",
187
+ f"{stat.duration:.2f}",
188
+ str(stat.test_files),
189
+ str(stat.source_files),
190
+ str(stat.deleted_files)
191
+ ]
192
+ lines.append(','.join(row))
193
+
194
+ report_content = '\n'.join(lines)
195
+
196
+ if output_file:
197
+ with open(output_file, 'w', encoding='utf-8') as f:
198
+ f.write(report_content)
199
+
200
+ return report_content
201
+
202
+ def _generate_html(self, output_file: str) -> str:
203
+ """生成HTML报告"""
204
+ html = f"""<!DOCTYPE html>
205
+ <html>
206
+ <head>
207
+ <title>单测覆盖率报告 - {self.report.project_name}</title>
208
+ <style>
209
+ body {{ font-family: Arial, sans-serif; margin: 20px; }}
210
+ table {{ border-collapse: collapse; width: 100%; margin-bottom: 20px; }}
211
+ th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
212
+ th {{ background-color: #4CAF50; color: white; }}
213
+ tr:nth-child(even) {{ background-color: #f2f2f2; }}
214
+ .summary {{ background-color: #f9f9f9; padding: 15px; border-radius: 5px; }}
215
+ h1 {{ color: #333; }}
216
+ h2 {{ color: #555; }}
217
+ </style>
218
+ </head>
219
+ <body>
220
+ <h1>单测覆盖率报告</h1>
221
+ <p><strong>项目:</strong> {self.report.project_name}</p>
222
+ <p><strong>生成时间:</strong> {self.report.generated_at.strftime('%Y-%m-%d %H:%M:%S')}</p>
223
+
224
+ <h2>各语言统计</h2>
225
+ <table>
226
+ <tr>
227
+ <th>语言</th>
228
+ <th>测试用例</th>
229
+ <th>通过</th>
230
+ <th>失败</th>
231
+ <th>错误</th>
232
+ <th>跳过</th>
233
+ <th>通过率</th>
234
+ <th>覆盖率</th>
235
+ <th>执行时间</th>
236
+ </tr>
237
+ """
238
+
239
+ for stat in self.report.stats:
240
+ html += f""" <tr>
241
+ <td>{stat.language}</td>
242
+ <td>{stat.total_tests}</td>
243
+ <td>{stat.passed_tests}</td>
244
+ <td>{stat.failed_tests}</td>
245
+ <td>{stat.error_tests}</td>
246
+ <td>{stat.skipped_tests}</td>
247
+ <td>{stat.pass_rate:.2f}%</td>
248
+ <td>{stat.coverage_rate:.2f}%</td>
249
+ <td>{stat.duration:.2f}s</td>
250
+ </tr>
251
+ """
252
+
253
+ html += """ </table>
254
+
255
+ <h2>总体统计</h2>
256
+ <div class="summary">
257
+ <p><strong>总执行时间:</strong> """ + f"{self.report.total_duration:.2f}s</p>"
258
+ html += f""" <p><strong>总体通过率:</strong> {self.report.overall_pass_rate:.2f}%</p>
259
+ <p><strong>总体覆盖率:</strong> {self.report.overall_coverage:.2f}%</p>
260
+ </div>
261
+ </body>
262
+ </html>"""
263
+
264
+ report_content = html
265
+
266
+ if output_file:
267
+ with open(output_file, 'w', encoding='utf-8') as f:
268
+ f.write(report_content)
269
+
270
+ return report_content
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 使用示例 - 演示覆盖率工具的基本用法
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ import tempfile
9
+
10
+ from coverage_tool.main import CoverageTool, ToolConfig
11
+ from coverage_tool.core import Language, ReportFormat
12
+
13
+ def create_sample_python_project(project_dir):
14
+ """创建一个示例Python项目"""
15
+ # 创建主模块
16
+ main_file = os.path.join(project_dir, "main.py")
17
+ with open(main_file, 'w') as f:
18
+ f.write("""
19
+ def add(a, b):
20
+ return a + b
21
+
22
+ def subtract(a, b):
23
+ return a - b
24
+
25
+ def multiply(a, b):
26
+ return a * b
27
+ """)
28
+
29
+ # 创建测试文件
30
+ test_file = os.path.join(project_dir, "test_main.py")
31
+ with open(test_file, 'w') as f:
32
+ f.write("""
33
+ import pytest
34
+ from main import add, subtract, multiply
35
+
36
+ def test_add():
37
+ assert add(2, 3) == 5
38
+ assert add(-1, 1) == 0
39
+
40
+ def test_subtract():
41
+ assert subtract(5, 3) == 2
42
+ assert subtract(0, 5) == -5
43
+
44
+ def test_multiply():
45
+ assert multiply(3, 4) == 12
46
+ assert multiply(-2, 3) == -6
47
+
48
+ # 这个测试会失败
49
+ def test_fail():
50
+ assert add(1, 1) == 3 # 错误的断言
51
+ """)
52
+
53
+ print(f"✓ 创建示例Python项目: {project_dir}")
54
+ return project_dir
55
+
56
+ def run_example():
57
+ """运行示例"""
58
+ print("=" * 60)
59
+ print("覆盖率工具使用示例")
60
+ print("=" * 60)
61
+
62
+ # 创建临时项目
63
+ with tempfile.TemporaryDirectory() as project_dir:
64
+ create_sample_python_project(project_dir)
65
+
66
+ print("\n配置工具...")
67
+ config = ToolConfig(
68
+ language=Language.PYTHON,
69
+ target_dir=project_dir,
70
+ output_file="coverage_report",
71
+ report_format=ReportFormat.TEXT,
72
+ max_compile_iterations=3,
73
+ max_cleanup_iterations=3
74
+ )
75
+
76
+ print("运行覆盖率分析...")
77
+ print("-" * 60)
78
+
79
+ try:
80
+ with CoverageTool(config) as tool:
81
+ report = tool.run()
82
+
83
+ print("\n" + "=" * 60)
84
+ print("分析结果")
85
+ print("=" * 60)
86
+
87
+ if report.stats:
88
+ stat = report.stats[0]
89
+ print(f"语言: {stat.language}")
90
+ print(f"测试用例: {stat.total_tests}")
91
+ print(f"通过: {stat.passed_tests}")
92
+ print(f"失败: {stat.failed_tests}")
93
+ print(f"覆盖率: {stat.coverage_rate:.2f}%")
94
+ print(f"执行时间: {stat.duration:.2f}s")
95
+
96
+ except Exception as e:
97
+ print(f"\n错误: {e}")
98
+ import traceback
99
+ traceback.print_exc()
100
+
101
+ if __name__ == "__main__":
102
+ run_example()
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 处理器包
4
+ 包含编译处理和环境检查等功能
5
+ """
6
+
7
+ from .compilation import SmartCompilationHandler
8
+ from .env_checker import EnvironmentChecker
9
+
10
+ __all__ = [
11
+ 'SmartCompilationHandler',
12
+ 'EnvironmentChecker',
13
+ ]