Files
Nitrox/Nitrox.Test/Patcher/PatchTestHelper.cs
2025-07-06 00:23:46 +02:00

88 lines
3.3 KiB
C#

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using FluentAssertions;
using HarmonyLib;
using NitroxModel.Helper;
using NitroxPatcher.PatternMatching;
namespace NitroxTest.Patcher
{
public static class PatchTestHelper
{
public static List<CodeInstruction> GenerateDummyInstructions(int count)
{
List<CodeInstruction> instructions = new List<CodeInstruction>();
for (int i = 0; i < count; i++)
{
instructions.Add(new CodeInstruction(OpCodes.Nop));
}
return instructions;
}
public static ReadOnlyCollection<CodeInstruction> GetInstructionsFromMethod(DynamicMethod targetMethod)
{
Validate.NotNull(targetMethod);
return GetInstructionsFromIL(GetILInstructions(targetMethod));
}
public static ReadOnlyCollection<CodeInstruction> GetInstructionsFromMethod(MethodInfo targetMethod)
{
Validate.NotNull(targetMethod);
return GetInstructionsFromIL(GetILInstructions(targetMethod));
}
public static IEnumerable<KeyValuePair<OpCode, object>> GetILInstructions(MethodInfo method)
{
return PatchProcessor.ReadMethodBody(method, method.GetILGenerator());
}
public static IEnumerable<KeyValuePair<OpCode, object>> GetILInstructions(DynamicMethod method)
{
return PatchProcessor.ReadMethodBody(method, method.GetILGenerator());
}
public static ILGenerator GetILGenerator(this MethodInfo method)
{
return new DynamicMethod(method.Name, method.ReturnType, method.GetParameters().Types()).GetILGenerator();
}
public static void TestPattern(MethodInfo targetMethod, InstructionsPattern pattern, out IEnumerable<CodeInstruction> originalIl, out IEnumerable<CodeInstruction> transformedIl)
{
bool shouldHappen = false;
originalIl = PatchProcessor.GetCurrentInstructions(targetMethod);
transformedIl = originalIl
.Transform(pattern, (_, _) =>
{
shouldHappen = true;
})
.ToArray(); // Required, otherwise nothing happens.
shouldHappen.Should().BeTrue();
}
/// <summary>
/// Clones the instructions so that the returned instructions are not the same reference.
/// </summary>
/// <remarks>
/// Useful for testing code differences before and after a Harmony transpiler.
/// </remarks>
public static List<CodeInstruction> Clone(this IEnumerable<CodeInstruction> instructions)
{
return new List<CodeInstruction>(instructions.Select(il => new CodeInstruction(il)));
}
private static ReadOnlyCollection<CodeInstruction> GetInstructionsFromIL(IEnumerable<KeyValuePair<OpCode, object>> il)
{
List<CodeInstruction> result = new List<CodeInstruction>();
foreach (KeyValuePair<OpCode, object> instruction in il)
{
result.Add(new CodeInstruction(instruction.Key, instruction.Value));
}
return result.AsReadOnly();
}
}
}