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"