using XFESystem.Browser;
using XFESystem.Core;
using XFESystem.Desktop;
using XFESystem.Platform;
namespace XFESystem.Platform.Tests;
public sealed class RenderingTests
{
[Fact]
public void NonBreakingSpacesHaveAdvanceAndNoInkAtMonacoSizes()
{
var fonts = PlatformFonts.CreateBundled();
foreach (double size in new[] { 1d, 13d, 16d, 26d })
{
Assert.True(fonts.HasGlyph('\u00A0', "Consolas", 400));
Assert.Equal(fonts.Measure(" ", size, "Consolas", 400), fonts.Measure("\u00A0", size, "Consolas", 400), 5);
var blank = fonts.Rasterize("\u00A0", size, "Consolas", 400);
Assert.All(blank.Alpha, value => Assert.Equal((byte)0, value));
var run = fonts.Rasterize("A\u00A0B", size, "Consolas", 400);
Assert.Contains(run.Alpha, value => value > 0);
}
}
[Fact]
public void OfficialMonacoDomPaintsActualTextWithIndependentFonts()
{
var output = new ArrayFramebufferPresenter(800, 430); var canvas = new CpuFramebufferCanvas(output);
var fonts = PlatformFonts.CreateBundled(); var traces = new List<string>();
var page = new BrowserService { Trace = traces.Add }; page.SetCanvas(canvas); page.ResizeViewport(800, 430);
try
{
page.Navigate("about:Monaco");
for (int tick = 0; tick < 20; tick++) page.AdvanceTime(tick * 16);
Assert.Contains("Official Monaco 0.52.2 - ready", page.CurrentPage.Text);
Assert.Contains("const answer = 6 * 7;", page.ReadEditorText());
var list = page.Layout(new(800, 430));
foreach (var item in list.Commands.Where(command => command.Kind == BrowserDrawKind.Text && command.Opacity > 0))
{
try { fonts.Rasterize(item.Text, item.Font.Size, item.Font.Family, item.Font.Weight); }
catch (Exception error)
{
Assert.Fail("Monaco actual text raster failed: " + error.Message + " | " + item.Font + " | " +
string.Join(" ", item.Text.Select(character => "U+" + ((int)character).ToString("X4"))));
}
}
page.Paint(canvas, new(0, 0, 800, 430), 0); canvas.Present();
Assert.DoesNotContain(traces, trace => trace.Contains("PAINT_FAILED", StringComparison.Ordinal));
Assert.True(page.RasterPaintCount > 0);
Assert.True(output.Pixels.ToArray().Distinct().Count() > 30);
EvidencePng.Write("Monaco.png", output.Pixels.Span, output.Width, output.Height);
}
finally { page.Release(); }
}
[Fact]
public void OfficialFontsProduceRealProportionalMonoCjkAndIconCoverage()
{
var fonts = PlatformFonts.CreateBundled();
Assert.True(fonts.Measure("WWW", 16, "system-ui", 400) > fonts.Measure("iii", 16, "system-ui", 400) * 2);
Assert.Equal(fonts.Measure("WWW", 16, "Consolas", 400), fonts.Measure("iii", 16, "Consolas", 400), 5);
Assert.True(fonts.HasGlyph('中', "system-ui", 400));
foreach (var text in new[] { "ABC", "中文", "MWig" })
{
var mask = fonts.Rasterize(text, 20, "system-ui", 400);
Assert.Equal(mask.Width * mask.Height, mask.Alpha.Length); Assert.Contains(mask.Alpha, alpha => alpha > 0 && alpha < 255);
}
Assert.NotEqual(fonts.Rasterize("Bold", 20, "system-ui", 400).Alpha, fonts.Rasterize("Bold", 20, "system-ui", 700).Alpha);
// Bootstrap Icons 1.11.3 CSS maps bi-copy to U+F759.
Assert.True(fonts.HasGlyph('\uF759', "bootstrap-icons", 400));
Assert.Contains(fonts.Rasterize("\uF759", 20, "bootstrap-icons", 400).Alpha, value => value > 0);
Assert.True(fonts.CachedGlyphBytes > 0);
}
[Fact]
public void TextMaskReturnedToCallerCannotPoisonCachedRendering()
{
var output = new ArrayFramebufferPresenter(120, 40); var canvas = new CpuFramebufferCanvas(output);
var exposed = canvas.RasterizeWebText("Test", 20, "system-ui", 400);
Array.Fill(exposed.Alpha, (byte)0);
canvas.WebText("Test", 0xFFFFFF, 0, 0, 20, "system-ui", 400); canvas.Present();
Assert.Contains(output.Pixels.ToArray(), pixel => pixel != 0);
}
[Fact]
public void FontPreparationTransfersExclusiveCachesAndRejectsRepeatAndIncompatibleScale()
{
var canvas = new CpuFramebufferCanvas(new ArrayFramebufferPresenter(100, 100));
var preparation = Assert.IsType<CpuFramebufferCanvas>(canvas.CreateEditorPreparationCanvas());
Assert.Equal(1, preparation.NativeWidth);
preparation.PrepareEditorText("中文 Mono", 16, "Consolas", 400);
Assert.True(preparation.CachedTextMaskCount > 0);
Assert.False(preparation.TryCompleteEditorPreparationHandoff(new CpuFramebufferCanvas(new ArrayFramebufferPresenter(100, 100), scalePercent: 200)));
Assert.True(preparation.TryCompleteEditorPreparationHandoff(canvas));
Assert.True(canvas.CachedTextMaskCount > 0); Assert.False(preparation.TryCompleteEditorPreparationHandoff(canvas));
Assert.Throws<InvalidOperationException>(() => preparation.PrepareEditorText("Late", 16, "Consolas", 400));
}
[Fact]
public void ExistingDesktopRendersWithIndependentCanvasAndPointerPresentation()
{
var output = new ArrayFramebufferPresenter(1024, 768); var canvas = new CpuFramebufferCanvas(output);
var desktop = new DesktopSession(canvas, new MemoryWorkspace());
desktop.Open(DesktopApplication.Files); desktop.Open(DesktopApplication.Terminal); desktop.Render();
Assert.True(desktop.LastFrameCompleted); Assert.Equal(1, canvas.FramesPresented);
Assert.True(output.Pixels.ToArray().Distinct().Count() > 100);
EvidencePng.Write("Desktop.png", output.Pixels.Span, output.Width, output.Height);
Assert.True(canvas.TryFingerprintPresentedPixels(new(400, 200, 128, 128), out var before));
desktop.Pointer(440, 220, false, timestampMs: 1000); desktop.Render();
Assert.True(desktop.LastFrameCompleted); Assert.Equal(2, canvas.FramesPresented);
Assert.True(canvas.TryFingerprintPresentedPixels(new(400, 200, 128, 128), out var after));
Assert.NotEqual(before.Fingerprint, after.Fingerprint);
}
[Fact]
public void RealDomJintCssAndNativeFontBackendRenderChangedBrowserText()
{
var output = new ArrayFramebufferPresenter(480, 220); var canvas = new CpuFramebufferCanvas(output);
var page = new BrowserService(new HtmlTransport()); page.SetCanvas(canvas); page.ResizeViewport(480, 220);
page.Navigate("http://platform.test/");
Assert.Contains("JavaScript 中文", page.CurrentPage.Text);
page.Paint(canvas, new(0, 0, 480, 220), 0); canvas.Present();
Assert.True(output.Pixels.ToArray().Count(pixel => pixel == 0x112233) > 10000);
Assert.Contains(output.Pixels.ToArray(), pixel => pixel != 0 && pixel != 0x112233 && pixel != 0xF0F0F0);
EvidencePng.Write("Browser.png", output.Pixels.Span, output.Width, output.Height);
page.Release();
}
private sealed class HtmlTransport : IWebTransport
{
public WebResponse Get(Uri address, BrowserLimits limits) => new(address, 200, "text/html", """
<html><style>body{margin:0;background:#f0f0f0;font-family:system-ui}#box{width:300px;height:120px;background:#112233;color:#fff;font-size:24px;padding:12px}</style>
<body><div id='box'>before</div><script>document.getElementById('box').textContent='JavaScript 中文';</script></body></html>
""");
}
}
using XFESystem.Browser;
using XFESystem.Core;
using XFESystem.Desktop;
using XFESystem.Platform;
namespace XFESystem.Platform.Tests;
public sealed class RenderingTests
{
[Fact]
public void NonBreakingSpacesHaveAdvanceAndNoInkAtMonacoSizes()
{
var fonts = PlatformFonts.CreateBundled();
foreach (double size in new[] { 1d, 13d, 16d, 26d })
{
Assert.True(fonts.HasGlyph('\u00A0', "Consolas", 400));
Assert.Equal(fonts.Measure(" ", size, "Consolas", 400), fonts.Measure("\u00A0", size, "Consolas", 400), 5);
var blank = fonts.Rasterize("\u00A0", size, "Consolas", 400);
Assert.All(blank.Alpha, value => Assert.Equal((byte)0, value));
var run = fonts.Rasterize("A\u00A0B", size, "Consolas", 400);
Assert.Contains(run.Alpha, value => value > 0);
}
}
[Fact]
public void OfficialMonacoDomPaintsActualTextWithIndependentFonts()
{
var output = new ArrayFramebufferPresenter(800, 430); var canvas = new CpuFramebufferCanvas(output);
var fonts = PlatformFonts.CreateBundled(); var traces = new List<string>();
var page = new BrowserService { Trace = traces.Add }; page.SetCanvas(canvas); page.ResizeViewport(800, 430);
try
{
page.Navigate("about:Monaco");
for (int tick = 0; tick < 20; tick++) page.AdvanceTime(tick * 16);
Assert.Contains("Official Monaco 0.52.2 - ready", page.CurrentPage.Text);
Assert.Contains("const answer = 6 * 7;", page.ReadEditorText());
var list = page.Layout(new(800, 430));
foreach (var item in list.Commands.Where(command => command.Kind == BrowserDrawKind.Text && command.Opacity > 0))
{
try { fonts.Rasterize(item.Text, item.Font.Size, item.Font.Family, item.Font.Weight); }
catch (Exception error)
{
Assert.Fail("Monaco actual text raster failed: " + error.Message + " | " + item.Font + " | " +
string.Join(" ", item.Text.Select(character => "U+" + ((int)character).ToString("X4"))));
}
}
page.Paint(canvas, new(0, 0, 800, 430), 0); canvas.Present();
Assert.DoesNotContain(traces, trace => trace.Contains("PAINT_FAILED", StringComparison.Ordinal));
Assert.True(page.RasterPaintCount > 0);
Assert.True(output.Pixels.ToArray().Distinct().Count() > 30);
EvidencePng.Write("Monaco.png", output.Pixels.Span, output.Width, output.Height);
}
finally { page.Release(); }
}
[Fact]
public void OfficialFontsProduceRealProportionalMonoCjkAndIconCoverage()
{
var fonts = PlatformFonts.CreateBundled();
Assert.True(fonts.Measure("WWW", 16, "system-ui", 400) > fonts.Measure("iii", 16, "system-ui", 400) * 2);
Assert.Equal(fonts.Measure("WWW", 16, "Consolas", 400), fonts.Measure("iii", 16, "Consolas", 400), 5);
Assert.True(fonts.HasGlyph('中', "system-ui", 400));
foreach (var text in new[] { "ABC", "中文", "MWig" })
{
var mask = fonts.Rasterize(text, 20, "system-ui", 400);
Assert.Equal(mask.Width * mask.Height, mask.Alpha.Length); Assert.Contains(mask.Alpha, alpha => alpha > 0 && alpha < 255);
}
Assert.NotEqual(fonts.Rasterize("Bold", 20, "system-ui", 400).Alpha, fonts.Rasterize("Bold", 20, "system-ui", 700).Alpha);
// Bootstrap Icons 1.11.3 CSS maps bi-copy to U+F759.
Assert.True(fonts.HasGlyph('\uF759', "bootstrap-icons", 400));
Assert.Contains(fonts.Rasterize("\uF759", 20, "bootstrap-icons", 400).Alpha, value => value > 0);
Assert.True(fonts.CachedGlyphBytes > 0);
}
[Fact]
public void TextMaskReturnedToCallerCannotPoisonCachedRendering()
{
var output = new ArrayFramebufferPresenter(120, 40); var canvas = new CpuFramebufferCanvas(output);
var exposed = canvas.RasterizeWebText("Test", 20, "system-ui", 400);
Array.Fill(exposed.Alpha, (byte)0);
canvas.WebText("Test", 0xFFFFFF, 0, 0, 20, "system-ui", 400); canvas.Present();
Assert.Contains(output.Pixels.ToArray(), pixel => pixel != 0);
}
[Fact]
public void FontPreparationTransfersExclusiveCachesAndRejectsRepeatAndIncompatibleScale()
{
var canvas = new CpuFramebufferCanvas(new ArrayFramebufferPresenter(100, 100));
var preparation = Assert.IsType<CpuFramebufferCanvas>(canvas.CreateEditorPreparationCanvas());
Assert.Equal(1, preparation.NativeWidth);
preparation.PrepareEditorText("中文 Mono", 16, "Consolas", 400);
Assert.True(preparation.CachedTextMaskCount > 0);
Assert.False(preparation.TryCompleteEditorPreparationHandoff(new CpuFramebufferCanvas(new ArrayFramebufferPresenter(100, 100), scalePercent: 200)));
Assert.True(preparation.TryCompleteEditorPreparationHandoff(canvas));
Assert.True(canvas.CachedTextMaskCount > 0); Assert.False(preparation.TryCompleteEditorPreparationHandoff(canvas));
Assert.Throws<InvalidOperationException>(() => preparation.PrepareEditorText("Late", 16, "Consolas", 400));
}
[Fact]
public void ExistingDesktopRendersWithIndependentCanvasAndPointerPresentation()
{
var output = new ArrayFramebufferPresenter(1024, 768); var canvas = new CpuFramebufferCanvas(output);
var desktop = new DesktopSession(canvas, new MemoryWorkspace());
desktop.Open(DesktopApplication.Files); desktop.Open(DesktopApplication.Terminal); desktop.Render();
Assert.True(desktop.LastFrameCompleted); Assert.Equal(1, canvas.FramesPresented);
Assert.True(output.Pixels.ToArray().Distinct().Count() > 100);
EvidencePng.Write("Desktop.png", output.Pixels.Span, output.Width, output.Height);
Assert.True(canvas.TryFingerprintPresentedPixels(new(400, 200, 128, 128), out var before));
desktop.Pointer(440, 220, false, timestampMs: 1000); desktop.Render();
Assert.True(desktop.LastFrameCompleted); Assert.Equal(2, canvas.FramesPresented);
Assert.True(canvas.TryFingerprintPresentedPixels(new(400, 200, 128, 128), out var after));
Assert.NotEqual(before.Fingerprint, after.Fingerprint);
}
[Fact]
public void RealDomJintCssAndNativeFontBackendRenderChangedBrowserText()
{
var output = new ArrayFramebufferPresenter(480, 220); var canvas = new CpuFramebufferCanvas(output);
var page = new BrowserService(new HtmlTransport()); page.SetCanvas(canvas); page.ResizeViewport(480, 220);
page.Navigate("http://platform.test/");
Assert.Contains("JavaScript 中文", page.CurrentPage.Text);
page.Paint(canvas, new(0, 0, 480, 220), 0); canvas.Present();
Assert.True(output.Pixels.ToArray().Count(pixel => pixel == 0x112233) > 10000);
Assert.Contains(output.Pixels.ToArray(), pixel => pixel != 0 && pixel != 0x112233 && pixel != 0xF0F0F0);
EvidencePng.Write("Browser.png", output.Pixels.Span, output.Width, output.Height);
page.Release();
}
private sealed class HtmlTransport : IWebTransport
{
public WebResponse Get(Uri address, BrowserLimits limits) => new(address, 200, "text/html", """
<html><style>body{margin:0;background:#f0f0f0;font-family:system-ui}#box{width:300px;height:120px;background:#112233;color:#fff;font-size:24px;padding:12px}</style>
<body><div id='box'>before</div><script>document.getElementById('box').textContent='JavaScript 中文';</script></body></html>
""");
}
}