Forge
csharp551a3611
1using Microsoft.Build.Locator;
2using Microsoft.CodeAnalysis;
3using Microsoft.CodeAnalysis.MSBuild;
4using RoslynMcp.ServiceLayer;
5
6namespace RoslynMcp.Tests;
7
8/// <summary>
9/// Интеграция с реальным solution (CascadeIDE): MVVM Toolkit генерирует свойства; без MSBuildLocator + GetSourceGeneratedDocuments
10/// <see cref="GetDiagnostics"/> мог бы выдавать ложный CS1061.
11/// </summary>
12public sealed class GetDiagnosticsSourceGeneratorsIntegrationTests
13{
14 static GetDiagnosticsSourceGeneratorsIntegrationTests()
15 {
16 // Один раз на процесс: после загрузки сборок MSBuild повторный RegisterDefaults падает.
17 MSBuildLocator.RegisterDefaults();
18 }
19
20 private static string? TryGetCascadeIdePaths()
21 {
22 // RoslynMcp.Tests/bin/Debug/net10.0 → …/open (родитель roslyn-mcp)
23 var openDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ".."));
24 var sln = Path.Combine(openDir, "cascade-ide", "CascadeIDE.sln");
25 var file = Path.Combine(openDir, "cascade-ide", "Services", "CockpitSurfaceSnapshotBuilder.cs");
26 return File.Exists(sln) && File.Exists(file) ? $"{sln}|{file}" : null;
27 }
28
29 [Fact]
30 public async Task CascadeIDE_semantic_model_has_no_cs1061_for_mvvm_generated_property_access()
31 {
32 var paths = TryGetCascadeIdePaths();
33 if (paths is null)
34 return;
35
36 var parts = paths.Split('|');
37 var solutionPath = parts[0];
38 var filePath = parts[1];
39
40 var workspace = MSBuildWorkspace.Create(RoslynMcpWorkspaceProperties.MsBuild);
41 try
42 {
43 var solution = await workspace.OpenSolutionAsync(solutionPath, cancellationToken: CancellationToken.None).ConfigureAwait(true);
44 var document = solution.Projects.SelectMany(p => p.Documents)
45 .First(d => string.Equals(Normalize(d.FilePath), Normalize(filePath), StringComparison.OrdinalIgnoreCase));
46 var projectId = document.Project.Id;
47 await workspace.CurrentSolution.GetProject(projectId)!.GetSourceGeneratedDocumentsAsync(CancellationToken.None).ConfigureAwait(true);
48 document = workspace.CurrentSolution.GetDocument(document.Id)
49 ?? throw new InvalidOperationException("Document missing after source generation.");
50 var semantic = await document.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(true);
51 Assert.NotNull(semantic);
52 var diags = semantic.GetDiagnostics();
53 Assert.DoesNotContain(diags, d => d.Id == "CS1061" && d.GetMessage().Contains("UiMode", StringComparison.Ordinal));
54 }
55 finally
56 {
57 workspace.Dispose();
58 }
59
60 static string Normalize(string? p) =>
61 string.IsNullOrEmpty(p) ? "" : Path.GetFullPath(p);
62 }
63
64 [Fact]
65 public async Task CascadeIDE_GetDiagnostics_text_matches_semantic_model_no_false_cs1061()
66 {
67 var paths = TryGetCascadeIdePaths();
68 if (paths is null)
69 return;
70
71 var parts = paths.Split('|');
72 var solutionPath = parts[0];
73 var filePath = parts[1];
74
75 var text = await GetDiagnostics.GetDiagnosticsAsync(solutionPath, filePath, CancellationToken.None).ConfigureAwait(true);
76
77 Assert.False(text.StartsWith("Error:", StringComparison.OrdinalIgnoreCase), text);
78 Assert.DoesNotContain("CS1061", text, StringComparison.Ordinal);
79 }
80}
81
View only · write via MCP/CIDE