empathy-framework 4.6.6__py3-none-any.whl → 4.7.1__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.
- empathy_framework-4.7.1.dist-info/METADATA +690 -0
- empathy_framework-4.7.1.dist-info/RECORD +379 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/top_level.txt +1 -2
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
- empathy_llm_toolkit/agent_factory/__init__.py +6 -6
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
- empathy_llm_toolkit/agents_md/__init__.py +22 -0
- empathy_llm_toolkit/agents_md/loader.py +218 -0
- empathy_llm_toolkit/agents_md/parser.py +271 -0
- empathy_llm_toolkit/agents_md/registry.py +307 -0
- empathy_llm_toolkit/commands/__init__.py +51 -0
- empathy_llm_toolkit/commands/context.py +375 -0
- empathy_llm_toolkit/commands/loader.py +301 -0
- empathy_llm_toolkit/commands/models.py +231 -0
- empathy_llm_toolkit/commands/parser.py +371 -0
- empathy_llm_toolkit/commands/registry.py +429 -0
- empathy_llm_toolkit/config/__init__.py +8 -8
- empathy_llm_toolkit/config/unified.py +3 -7
- empathy_llm_toolkit/context/__init__.py +22 -0
- empathy_llm_toolkit/context/compaction.py +455 -0
- empathy_llm_toolkit/context/manager.py +434 -0
- empathy_llm_toolkit/hooks/__init__.py +24 -0
- empathy_llm_toolkit/hooks/config.py +306 -0
- empathy_llm_toolkit/hooks/executor.py +289 -0
- empathy_llm_toolkit/hooks/registry.py +302 -0
- empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
- empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
- empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
- empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
- empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
- empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
- empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
- empathy_llm_toolkit/learning/__init__.py +30 -0
- empathy_llm_toolkit/learning/evaluator.py +438 -0
- empathy_llm_toolkit/learning/extractor.py +514 -0
- empathy_llm_toolkit/learning/storage.py +560 -0
- empathy_llm_toolkit/providers.py +4 -11
- empathy_llm_toolkit/security/__init__.py +17 -17
- empathy_llm_toolkit/utils/tokens.py +2 -5
- empathy_os/__init__.py +202 -70
- empathy_os/cache_monitor.py +5 -3
- empathy_os/cli/__init__.py +11 -55
- empathy_os/cli/__main__.py +29 -15
- empathy_os/cli/commands/inspection.py +21 -12
- empathy_os/cli/commands/memory.py +4 -12
- empathy_os/cli/commands/profiling.py +198 -0
- empathy_os/cli/commands/utilities.py +27 -7
- empathy_os/cli.py +28 -57
- empathy_os/cli_unified.py +525 -1164
- empathy_os/cost_tracker.py +9 -3
- empathy_os/dashboard/server.py +200 -2
- empathy_os/hot_reload/__init__.py +7 -7
- empathy_os/hot_reload/config.py +6 -7
- empathy_os/hot_reload/integration.py +35 -35
- empathy_os/hot_reload/reloader.py +57 -57
- empathy_os/hot_reload/watcher.py +28 -28
- empathy_os/hot_reload/websocket.py +2 -2
- empathy_os/memory/__init__.py +11 -4
- empathy_os/memory/claude_memory.py +1 -1
- empathy_os/memory/cross_session.py +8 -12
- empathy_os/memory/edges.py +6 -6
- empathy_os/memory/file_session.py +770 -0
- empathy_os/memory/graph.py +30 -30
- empathy_os/memory/nodes.py +6 -6
- empathy_os/memory/short_term.py +15 -9
- empathy_os/memory/unified.py +606 -140
- empathy_os/meta_workflows/agent_creator.py +3 -9
- empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
- empathy_os/meta_workflows/form_engine.py +6 -18
- empathy_os/meta_workflows/intent_detector.py +64 -24
- empathy_os/meta_workflows/models.py +3 -1
- empathy_os/meta_workflows/pattern_learner.py +13 -31
- empathy_os/meta_workflows/plan_generator.py +55 -47
- empathy_os/meta_workflows/session_context.py +2 -3
- empathy_os/meta_workflows/workflow.py +20 -51
- empathy_os/models/cli.py +2 -2
- empathy_os/models/tasks.py +1 -2
- empathy_os/models/telemetry.py +4 -1
- empathy_os/models/token_estimator.py +3 -1
- empathy_os/monitoring/alerts.py +938 -9
- empathy_os/monitoring/alerts_cli.py +346 -183
- empathy_os/orchestration/execution_strategies.py +12 -29
- empathy_os/orchestration/pattern_learner.py +20 -26
- empathy_os/orchestration/real_tools.py +6 -15
- empathy_os/platform_utils.py +2 -1
- empathy_os/plugins/__init__.py +2 -2
- empathy_os/plugins/base.py +64 -64
- empathy_os/plugins/registry.py +32 -32
- empathy_os/project_index/index.py +49 -15
- empathy_os/project_index/models.py +1 -2
- empathy_os/project_index/reports.py +1 -1
- empathy_os/project_index/scanner.py +1 -0
- empathy_os/redis_memory.py +10 -7
- empathy_os/resilience/__init__.py +1 -1
- empathy_os/resilience/health.py +10 -10
- empathy_os/routing/__init__.py +7 -7
- empathy_os/routing/chain_executor.py +37 -37
- empathy_os/routing/classifier.py +36 -36
- empathy_os/routing/smart_router.py +40 -40
- empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
- empathy_os/scaffolding/__init__.py +8 -8
- empathy_os/scaffolding/__main__.py +1 -1
- empathy_os/scaffolding/cli.py +28 -28
- empathy_os/socratic/__init__.py +3 -19
- empathy_os/socratic/ab_testing.py +25 -36
- empathy_os/socratic/blueprint.py +38 -38
- empathy_os/socratic/cli.py +34 -20
- empathy_os/socratic/collaboration.py +30 -28
- empathy_os/socratic/domain_templates.py +9 -1
- empathy_os/socratic/embeddings.py +17 -13
- empathy_os/socratic/engine.py +135 -70
- empathy_os/socratic/explainer.py +70 -60
- empathy_os/socratic/feedback.py +24 -19
- empathy_os/socratic/forms.py +15 -10
- empathy_os/socratic/generator.py +51 -35
- empathy_os/socratic/llm_analyzer.py +25 -23
- empathy_os/socratic/mcp_server.py +99 -159
- empathy_os/socratic/session.py +19 -13
- empathy_os/socratic/storage.py +98 -67
- empathy_os/socratic/success.py +38 -27
- empathy_os/socratic/visual_editor.py +51 -39
- empathy_os/socratic/web_ui.py +99 -66
- empathy_os/telemetry/cli.py +3 -1
- empathy_os/telemetry/usage_tracker.py +1 -3
- empathy_os/test_generator/__init__.py +3 -3
- empathy_os/test_generator/cli.py +28 -28
- empathy_os/test_generator/generator.py +64 -66
- empathy_os/test_generator/risk_analyzer.py +11 -11
- empathy_os/vscode_bridge 2.py +173 -0
- empathy_os/vscode_bridge.py +173 -0
- empathy_os/workflows/__init__.py +212 -120
- empathy_os/workflows/batch_processing.py +8 -24
- empathy_os/workflows/bug_predict.py +1 -1
- empathy_os/workflows/code_review.py +20 -5
- empathy_os/workflows/code_review_pipeline.py +13 -8
- empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
- empathy_os/workflows/manage_documentation.py +1 -0
- empathy_os/workflows/orchestrated_health_check.py +6 -11
- empathy_os/workflows/orchestrated_release_prep.py +3 -3
- empathy_os/workflows/pr_review.py +18 -10
- empathy_os/workflows/progressive/README 2.md +454 -0
- empathy_os/workflows/progressive/__init__ 2.py +92 -0
- empathy_os/workflows/progressive/__init__.py +2 -12
- empathy_os/workflows/progressive/cli 2.py +242 -0
- empathy_os/workflows/progressive/cli.py +14 -37
- empathy_os/workflows/progressive/core 2.py +488 -0
- empathy_os/workflows/progressive/core.py +12 -12
- empathy_os/workflows/progressive/orchestrator 2.py +701 -0
- empathy_os/workflows/progressive/orchestrator.py +166 -144
- empathy_os/workflows/progressive/reports 2.py +528 -0
- empathy_os/workflows/progressive/reports.py +22 -31
- empathy_os/workflows/progressive/telemetry 2.py +280 -0
- empathy_os/workflows/progressive/telemetry.py +8 -14
- empathy_os/workflows/progressive/test_gen 2.py +514 -0
- empathy_os/workflows/progressive/test_gen.py +29 -48
- empathy_os/workflows/progressive/workflow 2.py +628 -0
- empathy_os/workflows/progressive/workflow.py +31 -70
- empathy_os/workflows/release_prep.py +21 -6
- empathy_os/workflows/release_prep_crew.py +1 -0
- empathy_os/workflows/secure_release.py +13 -6
- empathy_os/workflows/security_audit.py +8 -3
- empathy_os/workflows/test_coverage_boost_crew.py +3 -2
- empathy_os/workflows/test_maintenance_crew.py +1 -0
- empathy_os/workflows/test_runner.py +16 -12
- empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
- empathy_software_plugin/cli.py +0 -122
- patterns/README.md +119 -0
- patterns/__init__.py +95 -0
- patterns/behavior.py +298 -0
- patterns/code_review_memory.json +441 -0
- patterns/core.py +97 -0
- patterns/debugging.json +3763 -0
- patterns/empathy.py +268 -0
- patterns/health_check_memory.json +505 -0
- patterns/input.py +161 -0
- patterns/memory_graph.json +8 -0
- patterns/refactoring_memory.json +1113 -0
- patterns/registry.py +663 -0
- patterns/security_memory.json +8 -0
- patterns/structural.py +415 -0
- patterns/validation.py +194 -0
- coach_wizards/__init__.py +0 -45
- coach_wizards/accessibility_wizard.py +0 -91
- coach_wizards/api_wizard.py +0 -91
- coach_wizards/base_wizard.py +0 -209
- coach_wizards/cicd_wizard.py +0 -91
- coach_wizards/code_reviewer_README.md +0 -60
- coach_wizards/code_reviewer_wizard.py +0 -180
- coach_wizards/compliance_wizard.py +0 -91
- coach_wizards/database_wizard.py +0 -91
- coach_wizards/debugging_wizard.py +0 -91
- coach_wizards/documentation_wizard.py +0 -91
- coach_wizards/generate_wizards.py +0 -347
- coach_wizards/localization_wizard.py +0 -173
- coach_wizards/migration_wizard.py +0 -91
- coach_wizards/monitoring_wizard.py +0 -91
- coach_wizards/observability_wizard.py +0 -91
- coach_wizards/performance_wizard.py +0 -91
- coach_wizards/prompt_engineering_wizard.py +0 -661
- coach_wizards/refactoring_wizard.py +0 -91
- coach_wizards/scaling_wizard.py +0 -90
- coach_wizards/security_wizard.py +0 -92
- coach_wizards/testing_wizard.py +0 -91
- empathy_framework-4.6.6.dist-info/METADATA +0 -1597
- empathy_framework-4.6.6.dist-info/RECORD +0 -410
- empathy_llm_toolkit/wizards/__init__.py +0 -43
- empathy_llm_toolkit/wizards/base_wizard.py +0 -364
- empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
- empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
- empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
- empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
- empathy_os/wizard_factory_cli.py +0 -170
- empathy_software_plugin/wizards/__init__.py +0 -42
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
- empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
- empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
- empathy_software_plugin/wizards/base_wizard.py +0 -288
- empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
- empathy_software_plugin/wizards/code_review_wizard.py +0 -604
- empathy_software_plugin/wizards/debugging/__init__.py +0 -50
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
- empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
- empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
- empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
- empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
- empathy_software_plugin/wizards/debugging/verification.py +0 -369
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
- empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
- empathy_software_plugin/wizards/performance/__init__.py +0 -9
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
- empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
- empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
- empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
- empathy_software_plugin/wizards/security/__init__.py +0 -32
- empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
- empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
- empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
- empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
- empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
- empathy_software_plugin/wizards/testing/__init__.py +0 -27
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
- empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
- empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
- empathy_software_plugin/wizards/testing_wizard.py +0 -274
- wizards/__init__.py +0 -82
- wizards/admission_assessment_wizard.py +0 -644
- wizards/care_plan.py +0 -321
- wizards/clinical_assessment.py +0 -769
- wizards/discharge_planning.py +0 -77
- wizards/discharge_summary_wizard.py +0 -468
- wizards/dosage_calculation.py +0 -497
- wizards/incident_report_wizard.py +0 -454
- wizards/medication_reconciliation.py +0 -85
- wizards/nursing_assessment.py +0 -171
- wizards/patient_education.py +0 -654
- wizards/quality_improvement.py +0 -705
- wizards/sbar_report.py +0 -324
- wizards/sbar_wizard.py +0 -608
- wizards/shift_handoff_wizard.py +0 -535
- wizards/soap_note_wizard.py +0 -679
- wizards/treatment_plan.py +0 -15
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/WHEEL +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/entry_points.txt +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"""Progressive test generation workflow with tier escalation.
|
|
2
|
+
|
|
3
|
+
This module implements test generation with automatic escalation from cheap
|
|
4
|
+
to capable to premium tiers based on test quality metrics.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
import logging
|
|
9
|
+
import subprocess
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from empathy_os.workflows.progressive.core import (
|
|
15
|
+
EscalationConfig,
|
|
16
|
+
FailureAnalysis,
|
|
17
|
+
ProgressiveWorkflowResult,
|
|
18
|
+
Tier,
|
|
19
|
+
TierResult,
|
|
20
|
+
)
|
|
21
|
+
from empathy_os.workflows.progressive.workflow import ProgressiveWorkflow
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
27
|
+
"""Test generation workflow with progressive tier escalation.
|
|
28
|
+
|
|
29
|
+
Generates tests for Python functions using a cost-efficient progressive
|
|
30
|
+
approach:
|
|
31
|
+
1. Start with cheap tier (gpt-4o-mini) for volume
|
|
32
|
+
2. Escalate failed tests to capable tier (claude-3-5-sonnet)
|
|
33
|
+
3. Escalate persistent failures to premium tier (claude-opus-4)
|
|
34
|
+
|
|
35
|
+
Quality metrics tracked:
|
|
36
|
+
- Syntax errors (AST parsing)
|
|
37
|
+
- Test execution (pass/fail)
|
|
38
|
+
- Code coverage
|
|
39
|
+
- Assertion depth
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> config = EscalationConfig(enabled=True, max_cost=10.00)
|
|
43
|
+
>>> workflow = ProgressiveTestGenWorkflow(config)
|
|
44
|
+
>>> result = workflow.execute(target_file="app.py")
|
|
45
|
+
>>> print(result.generate_report())
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, config: EscalationConfig | None = None):
|
|
49
|
+
"""Initialize progressive test generation workflow.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
config: Escalation configuration (uses defaults if None)
|
|
53
|
+
"""
|
|
54
|
+
super().__init__(config)
|
|
55
|
+
self.target_file: Path | None = None
|
|
56
|
+
|
|
57
|
+
def execute(self, target_file: str, **kwargs) -> ProgressiveWorkflowResult:
|
|
58
|
+
"""Generate tests for target file with progressive escalation.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
target_file: Path to Python file to generate tests for
|
|
62
|
+
**kwargs: Additional parameters
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Complete workflow results with progression history
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
FileNotFoundError: If target_file doesn't exist
|
|
69
|
+
BudgetExceededError: If cost exceeds budget
|
|
70
|
+
UserCancelledError: If user declines approval
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> result = workflow.execute(target_file="src/app.py")
|
|
74
|
+
>>> print(f"Generated {len(result.final_result.generated_items)} tests")
|
|
75
|
+
"""
|
|
76
|
+
self.target_file = Path(target_file)
|
|
77
|
+
|
|
78
|
+
if not self.target_file.exists():
|
|
79
|
+
raise FileNotFoundError(f"Target file not found: {target_file}")
|
|
80
|
+
|
|
81
|
+
logger.info(f"Generating tests for {target_file}")
|
|
82
|
+
|
|
83
|
+
# Parse target file to extract functions
|
|
84
|
+
functions = self._parse_functions(self.target_file)
|
|
85
|
+
|
|
86
|
+
if not functions:
|
|
87
|
+
logger.warning(f"No functions found in {target_file}")
|
|
88
|
+
return self._create_empty_result("test-gen")
|
|
89
|
+
|
|
90
|
+
logger.info(f"Found {len(functions)} functions to test")
|
|
91
|
+
|
|
92
|
+
# Execute with progressive escalation
|
|
93
|
+
return self._execute_progressive(
|
|
94
|
+
items=functions,
|
|
95
|
+
workflow_name="test-gen",
|
|
96
|
+
**kwargs
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def _parse_functions(self, file_path: Path) -> list[dict[str, Any]]:
|
|
100
|
+
"""Parse Python file to extract function definitions.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
file_path: Path to Python file
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
List of function metadata dicts with keys:
|
|
107
|
+
- name: Function name
|
|
108
|
+
- lineno: Line number
|
|
109
|
+
- args: List of argument names
|
|
110
|
+
- docstring: Function docstring (if present)
|
|
111
|
+
- code: Full function source code
|
|
112
|
+
|
|
113
|
+
Example:
|
|
114
|
+
>>> functions = workflow._parse_functions(Path("app.py"))
|
|
115
|
+
>>> print(functions[0]["name"])
|
|
116
|
+
'calculate_total'
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
source = file_path.read_text()
|
|
120
|
+
tree = ast.parse(source)
|
|
121
|
+
except SyntaxError as e:
|
|
122
|
+
logger.error(f"Syntax error in {file_path}: {e}")
|
|
123
|
+
return []
|
|
124
|
+
|
|
125
|
+
functions = []
|
|
126
|
+
|
|
127
|
+
for node in ast.walk(tree):
|
|
128
|
+
if isinstance(node, ast.FunctionDef):
|
|
129
|
+
# Extract function info
|
|
130
|
+
func_info = {
|
|
131
|
+
"name": node.name,
|
|
132
|
+
"lineno": node.lineno,
|
|
133
|
+
"args": [arg.arg for arg in node.args.args],
|
|
134
|
+
"docstring": ast.get_docstring(node) or "",
|
|
135
|
+
"code": ast.unparse(node), # Python 3.9+
|
|
136
|
+
"file": str(file_path)
|
|
137
|
+
}
|
|
138
|
+
functions.append(func_info)
|
|
139
|
+
|
|
140
|
+
return functions
|
|
141
|
+
|
|
142
|
+
def _execute_tier_impl(
|
|
143
|
+
self,
|
|
144
|
+
tier: Tier,
|
|
145
|
+
items: list[Any],
|
|
146
|
+
context: dict[str, Any] | None,
|
|
147
|
+
**kwargs
|
|
148
|
+
) -> list[dict[str, Any]]:
|
|
149
|
+
"""Execute test generation at specific tier.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
tier: Which tier to execute at
|
|
153
|
+
items: Functions to generate tests for
|
|
154
|
+
context: Context from previous tier (if escalating)
|
|
155
|
+
**kwargs: Additional parameters
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
List of generated test items with quality scores
|
|
159
|
+
|
|
160
|
+
Note:
|
|
161
|
+
This is a placeholder implementation. In production, this would
|
|
162
|
+
call the actual LLM API to generate tests.
|
|
163
|
+
"""
|
|
164
|
+
logger.info(f"Generating {len(items)} tests at {tier.value} tier")
|
|
165
|
+
|
|
166
|
+
# Build prompt for this tier
|
|
167
|
+
base_task = self._build_test_gen_task(items)
|
|
168
|
+
prompt = self.meta_orchestrator.build_tier_prompt(
|
|
169
|
+
tier,
|
|
170
|
+
base_task,
|
|
171
|
+
context
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# TODO: Call LLM API with prompt
|
|
175
|
+
# For now, simulate test generation
|
|
176
|
+
generated_tests = self._simulate_test_generation(tier, items)
|
|
177
|
+
|
|
178
|
+
return generated_tests
|
|
179
|
+
|
|
180
|
+
def _build_test_gen_task(self, functions: list[dict[str, Any]]) -> str:
|
|
181
|
+
"""Build task description for test generation.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
functions: List of function metadata
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Task description string
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
>>> task = workflow._build_test_gen_task([{"name": "foo", ...}])
|
|
191
|
+
>>> print(task)
|
|
192
|
+
'Generate pytest tests for 1 functions from app.py'
|
|
193
|
+
"""
|
|
194
|
+
file_name = self.target_file.name if self.target_file else "module"
|
|
195
|
+
func_names = [f["name"] for f in functions]
|
|
196
|
+
|
|
197
|
+
task = f"Generate pytest tests for {len(functions)} function(s) from {file_name}"
|
|
198
|
+
|
|
199
|
+
if len(func_names) <= 3:
|
|
200
|
+
task += f": {', '.join(func_names)}"
|
|
201
|
+
|
|
202
|
+
return task
|
|
203
|
+
|
|
204
|
+
def _simulate_test_generation(
|
|
205
|
+
self,
|
|
206
|
+
tier: Tier,
|
|
207
|
+
functions: list[dict[str, Any]]
|
|
208
|
+
) -> list[dict[str, Any]]:
|
|
209
|
+
"""Simulate test generation (placeholder for LLM integration).
|
|
210
|
+
|
|
211
|
+
In production, this would call the LLM API. For now, it generates
|
|
212
|
+
mock test data with varying quality based on tier.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
tier: Which tier is generating
|
|
216
|
+
functions: Functions to generate tests for
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
List of generated test items with quality metrics
|
|
220
|
+
|
|
221
|
+
Note:
|
|
222
|
+
This is temporary scaffolding. Real implementation will:
|
|
223
|
+
1. Call LLM API with tier-appropriate model
|
|
224
|
+
2. Parse generated test code
|
|
225
|
+
3. Validate syntax
|
|
226
|
+
4. Execute tests
|
|
227
|
+
5. Calculate coverage
|
|
228
|
+
"""
|
|
229
|
+
generated_tests = []
|
|
230
|
+
|
|
231
|
+
# Simulate different quality levels per tier
|
|
232
|
+
base_quality = {
|
|
233
|
+
Tier.CHEAP: 70,
|
|
234
|
+
Tier.CAPABLE: 85,
|
|
235
|
+
Tier.PREMIUM: 95
|
|
236
|
+
}[tier]
|
|
237
|
+
|
|
238
|
+
for func in functions:
|
|
239
|
+
# Generate mock test code
|
|
240
|
+
test_code = self._generate_mock_test(func)
|
|
241
|
+
|
|
242
|
+
# Analyze test quality
|
|
243
|
+
analysis = self._analyze_generated_test(test_code, func)
|
|
244
|
+
|
|
245
|
+
# Calculate quality score
|
|
246
|
+
quality_score = analysis.calculate_quality_score()
|
|
247
|
+
|
|
248
|
+
generated_tests.append({
|
|
249
|
+
"function_name": func["name"],
|
|
250
|
+
"test_code": test_code,
|
|
251
|
+
"quality_score": quality_score,
|
|
252
|
+
"passed": analysis.test_pass_rate > 0.5,
|
|
253
|
+
"coverage": analysis.coverage_percent,
|
|
254
|
+
"assertions": analysis.assertion_depth,
|
|
255
|
+
"confidence": analysis.confidence_score,
|
|
256
|
+
"syntax_errors": [str(e) for e in analysis.syntax_errors],
|
|
257
|
+
"error": "" if not analysis.syntax_errors else str(analysis.syntax_errors[0])
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
return generated_tests
|
|
261
|
+
|
|
262
|
+
def _generate_mock_test(self, func: dict[str, Any]) -> str:
|
|
263
|
+
"""Generate mock test code (placeholder).
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
func: Function metadata
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Generated test code as string
|
|
270
|
+
"""
|
|
271
|
+
func_name = func["name"]
|
|
272
|
+
args = func["args"]
|
|
273
|
+
|
|
274
|
+
# Generate simple test template
|
|
275
|
+
test_code = f'''def test_{func_name}():
|
|
276
|
+
"""Test {func_name} function."""
|
|
277
|
+
# Arrange
|
|
278
|
+
{self._generate_test_setup(args)}
|
|
279
|
+
|
|
280
|
+
# Act
|
|
281
|
+
result = {func_name}({", ".join(args)})
|
|
282
|
+
|
|
283
|
+
# Assert
|
|
284
|
+
assert result is not None
|
|
285
|
+
'''
|
|
286
|
+
|
|
287
|
+
return test_code
|
|
288
|
+
|
|
289
|
+
def _generate_test_setup(self, args: list[str]) -> str:
|
|
290
|
+
"""Generate test setup code for arguments.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
args: List of argument names
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Setup code as string
|
|
297
|
+
"""
|
|
298
|
+
if not args:
|
|
299
|
+
return "pass"
|
|
300
|
+
|
|
301
|
+
setup_lines = []
|
|
302
|
+
for arg in args:
|
|
303
|
+
# Simple type inference based on name
|
|
304
|
+
if "count" in arg or "num" in arg or "index" in arg:
|
|
305
|
+
setup_lines.append(f"{arg} = 1")
|
|
306
|
+
elif "name" in arg or "text" in arg or "message" in arg:
|
|
307
|
+
setup_lines.append(f'{arg} = "test"')
|
|
308
|
+
elif "items" in arg or "list" in arg:
|
|
309
|
+
setup_lines.append(f"{arg} = []")
|
|
310
|
+
else:
|
|
311
|
+
setup_lines.append(f'{arg} = "value"')
|
|
312
|
+
|
|
313
|
+
return "\n ".join(setup_lines)
|
|
314
|
+
|
|
315
|
+
def _analyze_generated_test(
|
|
316
|
+
self,
|
|
317
|
+
test_code: str,
|
|
318
|
+
func: dict[str, Any]
|
|
319
|
+
) -> FailureAnalysis:
|
|
320
|
+
"""Analyze quality of generated test.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
test_code: Generated test code
|
|
324
|
+
func: Original function metadata
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Failure analysis with quality metrics
|
|
328
|
+
"""
|
|
329
|
+
analysis = FailureAnalysis()
|
|
330
|
+
|
|
331
|
+
# 1. Check syntax
|
|
332
|
+
try:
|
|
333
|
+
ast.parse(test_code)
|
|
334
|
+
except SyntaxError as e:
|
|
335
|
+
analysis.syntax_errors.append(e)
|
|
336
|
+
return analysis # Can't proceed with invalid syntax
|
|
337
|
+
|
|
338
|
+
# 2. Count assertions
|
|
339
|
+
try:
|
|
340
|
+
tree = ast.parse(test_code)
|
|
341
|
+
assertion_count = sum(
|
|
342
|
+
1 for node in ast.walk(tree)
|
|
343
|
+
if isinstance(node, ast.Assert)
|
|
344
|
+
)
|
|
345
|
+
analysis.assertion_depth = assertion_count
|
|
346
|
+
except Exception as e:
|
|
347
|
+
logger.warning(f"Failed to count assertions: {e}")
|
|
348
|
+
analysis.assertion_depth = 0
|
|
349
|
+
|
|
350
|
+
# 3. Simulate test execution (placeholder)
|
|
351
|
+
# In production, would actually run the test
|
|
352
|
+
analysis.test_pass_rate = 0.8 # Mock: 80% pass rate
|
|
353
|
+
|
|
354
|
+
# 4. Simulate coverage (placeholder)
|
|
355
|
+
# In production, would use coverage.py
|
|
356
|
+
analysis.coverage_percent = 75.0 # Mock: 75% coverage
|
|
357
|
+
|
|
358
|
+
# 5. Estimate confidence (placeholder)
|
|
359
|
+
# In production, would parse from LLM response
|
|
360
|
+
analysis.confidence_score = 0.85 # Mock: 85% confidence
|
|
361
|
+
|
|
362
|
+
return analysis
|
|
363
|
+
|
|
364
|
+
def _create_empty_result(self, workflow_name: str) -> ProgressiveWorkflowResult:
|
|
365
|
+
"""Create empty result when no functions found.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
workflow_name: Name of workflow
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
Empty workflow result
|
|
372
|
+
"""
|
|
373
|
+
empty_result = TierResult(
|
|
374
|
+
tier=Tier.CHEAP,
|
|
375
|
+
model=self._get_model_for_tier(Tier.CHEAP),
|
|
376
|
+
attempt=1,
|
|
377
|
+
timestamp=datetime.now(),
|
|
378
|
+
generated_items=[],
|
|
379
|
+
failure_analysis=FailureAnalysis(),
|
|
380
|
+
cost=0.0,
|
|
381
|
+
duration=0.0
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
task_id = f"{workflow_name}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
385
|
+
|
|
386
|
+
return ProgressiveWorkflowResult(
|
|
387
|
+
workflow_name=workflow_name,
|
|
388
|
+
task_id=task_id,
|
|
389
|
+
tier_results=[empty_result],
|
|
390
|
+
final_result=empty_result,
|
|
391
|
+
total_cost=0.0,
|
|
392
|
+
total_duration=0.0,
|
|
393
|
+
success=False
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def execute_test_file(test_file: Path) -> dict[str, Any]:
|
|
398
|
+
"""Execute a test file using pytest.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
test_file: Path to test file
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Dict with execution results:
|
|
405
|
+
- passed: Number of tests passed
|
|
406
|
+
- failed: Number of tests failed
|
|
407
|
+
- pass_rate: Percentage passed (0.0-1.0)
|
|
408
|
+
- output: pytest output
|
|
409
|
+
|
|
410
|
+
Example:
|
|
411
|
+
>>> result = execute_test_file(Path("test_app.py"))
|
|
412
|
+
>>> print(f"Pass rate: {result['pass_rate']:.1%}")
|
|
413
|
+
"""
|
|
414
|
+
try:
|
|
415
|
+
result = subprocess.run(
|
|
416
|
+
["pytest", str(test_file), "-v", "--tb=short"],
|
|
417
|
+
capture_output=True,
|
|
418
|
+
text=True,
|
|
419
|
+
timeout=60
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
# Parse pytest output to get pass/fail counts
|
|
423
|
+
# This is a simple parser - production would be more robust
|
|
424
|
+
output = result.stdout + result.stderr
|
|
425
|
+
|
|
426
|
+
passed = output.count(" PASSED")
|
|
427
|
+
failed = output.count(" FAILED")
|
|
428
|
+
total = passed + failed
|
|
429
|
+
|
|
430
|
+
pass_rate = passed / total if total > 0 else 0.0
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
"passed": passed,
|
|
434
|
+
"failed": failed,
|
|
435
|
+
"total": total,
|
|
436
|
+
"pass_rate": pass_rate,
|
|
437
|
+
"output": output,
|
|
438
|
+
"returncode": result.returncode
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
except subprocess.TimeoutExpired:
|
|
442
|
+
return {
|
|
443
|
+
"passed": 0,
|
|
444
|
+
"failed": 0,
|
|
445
|
+
"total": 0,
|
|
446
|
+
"pass_rate": 0.0,
|
|
447
|
+
"output": "Test execution timed out",
|
|
448
|
+
"returncode": -1
|
|
449
|
+
}
|
|
450
|
+
except Exception as e:
|
|
451
|
+
logger.error(f"Failed to execute tests: {e}")
|
|
452
|
+
return {
|
|
453
|
+
"passed": 0,
|
|
454
|
+
"failed": 0,
|
|
455
|
+
"total": 0,
|
|
456
|
+
"pass_rate": 0.0,
|
|
457
|
+
"output": str(e),
|
|
458
|
+
"returncode": -1
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def calculate_coverage(test_file: Path, source_file: Path) -> float:
|
|
463
|
+
"""Calculate code coverage for a test file.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
test_file: Path to test file
|
|
467
|
+
source_file: Path to source file being tested
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Coverage percentage (0.0-100.0)
|
|
471
|
+
|
|
472
|
+
Example:
|
|
473
|
+
>>> coverage = calculate_coverage(
|
|
474
|
+
... Path("test_app.py"),
|
|
475
|
+
... Path("app.py")
|
|
476
|
+
... )
|
|
477
|
+
>>> print(f"Coverage: {coverage:.1f}%")
|
|
478
|
+
"""
|
|
479
|
+
try:
|
|
480
|
+
# Run pytest with coverage
|
|
481
|
+
result = subprocess.run(
|
|
482
|
+
[
|
|
483
|
+
"pytest",
|
|
484
|
+
str(test_file),
|
|
485
|
+
f"--cov={source_file.stem}",
|
|
486
|
+
"--cov-report=term-missing",
|
|
487
|
+
"--no-cov-on-fail"
|
|
488
|
+
],
|
|
489
|
+
capture_output=True,
|
|
490
|
+
text=True,
|
|
491
|
+
timeout=60,
|
|
492
|
+
cwd=source_file.parent
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
output = result.stdout + result.stderr
|
|
496
|
+
|
|
497
|
+
# Parse coverage percentage from output
|
|
498
|
+
# Look for line like: "app.py 85%"
|
|
499
|
+
for line in output.split("\n"):
|
|
500
|
+
if source_file.name in line and "%" in line:
|
|
501
|
+
# Extract percentage
|
|
502
|
+
parts = line.split()
|
|
503
|
+
for part in parts:
|
|
504
|
+
if "%" in part:
|
|
505
|
+
try:
|
|
506
|
+
return float(part.rstrip("%"))
|
|
507
|
+
except ValueError:
|
|
508
|
+
pass
|
|
509
|
+
|
|
510
|
+
return 0.0
|
|
511
|
+
|
|
512
|
+
except Exception as e:
|
|
513
|
+
logger.error(f"Failed to calculate coverage: {e}")
|
|
514
|
+
return 0.0
|
|
@@ -90,11 +90,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
90
90
|
logger.info(f"Found {len(functions)} functions to test")
|
|
91
91
|
|
|
92
92
|
# Execute with progressive escalation
|
|
93
|
-
return self._execute_progressive(
|
|
94
|
-
items=functions,
|
|
95
|
-
workflow_name="test-gen",
|
|
96
|
-
**kwargs
|
|
97
|
-
)
|
|
93
|
+
return self._execute_progressive(items=functions, workflow_name="test-gen", **kwargs)
|
|
98
94
|
|
|
99
95
|
def _parse_functions(self, file_path: Path) -> list[dict[str, Any]]:
|
|
100
96
|
"""Parse Python file to extract function definitions.
|
|
@@ -133,18 +129,14 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
133
129
|
"args": [arg.arg for arg in node.args.args],
|
|
134
130
|
"docstring": ast.get_docstring(node) or "",
|
|
135
131
|
"code": ast.unparse(node), # Python 3.9+
|
|
136
|
-
"file": str(file_path)
|
|
132
|
+
"file": str(file_path),
|
|
137
133
|
}
|
|
138
134
|
functions.append(func_info)
|
|
139
135
|
|
|
140
136
|
return functions
|
|
141
137
|
|
|
142
138
|
def _execute_tier_impl(
|
|
143
|
-
self,
|
|
144
|
-
tier: Tier,
|
|
145
|
-
items: list[Any],
|
|
146
|
-
context: dict[str, Any] | None,
|
|
147
|
-
**kwargs
|
|
139
|
+
self, tier: Tier, items: list[Any], context: dict[str, Any] | None, **kwargs
|
|
148
140
|
) -> list[dict[str, Any]]:
|
|
149
141
|
"""Execute test generation at specific tier.
|
|
150
142
|
|
|
@@ -165,11 +157,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
165
157
|
|
|
166
158
|
# Build prompt for this tier (prepared for future LLM integration)
|
|
167
159
|
base_task = self._build_test_gen_task(items)
|
|
168
|
-
_prompt = self.meta_orchestrator.build_tier_prompt( # noqa: F841
|
|
169
|
-
tier,
|
|
170
|
-
base_task,
|
|
171
|
-
context
|
|
172
|
-
)
|
|
160
|
+
_prompt = self.meta_orchestrator.build_tier_prompt(tier, base_task, context) # noqa: F841
|
|
173
161
|
|
|
174
162
|
# TODO: Call LLM API with _prompt
|
|
175
163
|
# For now, simulate test generation
|
|
@@ -202,9 +190,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
202
190
|
return task
|
|
203
191
|
|
|
204
192
|
def _simulate_test_generation(
|
|
205
|
-
self,
|
|
206
|
-
tier: Tier,
|
|
207
|
-
functions: list[dict[str, Any]]
|
|
193
|
+
self, tier: Tier, functions: list[dict[str, Any]]
|
|
208
194
|
) -> list[dict[str, Any]]:
|
|
209
195
|
"""Simulate test generation (placeholder for LLM integration).
|
|
210
196
|
|
|
@@ -232,7 +218,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
232
218
|
_base_quality = { # noqa: F841
|
|
233
219
|
Tier.CHEAP: 70,
|
|
234
220
|
Tier.CAPABLE: 85,
|
|
235
|
-
Tier.PREMIUM: 95
|
|
221
|
+
Tier.PREMIUM: 95,
|
|
236
222
|
}[tier]
|
|
237
223
|
|
|
238
224
|
for func in functions:
|
|
@@ -245,17 +231,19 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
245
231
|
# Calculate quality score
|
|
246
232
|
quality_score = analysis.calculate_quality_score()
|
|
247
233
|
|
|
248
|
-
generated_tests.append(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
234
|
+
generated_tests.append(
|
|
235
|
+
{
|
|
236
|
+
"function_name": func["name"],
|
|
237
|
+
"test_code": test_code,
|
|
238
|
+
"quality_score": quality_score,
|
|
239
|
+
"passed": analysis.test_pass_rate > 0.5,
|
|
240
|
+
"coverage": analysis.coverage_percent,
|
|
241
|
+
"assertions": analysis.assertion_depth,
|
|
242
|
+
"confidence": analysis.confidence_score,
|
|
243
|
+
"syntax_errors": [str(e) for e in analysis.syntax_errors],
|
|
244
|
+
"error": "" if not analysis.syntax_errors else str(analysis.syntax_errors[0]),
|
|
245
|
+
}
|
|
246
|
+
)
|
|
259
247
|
|
|
260
248
|
return generated_tests
|
|
261
249
|
|
|
@@ -312,11 +300,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
312
300
|
|
|
313
301
|
return "\n ".join(setup_lines)
|
|
314
302
|
|
|
315
|
-
def _analyze_generated_test(
|
|
316
|
-
self,
|
|
317
|
-
test_code: str,
|
|
318
|
-
func: dict[str, Any]
|
|
319
|
-
) -> FailureAnalysis:
|
|
303
|
+
def _analyze_generated_test(self, test_code: str, func: dict[str, Any]) -> FailureAnalysis:
|
|
320
304
|
"""Analyze quality of generated test.
|
|
321
305
|
|
|
322
306
|
Args:
|
|
@@ -338,10 +322,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
338
322
|
# 2. Count assertions
|
|
339
323
|
try:
|
|
340
324
|
tree = ast.parse(test_code)
|
|
341
|
-
assertion_count = sum(
|
|
342
|
-
1 for node in ast.walk(tree)
|
|
343
|
-
if isinstance(node, ast.Assert)
|
|
344
|
-
)
|
|
325
|
+
assertion_count = sum(1 for node in ast.walk(tree) if isinstance(node, ast.Assert))
|
|
345
326
|
analysis.assertion_depth = assertion_count
|
|
346
327
|
except Exception as e:
|
|
347
328
|
logger.warning(f"Failed to count assertions: {e}")
|
|
@@ -378,7 +359,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
378
359
|
generated_items=[],
|
|
379
360
|
failure_analysis=FailureAnalysis(),
|
|
380
361
|
cost=0.0,
|
|
381
|
-
duration=0.0
|
|
362
|
+
duration=0.0,
|
|
382
363
|
)
|
|
383
364
|
|
|
384
365
|
task_id = f"{workflow_name}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
@@ -390,7 +371,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
|
|
|
390
371
|
final_result=empty_result,
|
|
391
372
|
total_cost=0.0,
|
|
392
373
|
total_duration=0.0,
|
|
393
|
-
success=False
|
|
374
|
+
success=False,
|
|
394
375
|
)
|
|
395
376
|
|
|
396
377
|
|
|
@@ -416,7 +397,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
|
|
|
416
397
|
["pytest", str(test_file), "-v", "--tb=short"],
|
|
417
398
|
capture_output=True,
|
|
418
399
|
text=True,
|
|
419
|
-
timeout=60
|
|
400
|
+
timeout=60,
|
|
420
401
|
)
|
|
421
402
|
|
|
422
403
|
# Parse pytest output to get pass/fail counts
|
|
@@ -435,7 +416,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
|
|
|
435
416
|
"total": total,
|
|
436
417
|
"pass_rate": pass_rate,
|
|
437
418
|
"output": output,
|
|
438
|
-
"returncode": result.returncode
|
|
419
|
+
"returncode": result.returncode,
|
|
439
420
|
}
|
|
440
421
|
|
|
441
422
|
except subprocess.TimeoutExpired:
|
|
@@ -445,7 +426,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
|
|
|
445
426
|
"total": 0,
|
|
446
427
|
"pass_rate": 0.0,
|
|
447
428
|
"output": "Test execution timed out",
|
|
448
|
-
"returncode": -1
|
|
429
|
+
"returncode": -1,
|
|
449
430
|
}
|
|
450
431
|
except Exception as e:
|
|
451
432
|
logger.error(f"Failed to execute tests: {e}")
|
|
@@ -455,7 +436,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
|
|
|
455
436
|
"total": 0,
|
|
456
437
|
"pass_rate": 0.0,
|
|
457
438
|
"output": str(e),
|
|
458
|
-
"returncode": -1
|
|
439
|
+
"returncode": -1,
|
|
459
440
|
}
|
|
460
441
|
|
|
461
442
|
|
|
@@ -484,12 +465,12 @@ def calculate_coverage(test_file: Path, source_file: Path) -> float:
|
|
|
484
465
|
str(test_file),
|
|
485
466
|
f"--cov={source_file.stem}",
|
|
486
467
|
"--cov-report=term-missing",
|
|
487
|
-
"--no-cov-on-fail"
|
|
468
|
+
"--no-cov-on-fail",
|
|
488
469
|
],
|
|
489
470
|
capture_output=True,
|
|
490
471
|
text=True,
|
|
491
472
|
timeout=60,
|
|
492
|
-
cwd=source_file.parent
|
|
473
|
+
cwd=source_file.parent,
|
|
493
474
|
)
|
|
494
475
|
|
|
495
476
|
output = result.stdout + result.stderr
|