test: make channel logo generator testable #53

Merged
timothy merged 1 commits from feature/39-channel-logo-generator-testable into main 2026-06-30 18:20:49 +02:00
3 changed files with 69 additions and 5 deletions
@@ -32,4 +32,9 @@
<ProjectReference Include="..\ErsatzTV.Infrastructure\ErsatzTV.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\ErsatzTV\Resources\Fonts\Sen.ttf" Link="Resources\Fonts\Sen.ttf" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\ErsatzTV\wwwroot\images\ersatztv-500.png" Link="Resources\Images\ersatztv-500.png" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
@@ -0,0 +1,41 @@
using ErsatzTV.Core.Images;
using Microsoft.Extensions.Logging.Abstractions;
using NUnit.Framework;
using Shouldly;
using SkiaSharp;
namespace ErsatzTV.Core.Tests.Images;
[TestFixture]
public class ChannelLogoGeneratorTests
{
[TestCase("News", 100, 200)]
[TestCase("A Very Long Channel Name That Should Force Font Shrinking", 100, 200)]
[TestCase("Tall Logo", 180, 120)]
public void GenerateChannelLogo_WithInjectedAssets_ReturnsValidPngWithRequestedDimensions(
string text,
int logoHeight,
int logoWidth)
{
var generator = new ChannelLogoGenerator(
NullLogger<ChannelLogoGenerator>.Instance,
TestAssetPath("Resources", "Images", "ersatztv-500.png"),
TestAssetPath("Resources", "Fonts", "Sen.ttf"));
byte[] bytes = generator.GenerateChannelLogo(text, logoHeight, logoWidth, CancellationToken.None)
.Match(
Left: error => throw new AssertionException(error.Value),
Right: identity);
using var stream = new MemoryStream(bytes);
using var codec = SKCodec.Create(stream);
codec.ShouldNotBeNull();
codec.EncodedFormat.ShouldBe(SKEncodedImageFormat.Png);
codec.Info.Width.ShouldBe(logoWidth);
codec.Info.Height.ShouldBe(logoHeight);
}
private static string TestAssetPath(params string[] segments) =>
Path.Combine([TestContext.CurrentContext.TestDirectory, .. segments]);
}
+23 -5
View File
@@ -10,11 +10,25 @@ public class ChannelLogoGenerator : IChannelLogoGenerator
public const string GetRoute = "/iptv/logos/gen";
public const string GetRouteQueryParamName = "text";
private readonly string _fontPath;
private readonly ILogger _logger;
private readonly string _overlayImagePath;
public ChannelLogoGenerator(
ILogger<ChannelLogoGenerator> logger) =>
ILogger<ChannelLogoGenerator> logger)
: this(logger, DefaultOverlayImagePath(), DefaultFontPath())
{
}
public ChannelLogoGenerator(
ILogger<ChannelLogoGenerator> logger,
string overlayImagePath,
string fontPath)
{
_logger = logger;
_overlayImagePath = overlayImagePath;
_fontPath = fontPath;
}
public Either<BaseError, byte[]> GenerateChannelLogo(
string text,
@@ -29,13 +43,11 @@ public class ChannelLogoGenerator : IChannelLogoGenerator
canvas.Clear(SKColors.Black);
//etv logo
string overlayImagePath = Path.Combine("wwwroot", "images", "ersatztv-500.png");
using var overlayImage = SKBitmap.Decode(overlayImagePath);
using var overlayImage = SKBitmap.Decode(_overlayImagePath);
canvas.DrawBitmap(overlayImage, new SKRect(155, 60, 205, 110));
//Custom Font
string fontPath = Path.Combine(FileSystemLayout.ResourcesCacheFolder, "Sen.ttf");
using var fontTypeface = SKTypeface.FromFile(fontPath);
using var fontTypeface = SKTypeface.FromFile(_fontPath);
var fontSize = 30;
var font = new SKFont
{
@@ -80,4 +92,10 @@ public class ChannelLogoGenerator : IChannelLogoGenerator
public static string GenerateChannelLogoUrl(Channel channel) =>
$"http://localhost:{Settings.StreamingPort}{GetRoute}?{GetRouteQueryParamName}={channel.WebEncodedName}";
private static string DefaultFontPath() =>
Path.Combine(FileSystemLayout.ResourcesCacheFolder, "Sen.ttf");
private static string DefaultOverlayImagePath() =>
Path.Combine("wwwroot", "images", "ersatztv-500.png");
}