Skip to content

fix: benchmark scripts handle missing WASM grammars gracefully#311

Merged
carlos-alm merged 2 commits intomainfrom
fix/benchmark-wasm-graceful
Mar 4, 2026
Merged

fix: benchmark scripts handle missing WASM grammars gracefully#311
carlos-alm merged 2 commits intomainfrom
fix/benchmark-wasm-graceful

Conversation

@carlos-alm
Copy link
Contributor

Summary

  • Add isWasmAvailable() to parser.js — checks if required WASM grammar files (JS, TS, TSX) exist on disk
  • All 4 benchmark scripts (benchmark.js, query-benchmark.js, incremental-benchmark.js, token-benchmark.js) now check WASM availability before attempting WASM engine benchmarks
  • When neither engine is available, scripts print a helpful error and exit cleanly instead of crashing
  • Mirrors the existing isNativeAvailable() pattern for symmetric engine availability handling

Test plan

  • isWasmAvailable() returns false when grammars directory is empty
  • isWasmAvailable() returns true when grammars are built
  • All 476 integration tests pass
  • Biome lint/format passes on all changed files
  • Run benchmark.js without WASM grammars — should skip WASM and run native-only
  • Run benchmark.js with WASM grammars — should run both engines as before

Add isWasmAvailable() to parser.js that checks if required WASM grammar
files exist on disk. All 4 benchmark scripts now check WASM availability
before attempting WASM engine benchmarks, mirroring the existing pattern
for native engine availability. When neither engine is available, scripts
print a helpful error message and exit instead of crashing.

Impact: 2 functions changed, 5 affected
@claude
Copy link

claude bot commented Mar 4, 2026

Claude finished @carlos-alm's task —— View job


PR Review: Benchmark WASM Graceful Handling

Review Checklist

  • Phase 1: Root cause analysis
  • Phase 2: Configuration change detection
  • Phase 3: Test integrity verification
  • Phase 4: Code quality review
  • Final assessment and rating

Phase 1: Root Cause Analysis

Problem Being Solved: Benchmark scripts crash with unclear errors when WASM grammar files are missing. This happens when users run benchmarks without first running npm install (which builds WASM grammars via the prepare script).

Why This Approach: The PR adds isWasmAvailable() to check for required WASM grammar files before attempting WASM engine benchmarks. This mirrors the existing isNativeAvailable() pattern for symmetric engine availability handling.

Risk Assessment: LOW - This is defensive error handling that prevents crashes and provides helpful error messages. The change is minimal and follows existing patterns.

Phase 2: Configuration Changes

No concerning configuration changes detected

  • No linting rules modified
  • No test timeouts or coverage thresholds changed
  • No build configuration alterations
  • No TypeScript strict mode flags disabled

Phase 3: Test Integrity

⚠️ Missing test coverage for new functionality

  • isWasmAvailable() function has no test coverage
  • Author claims "all 476 integration tests pass" but cannot verify without approval to run tests
  • Test plan shows incomplete items (missing checkbox for benchmark runs)
  • New function should have unit tests to verify it correctly detects WASM grammar availability

Phase 4: Code Quality

✅ Implementation Quality - Good

  • isWasmAvailable() at line 110-114 correctly filters for required: true entries (JS, TS, TSX)
  • Follows existing isNativeAvailable() pattern for consistency
  • Uses fs.existsSync() which is appropriate for synchronous availability checking
  • Proper error messages in all benchmark scripts with actionable guidance

✅ Error Handling - Excellent

  • All 4 benchmark scripts now have consistent error handling:
    • benchmark.js:169-173: Clean exit with helpful message
    • query-benchmark.js:164-168: Same pattern
    • incremental-benchmark.js:163-167: Same pattern
    • token-benchmark.js:295-302: Consistent with others
  • Error messages provide clear next steps: "Run npm run build:wasm..."

✅ Code Consistency - Good

  • Mirrors existing isNativeAvailable() pattern perfectly
  • All scripts use identical availability check pattern
  • Export added to index.js:126 maintains API consistency

⚠️ Minor Issues

  1. Missing Tests: No unit tests for the new isWasmAvailable() function
  2. Incomplete Test Plan: Author's checklist shows unfinished manual testing items

Critical Concerns

None - This is solid defensive programming that follows established patterns.

Final Assessment

Root Cause Analysis

Problem Being Solved: Prevents benchmark crashes when WASM grammars are unavailable, providing clear error messages instead of cryptic failures.
Why This Approach: Mirrors existing isNativeAvailable() for symmetric availability detection across both engines.
Risk Assessment: MINIMAL - Pure defensive error handling with helpful guidance.

Critical Concerns (if any)

None. The implementation is sound and follows established patterns.

