Start UnitTests

extended-keycode-rework
Clive Galway 2 years ago
parent 4b2a82b5fa
commit 64cab136ea

@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencie
Dependencies\Readme.md = Dependencies\Readme.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{8EDF4429-251A-416D-BB68-93F227191BCF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -26,6 +28,10 @@ Global
{02CBCBB9-C17F-4C6A-8F93-D7EAF038CAED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02CBCBB9-C17F-4C6A-8F93-D7EAF038CAED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02CBCBB9-C17F-4C6A-8F93-D7EAF038CAED}.Release|Any CPU.Build.0 = Release|Any CPU
{8EDF4429-251A-416D-BB68-93F227191BCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8EDF4429-251A-416D-BB68-93F227191BCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8EDF4429-251A-416D-BB68-93F227191BCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8EDF4429-251A-416D-BB68-93F227191BCF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -17,13 +17,14 @@ The purpose of this class is to encapsulate the logic required to deal with this
*/
namespace AutoHotInterception.Helpers
{
class TranslatedKey
public class TranslatedKey
{
public ushort AhkCode { get; set; }
//public List<KeyStroke> Strokes { get; set; }
public KeyStroke FirstStroke { get; }
public KeyStroke SecondStroke { get; set; }
public bool IsExtended { get; }
public int State { get; set; }
public TranslatedKey(KeyStroke stroke, bool isExtended)
{
@ -32,13 +33,64 @@ namespace AutoHotInterception.Helpers
}
}
public static class SpecialKeys
{
public static SpecialKey NumpadEnter { get; set; } = new SpecialKey("Numpad Enter", 28, ExtMode.E0, CodeType.High, Order.Normal);
public static SpecialKey RightControl { get; } = new SpecialKey("Right Control", 29, ExtMode.E1, CodeType.High, Order.Normal);
public static SpecialKey NumpadDiv { get; } = new SpecialKey("Numpad Div", 53, ExtMode.E1, CodeType.High, Order.Normal);
public static SpecialKey RightShift { get; set; } = new SpecialKey("Right Shift", 54, ExtMode.E0, CodeType.High, Order.Normal);
public static SpecialKey Pause { get; set; } = new SpecialKey("Pause", 69, ExtMode.E0, CodeType.Low, Order.Prefixed);
public static SpecialKey Home { get; set; } = new SpecialKey("Home", 71, ExtMode.E1, CodeType.High, Order.Wrapped);
public static List<SpecialKey> List { get; set; } = new List<SpecialKey>()
{
NumpadEnter, RightControl, NumpadDiv, Pause, Home
};
}
// Whether the AHK ScanCode is Low (same as Interception) or Hight (Interception + 256)
public enum CodeType { Low, High };
// Whether Press/Release states are 0/1 (E0), 2/3 (E1) or 4/5 (E2)
public enum ExtMode { E0, E1, E2};
// Order of the strokes received
public enum Order { Normal /* Stroke order is Key press, Key release (No Extended Modifier) */
, Wrapped /* Stroke order is Ext Modifier press, Key press, Key release, Ext Modifier Release */
, Prefixed /* Stroke order is Ext Modifier press, Key press, Ext Modifier release, Key release */};
public class SpecialKey
{
public string Name { get; }
public ushort Code { get; }
public ExtMode ExtendedMode { get; }
public CodeType CodeType { get; }
public Order StrokeOrder { get; }
public SpecialKey(string name, ushort code, ExtMode extendedMode, CodeType codeType, Order strokeOrderW)
{
// The name of the key
Name = name;
// The code that identifies this key
Code = code;
// What values will be reported for press/release states for this key
ExtendedMode = extendedMode;
// Whether AHK uses a High (+256) or Low code for this key
CodeType = codeType;
}
}
class ScanCodeHelper
public class ScanCodeHelper
{
//private KeyStroke? _extendedBuffer;
private TranslatedKey _translatedKey;
// Converts Interception state to AHK state
private static List<ushort> _stateConverter = new List<ushort>() { 1, 0 , 1, 0, 1, 0 };
// Converts state to extended mode
private static List<ushort> _stateToExtendedMode = new List<ushort>() { 0, 0, 1, 1, 2, 2 };
// Keys which AHK assigns a
/*
// Keys which AHK assigns a high code to, even though state is 0/1
// These keys also do not generate extended key codes
private Dictionary<ushort, string> _highCodes = new Dictionary<ushort, string>()
{
{ 28, "Nummpad Enter" },
@ -47,9 +99,43 @@ namespace AutoHotInterception.Helpers
};
// Keys which have an extended state, but extended modifiers are not sent
private Dictionary<ushort, string> _noExtendedModifier = new Dictionary<ushort, string>()
{
{29, "Right Control" },
{53, "Numpad Div" },
{56, "Right Alt" },
{91, "Left Windows" },
{92, "Right Windows" },
{93, "Apps" }
};
*/
public ScanCodeHelper()
{
for (int i = 0; i < SpecialKeys.List.Count; i++)
{
var specialKey = SpecialKeys.List[i];
var dict = specialKey.CodeType == CodeType.Low ? _lowCodes : _highCodes;
dict.Add(specialKey.Code, specialKey.Name);
if (specialKey.ExtendedMode != ExtMode.E0 && specialKey.StrokeOrder == Order.Normal)
{
_noExtendedModifier.Add(specialKey.Code, specialKey.Name);
}
}
}
private Dictionary<ushort, string> _highCodes = new Dictionary<ushort, string>();
private Dictionary<ushort, string> _lowCodes = new Dictionary<ushort, string>();
// Keys which have E1 or E2 state, but do not send extended modifier
private Dictionary<ushort, string> _noExtendedModifier = new Dictionary<ushort, string>();
public TranslatedKey TranslateScanCode(KeyStroke stroke)
{
if(stroke.state > 1)
//if(stroke.state > 1 && !_noExtendedModifier.ContainsKey(stroke.code))
//if (stroke.state > 1 || stroke.code == SpecialKeys.Pause.Code )
if (stroke.state > 1 && !_noExtendedModifier.ContainsKey(stroke.code))
{
// Stroke is part of Extended key sequence of 2 keys
if (_translatedKey == null)
@ -67,13 +153,15 @@ namespace AutoHotInterception.Helpers
}
else
{
// Regular key
// Stroke is a single key sequence
_translatedKey = new TranslatedKey(stroke, false);
var code = stroke.code;
if (_highCodes.ContainsKey(code))
{
code += 256;
}
_translatedKey.AhkCode = code;
_translatedKey.State = _stateConverter[stroke.state];
}
var returnValue = _translatedKey;
if (!_translatedKey.IsExtended)

@ -0,0 +1,135 @@
// NUnit 3 tests
// See documentation : https://github.com/nunit/docs/wiki/NUnit-Documentation
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using AutoHotInterception.Helpers;
using NUnit.Framework;
using static AutoHotInterception.Helpers.ManagedWrapper;
namespace UnitTests
{
public class TestKey
{
public string Name { get; }
public List<KeyStroke> PressStrokes { get; }
public List<KeyStroke> ReleaseStrokes { get; }
public List<ExpectedResult> PressResults { get; }
public List<ExpectedResult> ReleaseResults { get; }
public TestKey(string name, List<KeyStroke> pressStrokes, List<KeyStroke> releaseStrokes,
List<ExpectedResult> pressResults, List<ExpectedResult> releaseResults)
{
Name = name;
PressStrokes = pressStrokes;
ReleaseStrokes = releaseStrokes;
PressResults = pressResults;
ReleaseResults = releaseResults;
}
}
public class ExpectedResult
{
public ushort Code { get; }
public ushort State { get; }
public ExpectedResult(ushort code, ushort state)
{
Code = code;
State = state;
}
}
[TestFixture]
public class ScanCodeHelperTests
{
ScanCodeHelper sch;
private static List<TestKey> _testKeys = new List<TestKey>()
{
new TestKey("Numpad Enter", Stroke(28, 0), Stroke(28, 1), Result(284, 1), Result(284, 0)),
new TestKey("Right Control", Stroke(29, 2), Stroke(29, 3), Result(285, 1), Result(285, 0)),
new TestKey("Numpad Div", Stroke(53, 2), Stroke(53, 3), Result(309, 1), Result(309, 0)),
new TestKey("Right Shift", Stroke(54, 0), Stroke(54, 1), Result(310, 1), Result(310, 0)),
};
[SetUp]
public void SetUpBeforeEachTest()
{
sch = new ScanCodeHelper();
}
private static List<KeyStroke> Stroke (ushort code1, ushort state1, ushort code2 = 0, ushort state2 = 0)
{
var strokes = new List<KeyStroke>();
strokes.Add(new KeyStroke() { code = code1, state = state1 });
if (code2 != 0)
{
strokes.Add(new KeyStroke() { code = code2, state = state2 });
}
return strokes;
}
private static List<ExpectedResult> Result(ushort? code1, ushort? state1, ushort? code2 = null, ushort? state2 = null)
{
var results = new List<ExpectedResult>();
if (code1 == null) results.Add(null);
else results.Add(new ExpectedResult((ushort)code1, (ushort)state1));
if (code2 == null) results.Add(null);
else results.Add(new ExpectedResult((ushort)code2, (ushort)state2));
return results;
}
[Test]
public void PressReleaseTests()
{
DoTest(_testKeys[3]);
//foreach (var testKey in _testKeys)
//{
// DoTest(testKey);
//}
}
private void DoTest(TestKey testKey)
{
Debug.WriteLine($"\nTesting key {testKey.Name}...");
Debug.WriteLine("Testing Press");
for (int i = 0; i < testKey.PressStrokes.Count; i++)
{
var stroke = testKey.PressStrokes[i];
Debug.WriteLine($"Sending stroke #{i+1} with code {stroke.code}, state {stroke.state}");
var expectedResult = testKey.PressResults[i];
var actualResult = sch.TranslateScanCode(stroke);
AssertResult(actualResult, expectedResult);
}
Debug.WriteLine("Testing Release");
for (int i = 0; i < testKey.ReleaseStrokes.Count; i++)
{
var stroke = testKey.ReleaseStrokes[i];
Debug.WriteLine($"Sending stroke #{i+1} with code {stroke.code}, state {stroke.state}");
var expectedResult = testKey.ReleaseResults[i];
var actualResult = sch.TranslateScanCode(stroke);
AssertResult(actualResult, expectedResult);
}
Debug.WriteLine("OK!");
}
void AssertResult(TranslatedKey actualResult, ExpectedResult expectedResult)
{
if (expectedResult == null)
{
Debug.WriteLine($"Expecting result of null");
Assert.That(actualResult == null, "Result should be null");
}
else
{
Debug.WriteLine($"Expecting code of {expectedResult.Code}, state of {expectedResult.State}");
Assert.That(actualResult != null, "Result should not be null");
Assert.That(actualResult.AhkCode, Is.EqualTo(expectedResult.Code), $"Code should be {expectedResult.Code}, got {actualResult.AhkCode}");
Assert.That(actualResult.State, Is.EqualTo(expectedResult.State), $"State should be {expectedResult.State}, got {actualResult.State}");
}
}
}
}

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="16.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\NUnit3TestAdapter.4.1.0\build\net35\NUnit3TestAdapter.props" Condition="Exists('..\packages\NUnit3TestAdapter.4.1.0\build\net35\NUnit3TestAdapter.props')" />
<Import Project="..\packages\NUnit.3.13.2\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.13.2\build\NUnit.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8EDF4429-251A-416D-BB68-93F227191BCF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests</RootNamespace>
<AssemblyName>UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="nunit.framework, Version=3.13.2.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.13.2\lib\net45\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="TestClass.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AutoHotInterception\AutoHotInterception.csproj">
<Project>{68fa4bc3-c277-44d0-8333-18d51dc3ca19}</Project>
<Name>AutoHotInterception</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\NUnit.3.13.2\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.13.2\build\NUnit.props'))" />
<Error Condition="!Exists('..\packages\NUnit3TestAdapter.4.1.0\build\net35\NUnit3TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit3TestAdapter.4.1.0\build\net35\NUnit3TestAdapter.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="3.13.2" targetFramework="net48" />
<package id="NUnit3TestAdapter" version="4.1.0" targetFramework="net48" />
</packages>
Loading…
Cancel
Save