From 163f7d1793e4d014858112329eac3b4f02757cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saulius=20Menkevi=C4=8Dius?= Date: Sun, 6 Sep 2026 19:37:06 +0300 Subject: [PATCH] Add regression test for Central Package Management project loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `projectWithCentralPackageManagement` fixture (a CPM-managed project plus a dependent project consuming it transitively) and a test in InitializationTests.fs that opens both projects and asserts no msbuildWorkspace [Failure] diagnostic is raised and hover/code intelligence works. Ref: https://github.com/razzmatazz/csharp-language-server/issues/318 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-sonnet-5) Co-Authored-By: eca-agent --- .../DependentProject/Consumer.cs | 8 +++ .../DependentProject/DependentProject.csproj | 12 ++++ .../Directory.Packages.props | 8 +++ .../Project/Class.cs | 9 +++ .../Project/Project.csproj | 11 +++ .../projectWithCentralPackageManagement.sln | 28 ++++++++ .../InitializationTests.fs | 71 +++++++++++++++++++ tests/CSharpLanguageServer.Tests/Tooling.fs | 1 + 8 files changed, 148 insertions(+) create mode 100644 tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/Consumer.cs create mode 100644 tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/DependentProject.csproj create mode 100644 tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Directory.Packages.props create mode 100644 tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Class.cs create mode 100644 tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Project.csproj create mode 100644 tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/projectWithCentralPackageManagement.sln diff --git a/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/Consumer.cs b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/Consumer.cs new file mode 100644 index 00000000..b3cc49a6 --- /dev/null +++ b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/Consumer.cs @@ -0,0 +1,8 @@ +public class Consumer +{ + public string Convert(object o) + { + var myClass = new MyClass(); + return myClass.ToJson(o); + } +} diff --git a/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/DependentProject.csproj b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/DependentProject.csproj new file mode 100644 index 00000000..6f61d3cc --- /dev/null +++ b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/DependentProject/DependentProject.csproj @@ -0,0 +1,12 @@ + + + net10.0 + + + + + + diff --git a/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Directory.Packages.props b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Directory.Packages.props new file mode 100644 index 00000000..e7741c6e --- /dev/null +++ b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Directory.Packages.props @@ -0,0 +1,8 @@ + + + true + + + + + diff --git a/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Class.cs b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Class.cs new file mode 100644 index 00000000..0f3a63e6 --- /dev/null +++ b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Class.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +public class MyClass +{ + public string ToJson(object o) + { + return JsonConvert.SerializeObject(o); + } +} diff --git a/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Project.csproj b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Project.csproj new file mode 100644 index 00000000..60726f81 --- /dev/null +++ b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/Project/Project.csproj @@ -0,0 +1,11 @@ + + + net10.0 + + + + + + diff --git a/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/projectWithCentralPackageManagement.sln b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/projectWithCentralPackageManagement.sln new file mode 100644 index 00000000..1da8f799 --- /dev/null +++ b/tests/CSharpLanguageServer.Tests/Fixtures/projectWithCentralPackageManagement/projectWithCentralPackageManagement.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project", "Project\Project.csproj", "{59DABAFE-31BE-47BB-B203-D16E9B90A1DE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependentProject", "DependentProject\DependentProject.csproj", "{6A1B2C3D-4E5F-47BB-B203-D16E9B90A1DF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {59DABAFE-31BE-47BB-B203-D16E9B90A1DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59DABAFE-31BE-47BB-B203-D16E9B90A1DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59DABAFE-31BE-47BB-B203-D16E9B90A1DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59DABAFE-31BE-47BB-B203-D16E9B90A1DE}.Release|Any CPU.Build.0 = Release|Any CPU + {6A1B2C3D-4E5F-47BB-B203-D16E9B90A1DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A1B2C3D-4E5F-47BB-B203-D16E9B90A1DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A1B2C3D-4E5F-47BB-B203-D16E9B90A1DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A1B2C3D-4E5F-47BB-B203-D16E9B90A1DF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tests/CSharpLanguageServer.Tests/InitializationTests.fs b/tests/CSharpLanguageServer.Tests/InitializationTests.fs index 713d3e52..2edde358 100644 --- a/tests/CSharpLanguageServer.Tests/InitializationTests.fs +++ b/tests/CSharpLanguageServer.Tests/InitializationTests.fs @@ -1,6 +1,7 @@ module CSharpLanguageServer.Tests.InitializationTests open System +open System.IO open NUnit.Framework @@ -269,6 +270,76 @@ let testPlatformSpecificTfmDoesNotBreakSiblingProjects () = Assert.That(errors.Length, Is.EqualTo(0), $"expected no errors on Project/Class.cs, got: {errorsStr}") | _ -> failwith "U2.C1 (full report) was expected" +/// Regression test for https://github.com/razzmatazz/csharp-language-server/issues/318 — +/// projects using NuGet Central Package Management (CPM) via `Directory.Packages.props` +/// (a `` with no `Version` attribute) reportedly fail to load in the +/// Roslyn MSBuildWorkspace, with a `[Failure]` msbuildWorkspace diagnostic and zero code +/// intelligence for the affected project. +[] +let testCentralPackageManagementProjectLoadsWithoutMsbuildWorkspaceFailure () = + // Pre-build both projects so that obj/project.assets.json exists for each (restored + // against the centrally-managed package version) before MSBuildWorkspace opens the + // solution. DependentProject has no direct PackageReference of its own — it only + // pulls in Newtonsoft.Json transitively via the ProjectReference to Project, + // mirroring the "DependentProject" symptom reported in the issue. + let prebuildProjects (solutionDir: string) = + for projectName in [ "Project"; "DependentProject" ] do + let projectDir = Path.Combine(solutionDir, projectName) + let exitCode, stdout, stderr = runDotnetBuild projectDir + + if exitCode <> 0 then + failwithf + "Pre-build of CPM fixture project '%s' failed (exit %d):\nstdout:\n%s\nstderr:\n%s" + projectName + exitCode + stdout + stderr + + use client = + activateFixtureExt "projectWithCentralPackageManagement" defaultClientProfile prebuildProjects id + + use classFile = client.Open("Project/Class.cs") + use consumerFile = client.Open("DependentProject/Consumer.cs") + + // The bug in #318 manifests as a `[Failure]` msbuildWorkspace.Diagnostics entry + // logged (via window/logMessage) while the solution is being loaded — either for the + // CPM-managed project itself, or for a project that depends on it. + let hasMsbuildWorkspaceFailure = + client.ServerMessageLogContains(fun m -> m.Contains("msbuildWorkspace.Diagnostics") && m.Contains("[Failure]")) + + Assert.That( + hasMsbuildWorkspaceFailure, + Is.False, + "Expected the CPM-managed project (and its dependent project) to load without a msbuildWorkspace [Failure] diagnostic" + ) + + // Sanity-check that code intelligence actually works for the CPM-managed package + // reference: hovering over `JsonConvert` should resolve to the Newtonsoft.Json type. + let hoverParams: HoverParams = + { TextDocument = { Uri = classFile.Uri } + Position = { Line = 6u; Character = 15u } // `JsonConvert` in `JsonConvert.SerializeObject(o)` + WorkDoneToken = None } + + let hover: Hover option = client.Request("textDocument/hover", hoverParams) + + match hover with + | Some { Contents = U3.C1 c } -> Assert.That(c.Value, Does.Contain("JsonConvert")) + | _ -> failwith "Expected hover to resolve JsonConvert from the CPM-managed Newtonsoft.Json reference" + + // Sanity-check code intelligence for the dependent project too: hovering over + // `MyClass` (defined in the CPM-managed project) should resolve. + let dependentHoverParams: HoverParams = + { TextDocument = { Uri = consumerFile.Uri } + Position = { Line = 4u; Character = 28u } // `MyClass` in `new MyClass()` + WorkDoneToken = None } + + let dependentHover: Hover option = + client.Request("textDocument/hover", dependentHoverParams) + + match dependentHover with + | Some { Contents = U3.C1 c } -> Assert.That(c.Value, Does.Contain("MyClass")) + | _ -> failwith "Expected hover to resolve MyClass from the ProjectReference to the CPM-managed project" + [] let testMultiTargetWorkspace () = let clientWorkspaceCaps: WorkspaceClientCapabilities = diff --git a/tests/CSharpLanguageServer.Tests/Tooling.fs b/tests/CSharpLanguageServer.Tests/Tooling.fs index 2fad6851..c5cf7e8e 100644 --- a/tests/CSharpLanguageServer.Tests/Tooling.fs +++ b/tests/CSharpLanguageServer.Tests/Tooling.fs @@ -555,6 +555,7 @@ let prepareTempTestDirFrom (sourceTestDir: DirectoryInfo) : string = let fileFilter (file: FileInfo) = file.Name = ".editorconfig" || file.Name = "global.json" + || file.Name = "Directory.Packages.props" || file.Extension = ".cs" || file.Extension = ".csproj" || file.Extension = ".sln"