using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnitTestSharp;
namespace Glass.Blacklight.UnitTests
{
///
/// Glass duplicates Blacklight.Core.Keyboard's KeyCode/KeyModifiers/KeyState/KeyInput/Chord wholesale rather
/// than referencing them, to keep Glass free of a Blacklight dependency. These tests are the tripwire for
/// the two copies drifting apart.
///
public class KeyboardMirrorTests : TestFixture
{
public void KeyCode_MembersMatchByNameAndOrder()
{
CheckEqual(Enum.GetNames(typeof(global::Blacklight.Core.Keyboard.KeyCode)),
Enum.GetNames(typeof(Glass.KeyCode)));
}
public void KeyModifiers_MembersMatchByNameAndValue()
{
CheckEqual(NamesAndValues(typeof(global::Blacklight.Core.Keyboard.KeyModifiers)),
NamesAndValues(typeof(Glass.KeyModifiers)));
}
public void KeyState_PublicSurfaceMatches()
{
CheckEqual(Describe(typeof(global::Blacklight.Core.Keyboard.KeyState)), Describe(typeof(Glass.KeyState)));
}
public void KeyInput_PublicSurfaceMatches()
{
CheckEqual(Describe(typeof(global::Blacklight.Core.Keyboard.KeyInput)), Describe(typeof(Glass.KeyInput)));
}
public void Chord_PublicSurfaceMatches()
{
CheckEqual(Describe(typeof(global::Blacklight.Core.Keyboard.Chord)), Describe(typeof(Glass.Chord)));
}
[IgnoreTest]
private static List NamesAndValues(Type enumType)
{
return Enum.GetNames(enumType)
.Select(name => $"{name}={Convert.ToInt64(Enum.Parse(enumType, name))}")
.OrderBy(entry => entry, StringComparer.Ordinal)
.ToList();
}
// Describes a type's public surface by member shape (kind/name/simple type names) rather than by exact
// Type identity, since the two mirrors live in different assemblies and so never share an actual Type.
[IgnoreTest]
private static List Describe(Type type)
{
var returnMe = new List();
foreach (ConstructorInfo ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
{
returnMe.Add($"ctor({ParameterShape(ctor.GetParameters())})");
}
const BindingFlags declaredMembers =
BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
foreach (FieldInfo field in type.GetFields(declaredMembers))
{
returnMe.Add($"field {field.FieldType.Name} {field.Name}");
}
foreach (PropertyInfo property in type.GetProperties(declaredMembers))
{
returnMe.Add($"property {property.PropertyType.Name} {property.Name}");
}
foreach (MethodInfo method in type.GetMethods(declaredMembers))
{
// Property get_/set_ accessors are already covered by the property above; everything else --
// ordinary methods, operators, and a record's compiler-generated Equals/GetHashCode/ToString --
// is signature-relevant.
if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
{
continue;
}
string parameters = ParameterShape(method.GetParameters());
returnMe.Add($"method {method.ReturnType.Name} {method.Name}({parameters})");
}
returnMe.Sort(StringComparer.Ordinal);
return returnMe;
}
[IgnoreTest]
private static string ParameterShape(ParameterInfo[] parameters)
{
return string.Join(",", parameters.Select(parameter => parameter.ParameterType.Name));
}
}
}