Forge
csharpdeeb25a2
1using System.Collections.Immutable;
2using System.Globalization;
3using Basic.Reference.Assemblies;
4using Microsoft.CodeAnalysis;
5using Microsoft.CodeAnalysis.CSharp;
6using Microsoft.CodeAnalysis.Diagnostics;
7using Xunit;
8
9namespace CascadeIDE.ArchitectureAnalyzers.Tests;
10
11public sealed class CockpitLayerArchitectureAnalyzerTests
12{
13 private static async Task<ImmutableArray<Diagnostic>> RunAnalyzerAsync(params (string Path, string Text)[] files)
14 {
15 var trees = new List<SyntaxTree>(files.Length);
16 foreach (var (path, text) in files)
17 trees.Add(CSharpSyntaxTree.ParseText(text, path: path));
18
19 var references = Net80.References.All;
20 var compilation = CSharpCompilation.Create(
21 "TestAssembly",
22 trees,
23 references,
24 new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
25
26 var analyzer = new CockpitLayerArchitectureAnalyzer();
27 var withAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
28 return await withAnalyzers.GetAnalyzerDiagnosticsAsync();
29 }
30
31 [Fact]
32 public async Task CASCOPE001_UsingAvalonia_InChannelsPath_Reports()
33 {
34 var diags = await RunAnalyzerAsync((
35 @"D:\repo\Cockpit\Channels\Bad.cs",
36 """
37 using Avalonia;
38 namespace CascadeIDE.Cockpit.Channels.Example;
39
40 public static class C { }
41 """));
42
43 var d = Assert.Single(diags);
44 Assert.Equal(CockpitLayerArchitectureAnalyzer.AvaloniaNamespaceId, d.Id);
45 Assert.Contains("Channels", d.GetMessage(CultureInfo.InvariantCulture), StringComparison.Ordinal);
46 }
47
48 [Fact]
49 public async Task CASCOPE002_UsingUiChrome_InCdsPath_Reports()
50 {
51 var diags = await RunAnalyzerAsync((
52 @"D:\repo\Cockpit\Cds\Bad.cs",
53 """
54 using CascadeIDE.Features.UiChrome;
55 namespace CascadeIDE.Cockpit.Cds.Example;
56
57 public static class C { }
58 """));
59
60 var d = Assert.Single(diags);
61 Assert.Equal(CockpitLayerArchitectureAnalyzer.FeaturesUiChromeId, d.Id);
62 }
63
64 [Fact]
65 public async Task UsingAvalonia_InSurfacePath_DoesNotReport()
66 {
67 var diags = await RunAnalyzerAsync((
68 @"D:\repo\Cockpit\Surface\Ok.cs",
69 """
70 using Avalonia;
71 namespace CascadeIDE.Cockpit.Surface.Example;
72
73 public static class C { }
74 """));
75
76 Assert.Empty(diags);
77 }
78
79 [Fact]
80 public async Task CASCOPE001_AvaloniaTypeInMember_InCompositionNamespace_Reports()
81 {
82 var diags = await RunAnalyzerAsync(
83 (@"D:\repo\Support\AvaloniaStub.cs", """
84namespace Avalonia.Controls;
85
86public sealed class Control { }
87"""),
88 (@"D:\repo\Cockpit\Composition\Bad.cs", """
89namespace CascadeIDE.Cockpit.Composition.Example;
90
91public sealed class Host
92{
93 public Avalonia.Controls.Control? Widget { get; set; }
94}
95"""));
96
97 var d = Assert.Single(diags);
98 Assert.Equal(CockpitLayerArchitectureAnalyzer.AvaloniaNamespaceId, d.Id);
99 Assert.Contains("Avalonia.Controls.Control", d.GetMessage(CultureInfo.InvariantCulture), StringComparison.Ordinal);
100 }
101
102 [Fact]
103 public async Task ChannelsNamespace_WithoutAvalonia_DoesNotReport()
104 {
105 var diags = await RunAnalyzerAsync((
106 @"D:\repo\Cockpit\Channels\Ok.cs",
107 """
108 namespace CascadeIDE.Cockpit.Channels.Example;
109
110 public static class C
111 {
112 public static string Id => "ok";
113 }
114 """));
115
116 Assert.Empty(diags);
117 }
118}
119
View only · write via MCP/CIDE