using XFESystem.Desktop;
using Xunit;
namespace XFESystem.Core.Tests;
public sealed class DisplayTimingTests
{
// Actual QEMU VGA MMIO BAR2 EDID captured via QMP pmemsave, not fabricated DTDs.
// Generator: https://github.com/qemu/qemu/blob/v11.1.0/hw/display/edid-generate.c
// Captures: build/CSharpBoot/DisplayChain-{1791939973026737100,1791940007931634500}/QemuEdid.bin
private const string Qemu240Hex = "00FFFFFFFFFFFF0049143412000000002A180104A561367806EE91A3544C99260F5054210800E1C0D1C0D100A940B300950081808140000000F7000A004082002820000000000000000000FD00327D1EA0FF010A202020202020000000FC0051454D55204D6F6E69746F720A0000001000000000000000000000000000000278020309004460591F6100000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000000000000000000157013170300030014353E0488FF0E3F05BF0372006F084A000900090065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090";
private const string Qemu160Hex = "00FFFFFFFFFFFF0049143412000000002A180104A561367806EE91A3544C99260F5054210800E1C0D1C0D100A940B300950081808140000000F7000A004082002820000000000000000000FD00327D1EA0FF010A202020202020000000FC0051454D55204D6F6E69746F720A0000001000000000000000000000000000000278020309004460591F610000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000000000000000000015701317030003001423D40288FF0E3F05BF0372006F084A0009000900E3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090";
[Theory]
[InlineData(true, 240000)]
[InlineData(false, 160000)]
public void ActualQemu4KTimingComesFromDisplayIdExtension(bool high, int expectedMilliHz)
{
var result = DisplayTiming.Read(Convert.FromHexString(high ? Qemu240Hex : Qemu160Hex), 3840, 2160);
Assert.True(result.IsValid); Assert.True(result.IsComplete);
Assert.Equal(2, result.ValidatedExtensionBlocks);
Assert.Equal(expectedMilliHz, result.AdvertisedRefreshMilliHz);
Assert.Contains("DisplayID 1.3 Type I", result.Source);
Assert.Equal(1, result.MatchedTimingCount);
Assert.Equal(970, result.PhysicalWidthMillimeters);
Assert.Equal(540, result.PhysicalHeightMillimeters);
}
[Fact]
public void QemuHighRateDoesNotMasqueradeAsAnotherFramebufferMode()
{
var result = DisplayTiming.Read(Convert.FromHexString(Qemu240Hex), 4096, 2160);
Assert.True(result.IsValid); Assert.False(result.HasAdvertisedTiming);
Assert.Equal(0, result.AdvertisedRefreshMilliHz); Assert.Equal(0, result.MatchedTimingCount);
}
[Fact]
public void MissingQemuExtensionDoesNotInvent240HzOrDiscardValidBaseMetadata()
{
var result = DisplayTiming.Read(Convert.FromHexString(Qemu240Hex).AsSpan(0, 128), 3840, 2160);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(0, result.AdvertisedRefreshMilliHz); Assert.Equal(970, result.PhysicalWidthMillimeters);
Assert.Contains("missing", result.FailureReason);
}
[Fact]
public void HighestExactResolutionAcrossBaseAndCtaDtdIsSelected()
{
byte[] bytes = WithCta();
SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850, 600, 340);
SetDtd(bytes.AsSpan(132, 18), 1920, 1080, 280, 45, 35640);
Checksum(bytes.AsSpan(0, 128)); Checksum(bytes.AsSpan(128, 128));
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.True(result.IsComplete); Assert.Equal(144000, result.AdvertisedRefreshMilliHz);
Assert.Equal(2, result.MatchedTimingCount); Assert.Contains("CTA DTD", result.Source);
Assert.Equal(600, result.PhysicalWidthMillimeters); Assert.Equal(340, result.PhysicalHeightMillimeters);
}
[Fact]
public void CorruptExtensionCannotContributeTimingAndBaseRemainsUsable()
{
byte[] bytes = WithCta();
SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850);
SetDtd(bytes.AsSpan(132, 18), 1920, 1080, 280, 45, 35640);
Checksum(bytes.AsSpan(0, 128)); Checksum(bytes.AsSpan(128, 128)); bytes[255] ^= 1;
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(60000, result.AdvertisedRefreshMilliHz); Assert.Equal(0, result.ValidatedExtensionBlocks);
}
[Fact]
public void RangeMaximumAloneIsNeverAConcreteTiming()
{
byte[] bytes = Base(); bytes[57] = 0xFD; bytes[59] = 30; bytes[60] = 240; Checksum(bytes);
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.True(result.IsValid); Assert.Equal(0, result.AdvertisedRefreshMilliHz);
}
[Fact]
public void MissingExtensionsPreserveActualBaseDtd()
{
byte[] bytes = Base(2);
SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850); Checksum(bytes);
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(60000, result.AdvertisedRefreshMilliHz); Assert.Equal(2, result.DeclaredExtensionBlocks);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(127)]
public void ShortInputIsRejectedWithoutReadingOutsideTheSpan(int length) =>
Assert.False(DisplayTiming.Read(new byte[length], 3840, 2160).IsValid);
[Theory]
[InlineData(0, 4)]
[InlineData(1, 2)]
[InlineData(1, 5)]
public void UnsupportedBaseVersionIsRejected(int major, int minor)
{
byte[] bytes = Base(); bytes[18] = (byte)major; bytes[19] = (byte)minor; Checksum(bytes);
Assert.False(DisplayTiming.Read(bytes, 1920, 1080).IsValid);
}
[Fact]
public void BadBaseChecksumRejectsAllExtensionInformation()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[20] ^= 1;
Assert.False(DisplayTiming.Read(bytes, 3840, 2160).IsValid);
}
[Fact]
public void BadDisplayIdInternalChecksumIsRejectedEvenWithValidOuterChecksum()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[264] ^= 1; Checksum(bytes.AsSpan(256, 128));
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.True(result.IsValid); Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Fact]
public void MalformedDisplayIdLengthCannotEscapeItsBlock()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[258] = 255; Checksum(bytes.AsSpan(256, 128));
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.True(result.IsValid); Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Fact]
public void MalformedTypeOnePayloadLengthIsRejectedBeforeAnyTimingIsRead()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[263] = 19; DisplayIdChecksum(bytes.AsSpan(256, 128));
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Theory]
[InlineData(0)]
[InlineData(4)]
public void UnsupportedCtaRevisionCannotContributeDtd(int revision)
{
byte[] bytes = WithCta(); bytes[129] = (byte)revision;
SetDtd(bytes.AsSpan(132, 18), 1920, 1080, 280, 45, 14850); Checksum(bytes.AsSpan(128, 128));
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Fact]
public void MalformedCtaDataBlockDoesNotTurnItsPayloadIntoDtd()
{
byte[] bytes = WithCta(); bytes[130] = 5; bytes[132] = 0x5F; Checksum(bytes.AsSpan(128, 128));
Assert.False(DisplayTiming.Read(bytes, 1920, 1080).IsComplete);
}
[Fact]
public void InterlacedTimingIsNotReportedAsProgressiveFramebufferRefresh()
{
byte[] bytes = Base(); SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850);
bytes[71] = 0x80; Checksum(bytes);
Assert.False(DisplayTiming.Read(bytes, 1920, 1080).HasAdvertisedTiming);
}
[Fact]
public void OversizedExtensionDeclarationIsBoundedAndNotReportedComplete()
{
var result = DisplayTiming.Read(Base(255), 1920, 1080);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(0, result.ValidatedExtensionBlocks); Assert.Equal(255, result.DeclaredExtensionBlocks);
}
[Fact]
public void EdidAspectEncodingIsNotMisreportedAsPhysicalDimensions()
{
byte[] bytes = Base(); bytes[21] = 178; bytes[22] = 0; Checksum(bytes);
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.Equal(0, result.PhysicalWidthMillimeters); Assert.Equal(0, result.PhysicalHeightMillimeters);
}
private static byte[] Base(byte extensions = 0)
{
byte[] bytes = new byte[128];
new byte[] { 0, 255, 255, 255, 255, 255, 255, 0 }.CopyTo(bytes, 0);
bytes[18] = 1; bytes[19] = 4; bytes[21] = 60; bytes[22] = 34; bytes[126] = extensions;
Checksum(bytes); return bytes;
}
private static byte[] WithCta()
{
byte[] bytes = Base(1); Array.Resize(ref bytes, 256);
bytes[128] = 2; bytes[129] = 3; bytes[130] = 4; Checksum(bytes.AsSpan(128, 128)); return bytes;
}
private static void SetDtd(Span<byte> dtd, int width, int height, int horizontalBlank, int verticalBlank,
int clockUnits, int physicalWidth = 0, int physicalHeight = 0)
{
dtd.Clear(); dtd[0] = (byte)clockUnits; dtd[1] = (byte)(clockUnits >> 8);
dtd[2] = (byte)width; dtd[3] = (byte)horizontalBlank;
dtd[4] = (byte)((width >> 8) << 4 | horizontalBlank >> 8);
dtd[5] = (byte)height; dtd[6] = (byte)verticalBlank;
dtd[7] = (byte)((height >> 8) << 4 | verticalBlank >> 8);
dtd[12] = (byte)physicalWidth; dtd[13] = (byte)physicalHeight;
dtd[14] = (byte)((physicalWidth >> 8) << 4 | physicalHeight >> 8);
}
private static void Checksum(Span<byte> block)
{
block[127] = 0; int sum = 0;
for (int index = 0; index < 127; index++) sum += block[index];
block[127] = (byte)(-sum & 255);
}
private static void DisplayIdChecksum(Span<byte> block)
{
int checksumIndex = 5 + block[2]; block[checksumIndex] = 0; int sum = 0;
for (int index = 1; index < checksumIndex; index++) sum += block[index];
block[checksumIndex] = (byte)(-sum & 255); Checksum(block);
}
}
using XFESystem.Desktop;
using Xunit;
namespace XFESystem.Core.Tests;
public sealed class DisplayTimingTests
{
// Actual QEMU VGA MMIO BAR2 EDID captured via QMP pmemsave, not fabricated DTDs.
// Generator: https://github.com/qemu/qemu/blob/v11.1.0/hw/display/edid-generate.c
// Captures: build/CSharpBoot/DisplayChain-{1791939973026737100,1791940007931634500}/QemuEdid.bin
private const string Qemu240Hex = "00FFFFFFFFFFFF0049143412000000002A180104A561367806EE91A3544C99260F5054210800E1C0D1C0D100A940B300950081808140000000F7000A004082002820000000000000000000FD00327D1EA0FF010A202020202020000000FC0051454D55204D6F6E69746F720A0000001000000000000000000000000000000278020309004460591F6100000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000010000000000000000000000000000000000000000000000000157013170300030014353E0488FF0E3F05BF0372006F084A000900090065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090";
private const string Qemu160Hex = "00FFFFFFFFFFFF0049143412000000002A180104A561367806EE91A3544C99260F5054210800E1C0D1C0D100A940B300950081808140000000F7000A004082002820000000000000000000FD00327D1EA0FF010A202020202020000000FC0051454D55204D6F6E69746F720A0000001000000000000000000000000000000278020309004460591F610000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000000000000000000015701317030003001423D40288FF0E3F05BF0372006F084A0009000900E3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090";
[Theory]
[InlineData(true, 240000)]
[InlineData(false, 160000)]
public void ActualQemu4KTimingComesFromDisplayIdExtension(bool high, int expectedMilliHz)
{
var result = DisplayTiming.Read(Convert.FromHexString(high ? Qemu240Hex : Qemu160Hex), 3840, 2160);
Assert.True(result.IsValid); Assert.True(result.IsComplete);
Assert.Equal(2, result.ValidatedExtensionBlocks);
Assert.Equal(expectedMilliHz, result.AdvertisedRefreshMilliHz);
Assert.Contains("DisplayID 1.3 Type I", result.Source);
Assert.Equal(1, result.MatchedTimingCount);
Assert.Equal(970, result.PhysicalWidthMillimeters);
Assert.Equal(540, result.PhysicalHeightMillimeters);
}
[Fact]
public void QemuHighRateDoesNotMasqueradeAsAnotherFramebufferMode()
{
var result = DisplayTiming.Read(Convert.FromHexString(Qemu240Hex), 4096, 2160);
Assert.True(result.IsValid); Assert.False(result.HasAdvertisedTiming);
Assert.Equal(0, result.AdvertisedRefreshMilliHz); Assert.Equal(0, result.MatchedTimingCount);
}
[Fact]
public void MissingQemuExtensionDoesNotInvent240HzOrDiscardValidBaseMetadata()
{
var result = DisplayTiming.Read(Convert.FromHexString(Qemu240Hex).AsSpan(0, 128), 3840, 2160);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(0, result.AdvertisedRefreshMilliHz); Assert.Equal(970, result.PhysicalWidthMillimeters);
Assert.Contains("missing", result.FailureReason);
}
[Fact]
public void HighestExactResolutionAcrossBaseAndCtaDtdIsSelected()
{
byte[] bytes = WithCta();
SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850, 600, 340);
SetDtd(bytes.AsSpan(132, 18), 1920, 1080, 280, 45, 35640);
Checksum(bytes.AsSpan(0, 128)); Checksum(bytes.AsSpan(128, 128));
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.True(result.IsComplete); Assert.Equal(144000, result.AdvertisedRefreshMilliHz);
Assert.Equal(2, result.MatchedTimingCount); Assert.Contains("CTA DTD", result.Source);
Assert.Equal(600, result.PhysicalWidthMillimeters); Assert.Equal(340, result.PhysicalHeightMillimeters);
}
[Fact]
public void CorruptExtensionCannotContributeTimingAndBaseRemainsUsable()
{
byte[] bytes = WithCta();
SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850);
SetDtd(bytes.AsSpan(132, 18), 1920, 1080, 280, 45, 35640);
Checksum(bytes.AsSpan(0, 128)); Checksum(bytes.AsSpan(128, 128)); bytes[255] ^= 1;
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(60000, result.AdvertisedRefreshMilliHz); Assert.Equal(0, result.ValidatedExtensionBlocks);
}
[Fact]
public void RangeMaximumAloneIsNeverAConcreteTiming()
{
byte[] bytes = Base(); bytes[57] = 0xFD; bytes[59] = 30; bytes[60] = 240; Checksum(bytes);
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.True(result.IsValid); Assert.Equal(0, result.AdvertisedRefreshMilliHz);
}
[Fact]
public void MissingExtensionsPreserveActualBaseDtd()
{
byte[] bytes = Base(2);
SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850); Checksum(bytes);
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(60000, result.AdvertisedRefreshMilliHz); Assert.Equal(2, result.DeclaredExtensionBlocks);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(127)]
public void ShortInputIsRejectedWithoutReadingOutsideTheSpan(int length) =>
Assert.False(DisplayTiming.Read(new byte[length], 3840, 2160).IsValid);
[Theory]
[InlineData(0, 4)]
[InlineData(1, 2)]
[InlineData(1, 5)]
public void UnsupportedBaseVersionIsRejected(int major, int minor)
{
byte[] bytes = Base(); bytes[18] = (byte)major; bytes[19] = (byte)minor; Checksum(bytes);
Assert.False(DisplayTiming.Read(bytes, 1920, 1080).IsValid);
}
[Fact]
public void BadBaseChecksumRejectsAllExtensionInformation()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[20] ^= 1;
Assert.False(DisplayTiming.Read(bytes, 3840, 2160).IsValid);
}
[Fact]
public void BadDisplayIdInternalChecksumIsRejectedEvenWithValidOuterChecksum()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[264] ^= 1; Checksum(bytes.AsSpan(256, 128));
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.True(result.IsValid); Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Fact]
public void MalformedDisplayIdLengthCannotEscapeItsBlock()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[258] = 255; Checksum(bytes.AsSpan(256, 128));
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.True(result.IsValid); Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Fact]
public void MalformedTypeOnePayloadLengthIsRejectedBeforeAnyTimingIsRead()
{
byte[] bytes = Convert.FromHexString(Qemu240Hex); bytes[263] = 19; DisplayIdChecksum(bytes.AsSpan(256, 128));
var result = DisplayTiming.Read(bytes, 3840, 2160);
Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Theory]
[InlineData(0)]
[InlineData(4)]
public void UnsupportedCtaRevisionCannotContributeDtd(int revision)
{
byte[] bytes = WithCta(); bytes[129] = (byte)revision;
SetDtd(bytes.AsSpan(132, 18), 1920, 1080, 280, 45, 14850); Checksum(bytes.AsSpan(128, 128));
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.False(result.IsComplete); Assert.False(result.HasAdvertisedTiming);
}
[Fact]
public void MalformedCtaDataBlockDoesNotTurnItsPayloadIntoDtd()
{
byte[] bytes = WithCta(); bytes[130] = 5; bytes[132] = 0x5F; Checksum(bytes.AsSpan(128, 128));
Assert.False(DisplayTiming.Read(bytes, 1920, 1080).IsComplete);
}
[Fact]
public void InterlacedTimingIsNotReportedAsProgressiveFramebufferRefresh()
{
byte[] bytes = Base(); SetDtd(bytes.AsSpan(54, 18), 1920, 1080, 280, 45, 14850);
bytes[71] = 0x80; Checksum(bytes);
Assert.False(DisplayTiming.Read(bytes, 1920, 1080).HasAdvertisedTiming);
}
[Fact]
public void OversizedExtensionDeclarationIsBoundedAndNotReportedComplete()
{
var result = DisplayTiming.Read(Base(255), 1920, 1080);
Assert.True(result.IsValid); Assert.False(result.IsComplete);
Assert.Equal(0, result.ValidatedExtensionBlocks); Assert.Equal(255, result.DeclaredExtensionBlocks);
}
[Fact]
public void EdidAspectEncodingIsNotMisreportedAsPhysicalDimensions()
{
byte[] bytes = Base(); bytes[21] = 178; bytes[22] = 0; Checksum(bytes);
var result = DisplayTiming.Read(bytes, 1920, 1080);
Assert.Equal(0, result.PhysicalWidthMillimeters); Assert.Equal(0, result.PhysicalHeightMillimeters);
}
private static byte[] Base(byte extensions = 0)
{
byte[] bytes = new byte[128];
new byte[] { 0, 255, 255, 255, 255, 255, 255, 0 }.CopyTo(bytes, 0);
bytes[18] = 1; bytes[19] = 4; bytes[21] = 60; bytes[22] = 34; bytes[126] = extensions;
Checksum(bytes); return bytes;
}
private static byte[] WithCta()
{
byte[] bytes = Base(1); Array.Resize(ref bytes, 256);
bytes[128] = 2; bytes[129] = 3; bytes[130] = 4; Checksum(bytes.AsSpan(128, 128)); return bytes;
}
private static void SetDtd(Span<byte> dtd, int width, int height, int horizontalBlank, int verticalBlank,
int clockUnits, int physicalWidth = 0, int physicalHeight = 0)
{
dtd.Clear(); dtd[0] = (byte)clockUnits; dtd[1] = (byte)(clockUnits >> 8);
dtd[2] = (byte)width; dtd[3] = (byte)horizontalBlank;
dtd[4] = (byte)((width >> 8) << 4 | horizontalBlank >> 8);
dtd[5] = (byte)height; dtd[6] = (byte)verticalBlank;
dtd[7] = (byte)((height >> 8) << 4 | verticalBlank >> 8);
dtd[12] = (byte)physicalWidth; dtd[13] = (byte)physicalHeight;
dtd[14] = (byte)((physicalWidth >> 8) << 4 | physicalHeight >> 8);
}
private static void Checksum(Span<byte> block)
{
block[127] = 0; int sum = 0;
for (int index = 0; index < 127; index++) sum += block[index];
block[127] = (byte)(-sum & 255);
}
private static void DisplayIdChecksum(Span<byte> block)
{
int checksumIndex = 5 + block[2]; block[checksumIndex] = 0; int sum = 0;
for (int index = 1; index < checksumIndex; index++) sum += block[index];
block[checksumIndex] = (byte)(-sum & 255); Checksum(block);
}
}