Skip to content

Commit 9958735

Browse files
committed
error handling and node path in comparator
1 parent f034f8f commit 9958735

8 files changed

Lines changed: 74 additions & 14 deletions

File tree

code_to_optimize_js/codeflash-compare-results.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ let Database;
3737
try {
3838
Database = require('better-sqlite3');
3939
} catch (e) {
40-
console.error(JSON.stringify({
40+
// Use console.log (stdout) for JSON output, not console.error (stderr)
41+
// Exit code 2 indicates a setup error (distinct from 1 = "not equivalent")
42+
console.log(JSON.stringify({
4143
equivalent: false,
4244
diffs: [],
43-
error: 'better-sqlite3 not installed'
45+
error: 'better-sqlite3 not installed. Run: npm install better-sqlite3'
4446
}));
45-
process.exit(1);
47+
process.exit(2);
4648
}
4749

4850
/**

code_to_optimize_js/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
"test": "jest",
88
"test:coverage": "jest --coverage"
99
},
10+
"codeflash": {
11+
"moduleRoot": ".",
12+
"testsRoot": "tests",
13+
"language": "javascript",
14+
"formatterCmds": ["npx prettier --write $file"]
15+
},
1016
"keywords": [
1117
"codeflash",
1218
"optimization",

codeflash/languages/base.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,12 +505,18 @@ def remove_test_functions(self, test_source: str, functions_to_remove: list[str]
505505

506506
# === Test Result Comparison ===
507507

508-
def compare_test_results(self, original_results_path: Path, candidate_results_path: Path) -> tuple[bool, list]:
508+
def compare_test_results(
509+
self,
510+
original_results_path: Path,
511+
candidate_results_path: Path,
512+
project_root: Path | None = None,
513+
) -> tuple[bool, list]:
509514
"""Compare test results between original and candidate code.
510515
511516
Args:
512517
original_results_path: Path to original test results (e.g., SQLite DB).
513518
candidate_results_path: Path to candidate test results.
519+
project_root: Project root directory (for finding node_modules, etc.).
514520
515521
Returns:
516522
Tuple of (are_equivalent, list of TestDiff objects).

codeflash/languages/javascript/comparator.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from __future__ import annotations
88

99
import json
10+
import os
1011
import subprocess
1112
from pathlib import Path
1213

@@ -19,6 +20,7 @@ def compare_test_results(
1920
original_sqlite_path: Path,
2021
candidate_sqlite_path: Path,
2122
comparator_script: Path | None = None,
23+
project_root: Path | None = None,
2224
) -> tuple[bool, list[TestDiff]]:
2325
"""Compare JavaScript test results using the Node.js comparator.
2426
@@ -32,6 +34,7 @@ def compare_test_results(
3234
original_sqlite_path: Path to SQLite database with original code results.
3335
candidate_sqlite_path: Path to SQLite database with candidate code results.
3436
comparator_script: Optional path to the comparison script.
37+
project_root: Project root directory where node_modules is installed.
3538
3639
Returns:
3740
Tuple of (all_equivalent, list of TestDiff objects).
@@ -50,29 +53,61 @@ def compare_test_results(
5053
logger.error(f"Candidate SQLite database not found: {candidate_sqlite_path}")
5154
return False, []
5255

56+
# Determine working directory - should be where node_modules is installed
57+
# The script needs better-sqlite3 which is installed in the project's node_modules
58+
cwd = project_root or Path.cwd()
59+
60+
# Set NODE_PATH to include the project's node_modules
61+
# This is needed because the script runs from the codeflash package directory,
62+
# but needs to resolve modules from the project's node_modules
63+
env = os.environ.copy()
64+
node_modules_path = cwd / "node_modules"
65+
if node_modules_path.exists():
66+
existing_node_path = env.get("NODE_PATH", "")
67+
if existing_node_path:
68+
env["NODE_PATH"] = f"{node_modules_path}:{existing_node_path}"
69+
else:
70+
env["NODE_PATH"] = str(node_modules_path)
71+
5372
try:
5473
result = subprocess.run(
5574
["node", str(script_path), str(original_sqlite_path), str(candidate_sqlite_path)],
5675
check=False,
5776
capture_output=True,
5877
text=True,
5978
timeout=60,
79+
cwd=str(cwd),
80+
env=env,
6081
)
6182

62-
# Parse the JSON output
83+
# Parse the JSON output first - errors are reported in JSON too
6384
try:
85+
if not result.stdout or not result.stdout.strip():
86+
logger.error("JavaScript comparator returned empty output")
87+
if result.stderr:
88+
logger.error(f"stderr: {result.stderr}")
89+
return False, []
6490
comparison = json.loads(result.stdout)
6591
except json.JSONDecodeError as e:
6692
logger.error(f"Failed to parse JavaScript comparator output: {e}")
67-
logger.debug(f"stdout: {result.stdout}")
68-
logger.debug(f"stderr: {result.stderr}")
93+
logger.error(f"stdout: {result.stdout[:500] if result.stdout else '(empty)'}")
94+
if result.stderr:
95+
logger.error(f"stderr: {result.stderr[:500]}")
6996
return False, []
7097

71-
# Check for errors
98+
# Check for errors in the JSON response
99+
# Exit code 0 = equivalent, 1 = not equivalent, 2 = setup error
72100
if comparison.get("error"):
73101
logger.error(f"JavaScript comparator error: {comparison['error']}")
74102
return False, []
75103

104+
# Check for unexpected exit codes (not 0 or 1)
105+
if result.returncode != 0 and result.returncode != 1:
106+
logger.error(f"JavaScript comparator failed with exit code {result.returncode}")
107+
if result.stderr:
108+
logger.error(f"stderr: {result.stderr}")
109+
return False, []
110+
76111
# Convert diffs to TestDiff objects
77112
test_diffs: list[TestDiff] = []
78113
for diff in comparison.get("diffs", []):

codeflash/languages/javascript/runtime/codeflash-compare-results.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ let Database;
3737
try {
3838
Database = require('better-sqlite3');
3939
} catch (e) {
40-
console.error(JSON.stringify({
40+
// Use console.log (stdout) for JSON output, not console.error (stderr)
41+
// Exit code 2 indicates a setup error (distinct from 1 = "not equivalent")
42+
console.log(JSON.stringify({
4143
equivalent: false,
4244
diffs: [],
43-
error: 'better-sqlite3 not installed'
45+
error: 'better-sqlite3 not installed. Run: npm install better-sqlite3'
4446
}));
45-
process.exit(1);
47+
process.exit(2);
4648
}
4749

4850
/**

codeflash/languages/javascript/support.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,20 +902,26 @@ def remove_test_functions(self, test_source: str, functions_to_remove: list[str]
902902

903903
# === Test Result Comparison ===
904904

905-
def compare_test_results(self, original_results_path: Path, candidate_results_path: Path) -> tuple[bool, list]:
905+
def compare_test_results(
906+
self,
907+
original_results_path: Path,
908+
candidate_results_path: Path,
909+
project_root: Path | None = None,
910+
) -> tuple[bool, list]:
906911
"""Compare test results between original and candidate code.
907912
908913
Args:
909914
original_results_path: Path to original test results SQLite DB.
910915
candidate_results_path: Path to candidate test results SQLite DB.
916+
project_root: Project root directory where node_modules is installed.
911917
912918
Returns:
913919
Tuple of (are_equivalent, list of TestDiff objects).
914920
915921
"""
916922
from codeflash.languages.javascript.comparator import compare_test_results
917923

918-
return compare_test_results(original_results_path, candidate_results_path)
924+
return compare_test_results(original_results_path, candidate_results_path, project_root=project_root)
919925

920926
# === Configuration ===
921927

codeflash/lsp/features/perform_optimization.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def sync_perform_optimization(server: CodeflashLanguageServer, cancel_event: thr
3535
code_context.read_writable_code.flat,
3636
file_name=current_function.file_path,
3737
function_name=current_function.function_name,
38+
language=current_function.language,
3839
)
3940
abort_if_cancelled(cancel_event)
4041

codeflash/optimization/function_optimizer.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2536,7 +2536,9 @@ def run_optimized_candidate(
25362536

25372537
if original_sqlite.exists() and candidate_sqlite.exists():
25382538
# Full comparison using captured return values via language support
2539-
match, diffs = self.language_support.compare_test_results(original_sqlite, candidate_sqlite)
2539+
match, diffs = self.language_support.compare_test_results(
2540+
original_sqlite, candidate_sqlite, project_root=self.args.project_root
2541+
)
25402542
# Cleanup SQLite files after comparison
25412543
candidate_sqlite.unlink(missing_ok=True)
25422544
else:

0 commit comments

Comments
 (0)