2026-02-04 19:01:00 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2026-03-12 19:40:58 +01:00
|
|
|
using NSubstitute;
|
|
|
|
|
using Shouldly;
|
2026-02-04 19:01:00 +01:00
|
|
|
using MaddoShared;
|
|
|
|
|
|
|
|
|
|
namespace MaddoShared.Tests
|
|
|
|
|
{
|
|
|
|
|
[TestClass]
|
|
|
|
|
public class ImageCreatorSharpTests
|
|
|
|
|
{
|
2026-02-21 15:53:52 +01:00
|
|
|
private ImageCreatorGDI CreateService(Action<PicSettings> customize = null)
|
2026-02-04 19:01:00 +01:00
|
|
|
{
|
|
|
|
|
var settings = new PicSettings
|
|
|
|
|
{
|
|
|
|
|
DimStandard = 20,
|
|
|
|
|
DimStandardMiniatura = 10,
|
|
|
|
|
LarghezzaSmall = 100,
|
|
|
|
|
AltezzaSmall = 100,
|
|
|
|
|
LarghezzaBig = 800,
|
|
|
|
|
AltezzaBig = 600,
|
|
|
|
|
Trasparenza = 50,
|
|
|
|
|
IlFont = "Arial",
|
|
|
|
|
Grassetto = false,
|
|
|
|
|
Posizione = "CENTRO",
|
|
|
|
|
Allineamento = "CENTRO",
|
|
|
|
|
Margine = 10,
|
|
|
|
|
MargVert = 10,
|
|
|
|
|
TestoMin = false,
|
|
|
|
|
AggNumTempMin = false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
customize?.Invoke(settings);
|
|
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
var logger = Substitute.For<ILogger<ImageCreatorGDI>>();
|
2026-02-21 15:53:52 +01:00
|
|
|
return new ImageCreatorGDI(settings, logger);
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void CalculateThumbnailSize_Larghezza_UsesWidthScaling()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService();
|
|
|
|
|
var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var size = (Size)mi.Invoke(svc, new object[] { 400, 200, 200, "Larghezza" });
|
|
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
size.Width.ShouldBe(200);
|
|
|
|
|
size.Height.ShouldBe(100);
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void CalculateThumbnailSize_Altezza_UsesHeightScaling()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService();
|
|
|
|
|
var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var size = (Size)mi.Invoke(svc, new object[] { 200, 400, 200, "Altezza" });
|
|
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
size.Width.ShouldBe(100);
|
|
|
|
|
size.Height.ShouldBe(200);
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void IsSameDirectory_IsCaseInsensitive()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService();
|
|
|
|
|
var mi = svc.GetType().GetMethod("IsSameDirectory", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
bool same = (bool)mi.Invoke(svc, new object[] { @"C:\Temp", @"c:\temp" });
|
2026-03-12 19:40:58 +01:00
|
|
|
same.ShouldBeTrue();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
bool notSame = (bool)mi.Invoke(svc, new object[] { @"C:\TempA", @"c:\temp" });
|
2026-03-12 19:40:58 +01:00
|
|
|
notSame.ShouldBeFalse();
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void UpdateFilenameWithCode_InsertsCodeBeforeExtension()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService(s => s.Codice = "_X");
|
|
|
|
|
var mi = svc.GetType().GetMethod("UpdateFilenameWithCode", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var state = new ImageState { NomeFileSmall = "photo123.jpg" };
|
|
|
|
|
mi.Invoke(svc, new object[] { state });
|
|
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
state.NomeFileSmall.ShouldBe("photo123_X.jpg");
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[DataTestMethod]
|
|
|
|
|
[DataRow("SINISTRA")]
|
|
|
|
|
[DataRow("CENTRO")]
|
|
|
|
|
[DataRow("DESTRA")]
|
|
|
|
|
public void CalculateHorizontalAlignment_RespectsAlignment(string alignment)
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService(s => { s.Allineamento = alignment; s.Margine = 20; });
|
|
|
|
|
|
|
|
|
|
var mi = svc.GetType().GetMethod("CalculateHorizontalAlignment", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var center = (float)mi.Invoke(svc, new object[] { 800, 100f });
|
|
|
|
|
|
|
|
|
|
if (alignment == "SINISTRA")
|
2026-03-12 19:40:58 +01:00
|
|
|
center.ShouldBeInRange(0f, 400f);
|
2026-02-04 19:01:00 +01:00
|
|
|
if (alignment == "DESTRA")
|
2026-03-12 19:40:58 +01:00
|
|
|
center.ShouldBeInRange(400f, 800f);
|
2026-02-04 19:01:00 +01:00
|
|
|
if (alignment == "CENTRO")
|
2026-03-12 19:40:58 +01:00
|
|
|
center.ShouldBe(800 / 2f, 0.0001f);
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void SetVerticalPosition_AltoAndBasso_SetExpectedValues()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService(s => s.Posizione = "ALTO");
|
|
|
|
|
var mi = svc.GetType().GetMethod("SetVerticalPosition", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var state = new ImageState();
|
|
|
|
|
|
|
|
|
|
// ALTO
|
|
|
|
|
mi.Invoke(svc, new object[] { 500, 20f, state });
|
2026-03-12 19:40:58 +01:00
|
|
|
state.YPosFromBottom1.ShouldBe(10f);
|
|
|
|
|
state.YPosFromBottom4.ShouldBe(10f);
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
// BASSO
|
|
|
|
|
state = new ImageState();
|
|
|
|
|
svc = CreateService(s => { s.Posizione = "BASSO"; s.Margine = 10; s.MargVert = 5; });
|
|
|
|
|
mi = svc.GetType().GetMethod("SetVerticalPosition", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
|
mi.Invoke(svc, new object[] { 200, 20f, state });
|
|
|
|
|
|
|
|
|
|
var expected1 = (float)(200 - 20 - (200 * 10 / 100.0));
|
|
|
|
|
var expected4 = (float)(200 - 20 - (200 * 5 / 100.0));
|
2026-03-12 19:40:58 +01:00
|
|
|
state.YPosFromBottom1.ShouldBe(expected1, 0.001f);
|
|
|
|
|
state.YPosFromBottom4.ShouldBe(expected4, 0.001f);
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void FormatTimeText_WithAndWithoutFileName_ProducesExpectedStrings()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService();
|
|
|
|
|
var mi = svc.GetType().GetMethod("FormatTimeText", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var state = new ImageState
|
|
|
|
|
{
|
|
|
|
|
NomeFileBig = "file.jpg",
|
|
|
|
|
TestoOrario = "T:",
|
|
|
|
|
DataPartenzaI = new DateTime(2024, 01, 01, 12, 0, 0),
|
|
|
|
|
DataFoto = new DateTime(2024, 01, 01, 11, 59, 0)
|
|
|
|
|
};
|
|
|
|
|
var withoutName = (string)mi.Invoke(svc, new object[] { state, false });
|
2026-03-12 19:40:58 +01:00
|
|
|
withoutName.ShouldStartWith(Environment.NewLine);
|
|
|
|
|
withoutName.ShouldContain("T:");
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var withName = (string)mi.Invoke(svc, new object[] { state, true });
|
2026-03-12 19:40:58 +01:00
|
|
|
withName.ShouldContain("file.jpg");
|
|
|
|
|
withName.ShouldContain("T:");
|
|
|
|
|
withName.ShouldContain(Environment.NewLine);
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void PrepareSignatureText_SetsSmallSignature_AccordingFlags()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService();
|
|
|
|
|
var miPrep = svc.GetType().GetMethod("PrepareSignatureText", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
miPrep.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var state = new ImageState { NomeFileBig = "bigname.jpg" };
|
|
|
|
|
|
|
|
|
|
svc = CreateService(s => s.TestoMin = true);
|
|
|
|
|
miPrep.Invoke(svc, new object[] { state });
|
2026-03-12 19:40:58 +01:00
|
|
|
state.TestoFirmaPiccola.ShouldBe("bigname.jpg");
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
state.TestoFirmaPiccola = "";
|
|
|
|
|
svc = CreateService(s => { s.TestoMin = false; s.AggNumTempMin = true; });
|
|
|
|
|
miPrep.Invoke(svc, new object[] { state });
|
2026-03-12 19:40:58 +01:00
|
|
|
state.TestoFirmaPiccola.ShouldBe("bigname.jpg ");
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void ShouldRenderText_ReturnsCorrectFlag()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService(s => { s.UsaOrarioMiniatura = false; s.TestoMin = false; s.AggTempoGaraMin = false; s.AggNumTempMin = false; });
|
|
|
|
|
var mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
mi.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
var res = (bool)mi.Invoke(svc, Array.Empty<object>());
|
2026-03-12 19:40:58 +01:00
|
|
|
res.ShouldBeFalse();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
svc = CreateService(s => s.TestoMin = true);
|
|
|
|
|
mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
|
res = (bool)mi.Invoke(svc, Array.Empty<object>());
|
2026-03-12 19:40:58 +01:00
|
|
|
res.ShouldBeTrue();
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
|
public void FindBestFontSize_And_AdjustFontToFitWidth_ModifySizes()
|
|
|
|
|
{
|
|
|
|
|
var svc = CreateService(s => { s.IlFont = "Arial"; s.DimStandardMiniatura = 30; });
|
|
|
|
|
|
|
|
|
|
using var bmp = new Bitmap(400, 100);
|
|
|
|
|
using var g = Graphics.FromImage(bmp);
|
|
|
|
|
|
|
|
|
|
var miFind = svc.GetType().GetMethod("FindBestFontSize", BindingFlags.NonPublic | BindingFlags.Instance);
|
2026-03-12 19:40:58 +01:00
|
|
|
miFind.ShouldNotBeNull();
|
2026-02-04 19:01:00 +01:00
|
|
|
|
|
|
|
|
int best = (int)miFind.Invoke(svc, new object[] { g, "A very long text that won't fit", "Arial", 40, false, 50, 5 });
|
2026-03-12 19:40:58 +01:00
|
|
|
best.ShouldBeInRange(5, 40);
|
2026-02-04 19:01:00 +01:00
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
// The helper AdjustFontToFitWidth was in an earlier refactor; replicate its logic here
|
2026-02-04 19:01:00 +01:00
|
|
|
var imageState = new ImageState { DimensioneStandardMiniatura = 30, TestoFirmaPiccola = "A very long test string" };
|
|
|
|
|
var initialFont = new Font("Arial", imageState.DimensioneStandardMiniatura);
|
|
|
|
|
var textSize = g.MeasureString(imageState.TestoFirmaPiccola, initialFont);
|
|
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
int tempFontSize = imageState.DimensioneStandardMiniatura;
|
|
|
|
|
while ((textSize.Width > 50) && tempFontSize > 5)
|
|
|
|
|
{
|
|
|
|
|
tempFontSize = (tempFontSize > 20) ? tempFontSize - 5 : tempFontSize - 1;
|
|
|
|
|
using var tempFont = new Font("Arial", tempFontSize);
|
|
|
|
|
textSize = g.MeasureString(imageState.TestoFirmaPiccola, tempFont);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var updatedSize = textSize;
|
|
|
|
|
imageState.DimensioneStandardMiniatura = tempFontSize;
|
2026-02-04 19:01:00 +01:00
|
|
|
|
2026-03-12 19:40:58 +01:00
|
|
|
imageState.DimensioneStandardMiniatura.ShouldBeLessThanOrEqualTo(30);
|
|
|
|
|
(updatedSize.Width <= 50 || imageState.DimensioneStandardMiniatura <= 5).ShouldBeTrue();
|
2026-02-04 19:01:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|