Final Recommendation

  • Rating: ⭐⭐⭐☆☆ (3/5)
  • Action: APPROVE WITH CONDITIONS
  • Reasoning: Good defensive error handling that solves a real UX problem. Implementation follows existing patterns and is technically sound. However, missing test coverage for the new function prevents a higher rating. The functionality is straightforward enough that this isn't a blocking issue.

Condition for approval: Add unit tests for isWasmAvailable() in a follow-up PR to ensure the function correctly detects grammar availability under different scenarios.

@greptile-apps
Copy link

greptile-apps bot commented Mar 4, 2026

Greptile Summary

Added isWasmAvailable() to mirror isNativeAvailable() for symmetric engine handling. All 4 benchmark scripts now gracefully skip WASM when grammars are missing instead of crashing.

Key improvements:

  • scripts/query-benchmark.js now correctly reuses benchmark targets across both engines (addresses previous review feedback)
  • Consistent error handling: scripts exit cleanly with helpful instructions when neither engine is available
  • Comprehensive unit tests verify isWasmAvailable() checks only required grammars (JS, TS, TSX)

The implementation is clean, well-tested, and maintains consistency across all benchmark scripts.

Confidence Score: 5/5

  • This PR is safe to merge with no issues found
  • The changes are well-implemented, address previous review feedback correctly, include comprehensive unit tests, and follow consistent patterns across all affected files
  • No files require special attention

Important Files Changed

Filename Overview
src/parser.js Added isWasmAvailable() function to check required WASM grammar files exist
tests/unit/parser.test.js New unit tests for isWasmAvailable() with comprehensive coverage
scripts/query-benchmark.js Added WASM check and fixed target reuse issue from previous review

Last reviewed commit: 1ac5d28

Comment on lines 175 to 191
const targets = selectTargets();
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
wasm = benchmarkQueries(targets);
} else {
console.error('WASM grammars not built — skipping WASM benchmark');
}

