mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-18 14:37:19 +08:00
181 lines
6.2 KiB
C#
181 lines
6.2 KiB
C#
using System.Collections.Immutable;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.Diagnostics;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace AtomUI.Generator.Tests.Localization;
|
|
|
|
public class LanguageCatalogGeneratorTests
|
|
{
|
|
[Fact]
|
|
public void Accepts_A_Key_Based_Catalog_Contract()
|
|
{
|
|
var result = RunGenerator("""
|
|
[LanguageCatalog]
|
|
public enum LoginLangResourceKind
|
|
{
|
|
Title,
|
|
SignIn,
|
|
UserName
|
|
}
|
|
""");
|
|
|
|
result.Diagnostics.ShouldNotContain(static diagnostic =>
|
|
diagnostic.Id == "ATOMUILOC003" || diagnostic.Id == "ATOMUILOC004");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(
|
|
"[LanguageCatalog] public sealed class LoginLangResourceKind { }",
|
|
"ATOMUILOC003",
|
|
"LoginLangResourceKind",
|
|
"non-generic enum")]
|
|
[InlineData(
|
|
"[System.Flags, LanguageCatalog] public enum LoginLangResourceKind { Title }",
|
|
"ATOMUILOC003",
|
|
"LoginLangResourceKind",
|
|
"Flags")]
|
|
[InlineData(
|
|
"[LanguageCatalog] public enum LoginLangResourceKind { }",
|
|
"ATOMUILOC003",
|
|
"LoginLangResourceKind",
|
|
"at least one")]
|
|
[InlineData(
|
|
"[LanguageCatalog] internal enum LoginLangResourceKind { Title }",
|
|
"ATOMUILOC003",
|
|
"LoginLangResourceKind",
|
|
"public")]
|
|
public void Reports_Invalid_Catalog_Declarations(
|
|
string declaration,
|
|
string diagnosticId,
|
|
string locationText,
|
|
string messageFragment)
|
|
{
|
|
var diagnostic = RunGenerator(declaration).Diagnostics.ShouldHaveSingleItem();
|
|
|
|
AssertDiagnostic(diagnostic, diagnosticId, locationText, messageFragment);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(
|
|
"[LanguageCatalog] public enum LoginLangResourceKind { Title = 0 }",
|
|
"0",
|
|
"explicit numeric")]
|
|
[InlineData(
|
|
"[LanguageCatalog] public enum LoginLangResourceKind { Title = -1 }",
|
|
"-1",
|
|
"explicit numeric")]
|
|
public void Reports_Explicit_Catalog_Unit_Values(
|
|
string declaration,
|
|
string locationText,
|
|
string messageFragment)
|
|
{
|
|
var diagnostic = RunGenerator(declaration).Diagnostics.ShouldHaveSingleItem();
|
|
|
|
AssertDiagnostic(diagnostic, "ATOMUILOC004", locationText, messageFragment);
|
|
}
|
|
|
|
[Fact]
|
|
public void Reports_Explicit_Values_On_All_Catalog_Units()
|
|
{
|
|
var diagnostic = RunGenerator("""
|
|
[LanguageCatalog]
|
|
public enum LoginLangResourceKind
|
|
{
|
|
Title = 1,
|
|
Heading = 1
|
|
}
|
|
""").Diagnostics;
|
|
|
|
diagnostic.Length.ShouldBe(2);
|
|
diagnostic.ShouldAllBe(item =>
|
|
item.Id == "ATOMUILOC004" &&
|
|
item.GetMessage().Contains("explicit numeric", StringComparison.Ordinal));
|
|
}
|
|
|
|
private static void AssertDiagnostic(
|
|
Diagnostic diagnostic,
|
|
string diagnosticId,
|
|
string locationText,
|
|
string messageFragment)
|
|
{
|
|
diagnostic.Id.ShouldBe(diagnosticId);
|
|
diagnostic.Severity.ShouldBe(DiagnosticSeverity.Error);
|
|
diagnostic.GetMessage().ShouldContain(messageFragment);
|
|
diagnostic.Location.SourceTree.ShouldNotBeNull();
|
|
diagnostic.Location.SourceTree!.GetText(TestContext.Current.CancellationToken)
|
|
.ToString(diagnostic.Location.SourceSpan)
|
|
.ShouldBe(locationText);
|
|
}
|
|
|
|
private static GeneratorRunResult RunGenerator(string declaration)
|
|
{
|
|
var compilation = CreateCompilation(declaration);
|
|
GeneratorDriver driver = CSharpGeneratorDriver.Create(
|
|
[new LocalizationGenerator().AsSourceGenerator()],
|
|
parseOptions: (CSharpParseOptions)compilation.SyntaxTrees[0].Options,
|
|
optionsProvider: new TestAnalyzerConfigOptionsProvider());
|
|
driver = driver.RunGenerators(
|
|
compilation,
|
|
TestContext.Current.CancellationToken);
|
|
return driver.GetRunResult().Results.ShouldHaveSingleItem();
|
|
}
|
|
|
|
private static CSharpCompilation CreateCompilation(string declaration)
|
|
{
|
|
var references = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES"))!
|
|
.Split(Path.PathSeparator)
|
|
.Select(static path => MetadataReference.CreateFromFile(path))
|
|
.Cast<MetadataReference>()
|
|
.ToImmutableArray();
|
|
var source = $$"""
|
|
namespace AtomUI.Localization
|
|
{
|
|
[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = false)]
|
|
public sealed class LanguageCatalogAttribute : System.Attribute;
|
|
}
|
|
|
|
namespace TestApp.Localization
|
|
{
|
|
using AtomUI.Localization;
|
|
{{declaration}}
|
|
}
|
|
""";
|
|
|
|
return CSharpCompilation.Create(
|
|
"TestApp",
|
|
[CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Latest))],
|
|
references,
|
|
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
|
}
|
|
|
|
private sealed class TestAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider
|
|
{
|
|
private static readonly AnalyzerConfigOptions s_empty = new TestAnalyzerConfigOptions(
|
|
new Dictionary<string, string>());
|
|
private static readonly AnalyzerConfigOptions s_global = new TestAnalyzerConfigOptions(
|
|
new Dictionary<string, string>(StringComparer.Ordinal)
|
|
{
|
|
["build_property.PackageId"] = "Test.Package",
|
|
["build_property.AssemblyName"] = "TestApp"
|
|
});
|
|
|
|
public override AnalyzerConfigOptions GlobalOptions => s_global;
|
|
|
|
public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => s_empty;
|
|
|
|
public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => s_empty;
|
|
}
|
|
|
|
private sealed class TestAnalyzerConfigOptions(
|
|
IReadOnlyDictionary<string, string> values) : AnalyzerConfigOptions
|
|
{
|
|
public override bool TryGetValue(string key, out string value)
|
|
{
|
|
return values.TryGetValue(key, out value!);
|
|
}
|
|
}
|
|
}
|