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
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">

<!-- Simulate consuming nuget package props file. -->
<Import Project="../Figgle.Generator/build/Figgle.Generator.props" />

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="../Figgle.Generator.Tests/ANSI Shadow.flf" FontName="My External Font" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Figgle.Generator\Figgle.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

<!--
Figgle.Generator has a runtime dependency to Figgle.dll which is packaged into the generator's nuget package
and loaded automatically when a project consumes the nuget.

Since this test project is referencing the generator manually, we must also manually include
Figgle.dll as an "analyzer" so the compiler can find it when running the generator.
-->
<ProjectReference Include="..\Figgle\Figgle.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System;
using Xunit;

namespace Figgle.Generator.AcceptanceTests;

[GenerateFiggleText("HelloWorld", "My External Font", "Hello World!")]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A possible extension to this would be to generate const strings for any external font names, so that if the file is removed or the build item removed, then the code wouldn't compile. Some open questions (like how to create a valid C# member name from an arbitrary file name) but thought I'd throw it out there. This PR is great as is.

public partial class FiggleSourceGeneratorAcceptanceTests
{
[Fact]
public void HelloWorldTextWithExternalFontIsSourceGenerated_RenderedTextMatches()
{
string expectedText = """
██╗ ██╗███████╗██╗ ██╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗ ██╗
██║ ██║██╔════╝██║ ██║ ██╔═══██╗ ██║ ██║██╔═══██╗██╔══██╗██║ ██╔══██╗██║
███████║█████╗ ██║ ██║ ██║ ██║ ██║ █╗ ██║██║ ██║██████╔╝██║ ██║ ██║██║
██╔══██║██╔══╝ ██║ ██║ ██║ ██║ ██║███╗██║██║ ██║██╔══██╗██║ ██║ ██║╚═╝
██║ ██║███████╗███████╗███████╗╚██████╔╝ ╚███╔███╔╝╚██████╔╝██║ ██║███████╗██████╔╝██╗
╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝
""";

Assert.Equal(expectedText, HelloWorld.Trim(), NewlineIgnoreComparer.Instance);
}

private sealed class NewlineIgnoreComparer : IEqualityComparer<string>
{
public static NewlineIgnoreComparer Instance { get; } = new();

public bool Equals(string? x, string? y)
{
return StringComparer.Ordinal.Equals(Normalize(x), Normalize(y));
}

public int GetHashCode(string obj)
{
return StringComparer.Ordinal.GetHashCode(Normalize(obj));
}

[return: NotNullIfNotNull("s")]
private static string? Normalize(string? s) => s?.Replace("\r", "");
}
}
1 change: 1 addition & 0 deletions Figgle.Generator.Tests/Figgle.Generator.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ItemGroup>
<None Update="ANSI Shadow.flf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<DependentUpon>FiggleSourceGeneratorTests_ExternalFonts.cs</DependentUpon>
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: I put the font file under the test file that uses it to reduce clutter in the Solution Explorer:

image

</None>
</ItemGroup>

Expand Down
91 changes: 16 additions & 75 deletions Figgle.Generator.Tests/FiggleSourceGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using Xunit;
using Xunit.Sdk;

namespace Figgle.Generator.Tests;

public class FiggleSourceGeneratorTests
public partial class FiggleSourceGeneratorTests
Copy link
Copy Markdown
Collaborator Author

@jonathanou jonathanou May 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: The code for testing external fonts was getting a bit much and was cluttering the original test file.

To improve code organization I split the external font tests and support code to another file. Using partial here allows the new test file to still access the private helpers like ValidateOutput.

{
private readonly ImmutableArray<MetadataReference> _references;

Expand Down Expand Up @@ -614,64 +615,22 @@ internal partial class Inner
Assert.Equal("Unable to generate Figgle text for nested type 'Inner'. Generation is only supported for non-nested types.", diagnostic.GetMessage());
}

