Files
clice/tests/integration/test_pch.py
ykiko 21a969af27 feat: integrate PCH into MasterServer build drain (#381)
## Summary
- Add `ensure_pch()` helper to MasterServer that builds/reuses
precompiled headers via stateless workers, with preamble hash-based
staleness detection (xxh3_64bits)
- Fix `BuildPCHParams` to carry `preamble_bound` so the stateless worker
truncates content at the preamble boundary (fixes redefinition errors
when PCH included full file)
- Wire PCH into both `run_build_drain` (stateful compile path) and
`forward_stateless` (completion/signatureHelp path)
- Add PCH state cleanup on `didClose` and hash invalidation on `didSave`

## Test plan
- [x] 398 unit tests pass (including 6 new PCH tests: PreambleHash x3,
PCHWorker x2, BuildPCHRequest assertion)
- [x] 5 new integration tests pass (`test_pch.py`: diagnostics on open,
body edit recompile, no-include file, hover with PCH, completion with
PCH)
- [x] 21 existing integration tests pass unchanged
- [x] Build succeeds with 0 errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Precompiled header (PCH) caching to speed compilations and reduce edit
latency
* Automatic attachment of cached PCH to compile requests, improving
hover and completion responsiveness
* Module-aware completions expanded to include available module
artifacts from other files

* **Bug Fixes**
* PCH cache cleared on file close; saving now triggers broader PCH
invalidation to prevent stale PCH use

* **Tests**
* Added unit and integration tests exercising PCH build, reuse, and
editor interactions
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 18:33:33 +08:00

105 lines
4.1 KiB
Python

"""Integration tests for PCH (precompiled header) functionality in MasterServer."""
import asyncio
import pytest
from lsprotocol.types import (
CompletionParams,
DidChangeTextDocumentParams,
DidCloseTextDocumentParams,
HoverParams,
Position,
TextDocumentContentChangeWholeDocument,
TextDocumentIdentifier,
VersionedTextDocumentIdentifier,
)
def _doc(uri: str) -> TextDocumentIdentifier:
return TextDocumentIdentifier(uri=uri)
@pytest.mark.workspace("pch_test")
async def test_pch_diagnostics_on_open(client, workspace):
"""Opening a file with #include should trigger PCH build and return clean diagnostics."""
uri, _ = await client.open_and_wait(workspace / "main.cpp")
assert uri in client.diagnostics
# main.cpp is well-formed, so diagnostics list should be empty (no errors).
diags = client.diagnostics[uri]
assert len(diags) == 0, f"Expected no diagnostics, got: {diags}"
client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))
@pytest.mark.workspace("pch_test")
async def test_pch_body_edit_triggers_recompile(client, workspace):
"""Editing only the body (not the preamble) should trigger recompilation."""
uri, content = await client.open_and_wait(workspace / "main.cpp")
# Edit only the function body — preamble (#include "common.h") unchanged.
new_content = content.replace("return result;", "return result + 1;")
event = client.wait_for_diagnostics(uri)
client.text_document_did_change(
DidChangeTextDocumentParams(
text_document=VersionedTextDocumentIdentifier(uri=uri, version=1),
content_changes=[TextDocumentContentChangeWholeDocument(text=new_content)],
)
)
# The key assertion: recompilation completes (diagnostics event fires).
await asyncio.wait_for(event.wait(), timeout=30.0)
assert uri in client.diagnostics
client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))
@pytest.mark.workspace("pch_test")
async def test_no_pch_for_no_includes(client, workspace):
"""A file with no #include directives should compile without PCH."""
uri, _ = await client.open_and_wait(workspace / "no_includes.cpp")
assert uri in client.diagnostics
diags = client.diagnostics[uri]
assert len(diags) == 0, f"Expected no diagnostics, got: {diags}"
client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))
@pytest.mark.workspace("pch_test")
async def test_hover_on_local_symbol(client, workspace):
"""Hover on a locally defined symbol should work when PCH is active."""
uri, _ = await client.open_and_wait(workspace / "main.cpp")
# Hover over "add" on line 2 (0-indexed): "int add(int a, int b) {"
result = await client.text_document_hover_async(
HoverParams(text_document=_doc(uri), position=Position(line=2, character=4))
)
assert result is not None
client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))
@pytest.mark.workspace("pch_test")
async def test_completion_with_pch(client, workspace):
"""Completion should see symbols from PCH headers."""
uri, content = await client.open_and_wait(workspace / "main.cpp")
# Add a line that starts typing "Poi" to trigger completion for Point.
new_content = content + "\nPoi"
lines = new_content.split("\n")
last_line = len(lines) - 1
event = client.wait_for_diagnostics(uri)
client.text_document_did_change(
DidChangeTextDocumentParams(
text_document=VersionedTextDocumentIdentifier(uri=uri, version=1),
content_changes=[TextDocumentContentChangeWholeDocument(text=new_content)],
)
)
# Brief wait for the change to be processed.
await asyncio.sleep(1.0)
result = await client.text_document_completion_async(
CompletionParams(
text_document=_doc(uri),
position=Position(line=last_line, character=3),
)
)
# Completion should return results.
assert result is not None
client.text_document_did_close(DidCloseTextDocumentParams(text_document=_doc(uri)))