Skip to content

Commit 7294da9

Browse files
committed
Add local examples testing option
1 parent 44c0954 commit 7294da9

File tree

9 files changed

+271
-50
lines changed

9 files changed

+271
-50
lines changed

qualityflow/README.md

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ python run.py
9797
- Compare LLM vs baseline test approaches
9898
- Experiment with different configurations
9999

100+
### Local Testing Option
101+
102+
For offline development or controlled testing, use the local examples:
103+
104+
```bash
105+
python run.py --config configs/experiment.local.yaml
106+
```
107+
108+
This uses the included `examples/toy_lib/` code instead of cloning external repositories.
109+
100110
## ⚙️ Configuration
101111

102112
### Key Parameters
@@ -125,14 +135,17 @@ steps:
125135
### Pipeline Options
126136
127137
```bash
128-
# Use fake provider (no API key needed)
129-
python run.py # Uses config defaults
138+
# Default: uses remote repository (requests library)
139+
python run.py
130140

131-
# Force fresh execution (no caching)
132-
python run.py --no-cache
141+
# Local testing with included examples
142+
python run.py --config configs/experiment.local.yaml
133143

134-
# Use different config
144+
# High-quality test generation
135145
python run.py --config configs/experiment.strict.yaml
146+
147+
# Force fresh execution (no caching)
148+
python run.py --no-cache
136149
```
137150

