using RioJoy.Core.Mapping; using Xunit; namespace RioJoy.Core.Tests.Mapping; public class InputRouterTests { private const byte SolidDim = 0x14; private const byte SolidBright = 0x3C; private static (InputRouter router, RecordingSink sink) Build( int address, ushort raw, int mouseStep = 50) { var map = new RioInputMap { [address] = new RioMapEntry(raw) }; var sink = new RecordingSink(); var router = new InputRouter(map, sink, sink, sink, sink, mouseStep); return (router, sink); } [Fact] public void Keyboard_WithModifiers_PressesInOrderAndLights() { // 0x8749 = hasLamp + alt + ctrl + shift, VK 0x49 (InputRouter router, RecordingSink sink) = Build(0x05, 0x8749); router.Press(0x05); Assert.Equal(new[] { "KeyDown(0x10,ext=False)", // SHIFT "KeyDown(0x11,ext=False)", // CTRL "KeyDown(0x12,ext=False)", // ALT "KeyDown(0x49,ext=False)", // key $"Lamp(0x05,0x{SolidBright:X2})", }, sink.Log); sink.Log.Clear(); router.Release(0x05); Assert.Equal(new[] { "KeyUp(0x49,ext=False)", // key first "KeyUp(0x12,ext=False)", // then ALT, CTRL, SHIFT (reverse) "KeyUp(0x11,ext=False)", "KeyUp(0x10,ext=False)", $"Lamp(0x05,0x{SolidDim:X2})", }, sink.Log); } [Fact] public void Keyboard_Extended_NoLamp() { // 0x08BE = extended, VK 0xBE, no lamp (InputRouter router, RecordingSink sink) = Build(0x10, 0x08BE); router.Press(0x10); Assert.Equal(new[] { "KeyDown(0xBE,ext=True)" }, sink.Log); sink.Log.Clear(); router.Release(0x10); Assert.Equal(new[] { "KeyUp(0xBE,ext=True)" }, sink.Log); } [Fact] public void Joystick_PressReleaseWithLamp() { // 0x9009 = hasLamp + joy, button 9 (InputRouter router, RecordingSink sink) = Build(0x03, 0x9009); router.Press(0x03); Assert.Equal(new[] { "Joy(9,True)", $"Lamp(0x03,0x{SolidBright:X2})" }, sink.Log); sink.Log.Clear(); router.Release(0x03); Assert.Equal(new[] { "Joy(9,False)", $"Lamp(0x03,0x{SolidDim:X2})" }, sink.Log); } [Fact] public void Hat_PressSetsDirection_ReleaseCenters() { // 0x2002 = hat, value 2 = Down (InputRouter router, RecordingSink sink) = Build(0x04, 0x2002); router.Press(0x04); Assert.Equal(new[] { "Hat(Down)" }, sink.Log); sink.Log.Clear(); router.Release(0x04); Assert.Equal(new[] { "Hat(Centered)" }, sink.Log); } [Theory] [InlineData(0x4000, "MouseMove(0,-50)")] // up [InlineData(0x4001, "MouseMove(50,0)")] // right [InlineData(0x4002, "MouseMove(0,50)")] // down [InlineData(0x4003, "MouseMove(-50,0)")] // left public void Mouse_MovePressOnly(ushort raw, string expectedMove) { (InputRouter router, RecordingSink sink) = Build(0x06, raw); router.Press(0x06); Assert.Equal(new[] { expectedMove }, sink.Log); sink.Log.Clear(); router.Release(0x06); // moves do nothing on release Assert.Empty(sink.Log); } [Fact] public void Mouse_ClickDownAndUp() { (InputRouter router, RecordingSink sink) = Build(0x06, 0x4004); // left click router.Press(0x06); router.Release(0x06); Assert.Equal(new[] { "MouseButton(Left,True)", "MouseButton(Left,False)" }, sink.Log); } [Fact] public void Mouse_MoveStepIsConfigurable() { (InputRouter router, RecordingSink sink) = Build(0x06, 0x4001, mouseStep: 10); router.Press(0x06); Assert.Equal(new[] { "MouseMove(10,0)" }, sink.Log); } [Fact] public void RioCommand_Executes_AndSkipsLampEvenIfFlagged() { // 0xF006 = hasLamp + joy + hat + mouse (command), value 6 = GeneralReset (InputRouter router, RecordingSink sink) = Build(0x07, 0xF006); router.Press(0x07); Assert.Equal(new[] { "Cmd(GeneralReset)" }, sink.Log); // no lamp sink.Log.Clear(); router.Release(0x07); Assert.Empty(sink.Log); // release does nothing } [Fact] public void Unmapped_Entry_DoesNothing() { (InputRouter router, RecordingSink sink) = Build(0x08, 0x0000); router.Press(0x08); router.Release(0x08); Assert.Empty(sink.Log); } [Fact] public void InitializeLamps_DimsOnlyLampEntries() { var map = new RioInputMap { [0x00] = new RioMapEntry(0x8041), // hasLamp keyboard [0x01] = new RioMapEntry(0x0041), // no lamp [0x02] = new RioMapEntry(0x9009), // hasLamp joystick }; var sink = new RecordingSink(); var router = new InputRouter(map, sink, sink, sink, sink); router.InitializeLamps(); Assert.Equal(new[] { $"Lamp(0x00,0x{SolidDim:X2})", $"Lamp(0x02,0x{SolidDim:X2})", }, sink.Log); } }