pintest-cli 0.2.3__tar.gz → 0.2.5__tar.gz
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.
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/PKG-INFO +1 -1
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/__init__.py +1 -1
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/build_mapping_iterative.py +4 -14
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/cli.py +41 -8
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/cloud_mapping_db.py +57 -20
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/git_diff_parser.py +7 -1
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/pre_commit_hook.py +8 -136
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/update_mapping.py +4 -11
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest_cli.egg-info/PKG-INFO +1 -1
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/setup.py +1 -1
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/README.md +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/config.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/coverage_mapper.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/post_commit_hook.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/range_set.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest/test_mapping_db_v2.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest_cli.egg-info/SOURCES.txt +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest_cli.egg-info/dependency_links.txt +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest_cli.egg-info/entry_points.txt +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest_cli.egg-info/requires.txt +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/pintest_cli.egg-info/top_level.txt +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/setup.cfg +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/tests/__init__.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/tests/test_git_diff_parser.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/tests/test_new_feature.py +0 -0
- {pintest_cli-0.2.3 → pintest_cli-0.2.5}/tests/test_range_set.py +0 -0
|
@@ -28,8 +28,6 @@ from pintest.test_mapping_db_v2 import TestMappingDBV2
|
|
|
28
28
|
from pintest.pre_commit_hook import (
|
|
29
29
|
collect_all_tests,
|
|
30
30
|
find_unmapped_tests,
|
|
31
|
-
ensure_docker_containers,
|
|
32
|
-
run_preflight_scripts
|
|
33
31
|
)
|
|
34
32
|
|
|
35
33
|
|
|
@@ -54,7 +52,7 @@ def run_test_chunk_with_mapping(
|
|
|
54
52
|
# Run chunk of tests with coverage
|
|
55
53
|
cmd = [
|
|
56
54
|
sys.executable, "-m", "pytest",
|
|
57
|
-
"--cov
|
|
55
|
+
"--cov",
|
|
58
56
|
"--cov-context=test",
|
|
59
57
|
"--cov-append",
|
|
60
58
|
"--cov-report=",
|
|
@@ -85,7 +83,9 @@ def run_test_chunk_with_mapping(
|
|
|
85
83
|
|
|
86
84
|
# Find and use coverage data files
|
|
87
85
|
coverage_file = repo_root / ".coverage"
|
|
88
|
-
|
|
86
|
+
if not coverage_file.exists() and (repo_root / "coverage" / ".coverage").exists():
|
|
87
|
+
coverage_file = repo_root / "coverage" / ".coverage"
|
|
88
|
+
coverage_chunks = list(repo_root.glob(".coverage.*")) + list((repo_root / "coverage").glob(".coverage.*"))
|
|
89
89
|
|
|
90
90
|
# Filter out empty files
|
|
91
91
|
valid_chunks = []
|
|
@@ -171,16 +171,6 @@ def build_mapping_iteratively(
|
|
|
171
171
|
print(f"Repository: {repo_root}")
|
|
172
172
|
print(f"Mapping DB: {mapping_db}")
|
|
173
173
|
|
|
174
|
-
# Ensure Docker and preflight checks
|
|
175
|
-
print("\n🔧 Pre-checks...")
|
|
176
|
-
if not ensure_docker_containers(repo_root, verbose):
|
|
177
|
-
print("❌ Docker container check failed.")
|
|
178
|
-
return 1
|
|
179
|
-
|
|
180
|
-
if not run_preflight_scripts(repo_root, verbose):
|
|
181
|
-
print("❌ Preflight checks failed.")
|
|
182
|
-
return 1
|
|
183
|
-
|
|
184
174
|
# Initialize mapping database
|
|
185
175
|
with TestMappingDBV2(mapping_db) as db:
|
|
186
176
|
db.initialize_schema()
|
|
@@ -70,7 +70,7 @@ class PintestRunner:
|
|
|
70
70
|
# CloudMappingDB or optimized interface
|
|
71
71
|
# Format changes for the API: [{"file": "path", "lines": [1, 2]}, ...]
|
|
72
72
|
formatted_changes = [
|
|
73
|
-
{"file": path, "lines": change.get_all_changed_lines()}
|
|
73
|
+
{"file": path, "lines": list(change.get_all_changed_lines())}
|
|
74
74
|
for path, change in python_changes.items()
|
|
75
75
|
if not change.is_new and change.get_all_changed_lines()
|
|
76
76
|
]
|
|
@@ -138,7 +138,10 @@ class PintestRunner:
|
|
|
138
138
|
return 0
|
|
139
139
|
|
|
140
140
|
# Build pytest command
|
|
141
|
-
cmd = ["pytest"]
|
|
141
|
+
cmd = [sys.executable, "-m", "pytest"]
|
|
142
|
+
|
|
143
|
+
# Automatically generate coverage for local updates (suppress terminal report)
|
|
144
|
+
cmd.extend(["--cov", "--cov-context=test", "--cov-append", "--cov-report="])
|
|
142
145
|
|
|
143
146
|
if verbose:
|
|
144
147
|
cmd.append("-v")
|
|
@@ -187,11 +190,28 @@ def cmd_run(args):
|
|
|
187
190
|
if not mapping_db:
|
|
188
191
|
mapping_db = repo_root / ".test_mapping.db"
|
|
189
192
|
|
|
190
|
-
if mapping_db.exists():
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
if not mapping_db.exists():
|
|
194
|
+
print("🏗️ No local mapping DB found. Initializing build...", file=sys.stderr)
|
|
195
|
+
from .build_mapping_iterative import build_mapping_iteratively
|
|
196
|
+
|
|
197
|
+
# Use test_dir from config or default "tests"
|
|
198
|
+
cfg_local = Config.load()
|
|
199
|
+
default_test_dir = "tests"
|
|
200
|
+
if getattr(cfg_local.cloud, "test_dir", None):
|
|
201
|
+
default_test_dir = cfg_local.cloud.test_dir
|
|
202
|
+
|
|
203
|
+
exit_code = build_mapping_iteratively(
|
|
204
|
+
repo_root,
|
|
205
|
+
mapping_db,
|
|
206
|
+
test_dir=default_test_dir,
|
|
207
|
+
verbose=args.verbose
|
|
208
|
+
)
|
|
209
|
+
sys.exit(exit_code)
|
|
210
|
+
|
|
211
|
+
if args.verbose:
|
|
212
|
+
print(f"🖥️ Local mode: {mapping_db}", file=sys.stderr)
|
|
213
|
+
mapping_db_obj = TestMappingDBV2(mapping_db)
|
|
214
|
+
mapping_db_obj.connect()
|
|
195
215
|
|
|
196
216
|
# Initialize runner
|
|
197
217
|
runner = PintestRunner(
|
|
@@ -201,9 +221,13 @@ def cmd_run(args):
|
|
|
201
221
|
)
|
|
202
222
|
|
|
203
223
|
try:
|
|
224
|
+
base_branch = args.base_branch
|
|
225
|
+
if base_branch == "master" and cfg.is_cloud_enabled and getattr(cfg.cloud, "branch", None):
|
|
226
|
+
base_branch = cfg.cloud.branch
|
|
227
|
+
|
|
204
228
|
# Find affected tests
|
|
205
229
|
affected_tests = runner.find_affected_tests(
|
|
206
|
-
|
|
230
|
+
base_branch
|
|
207
231
|
)
|
|
208
232
|
|
|
209
233
|
# Unmapped tests discovery (Cloud mode only)
|
|
@@ -240,6 +264,13 @@ def cmd_run(args):
|
|
|
240
264
|
verbose=args.verbose,
|
|
241
265
|
pytest_args=pytest_extra_args
|
|
242
266
|
)
|
|
267
|
+
|
|
268
|
+
# Auto-update the local database with the new coverage
|
|
269
|
+
if not use_cloud and not args.dry_run:
|
|
270
|
+
print("\n🔄 Updating mapping database with new coverage...")
|
|
271
|
+
from .update_mapping import update_mapping
|
|
272
|
+
update_mapping(repo_root, mapping_db=mapping_db, verbose=args.verbose)
|
|
273
|
+
|
|
243
274
|
sys.exit(exit_code)
|
|
244
275
|
|
|
245
276
|
except KeyboardInterrupt:
|
|
@@ -483,6 +514,8 @@ def cmd_push(args):
|
|
|
483
514
|
sys.exit(1)
|
|
484
515
|
|
|
485
516
|
coverage_file = repo_root / ".coverage"
|
|
517
|
+
if not coverage_file.exists() and (repo_root / "coverage" / ".coverage").exists():
|
|
518
|
+
coverage_file = repo_root / "coverage" / ".coverage"
|
|
486
519
|
if not coverage_file.exists():
|
|
487
520
|
print(f"❌ Coverage file not found at {coverage_file}")
|
|
488
521
|
print("Run your tests first (e.g. 'pytest --cov --cov-context=test')")
|
|
@@ -114,8 +114,7 @@ class CloudMappingDB:
|
|
|
114
114
|
print(f"⚠️ No coverage file at {coverage_file} — skipping cloud push")
|
|
115
115
|
return False
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
print("☁️ Parsing coverage data for cloud upload...", flush=True)
|
|
117
|
+
print("☁️ Parsing coverage data for cloud upload...", flush=True)
|
|
119
118
|
|
|
120
119
|
mapper = CoverageMapper(coverage_file)
|
|
121
120
|
try:
|
|
@@ -147,21 +146,33 @@ class CloudMappingDB:
|
|
|
147
146
|
})
|
|
148
147
|
|
|
149
148
|
if not mappings:
|
|
150
|
-
|
|
151
|
-
print("ℹ️ No coverage mappings to push")
|
|
149
|
+
print("ℹ️ No coverage mappings to push")
|
|
152
150
|
return True
|
|
153
151
|
|
|
154
|
-
|
|
155
|
-
print(f"☁️ Pushing {len(mappings)} mappings to Pintest...", flush=True)
|
|
152
|
+
print(f"☁️ Pushing {len(mappings)} mappings to Pintest...", flush=True)
|
|
156
153
|
|
|
157
|
-
|
|
154
|
+
payload_base = {
|
|
158
155
|
"branch": self._branch,
|
|
159
|
-
"mappings": mappings,
|
|
160
156
|
}
|
|
161
157
|
if run_stats:
|
|
162
|
-
|
|
158
|
+
payload_base["run_stats"] = run_stats
|
|
163
159
|
|
|
164
|
-
|
|
160
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
161
|
+
|
|
162
|
+
# Send in chunks of 1,000 parallelized in 10 threads
|
|
163
|
+
CHUNK_SIZE = 1000
|
|
164
|
+
success = True
|
|
165
|
+
total_inserted = 0
|
|
166
|
+
total_updated = 0
|
|
167
|
+
total_tests_cloud = 0
|
|
168
|
+
|
|
169
|
+
chunks = [mappings[i:i + CHUNK_SIZE] for i in range(0, len(mappings), CHUNK_SIZE)]
|
|
170
|
+
total_chunks = len(chunks)
|
|
171
|
+
|
|
172
|
+
def push_chunk(chunk_num, chunk_data):
|
|
173
|
+
print(f"☁️ Pushing chunk {chunk_num}/{total_chunks} ({len(chunk_data)} mappings)...", flush=True)
|
|
174
|
+
payload = payload_base.copy()
|
|
175
|
+
payload["mappings"] = chunk_data
|
|
165
176
|
resp = self.session.post(
|
|
166
177
|
f"{self._api}/api/v1/repos/{self._repo_id}/coverage",
|
|
167
178
|
json=payload,
|
|
@@ -169,16 +180,42 @@ class CloudMappingDB:
|
|
|
169
180
|
)
|
|
170
181
|
resp.raise_for_status()
|
|
171
182
|
data = resp.json()
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
183
|
+
inserted = data.get('inserted', 0)
|
|
184
|
+
updated = data.get('updated', 0)
|
|
185
|
+
t_tests = data.get('total_tests', 0)
|
|
186
|
+
print(f" ✓ Chunk {chunk_num}/{total_chunks} complete ({inserted} new, {updated} updated)", flush=True)
|
|
187
|
+
return inserted, updated, t_tests
|
|
188
|
+
|
|
189
|
+
with ThreadPoolExecutor(max_workers=10) as executor:
|
|
190
|
+
futures = {executor.submit(push_chunk, idx + 1, chunk): idx + 1 for idx, chunk in enumerate(chunks)}
|
|
191
|
+
for future in as_completed(futures):
|
|
192
|
+
chunk_num = futures[future]
|
|
193
|
+
try:
|
|
194
|
+
inserted, updated, t_tests = future.result()
|
|
195
|
+
total_inserted += inserted
|
|
196
|
+
total_updated += updated
|
|
197
|
+
if t_tests > total_tests_cloud:
|
|
198
|
+
total_tests_cloud = t_tests
|
|
199
|
+
except requests.RequestException as e:
|
|
200
|
+
print(f"⚠️ Cloud push failed on chunk {chunk_num}: {e}", file=sys.stderr)
|
|
201
|
+
success = False
|
|
202
|
+
for f in futures:
|
|
203
|
+
f.cancel()
|
|
204
|
+
except Exception as e:
|
|
205
|
+
if success:
|
|
206
|
+
print(f"⚠️ Cloud push failed on chunk {chunk_num}: {e}", file=sys.stderr)
|
|
207
|
+
success = False
|
|
208
|
+
for f in futures:
|
|
209
|
+
f.cancel()
|
|
210
|
+
|
|
211
|
+
if success:
|
|
212
|
+
print(
|
|
213
|
+
f"☁️ Cloud sync complete: {total_inserted} new, "
|
|
214
|
+
f"{total_updated} updated, "
|
|
215
|
+
f"{total_tests_cloud} total tests",
|
|
216
|
+
flush=True
|
|
217
|
+
)
|
|
218
|
+
return success
|
|
182
219
|
|
|
183
220
|
def get_all_test_names(self) -> Set[str]:
|
|
184
221
|
"""Fetch all unique test names from the cloud."""
|
|
@@ -62,7 +62,13 @@ class GitDiffParser:
|
|
|
62
62
|
# For pre-commit hooks, we usually want staged changes
|
|
63
63
|
cmd = ["git", "diff", "--cached", "--unified=0", "HEAD"]
|
|
64
64
|
else:
|
|
65
|
-
|
|
65
|
+
# Find merge base to handle branches properly
|
|
66
|
+
mb_cmd = ["git", "merge-base", base_branch, "HEAD"]
|
|
67
|
+
mb_result = subprocess.run(mb_cmd, cwd=self.repo_root, capture_output=True, text=True, check=True)
|
|
68
|
+
merge_base = mb_result.stdout.strip()
|
|
69
|
+
|
|
70
|
+
# Diff from merge base to working tree (includes uncommitted changes)
|
|
71
|
+
cmd = ["git", "diff", "--unified=0", merge_base]
|
|
66
72
|
|
|
67
73
|
result = subprocess.run(
|
|
68
74
|
cmd,
|
|
@@ -115,140 +115,6 @@ def ensure_git_lfs(repo_root: Path, verbose: bool = False) -> bool:
|
|
|
115
115
|
return True # Not critical - continue anyway
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
def ensure_docker_containers(repo_root: Path, verbose: bool = False) -> bool:
|
|
119
|
-
"""
|
|
120
|
-
Ensure Docker containers are running from a docker-compose-ut.yml or docker-compose.yml file.
|
|
121
|
-
|
|
122
|
-
Searches for a compose file in common locations under the repo root.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
repo_root: Repository root directory
|
|
126
|
-
verbose: Print verbose output
|
|
127
|
-
|
|
128
|
-
Returns:
|
|
129
|
-
True if containers are running (or no compose file found), False on failure
|
|
130
|
-
"""
|
|
131
|
-
# Search for a docker-compose file in common locations
|
|
132
|
-
candidates = [
|
|
133
|
-
repo_root / "docker-compose-ut.yml",
|
|
134
|
-
repo_root / "docker-compose.yml",
|
|
135
|
-
] + list(repo_root.rglob("docker-compose-ut.yml"))
|
|
136
|
-
|
|
137
|
-
docker_compose_path = next((p for p in candidates if p.exists()), None)
|
|
138
|
-
|
|
139
|
-
if docker_compose_path is None:
|
|
140
|
-
if verbose:
|
|
141
|
-
print("ℹ️ No docker-compose file found, skipping Docker check")
|
|
142
|
-
return True # No compose file in this repo, skip Docker check
|
|
143
|
-
|
|
144
|
-
if verbose:
|
|
145
|
-
print("🐳 Checking Docker containers...", flush=True)
|
|
146
|
-
|
|
147
|
-
try:
|
|
148
|
-
# Check if containers are running
|
|
149
|
-
# Note: In Docker Compose v5.x, ps -q may return exit code 1 when no containers exist
|
|
150
|
-
result = subprocess.run(
|
|
151
|
-
["docker-compose", "-f", str(docker_compose_path), "ps", "-q"],
|
|
152
|
-
capture_output=True,
|
|
153
|
-
text=True,
|
|
154
|
-
check=False # Don't raise on non-zero exit (containers may not exist yet)
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
# If ps command failed or no containers running, start them
|
|
158
|
-
if result.returncode != 0 or not result.stdout.strip():
|
|
159
|
-
# No containers running or project doesn't exist yet, start them
|
|
160
|
-
if verbose:
|
|
161
|
-
print(" Starting Docker containers...")
|
|
162
|
-
start_result = subprocess.run(
|
|
163
|
-
["docker-compose", "-f", str(docker_compose_path), "up", "-d"],
|
|
164
|
-
capture_output=not verbose,
|
|
165
|
-
text=True,
|
|
166
|
-
check=False
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
if start_result.returncode != 0:
|
|
170
|
-
print(f"❌ Failed to start Docker containers (exit code {start_result.returncode})", file=sys.stderr)
|
|
171
|
-
if start_result.stderr:
|
|
172
|
-
print(start_result.stderr, file=sys.stderr)
|
|
173
|
-
return False
|
|
174
|
-
|
|
175
|
-
if verbose:
|
|
176
|
-
print(" ✓ Docker containers started")
|
|
177
|
-
else:
|
|
178
|
-
if verbose:
|
|
179
|
-
print(" ✓ Docker containers already running")
|
|
180
|
-
|
|
181
|
-
return True
|
|
182
|
-
|
|
183
|
-
except FileNotFoundError:
|
|
184
|
-
print("❌ docker-compose not found. Install it: https://docs.docker.com/compose/install/", file=sys.stderr)
|
|
185
|
-
return False
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def run_preflight_scripts(repo_root: Path, verbose: bool = False) -> bool:
|
|
189
|
-
"""
|
|
190
|
-
Run preflight scripts for Neo4j and Postgres.
|
|
191
|
-
|
|
192
|
-
Args:
|
|
193
|
-
repo_root: Repository root directory
|
|
194
|
-
verbose: Print verbose output
|
|
195
|
-
|
|
196
|
-
Returns:
|
|
197
|
-
True if all preflight scripts passed, False otherwise
|
|
198
|
-
"""
|
|
199
|
-
script_names = [
|
|
200
|
-
"preflight-postgres.sh",
|
|
201
|
-
"preflight-neo4j.sh"
|
|
202
|
-
]
|
|
203
|
-
|
|
204
|
-
if verbose:
|
|
205
|
-
print("🔧 Running preflight checks...", flush=True)
|
|
206
|
-
|
|
207
|
-
for script_name in script_names:
|
|
208
|
-
# Try multiple possible locations for the script
|
|
209
|
-
possible_locations = [
|
|
210
|
-
repo_root / "scripts" / script_name, # Workspace root scripts
|
|
211
|
-
Path.cwd() / "scripts" / script_name, # Current directory scripts
|
|
212
|
-
]
|
|
213
|
-
|
|
214
|
-
script = None
|
|
215
|
-
for location in possible_locations:
|
|
216
|
-
if location.exists():
|
|
217
|
-
script = location
|
|
218
|
-
break
|
|
219
|
-
|
|
220
|
-
if script is None:
|
|
221
|
-
if verbose:
|
|
222
|
-
print(f" ⚠️ Preflight script not found: {script_name} (tried {len(possible_locations)} locations)")
|
|
223
|
-
continue
|
|
224
|
-
|
|
225
|
-
try:
|
|
226
|
-
if verbose:
|
|
227
|
-
print(f" Running {script.name}...", flush=True)
|
|
228
|
-
|
|
229
|
-
result = subprocess.run(
|
|
230
|
-
[str(script)],
|
|
231
|
-
cwd=repo_root,
|
|
232
|
-
capture_output=not verbose,
|
|
233
|
-
text=True,
|
|
234
|
-
check=True
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
if verbose:
|
|
238
|
-
print(f" ✓ {script.name} passed")
|
|
239
|
-
|
|
240
|
-
except subprocess.CalledProcessError as e:
|
|
241
|
-
print(f"❌ Preflight check failed: {script.name}", file=sys.stderr)
|
|
242
|
-
if not verbose and e.stderr:
|
|
243
|
-
print(e.stderr, file=sys.stderr)
|
|
244
|
-
return False
|
|
245
|
-
|
|
246
|
-
if verbose:
|
|
247
|
-
print(" ✓ All preflight checks passed")
|
|
248
|
-
|
|
249
|
-
return True
|
|
250
|
-
|
|
251
|
-
|
|
252
118
|
def should_exclude_test(test_name: str) -> bool:
|
|
253
119
|
"""
|
|
254
120
|
Check if a test should be excluded from runs and mapping.
|
|
@@ -560,7 +426,7 @@ def run_test_chunk_with_mapping_update(
|
|
|
560
426
|
# Run tests with coverage - output flows directly to terminal
|
|
561
427
|
cmd = [
|
|
562
428
|
sys.executable, "-m", "pytest",
|
|
563
|
-
"--cov
|
|
429
|
+
"--cov",
|
|
564
430
|
"--cov-context=test",
|
|
565
431
|
"--cov-append",
|
|
566
432
|
"--cov-report=",
|
|
@@ -591,6 +457,8 @@ def run_test_chunk_with_mapping_update(
|
|
|
591
457
|
|
|
592
458
|
try:
|
|
593
459
|
coverage_file = repo_root / ".coverage"
|
|
460
|
+
if not coverage_file.exists() and (repo_root / "coverage" / ".coverage").exists():
|
|
461
|
+
coverage_file = repo_root / "coverage" / ".coverage"
|
|
594
462
|
if not coverage_file.exists():
|
|
595
463
|
print(f" ⚠️ No coverage file found at {coverage_file}", flush=True)
|
|
596
464
|
print(f" Tests may have crashed before generating coverage data.", flush=True)
|
|
@@ -690,7 +558,7 @@ def run_single_test_with_mapping_update(
|
|
|
690
558
|
# Run single test with coverage
|
|
691
559
|
cmd = [
|
|
692
560
|
"python", "-m", "pytest",
|
|
693
|
-
"--cov
|
|
561
|
+
"--cov",
|
|
694
562
|
"--cov-context=test",
|
|
695
563
|
"--cov-append",
|
|
696
564
|
"--cov-report=",
|
|
@@ -713,6 +581,8 @@ def run_single_test_with_mapping_update(
|
|
|
713
581
|
# Update mapping database with new coverage
|
|
714
582
|
try:
|
|
715
583
|
coverage_file = repo_root / ".coverage"
|
|
584
|
+
if not coverage_file.exists() and (repo_root / "coverage" / ".coverage").exists():
|
|
585
|
+
coverage_file = repo_root / "coverage" / ".coverage"
|
|
716
586
|
if coverage_file.exists():
|
|
717
587
|
with TestMappingDBV2(mapping_db_path) as db:
|
|
718
588
|
db.import_from_coverage(coverage_file, incremental=True)
|
|
@@ -925,6 +795,8 @@ def combine_coverage_files(repo_root: Path, new_coverage: Path):
|
|
|
925
795
|
new_coverage: Path to new coverage file from test run
|
|
926
796
|
"""
|
|
927
797
|
existing_coverage = repo_root / ".coverage"
|
|
798
|
+
if not existing_coverage.exists() and (repo_root / "coverage" / ".coverage").exists():
|
|
799
|
+
existing_coverage = repo_root / "coverage" / ".coverage"
|
|
928
800
|
|
|
929
801
|
if not new_coverage.exists():
|
|
930
802
|
print(f"Warning: New coverage file not found: {new_coverage}", file=sys.stderr)
|
|
@@ -15,7 +15,7 @@ SCRIPT_DIR = Path(__file__).parent
|
|
|
15
15
|
sys.path.insert(0, str(SCRIPT_DIR))
|
|
16
16
|
|
|
17
17
|
from pintest.test_mapping_db_v2 import TestMappingDBV2
|
|
18
|
-
from pintest.pre_commit_hook import ensure_docker_containers, run_preflight_scripts
|
|
18
|
+
# from pintest.pre_commit_hook import ensure_docker_containers, run_preflight_scripts
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def update_mapping(
|
|
@@ -38,21 +38,14 @@ def update_mapping(
|
|
|
38
38
|
# Default paths
|
|
39
39
|
if coverage_file is None:
|
|
40
40
|
coverage_file = repo_root / ".coverage"
|
|
41
|
+
if not coverage_file.exists() and (repo_root / "coverage" / ".coverage").exists():
|
|
42
|
+
coverage_file = repo_root / "coverage" / ".coverage"
|
|
41
43
|
|
|
42
44
|
if mapping_db is None:
|
|
43
45
|
mapping_db = repo_root / ".test_mapping.db"
|
|
44
46
|
|
|
45
|
-
# Ensure Docker containers are running (if a compose file is present)
|
|
46
47
|
if verbose:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if not ensure_docker_containers(repo_root, verbose):
|
|
50
|
-
print("❌ Docker container check failed.", file=sys.stderr)
|
|
51
|
-
return 1
|
|
52
|
-
|
|
53
|
-
if not run_preflight_scripts(repo_root, verbose):
|
|
54
|
-
print("❌ Preflight checks failed.", file=sys.stderr)
|
|
55
|
-
return 1
|
|
48
|
+
pass
|
|
56
49
|
|
|
57
50
|
if not coverage_file.exists():
|
|
58
51
|
print(f"❌ Error: Coverage file not found: {coverage_file}", file=sys.stderr)
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="pintest-cli",
|
|
8
|
-
version="0.2.
|
|
8
|
+
version="0.2.5",
|
|
9
9
|
description="Run only the tests affected by your code changes.",
|
|
10
10
|
long_description=long_description,
|
|
11
11
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|