152 lines
6.9 KiB
C#
152 lines
6.9 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using BlockModLoader;
|
|
|
|
internal static class BlockModLoaderTests
|
|
{
|
|
internal static void Run()
|
|
{
|
|
var unrelated = new[]
|
|
{
|
|
typeof(LoaderGuard).Assembly, CreateAssembly("MelonLoader.Helper"),
|
|
CreateAssembly("BepInEx.Helper"), CreateAssembly("BepInEx.Core.Helper"),
|
|
CreateAssembly("UnityModManager.Helper"), CreateAssembly("Harmony")
|
|
};
|
|
Action<string> unexpected = message => { throw new Exception("Blocked an unrelated assembly."); };
|
|
LoaderGuard.EnsureAllowed("SampleMod", new Assembly[0], unexpected, unexpected);
|
|
LoaderGuard.EnsureAllowed("SampleMod", unrelated, unexpected, unexpected);
|
|
|
|
CheckLoader("MelonLoader", "MelonLoader");
|
|
CheckLoader("melonloader", "MelonLoader");
|
|
CheckLoader("BepInEx", "BepInEx");
|
|
CheckLoader("bepinex", "BepInEx");
|
|
CheckLoader("BepInEx.Core", "BepInEx");
|
|
CheckLoader("bepinex.core", "BepInEx");
|
|
CheckLoader("UnityModManager", "Unity Mod Manager (UMM)");
|
|
CheckLoader("unitymodmanager", "Unity Mod Manager (UMM)");
|
|
CheckWhiteList();
|
|
|
|
foreach (var loaderName in new[] { "MelonLoader", "BepInEx", "BepInEx.Core", "UnityModManager" })
|
|
{
|
|
foreach (var recommendation in new[] { null, "", " \t ", "CustomLoader", " UMM " })
|
|
CheckRecommendation(loaderName, recommendation);
|
|
}
|
|
LoaderGuard.EnsureAllowed("SampleMod", unrelated, unexpected, unexpected, "CustomLoader");
|
|
|
|
var alerts = 0;
|
|
var terminations = 0;
|
|
LoaderGuard.EnsureAllowed("SampleMod", new[]
|
|
{ unrelated[1], CreateAssembly("MelonLoader"), CreateAssembly("BepInEx.Core"), CreateAssembly("UnityModManager") },
|
|
message => alerts++, message => terminations++);
|
|
if (alerts != 1 || terminations != 1)
|
|
throw new Exception("Multiple loaders should trigger only one notification and termination.");
|
|
}
|
|
|
|
private static void CheckWhiteList()
|
|
{
|
|
var names = new[] { "melonloader", "bepinex", "bepinex.core", "unitymodmanager" };
|
|
var types = new[] { WhiteListLoader.MelonLoader, WhiteListLoader.BepInEx,
|
|
WhiteListLoader.BepInEx, WhiteListLoader.UMM };
|
|
for (var flags = 0; flags <= (int)WhiteListLoader.All; flags++)
|
|
{
|
|
var whiteList = (WhiteListLoader)flags;
|
|
for (var i = 0; i < names.Length; i++)
|
|
{
|
|
var alerts = 0;
|
|
var terminations = 0;
|
|
LoaderGuard.EnsureAllowed("SampleMod", new[] { CreateAssembly(names[i]) },
|
|
message => alerts++, message => terminations++, "CustomLoader", whiteList);
|
|
var expected = (whiteList & types[i]) != 0 ? 0 : 1;
|
|
if (alerts != expected || terminations != expected)
|
|
throw new Exception("Whitelist incorrectly handled " + names[i] + " with " + whiteList);
|
|
}
|
|
|
|
var mixedAlerts = 0;
|
|
var mixedTerminations = 0;
|
|
LoaderGuard.EnsureAllowed("SampleMod", new[] { CreateAssembly("BepInEx.Core"),
|
|
CreateAssembly("UnityModManager"), CreateAssembly("MelonLoader") },
|
|
message => mixedAlerts++, message => mixedTerminations++, null, whiteList);
|
|
var mixedExpected = whiteList == WhiteListLoader.All ? 0 : 1;
|
|
if (mixedAlerts != mixedExpected || mixedTerminations != mixedExpected)
|
|
throw new Exception("An allowed loader bypassed detection of a blocked loader.");
|
|
}
|
|
|
|
foreach (var invalid in new[] { (WhiteListLoader)8, (WhiteListLoader)(-1) })
|
|
{
|
|
try
|
|
{
|
|
LoaderGuard.EnsureAllowed("SampleMod", new Assembly[0],
|
|
message => { }, message => { }, null, invalid);
|
|
throw new Exception("Expected invalid whitelist flags to be rejected.");
|
|
}
|
|
catch (ArgumentOutOfRangeException) { }
|
|
}
|
|
}
|
|
|
|
private static void CheckRecommendation(string loaderName, string recommendation)
|
|
{
|
|
string alertMessage = null;
|
|
var terminations = 0;
|
|
LoaderGuard.EnsureAllowed("SampleMod", new[] { CreateAssembly(loaderName) },
|
|
message =>
|
|
{
|
|
alertMessage = message;
|
|
if (string.IsNullOrWhiteSpace(recommendation))
|
|
{
|
|
if (message.Contains("Recommended mod loader:"))
|
|
throw new Exception("An empty recommendation should not be displayed.");
|
|
}
|
|
else if (!message.Contains("Recommended mod loader: " + recommendation.Trim() + ".\n\n"))
|
|
throw new Exception("The configured recommendation was not displayed.");
|
|
},
|
|
message =>
|
|
{
|
|
if (alertMessage == null || message != alertMessage)
|
|
throw new Exception("Recommended loader message was lost before termination.");
|
|
terminations++;
|
|
}, recommendation);
|
|
if (terminations != 1)
|
|
throw new Exception("A recommendation changed loader blocking behavior.");
|
|
}
|
|
|
|
private static void CheckLoader(string assemblyName, string loaderName)
|
|
{
|
|
var loader = CreateAssembly(assemblyName);
|
|
var alerts = 0;
|
|
var terminations = 0;
|
|
string alertMessage = null;
|
|
LoaderGuard.EnsureAllowed("SampleMod", new[] { typeof(LoaderGuard).Assembly, loader },
|
|
message =>
|
|
{
|
|
if (!message.Contains("SampleMod cannot run while " + loaderName + " is active.")
|
|
|| !message.Contains("follow this mod's installation instructions.")
|
|
|| !message.Contains("The game will close"))
|
|
throw new Exception("Missing mod name, detected loader, or installation guidance.");
|
|
alertMessage = message;
|
|
alerts++;
|
|
},
|
|
message =>
|
|
{
|
|
if (alerts != 1 || message != alertMessage)
|
|
throw new Exception("Termination preceded notification or used a different message.");
|
|
terminations++;
|
|
});
|
|
if (alerts != 1 || terminations != 1) throw new Exception(assemblyName + " was not blocked.");
|
|
|
|
try
|
|
{
|
|
LoaderGuard.EnsureAllowed("SampleMod", new[] { loader },
|
|
message => { throw new InvalidOperationException("Dialog unavailable."); },
|
|
message => terminations++);
|
|
throw new Exception("Expected the simulated dialog failure.");
|
|
}
|
|
catch (InvalidOperationException) { }
|
|
if (terminations != 2) throw new Exception("Dialog failure bypassed termination for " + assemblyName);
|
|
}
|
|
|
|
private static Assembly CreateAssembly(string name)
|
|
{
|
|
return AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.Run);
|
|
}
|
|
}
|