138151
## 🔬 Advanced Usage
@@ -213,7 +226,8 @@ qualityflow/
213226
214227
├── configs/ # Pipeline configurations
215228
│ ├── experiment.default.yaml # Standard experiment settings
216-
│ └── experiment.strict.yaml # High-quality gates
229+
│ ├── experiment.strict.yaml # High-quality gates
230+
│ └── experiment.local.yaml # Local examples testing
217231
218232
├── pipelines/ # Pipeline definitions
219233
│ └── generate_and_evaluate.py # Main pipeline
@@ -233,9 +247,9 @@ qualityflow/
233247
│ └── unit_test_strict_v2.jinja # Comprehensive test generation
234248
235249
├── examples/ # Demo code for testing
236-
│ └── toy_lib/ # Sample library
237-
│ ├── calculator.py
238-
│ └── string_utils.py
250+
│ └── toy_lib/ # Sample library with test-friendly code
251+
│ ├── calculator.py # Calculator class with edge cases
252+
│ └── string_utils.py # String utilities with validation
239253
240254
└── run.py # Main entry point
241255
```
@@ -246,6 +260,7 @@ qualityflow/
246260
- **Prompt Templates**: Jinja2 templates for LLM test generation
247261
- **Configuration**: YAML-driven experiment settings
248262
- **Test Generation**: Both LLM-based and heuristic approaches for comparison
263+
- **Example Code**: Sample Python modules (`toy_lib`) designed for effective test generation demonstration
249264

250265
## 🚀 Production Deployment
251266

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# QualityFlow Local Examples Configuration
2+
# Use local toy_lib examples instead of remote repositories
3+
4+
# Pipeline configuration
5+
name: "generate_and_evaluate"
6+
version: "1.0"
7+
8+
# Source configuration - using local examples
9+
steps:
10+
select_input:
11+
parameters:
12+
# Use local examples instead of remote repo
13+
repo_url: "local"
14+
ref: "main"
15+
target_glob: "examples/**/*.py" # Target the toy_lib examples
16+
17+
analyze_code:
18+
parameters:
19+
strategy: "all" # Include all example files
20+
max_files: 5 # Process all toy_lib files
21+
22+
# LLM generation configuration
23+
gen_tests_agent:
24+
parameters:
25+
provider: "fake" # Use fake provider by default for local testing
26+
model: "gpt-4o-mini"
27+
prompt_path: "prompts/unit_test_v1.jinja"
28+
max_tests_per_file: 3
29+
max_files: 5 # Process all toy_lib files
30+
31+
# Baseline test generation
32+
gen_tests_baseline:
33+
parameters:
34+
enabled: true
35+
max_files: 5 # Match agent max_files
36+
37+
# Resource configuration
38+
settings:
39+
docker:
40+
requirements: requirements.txt

qualityflow/pipelines/generate_and_evaluate.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ def generate_and_evaluate() -> None:
3232
workspace_dir, commit_sha = fetch_source(spec)
3333

3434
# Step 3: Analyze and select code files
35-
code_summary = analyze_code(workspace_dir, commit_sha)
35+
code_summary = analyze_code(workspace_dir, commit_sha, spec)
3636

3737
# Step 4: Generate tests using LLM agent
38-
agent_tests_dir, prompt_used = gen_tests_agent(workspace_dir, code_summary)
38+
agent_tests_dir, test_summary = gen_tests_agent(
39+
workspace_dir, code_summary
40+
)
3941

4042
# Step 5: Generate baseline tests (optional)
4143
baseline_tests_dir = gen_tests_baseline(workspace_dir, code_summary)
@@ -52,7 +54,7 @@ def generate_and_evaluate() -> None:
5254
report(
5355
workspace_dir,
5456
commit_sha,
55-
prompt_used,
57+
test_summary,
5658
agent_results,
5759
baseline_results,
5860
)

qualityflow/run.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def main(config: Union[str, None], no_cache: bool):
4040
except Exception:
4141
# Fallback to current working directory
4242
default_config = Path.cwd() / "configs" / "experiment.default.yaml"
43-
43+
4444
chosen_config = config or str(default_config)
4545

4646
try:

qualityflow/steps/analyze_code.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class SelectionStrategy(str, Enum):
2828
def analyze_code(
2929
workspace_dir: Path,
3030
commit_sha: str,
31-
target_glob: str = "src/**/*.py",
31+
source_spec: Dict[str, str],
3232
strategy: SelectionStrategy = SelectionStrategy.LOW_COVERAGE,
3333
max_files: int = 10,
3434
) -> Annotated[Dict, "code_summary"]:
@@ -38,14 +38,19 @@ def analyze_code(
3838
Args:
3939
workspace_dir: Path to workspace directory
4040
commit_sha: Git commit SHA
41-
target_glob: Glob pattern for target files
41+
source_spec: Source specification containing target_glob and other settings
4242
strategy: File selection strategy
4343
max_files: Maximum number of files to select
4444
4545
Returns:
4646
Code summary dictionary containing selected files and metadata
4747
"""
48-
logger.info(f"Analyzing code in {workspace_dir} with strategy {strategy}")
48+
# Extract target_glob from source spec
49+
target_glob = source_spec.get("target_glob", "src/**/*.py")
50+
51+
logger.info(
52+
f"Analyzing code in {workspace_dir} with strategy {strategy} and glob {target_glob}"
53+
)
4954

5055
workspace_path = Path(workspace_dir)
5156

qualityflow/steps/fetch_source.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def fetch_source(
2121
source_spec: Dict[str, str],
2222
) -> Tuple[Annotated[Path, "workspace_dir"], Annotated[str, "commit_sha"]]:
2323
"""
24-
Fetch and materialize workspace from git repository.
24+
Fetch and materialize workspace from git repository or use local examples.
2525
2626
Args:
2727
source_spec: Source specification from select_input step
@@ -32,9 +32,53 @@ def fetch_source(
3232
repo_url = source_spec["repo_url"]
3333
ref = source_spec["ref"]
3434

35+
# Handle local examples case
36+
if repo_url == "local":
37+
logger.info("Using local QualityFlow examples")
38+
try:
39+
# Get the project root (QualityFlow directory)
40+
current_file = Path(__file__).resolve()
41+
project_root = (
42+
current_file.parent.parent
43+
) # Go up from steps/ to project root
44+
45+
# Create temporary workspace and copy examples
46+
workspace_dir = tempfile.mkdtemp(
47+
prefix="qualityflow_local_workspace_"
48+
)
49+
workspace_path = Path(workspace_dir)
50+
51+
# Copy examples directory to the temporary workspace
52+
import shutil
53+
54+
examples_src = project_root / "examples"
55+
examples_dest = workspace_path / "examples"
56+
57+
if examples_src.exists():
58+
shutil.copytree(examples_src, examples_dest)
59+
logger.info(
60+
f"Copied examples from {examples_src} to {examples_dest}"
61+
)
62+
else:
63+
logger.warning(
64+
f"Examples directory not found at {examples_src}"
65+
)
66+
67+
commit_sha = "local-examples"
68+
logger.info(f"Local workspace ready at {workspace_path}")
69+
return workspace_path, commit_sha
70+
71+
except Exception as e:
72+
logger.error(f"Failed to set up local workspace: {e}")
73+
# Fallback to current working directory
74+
workspace_dir = tempfile.mkdtemp(
75+
prefix="qualityflow_fallback_workspace_"
76+
)
77+
return Path(workspace_dir), "local-fallback"
78+
3579
logger.info(f"Fetching source from {repo_url}@{ref}")
3680

37-
# Create temporary workspace
81+
# Create temporary workspace for remote repositories
3882
workspace_dir = tempfile.mkdtemp(prefix="qualityflow_workspace_")
3983
workspace_path = Path(workspace_dir)
4084

0 commit comments

Comments
 (0)