-
Notifications
You must be signed in to change notification settings - Fork 103
feat: Add clangd integration tests and documentation #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
a2bd3c5
feat: Add clangd integration tests and documentation
Shamazo a89ceed
Add clangd hover tests
Shamazo 977ecf8
Add clangd references test
Shamazo 2e95b27
Open file in FindReferences
Shamazo 23d89bf
Explicitly pass --compile-commands-dir to clangd in integration tests
Shamazo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| --- | ||
|
|
||
| Symbol: TestClass | ||
| /TEST_OUTPUT/workspace/src/consumer.cpp | ||
| Range: L6:C1 - L9:C2 | ||
|
|
||
| 6|class TestClass { | ||
| 7| public: | ||
| 8| void method() {} | ||
| 9|}; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
|
|
||
| Symbol: TEST_CONSTANT | ||
| /TEST_OUTPUT/workspace/src/helper.cpp | ||
| Range: L4:C1 - L4:C29 | ||
|
|
||
| 4|const int TEST_CONSTANT = 42; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| --- | ||
|
|
||
| Symbol: foo_bar | ||
| /TEST_OUTPUT/workspace/src/main.cpp | ||
| Range: L5:C1 - L8:C2 | ||
|
|
||
| 5|void foo_bar() { | ||
| 6| std::cout << "Hello, World!" << std::endl; | ||
| 7| return; | ||
| 8|} | ||
|
|
8 changes: 8 additions & 0 deletions
8
integrationtests/snapshots/clangd/definition/helperFunction.snap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
|
|
||
| Symbol: helperFunction | ||
| /TEST_OUTPUT/workspace/clangd/include/helper.hpp | ||
| Range: L1:C1 - L1:C22 | ||
|
|
||
| 1|void helperFunction(); | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| --- | ||
|
|
||
| Symbol: method | ||
| /TEST_OUTPUT/workspace/src/consumer.cpp | ||
| Range: L6:C1 - L9:C2 | ||
|
|
||
| 6|class TestClass { | ||
| 7| public: | ||
| 8| void method() {} | ||
| 9|}; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
|
|
||
| Symbol: TestStruct | ||
| /TEST_OUTPUT/workspace/src/types.cpp | ||
| Range: L6:C1 - L8:C2 | ||
|
|
||
| 6|struct TestStruct { | ||
| 7| int value; | ||
| 8|}; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
|
|
||
| Symbol: TestType | ||
| /TEST_OUTPUT/workspace/src/types.cpp | ||
| Range: L10:C1 - L10:C21 | ||
|
|
||
| 10|using TestType = int; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
|
|
||
| Symbol: TEST_VARIABLE | ||
| /TEST_OUTPUT/workspace/src/helper.cpp | ||
| Range: L5:C1 - L5:C24 | ||
|
|
||
| 5|int TEST_VARIABLE = 100; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /TEST_OUTPUT/workspace/src/clean.cpp |
10 changes: 10 additions & 0 deletions
10
integrationtests/snapshots/clangd/diagnostics/unreachable.snap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /TEST_OUTPUT/workspace/src/main.cpp | ||
| Diagnostics in File: 1 | ||
| WARNING at L15:C38: Code will never be executed (Source: clang, Code: -Wunreachable-code) | ||
|
|
||
| 10|int main() { | ||
| ... | ||
| 13| | ||
| 14| // Intentional error: unreachable code | ||
| 15| std::cout << "This is unreachable" << std::endl; | ||
| 16|} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| \ | ||
| # Clangd Integration Tests | ||
|
|
||
| This directory contains integration tests for `clangd`, the C/C++ language server. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Before running these tests, you must generate the `compile_commands.json` file in the `integrationtests/workspaces/clangd` directory. This can typically be done by navigating to that directory and running a tool like `bear` with your build command (e.g., `bear -- make`). | ||
|
|
||
| The GitHub Actions workflow for these tests uses the following command from the root of the repository: | ||
| ```bash | ||
| cd integrationtests/workspaces/clangd | ||
| bear -- make | ||
| cd ../../../.. | ||
| ``` | ||
|
|
||
| ## Clangd Version | ||
|
|
||
| These tests are currently run against **clangd version 16**. While they may pass with other versions of clangd, compatibility is not guaranteed. |
176 changes: 176 additions & 0 deletions
176
integrationtests/tests/clangd/definition/definition_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| package definition_test | ||
|
|
||
| import ( | ||
| "context" | ||
| "path/filepath" | ||
| "strings" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/isaacphi/mcp-language-server/integrationtests/tests/clangd/internal" | ||
| "github.com/isaacphi/mcp-language-server/integrationtests/tests/common" | ||
| "github.com/isaacphi/mcp-language-server/internal/tools" | ||
| ) | ||
|
|
||
| // TestReadDefinition tests the ReadDefinition tool with various C++ type definitions | ||
| func TestReadDefinition(t *testing.T) { | ||
| // Helper function to open all files and wait for indexing | ||
| openAllFilesAndWait := func(suite *common.TestSuite, ctx context.Context) { | ||
| // Open all files to ensure clangd indexes everything | ||
| filesToOpen := []string{ | ||
| "src/main.cpp", | ||
| "src/types.cpp", | ||
| "src/helper.cpp", | ||
| "src/consumer.cpp", | ||
| "src/another_consumer.cpp", | ||
| "src/clean.cpp", | ||
| } | ||
|
|
||
| for _, file := range filesToOpen { | ||
| filePath := filepath.Join(suite.WorkspaceDir, file) | ||
| err := suite.Client.OpenFile(ctx, filePath) | ||
| if err != nil { | ||
| // Don't fail the test, some files might not exist in certain tests | ||
| t.Logf("Note: Failed to open %s: %v", file, err) | ||
| } | ||
| } | ||
| // Wait for indexing to complete. clangd won't index files until they are opened. | ||
| time.Sleep(10 * time.Second) | ||
| } | ||
|
|
||
| suite := internal.GetTestSuite(t) | ||
|
|
||
| ctx, cancel := context.WithTimeout(suite.Context, 10*time.Second) | ||
| defer cancel() | ||
|
|
||
| // Open all files and wait for clangd to index them | ||
| openAllFilesAndWait(suite, ctx) | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| symbolName string | ||
| expectedText string | ||
| snapshotName string | ||
| }{ | ||
| { | ||
| name: "Function", | ||
| symbolName: "foo_bar", | ||
| expectedText: "void foo_bar()", | ||
| snapshotName: "foobar", | ||
| }, | ||
| { | ||
| name: "Class", | ||
| symbolName: "TestClass", | ||
| expectedText: "class TestClass", | ||
| snapshotName: "class", | ||
| }, | ||
| { | ||
| name: "Method", | ||
| symbolName: "method", | ||
| expectedText: "void method()", | ||
| snapshotName: "method", | ||
| }, | ||
| { | ||
| name: "Struct", | ||
| symbolName: "TestStruct", | ||
| expectedText: "struct TestStruct", | ||
| snapshotName: "struct", | ||
| }, | ||
| { | ||
| name: "Type", | ||
| symbolName: "TestType", | ||
| expectedText: "using TestType", | ||
| snapshotName: "type", | ||
| }, | ||
| { | ||
| name: "Constant", | ||
| symbolName: "TEST_CONSTANT", | ||
| expectedText: "const int TEST_CONSTANT", | ||
| snapshotName: "constant", | ||
| }, | ||
| { | ||
| name: "Variable", | ||
| symbolName: "TEST_VARIABLE", | ||
| expectedText: "int TEST_VARIABLE", | ||
| snapshotName: "variable", | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range tests { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| // Call the ReadDefinition tool | ||
| result, err := tools.ReadDefinition(ctx, suite.Client, tc.symbolName) | ||
| if err != nil { | ||
| t.Fatalf("Failed to read definition: %v", err) | ||
| } | ||
|
|
||
| // Check that the result contains relevant information | ||
| if !strings.Contains(result, tc.expectedText) { | ||
| t.Errorf("Definition does not contain expected text: %s", tc.expectedText) | ||
| } | ||
|
|
||
| // Use snapshot testing to verify exact output | ||
| common.SnapshotTest(t, "clangd", "definition", tc.snapshotName, result) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestReadDefinitionInAnotherFile(t *testing.T) { | ||
| // Helper function to open all files and wait for indexing | ||
| openAllFilesAndWait := func(suite *common.TestSuite, ctx context.Context) { | ||
| // Open all files to ensure clangd indexes everything | ||
| filesToOpen := []string{ | ||
| "src/main.cpp", | ||
| } | ||
|
|
||
| for _, file := range filesToOpen { | ||
| filePath := filepath.Join(suite.WorkspaceDir, file) | ||
| err := suite.Client.OpenFile(ctx, filePath) | ||
| if err != nil { | ||
| // Don't fail the test, some files might not exist in certain tests | ||
| t.Logf("Note: Failed to open %s: %v", file, err) | ||
| } | ||
| } | ||
| time.Sleep(5 * time.Second) | ||
| } | ||
|
|
||
| suite := internal.GetTestSuite(t) | ||
|
|
||
| ctx, cancel := context.WithTimeout(suite.Context, 10*time.Second) | ||
| defer cancel() | ||
|
|
||
| // Open all files and wait for clangd to index them | ||
| openAllFilesAndWait(suite, ctx) | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| symbolName string | ||
| expectedText string | ||
| snapshotName string | ||
| }{ | ||
| { | ||
| name: "Function", | ||
| symbolName: "helperFunction", | ||
| expectedText: "void helperFunction()", | ||
| snapshotName: "helperFunction", | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range tests { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| // Call the ReadDefinition tool | ||
| result, err := tools.ReadDefinition(ctx, suite.Client, tc.symbolName) | ||
| if err != nil { | ||
| t.Fatalf("Failed to read definition: %v", err) | ||
| } | ||
|
|
||
| // Check that the result contains relevant information | ||
| if !strings.Contains(result, tc.expectedText) { | ||
| t.Errorf("Definition does not contain expected text: %s", tc.expectedText) | ||
| } | ||
|
|
||
| // Use snapshot testing to verify exact output | ||
| common.SnapshotTest(t, "clangd", "definition", tc.snapshotName, result) | ||
| }) | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it work without this here? Opening all files was a hack I added for the typescript language server only but other language servers don't need to open every file, only the project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the tests were failing without it because clangd does not start indexing until the first time a file is opened (clangd discussion). Then it will eventually do background indexing for other files defined in compile_commands.json. I actually had a bug with how I was invoking clangd previously, which I have now fixed. We only need to open a single file for clangd to index all the files when we correctly pass it the compile-commands-dir.
Right now, the most robust way to do the tests is to open any file and wait, but I think long-term, using LSP progress notifications would be better. However, this requires larger changes to the code base. I am not an LSP expert, so I used Gemini to get a sense of how that may work:
https://g.co/gemini/share/3648aef3aa59