[Theory]
[InlineData("ANSI Shadow")]
[InlineData("ansi shadow")]
public void ExternalFontInAdditionalFiles_RendersText(string fontName)
{
string source =
$$"""
namespace Test.Namespace
{
[GenerateFiggleText("Member", "{{fontName}}", "Figgle")]
internal partial class DemoUsage
{
}
}
""";
string expected =
"""
// Copyright Drew Noakes. Licensed under the Apache-2.0 license. See the LICENSE file for more details.

// <auto-generated>
// This code was generated by Figgle.Generator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>

namespace Test.Namespace
{
partial class DemoUsage
{
public static string Member { get; } = @"███████╗██╗ ██████╗ ██████╗ ██╗ ███████╗
██╔════╝██║██╔════╝ ██╔════╝ ██║ ██╔════╝
█████╗ ██║██║ ███╗██║ ███╗██║ █████╗
██╔══╝ ██║██║ ██║██║ ██║██║ ██╔══╝
██║ ██║╚██████╔╝╚██████╔╝███████╗███████╗
╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝

";
}
}
""";

using var fontStream = File.OpenRead("ANSI Shadow.flf");
var additionalFonts = ImmutableArray.Create(new ExternalFontAdditionalText("ANSI Shadow.flf", fontStream));
ValidateOutput(source, additionalFonts, expected);
}

private void ValidateOutput(string source, params string[] outputs)
{
ValidateOutput(source, ImmutableArray<ExternalFontAdditionalText>.Empty, outputs);
ValidateOutput(
source,
ImmutableArray<ExternalFontAdditionalText>.Empty,
optionsProvider: null,
outputs);
}

private void ValidateOutput(
string source,
ImmutableArray<ExternalFontAdditionalText> additionalFonts,
TestAnalyzerConfigOptionsProvider? optionsProvider,
params string[] outputs)
{
var (compilation, diagnostics) = RunGenerator(source, additionalFonts);
var (compilation, diagnostics) = RunGenerator(source, additionalFonts, optionsProvider);

ValidateNoErrors(diagnostics);

Expand All @@ -683,23 +642,23 @@ private void ValidateOutput(

private (Compilation Compilation, ImmutableArray<Diagnostic> Diagnostics) RunGenerator(
string source,
ImmutableArray<ExternalFontAdditionalText>? optionalAdditionalFonts = null)
ImmutableArray<ExternalFontAdditionalText>? additionalFonts = null,
TestAnalyzerConfigOptionsProvider? optionsProvider = null)
{
var additionalFonts = optionalAdditionalFonts
?? ImmutableArray<ExternalFontAdditionalText>.Empty;

var syntaxTree = CSharpSyntaxTree.ParseText(source);

var compilation = CSharpCompilation.Create(
"testAssembly",
new SyntaxTree[] { syntaxTree },
[syntaxTree],
_references,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

ISourceGenerator generator = new FiggleSourceGenerator();

var driver = CSharpGeneratorDriver.Create(generator)
.AddAdditionalTexts(additionalFonts.As<AdditionalText>());
var driver = CSharpGeneratorDriver.Create(
[generator],
additionalTexts: additionalFonts,
optionsProvider: optionsProvider);

driver.RunGeneratorsAndUpdateCompilation(
compilation,
Expand All @@ -722,24 +681,6 @@ private static void ValidateNoErrors(ImmutableArray<Diagnostic> diagnostics)
}
}

private sealed class ExternalFontAdditionalText : AdditionalText
{
private readonly SourceText _sourceText;

public ExternalFontAdditionalText(string path, Stream externalFont)
{
Path = path;
_sourceText = SourceText.From(externalFont);
}

public override string Path { get; }

public override SourceText? GetText(CancellationToken cancellationToken = default)
{
return _sourceText;
}
}

private sealed class NewlineIgnoreComparer : IEqualityComparer<string>
{
public static NewlineIgnoreComparer Instance { get; } = new();
Expand Down
Loading