Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<ItemGroup>
<PackageVersion Include="AwesomeAssertions" Version="9.4.0" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.6.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.300" />
Expand Down
11 changes: 1 addition & 10 deletions LanguageTagsTests/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,5 @@ root = false
# Allow underscores in test method names
dotnet_diagnostic.CA1707.severity = none

# Ignore unused private members
dotnet_diagnostic.IDE0052.severity = none

# Ignore expression value is never used
dotnet_diagnostic.IDE0058.severity = none

# Ignore missing XML docs for public test APIs
dotnet_diagnostic.CS1591.severity = none

# Ignore making public types internal
# Fixture and collection-definition types must be public for xUnit discovery
dotnet_diagnostic.CA1515.severity = none
34 changes: 34 additions & 0 deletions LanguageTagsTests/Iso6392Tests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace ptr727.LanguageTags.Tests;

public sealed class Iso6392Tests : SingleInstanceFixture
Expand Down Expand Up @@ -126,4 +129,35 @@ public void Find_Empty_ReturnsNull()
Iso6392Record? record = iso6392.Find(string.Empty, false);
_ = record.Should().BeNull();
}

[Fact]
public async Task SaveCodeAsync_GeneratesCode()
{
Iso6392Data fromData = await Iso6392Data.FromDataAsync(
GetDataFilePath(Iso6392Data.DataFileName)
);
_ = fromData.RecordList.Length.Should().BeGreaterThan(0);

string tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.cs");
try
{
await fromData.SaveCodeAsync(tempFile);
string code = await File.ReadAllTextAsync(tempFile);

// Emitted code must parse as valid C# (catches literal/escaping regressions)
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
IEnumerable<string> syntaxErrors = tree.GetDiagnostics()
Comment thread
ptr727 marked this conversation as resolved.
.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.Select(diagnostic => diagnostic.ToString());
_ = syntaxErrors.Should().BeEmpty();
_ = code.Should().Contain("public static Iso6392Data Create() =>");
}
finally
{
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
}
}
34 changes: 34 additions & 0 deletions LanguageTagsTests/Iso6393Tests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace ptr727.LanguageTags.Tests;

public sealed class Iso6393Tests : SingleInstanceFixture
Expand Down Expand Up @@ -126,4 +129,35 @@ public void Find_Empty_ReturnsNull()
Iso6393Record? record = iso6393.Find(string.Empty, false);
_ = record.Should().BeNull();
}

[Fact]
public async Task SaveCodeAsync_GeneratesCode()
{
Iso6393Data fromData = await Iso6393Data.FromDataAsync(
GetDataFilePath(Iso6393Data.DataFileName)
);
_ = fromData.RecordList.Length.Should().BeGreaterThan(0);

string tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.cs");
try
{
await fromData.SaveCodeAsync(tempFile);
string code = await File.ReadAllTextAsync(tempFile);

// Emitted code must parse as valid C# (catches literal/escaping regressions)
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
IEnumerable<string> syntaxErrors = tree.GetDiagnostics()
.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.Select(diagnostic => diagnostic.ToString());
_ = syntaxErrors.Should().BeEmpty();
_ = code.Should().Contain("public static Iso6393Data Create() =>");
}
finally
{
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
}
}
40 changes: 40 additions & 0 deletions LanguageTagsTests/LanguageSchemaTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace ptr727.LanguageTags.Tests;

public sealed class LanguageSchemaTests
{
[Theory]
[InlineData(null, "null")]
[InlineData("", "null")]
[InlineData("value", "\"value\"")]
public void GetCodeGenString_String(string? input, string expected) =>
_ = LanguageSchema.GetCodeGenString(input).Should().Be(expected);

[Fact]
public void GetCodeGenString_DateOnly_Null() =>
_ = LanguageSchema.GetCodeGenString((DateOnly?)null).Should().Be("null");

[Fact]
public void GetCodeGenString_DateOnly_Value() =>
_ = LanguageSchema
.GetCodeGenString(new DateOnly(2024, 3, 7))
.Should()
.Be("new DateOnly(2024, 3, 7)");

[Fact]
public void GetCodeGenString_RecordType() =>
_ = LanguageSchema
.GetCodeGenString(Rfc5646Record.RecordType.Language)
.Should()
.Be("Rfc5646Record.RecordType.Language");

[Fact]
public void GetCodeGenString_RecordScope() =>
_ = LanguageSchema
.GetCodeGenString(Rfc5646Record.RecordScope.MacroLanguage)
.Should()
.Be("Rfc5646Record.RecordScope.MacroLanguage");

[Fact]
public void GetCodeGenString_List_EscapesQuotes() =>
_ = LanguageSchema.GetCodeGenString(["a", "b\"c"]).Should().Be("[@\"a\", @\"b\"\"c\"]");
}
1 change: 1 addition & 0 deletions LanguageTagsTests/LanguageTagsTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AwesomeAssertions" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit.analyzers">
<PrivateAssets>all</PrivateAssets>
Expand Down
59 changes: 59 additions & 0 deletions LanguageTagsTests/LogExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Microsoft.Extensions.Logging;

