Forge
csharp4405de34
1using System.Collections.Immutable;
2using Basic.Reference.Assemblies;
3using Microsoft.CodeAnalysis;
4using Microsoft.CodeAnalysis.CSharp;
5using Microsoft.CodeAnalysis.Diagnostics;
6using Xunit;
7
8namespace CascadeIDE.ArchitectureAnalyzers.Tests;
9
10public sealed class SkiaPipelineArchitectureAnalyzerTests
11{
12 private static async Task<ImmutableArray<Diagnostic>> RunAnalyzerAsync(params (string Path, string Text)[] files)
13 {
14 var trees = new List<SyntaxTree>(files.Length);
15 foreach (var (path, text) in files)
16 trees.Add(CSharpSyntaxTree.ParseText(text, path: path));
17
18 var compilation = CSharpCompilation.Create(
19 "TestAssembly",
20 trees,
21 Net80.References.All,
22 new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
23
24 var analyzer = new SkiaPipelineArchitectureAnalyzer();
25 var withAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
26 return await withAnalyzers.GetAnalyzerDiagnosticsAsync();
27 }
28
29 [Fact]
30 public async Task CASCOPE007_AvaloniaUsing_InSkiaInstruments_Reports()
31 {
32 var diags = await RunAnalyzerAsync((
33 @"D:\repo\Services\SkiaInstruments\Bad.cs",
34 """
35 using Avalonia.Controls;
36 namespace CascadeIDE.Services.SkiaInstruments;
37 public static class C { }
38 """));
39
40 var d = Assert.Single(diags);
41 Assert.Equal(SkiaPipelineArchitectureAnalyzer.SkiaBoundaryId, d.Id);
42 }
43
44 [Fact]
45 public async Task CASCOPE008_CodeNavigationMapCompositor_MissingDeclutter_Reports()
46 {
47 var diags = await RunAnalyzerAsync((
48 @"D:\repo\Services\Navigation\CodeNavigationMapCompositor.cs",
49 """
50 namespace CascadeIDE.Services.Navigation;
51 public sealed class CodeNavigationMapCompositor
52 {
53 private readonly Intent _intentStage = new();
54 private readonly Layout _layoutStage = new();
55 public void Compose()
56 {
57 _intentStage.Resolve();
58 _layoutStage.Layout();
59 }
60 }
61
62 public sealed class Intent { public void Resolve() { } }
63 public sealed class Layout { public void Layout() { } }
64 """));
65
66 var d = Assert.Single(diags);
67 Assert.Equal(SkiaPipelineArchitectureAnalyzer.CodeNavigationMapCompositorStageFlowId, d.Id);
68 }
69
70 [Fact]
71 public async Task CASCOPE009_LayoutEngineOutsideLayoutStage_Reports()
72 {
73 var diags = await RunAnalyzerAsync((
74 @"D:\repo\Services\Navigation\Bad.cs",
75 """
76 namespace CascadeIDE.Services.Navigation;
77 public sealed class AnyCompositor
78 {
79 public object X() => new StarGraphLayoutEngine();
80 }
81 public sealed class StarGraphLayoutEngine { }
82 """));
83
84 var d = Assert.Single(diags);
85 Assert.Equal(SkiaPipelineArchitectureAnalyzer.LayoutBypassId, d.Id);
86 }
87
88 [Fact]
89 public async Task CASCOPE010_ViewSkia_UsesPipelineState_Reports()
90 {
91 var diags = await RunAnalyzerAsync((
92 @"D:\repo\Views\SkiaHostRendererPipeline.cs",
93 """
94 using CascadeIDE.Features.WorkspaceNavigation.Application;
95 namespace CascadeIDE.Views;
96 public sealed class SkiaHostRendererPipeline
97 {
98 public void Render(CodeNavigationMapPipelineState state) { }
99 }
100
101 namespace CascadeIDE.Services.Navigation
102 {
103 public struct CodeNavigationMapPipelineState { }
104 }
105 """));
106
107 var d = Assert.Single(diags);
108 Assert.Equal(SkiaPipelineArchitectureAnalyzer.SkiaViewDomainLeakId, d.Id);
109 }
110}
111
View only · write via MCP/CIDE