- TargetFrameworks net48;net40. net48 keeps x64 + ViGEm + System.IO.Ports package; net40 adds Microsoft.Bcl.Async + System.ValueTuple and uses the in-box SerialPort. - Compat/TaskCompat bridges Task.Run/Delay/WhenAny/WhenAll (TaskEx on net40) and SemaphoreSlim.WaitAsync (net40 blocks briefly - trivial at 9600 baud). - IReadOnlyList/IReadOnlyDictionary -> IList/IDictionary throughout (net40 predates the IReadOnly* interfaces and the Bcl backport cannot make arrays implement them). - HashCode.Combine replaced with a manual combine (Bcl.HashCode has no net40 build); Marshal.SizeOf<T> -> typeof form; ViGEmJoystickSink gated #if !NET40. HidFeederJoystickSink stays on both flavors - it will drive RioGamepadXP.sys on XP via the same contract. Both TFMs build; 275 tests green on net48. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
74 lines
2.2 KiB
C#
74 lines
2.2 KiB
C#
using RioJoy.Core.Overlay;
|
|
using Xunit;
|
|
|
|
namespace RioJoy.Core.Tests.Overlay;
|
|
|
|
public class OverlayHitTesterTests
|
|
{
|
|
// Two stacked cells (the chroma-split overlap case) + one disjoint cell.
|
|
private static OverlayTemplate Template() => new()
|
|
{
|
|
Width = 1000,
|
|
Height = 400,
|
|
Regions =
|
|
{
|
|
new OverlayRegion { Name = "big", X = 0, Y = 0, Width = 200, Height = 100 },
|
|
new OverlayRegion { Name = "small", X = 50, Y = 25, Width = 100, Height = 50 },
|
|
new OverlayRegion { Name = "solo", X = 500, Y = 200, Width = 80, Height = 40 },
|
|
},
|
|
};
|
|
|
|
[Fact]
|
|
public void RegionsAt_ReturnsSmallestFirst()
|
|
{
|
|
IList<OverlayRegion> hits = OverlayHitTester.RegionsAt(Template(), 100, 50);
|
|
|
|
Assert.Equal(new[] { "small", "big" }, hits.Select(r => r.Name));
|
|
}
|
|
|
|
[Fact]
|
|
public void RegionsAt_MissReturnsEmpty()
|
|
{
|
|
Assert.Empty(OverlayHitTester.RegionsAt(Template(), 999, 399));
|
|
}
|
|
|
|
[Fact]
|
|
public void RegionsAt_EdgesAreInclusiveLeftTop_ExclusiveRightBottom()
|
|
{
|
|
OverlayTemplate t = Template();
|
|
|
|
Assert.Contains(OverlayHitTester.RegionsAt(t, 500, 200), r => r.Name == "solo");
|
|
Assert.Empty(OverlayHitTester.RegionsAt(t, 580, 240)); // right/bottom edge excluded
|
|
}
|
|
|
|
[Fact]
|
|
public void NextAt_FirstClickSelectsSmallest()
|
|
{
|
|
OverlayRegion? hit = OverlayHitTester.NextAt(Template(), 100, 50, currentName: null);
|
|
|
|
Assert.Equal("small", hit?.Name);
|
|
}
|
|
|
|
[Fact]
|
|
public void NextAt_CyclesThroughStackAndWraps()
|
|
{
|
|
OverlayTemplate t = Template();
|
|
|
|
Assert.Equal("big", OverlayHitTester.NextAt(t, 100, 50, "small")?.Name);
|
|
Assert.Equal("small", OverlayHitTester.NextAt(t, 100, 50, "big")?.Name); // wrap
|
|
Assert.Equal("small", OverlayHitTester.NextAt(t, 100, 50, "SOLO")?.Name); // current not in stack
|
|
}
|
|
|
|
[Fact]
|
|
public void NextAt_CurrentNameIsCaseInsensitive()
|
|
{
|
|
Assert.Equal("big", OverlayHitTester.NextAt(Template(), 100, 50, "SMALL")?.Name);
|
|
}
|
|
|
|
[Fact]
|
|
public void NextAt_MissReturnsNull()
|
|
{
|
|
Assert.Null(OverlayHitTester.NextAt(Template(), 5, 395, "small"));
|
|
}
|
|
}
|