namespace ptr727.LanguageTags.Tests;

public sealed class LogExtensionsTests
{
[Fact]
public void LogAndPropagate_LogsExceptionAtErrorAndReturnsFalse()
{
CapturingLogger logger = new();
InvalidOperationException exception = new("test");

bool result = logger.LogAndPropagate(exception);

_ = result.Should().BeFalse();
_ = logger.Entries.Should().ContainSingle();
_ = logger.Entries[0].Level.Should().Be(LogLevel.Error);
_ = logger.Entries[0].Exception.Should().BeSameAs(exception);
}

[Fact]
public void LogAndHandle_LogsExceptionAtErrorAndReturnsTrue()
{
CapturingLogger logger = new();
InvalidOperationException exception = new("test");

bool result = logger.LogAndHandle(exception);

_ = result.Should().BeTrue();
_ = logger.Entries.Should().ContainSingle();
_ = logger.Entries[0].Level.Should().Be(LogLevel.Error);
_ = logger.Entries[0].Exception.Should().BeSameAs(exception);
}

private sealed class CapturingLogger : ILogger
{
public List<(LogLevel Level, Exception? Exception)> Entries { get; } = [];

public IDisposable BeginScope<TState>(TState state)
where TState : notnull => NullScope.Instance;

public bool IsEnabled(LogLevel logLevel) => true;

public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter
) => Entries.Add((logLevel, exception));

private sealed class NullScope : IDisposable
{
public static readonly NullScope Instance = new();

public void Dispose() { }
}
}
}
34 changes: 34 additions & 0 deletions LanguageTagsTests/Rfc5646Tests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace ptr727.LanguageTags.Tests;

public class Rfc5646Tests : SingleInstanceFixture
Expand Down Expand Up @@ -142,4 +145,35 @@ public void FileDate_IsSet()
_ = rfc5646.FileDate.Should().NotBeNull();
_ = rfc5646.FileDate.Should().HaveValue();
}

[Fact]
public async Task SaveCodeAsync_GeneratesCode()
{
Rfc5646Data fromData = await Rfc5646Data.FromDataAsync(
GetDataFilePath(Rfc5646Data.DataFileName)
);
_ = fromData.RecordList.Length.Should().BeGreaterThan(0);

string tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.cs");
try
{
await fromData.SaveCodeAsync(tempFile);
string code = await File.ReadAllTextAsync(tempFile);

// Emitted code must parse as valid C# (catches literal/escaping regressions)
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
IEnumerable<string> syntaxErrors = tree.GetDiagnostics()
.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.Select(diagnostic => diagnostic.ToString());
_ = syntaxErrors.Should().BeEmpty();
_ = code.Should().Contain("public static Rfc5646Data Create() =>");
}
finally
{
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
}
}
32 changes: 32 additions & 0 deletions LanguageTagsTests/UnM49Tests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace ptr727.LanguageTags.Tests;

public sealed class UnM49Tests : SingleInstanceFixture
Expand Down Expand Up @@ -128,4 +131,33 @@ public void GetAncestors_Unknown_ReturnsEmpty()
UnM49Data unM49 = UnM49Data.Create();
_ = unM49.GetAncestors("ZZ").Should().BeEmpty();
}

[Fact]
public async Task SaveCodeAsync_GeneratesCode()
{
UnM49Data fromData = await UnM49Data.FromDataAsync(GetDataFilePath(UnM49Data.DataFileName));
_ = fromData.RecordList.Length.Should().BeGreaterThan(0);

string tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.cs");
try
{
await fromData.SaveCodeAsync(tempFile);
string code = await File.ReadAllTextAsync(tempFile);

// Emitted code must parse as valid C# (catches literal/escaping regressions)
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
IEnumerable<string> syntaxErrors = tree.GetDiagnostics()
.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.Select(diagnostic => diagnostic.ToString());
_ = syntaxErrors.Should().BeEmpty();
_ = code.Should().Contain("public static UnM49Data Create() =>");
}
finally
{
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
}
}
27 changes: 27 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Codecov for the LanguageTags NuGet library only. Generated code (embedded *DataGen.cs tables and .g.cs
# source-generator output) is ignored so coverage reflects hand-written code, not generated lines.
# Kept informational until a default-branch upload sets a baseline under these ignores; then flip to blocking.

coverage:
status:
project:
default:
target: auto # compare each commit to its base
threshold: 1% # allowed drop before failing
informational: true # report-only; set false to block
patch:
default:
target: auto
threshold: 1%
informational: true

comment:
layout: "condensed_header, diff, files, components"
require_changes: false

ignore:
- "LanguageTagsCreate"
- "LanguageTagsTests"
- "**/*Gen.cs"
- "**/*.g.cs"
- ".artifacts/**"