let native = null;
if (isNativeAvailable()) {
if (hasNative) {
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
await buildGraph(root, { engine: 'native', incremental: false });

const targets = selectTargets();
if (!hasWasm) {
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
}
native = benchmarkQueries(targets);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

targets selected separately for each engine breaks benchmark validity. When both engines are available, WASM benchmarks with one set of targets (hub/mid/leaf from WASM graph), then native benchmarks with different targets (from native graph). This makes the comparison invalid. Should select targets once before the engine blocks and reuse for both.

Suggested change
const targets = selectTargets();
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
wasm = benchmarkQueries(targets);
} else {
console.error('WASM grammars not built — skipping WASM benchmark');
}
let native = null;
if (isNativeAvailable()) {
if (hasNative) {
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
await buildGraph(root, { engine: 'native', incremental: false });
const targets = selectTargets();
if (!hasWasm) {
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
}
native = benchmarkQueries(targets);
const targets = selectTargets();
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
wasm = benchmarkQueries(targets);
} else {
console.error('WASM grammars not built — skipping WASM benchmark');
}
let native = null;
if (hasNative) {
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
await buildGraph(root, { engine: 'native', incremental: false });
native = benchmarkQueries(targets);

Comment on lines +170 to 191
let wasm = null;
if (hasWasm) {
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
await buildGraph(root, { engine: 'wasm', incremental: false });

const wasm = benchmarkQueries(targets);
const targets = selectTargets();
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
wasm = benchmarkQueries(targets);
} else {
console.error('WASM grammars not built — skipping WASM benchmark');
}

let native = null;
if (isNativeAvailable()) {
if (hasNative) {
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
await buildGraph(root, { engine: 'native', incremental: false });

const targets = selectTargets();
if (!hasWasm) {
console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
}
native = benchmarkQueries(targets);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when both engines are available, targets is selected separately inside each block (line 175 and 187), causing WASM and native to benchmark different functions. For a valid comparison, both should use the same targets.

Fix: move let targets = null; outside the blocks, select once in WASM block, reuse in native:

let wasm = null;
let targets = null;
if (hasWasm) {
	if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
	await buildGraph(root, { engine: 'wasm', incremental: false });
	targets = selectTargets();
	console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
	wasm = benchmarkQueries(targets);
} else {
	console.error('WASM grammars not built — skipping WASM benchmark');
}

let native = null;
if (hasNative) {
	if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
	await buildGraph(root, { engine: 'native', incremental: false });
	if (!targets) {
		targets = selectTargets();
		console.error(`Targets: hub=${targets.hub}, mid=${targets.mid}, leaf=${targets.leaf}`);
	}
	native = benchmarkQueries(targets);
}

…le tests

- query-benchmark.js: select targets once from first built graph, reuse
  for both engines to ensure valid comparisons
- Add unit tests for isWasmAvailable() covering all branches: all present,
  some missing, all missing, and verifying only required grammars are checked
@claude
Copy link

claude bot commented Mar 4, 2026

Claude finished @carlos-alm's task —— View job


PR Review: Benchmark WASM Graceful Handling

Review Checklist

  • Phase 1: Root cause analysis
  • Phase 2: Configuration change detection
  • Phase 3: Test integrity verification
  • Phase 4: Code quality review
  • Final assessment and rating

Phase 1: Root Cause Analysis

Problem Being Solved: Benchmark scripts crash with unclear errors when WASM grammar files are missing. This happens when users run benchmarks without first running npm install (which builds WASM grammars via the prepare script).

Why This Approach: The PR adds isWasmAvailable() to check for required WASM grammar files before attempting WASM engine benchmarks. This mirrors the existing isNativeAvailable() pattern for symmetric engine availability handling.

Risk Assessment: LOW - This is defensive error handling that prevents crashes and provides helpful error messages. The change is minimal and follows existing patterns.

Phase 2: Configuration Changes

No concerning configuration changes detected

  • No linting rules modified
  • No test timeouts or coverage thresholds changed
  • No build configuration alterations
  • No TypeScript strict mode flags disabled

Phase 3: Test Integrity

Good test coverage for new functionality

  • Added comprehensive unit tests for isWasmAvailable() in tests/unit/parser.test.js (new file)
  • Tests cover all branches: all grammars present, some missing, all missing
  • Verifies only required grammars (JS, TS, TSX) are checked
  • Tests are well-structured with proper mocking of filesystem operations
  • Previous reviews mentioned missing test coverage - this has been addressed

Phase 4: Code Quality

✅ Implementation Quality - Good

  • isWasmAvailable() at line 110-114 correctly filters for required: true entries (JS, TS, TSX)
  • Follows existing isNativeAvailable() pattern for consistency
  • Uses fs.existsSync() which is appropriate for synchronous availability checking
  • Proper error messages in all benchmark scripts with actionable guidance

✅ Error Handling - Excellent

  • All 4 benchmark scripts now have consistent error handling:
    • benchmark.js:169-173: Clean exit with helpful message
    • query-benchmark.js:164-168: Same pattern
    • incremental-benchmark.js:163-167: Same pattern
    • token-benchmark.js:295-302: Consistent with others
  • Error messages provide clear next steps: "Run npm run build:wasm..."

✅ Code Consistency - Good

  • Mirrors existing isNativeAvailable() pattern perfectly
  • All scripts use identical availability check pattern
  • Export added to index.js:126 maintains API consistency

✅ Logic Bug Fixed

  • Previous Greptile review identified a bug in query-benchmark.js where targets were selected separately for each engine
  • This has been properly fixed: targets are now selected once from the first built graph and reused for both engines (lines 171-193)
  • Ensures valid benchmark comparisons when both engines are available

Critical Concerns

None - This is solid defensive programming that follows established patterns and addresses previous review feedback.

Final Assessment

Root Cause Analysis

Problem Being Solved: Prevents benchmark crashes when WASM grammars are unavailable, providing clear error messages instead of cryptic failures.
Why This Approach: Mirrors existing isNativeAvailable() for symmetric availability detection across both engines.
Risk Assessment: MINIMAL - Pure defensive error handling with helpful guidance.

Critical Concerns (if any)

None. The implementation is sound, follows existing patterns, and addresses all previous review concerns.

Final Recommendation

  • Rating: ⭐⭐⭐⭐☆ (4/5)
  • Action: APPROVE
  • Reasoning: Excellent defensive error handling that solves a real UX problem. Implementation follows existing patterns, is technically sound, has comprehensive test coverage, and fixes the logic bug identified in previous reviews. The only minor point preventing 5 stars is that this is infrastructure improvement rather than core functionality enhancement.

@carlos-alm
Copy link
Contributor Author

Addressed review feedback in 1ac5d28:

Greptile — targets selected separately for each engine (query-benchmark.js):
Fixed. Targets are now selected once from whichever engine builds first, then reused for both. The native block only calls selectTargets() if WASM was unavailable (so no targets exist yet).

Claude — missing test coverage for isWasmAvailable():
Added tests/unit/parser.test.js with 6 tests covering: returns boolean, all grammars present, some missing, all missing, only checks required grammars (JS/TS/TSX), and verifies paths point to grammars/ directory.

@greptileai

@carlos-alm carlos-alm merged commit 189cefb into main Mar 4, 2026
16 checks passed
@carlos-alm carlos-alm deleted the fix/benchmark-wasm-graceful branch March 4, 2026 03:11
@github-actions github-actions bot locked and limited conversation to collaborators Mar 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant