baseline: OpenMU upstream b5a0961 (fresh source)

This commit is contained in:
Acentech Dev
2026-07-14 19:00:35 +03:00
parent 450402e47d
commit 36fc125d5c
11968 changed files with 748705 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
// <copyright file="CustomPlugInContainerTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.Reflection;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
/// <summary>
/// Tests for the <see cref="PlugInManager"/>.
/// </summary>
[TestFixture]
public class CustomPlugInContainerTest
{
/// <summary>
/// Tests if creating the plugin container with an interface without a <see cref="CustomPlugInContainerAttribute"/> throws an exception.
/// </summary>
[Test]
public void CreatingContainerWithNonMarkedTypeThrowsException()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
var mock = new Mock<CustomPlugInContainerBase<ITestCustomPlugIn>>(manager);
var exception = Assert.Throws<TargetInvocationException>(() =>
{
_ = mock.Object;
});
Assert.That(exception?.InnerException, Is.InstanceOf<ArgumentException>());
}
/// <summary>
/// Tests if a plugin can be retrieved from the custom container, when the plugin has been registered after the container was created.
/// </summary>
[Test]
public void GetPlugInFromCustomContainerWithRegisteredPlugInAfterRegistration()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
var container = new CustomTestPlugInContainer(manager);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn>();
var plugIn = container.GetPlugIn<ITestCustomPlugIn>();
Assert.That(plugIn, Is.Not.Null);
}
/// <summary>
/// Tests if a plugin can be retrieved from the custom container, when the plugin has been registered before the container was created.
/// </summary>
[Test]
public void GetPlugInFromCustomContainerWithInitiallyRegisteredPlugIn()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn>();
var container = new CustomTestPlugInContainer(manager);
var plugIn = container.GetPlugIn<ITestCustomPlugIn>();
Assert.That(plugIn, Is.Not.Null);
}
/// <summary>
/// Tests if a plugin can't be retrieved from the custom container when the plugin has been deactivated before.
/// </summary>
[Test]
public void DontGetPlugInFromCustomContainerAfterDeactivation()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn>();
var container = new CustomTestPlugInContainer(manager);
manager.DeactivatePlugIn<TestCustomPlugIn>();
var plugIn = container.GetPlugIn<ITestCustomPlugIn>();
Assert.That(plugIn, Is.Null);
}
/// <summary>
/// Tests if a plugin can't be retrieved from the custom container when the plugin isn't suitable for the container and therefore not effective.
/// </summary>
[Test]
public void DontGetPlugInFromCustomContainerIfItDoesntSuit()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
var container = new CustomTestPlugInContainer(manager) { CreateNewPlugIns = false };
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn>();
var plugIn = container.GetPlugIn<ITestCustomPlugIn>();
Assert.That(plugIn, Is.Null);
}
/// <summary>
/// Tests if the custom container replaces the plugin implementation if a new (more suitable) plugin is registered.
/// </summary>
[Test]
public void ReplacePlugInAtCustomContainer()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn>();
var container = new CustomTestPlugInContainer(manager);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn2>();
var plugIn = container.GetPlugIn<ITestCustomPlugIn>();
Assert.That(plugIn, Is.InstanceOf<TestCustomPlugIn2>());
}
/// <summary>
/// Tests if a plugin which got deactivated by another plugin, gets reactivated as soon as the other plugin gets deactivated.
/// </summary>
[Test]
public void ReactivatePlugInAtCustomContainer()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn>();
var container = new CustomTestPlugInContainer(manager);
manager.RegisterPlugIn<ITestCustomPlugIn, TestCustomPlugIn2>();
manager.DeactivatePlugIn<TestCustomPlugIn2>();
var plugIn = container.GetPlugIn<ITestCustomPlugIn>();
Assert.That(plugIn, Is.InstanceOf<TestCustomPlugIn>());
}
/// <summary>
/// Tests if a plugin can be retrieved with both of its implemented interfaces.
/// </summary>
[Test]
public void GetPlugInFromCustomContainerWithAllImplementedInterfaces()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), null, null);
var container = new CustomTestPlugInContainer(manager);
container.AddPlugIn(new TestCustomPlugIn2(), true);
Assert.That(container.GetPlugIn<ITestCustomPlugIn>(), Is.Not.Null);
Assert.That(container.GetPlugIn<IAnotherCustomPlugIn>(), Is.Not.Null);
}
}

View File

@@ -0,0 +1,47 @@
// <copyright file="CustomTestPlugInContainer.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
/// <summary>
/// A test implementation of a <see cref="CustomPlugInContainerBase{TPlugIn}"/>.
/// </summary>
public class CustomTestPlugInContainer : CustomPlugInContainerBase<ICustomTestPlugInContainer>
{
/// <summary>
/// Initializes a new instance of the <see cref="CustomTestPlugInContainer"/> class.
/// </summary>
/// <param name="manager">The plugin manager which manages this instance.</param>
public CustomTestPlugInContainer(PlugInManager manager)
: base(manager)
{
this.Initialize();
}
/// <summary>
/// Gets or sets a value indicating whether this instance creates new plug ins in <see cref="CreatePlugInIfSuitable"/>.
/// </summary>
public bool CreateNewPlugIns { get; set; } = true;
/// <inheritdoc />
protected override void CreatePlugInIfSuitable(Type plugInType)
{
if (this.CreateNewPlugIns)
{
this.AddPlugIn((ITestCustomPlugIn)Activator.CreateInstance(plugInType)!, true);
}
}
/// <inheritdoc />
protected override ICustomTestPlugInContainer? DetermineEffectivePlugIn(Type interfaceType)
{
return this.ActivePlugIns.FirstOrDefault(interfaceType.IsInstanceOfType);
}
/// <inheritdoc />
protected override bool IsNewPlugInReplacingOld(ICustomTestPlugInContainer currentEffectivePlugIn, ICustomTestPlugInContainer activatedPlugIn)
{
return true;
}
}

View File

@@ -0,0 +1,63 @@
// <copyright file="ExamplePlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// The implementation of the <see cref="IExamplePlugIn"/> which tells us if it got executed.
/// </summary>
/// <seealso cref="IExamplePlugIn" />
[Guid("9FCA692F-2BD5-4310-8755-E20761F94180")]
[PlugIn]
[Display(Name = nameof(ExamplePlugIn), Description = "Just an example plugin.")]
internal class ExamplePlugIn : IExamplePlugIn
{
/// <summary>
/// Gets a value indicating whether the plugin instance was executed in the test.
/// </summary>
public bool WasExecuted { get; private set; }
/// <inheritdoc />
public void DoStuff(Player player, string text, MyEventArgs args)
{
this.WasExecuted = true;
args.WasExecuted = true;
}
/// <summary>
/// A plugin of a nested type.
/// </summary>
/// <seealso cref="IExamplePlugIn" />
[Guid("B6D7E11D-E99D-4466-BAE1-87B043ED345D")]
[PlugIn]
[Display(Name = nameof(NestedPlugIn), Description = "A nested example plugin.")]
internal class NestedPlugIn : IExamplePlugIn
{
/// <inheritdoc/>
public void DoStuff(Player player, string text, MyEventArgs args)
{
// do nothing
}
}
/// <summary>
/// A plugin of a nested type which doesn't have a guid.
/// </summary>
/// <seealso cref="IExamplePlugIn" />
[PlugIn]
[Display(Name = nameof(NestedPlugIn), Description = "A nested example plugin without Guid.")]
internal class NestedWithoutGuid : IExamplePlugIn
{
/// <inheritdoc/>
public void DoStuff(Player player, string text, MyEventArgs args)
{
// does nothing, too
}
}
}

View File

@@ -0,0 +1,40 @@
// <copyright file="ExampleStrategyPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
/// <summary>
/// A test strategy plugin.
/// </summary>
/// <seealso cref="MUnique.OpenMU.PlugIns.Tests.IExampleStrategyPlugIn" />
[Guid("69A6FCD1-E828-4841-BE91-E064231ED7B9")]
[PlugIn]
[Display(Name = nameof(ExampleStrategyPlugIn), Description = "A test strategy plugin.")]
public class ExampleStrategyPlugIn : IExampleStrategyPlugIn
{
/// <summary>
/// Gets the command key which is handled by this strategy plugin type.
/// </summary>
public static string CommandKey => "/mytest";
/// <inheritdoc />
public string Key => CommandKey;
/// <summary>
/// Gets the handled command.
/// </summary>
/// <value>
/// The handled command.
/// </value>
public string? HandledCommand { get; private set; }
/// <inheritdoc/>
public void HandleCommand(string command)
{
this.HandledCommand = command;
}
}

View File

@@ -0,0 +1,16 @@
// <copyright file="ICustomTestPlugInContainer.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.Runtime.InteropServices;
/// <summary>
/// A common interface for all plugins managed by the <see cref="CustomTestPlugInContainer"/>.
/// </summary>
[CustomPlugInContainer("test custom container interface", "")]
[Guid("AD127356-FF4D-47EE-9E36-52DB0C2881B6")]
public interface ICustomTestPlugInContainer
{
}

View File

@@ -0,0 +1,24 @@
// <copyright file="IExamplePlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.Runtime.InteropServices;
using MUnique.OpenMU.GameLogic;
/// <summary>
/// Example interface for a plugin.
/// </summary>
[Guid("34AEED37-9D62-4AE1-9320-91BB620B39C2")]
[PlugInPoint("Example PlugIn Point", "This plugin point is an example.")]
public interface IExamplePlugIn
{
/// <summary>
/// Does some stuff.
/// </summary>
/// <param name="player">The player.</param>
/// <param name="text">The text.</param>
/// <param name="args">The <see cref="MyEventArgs"/> instance containing the event data.</param>
void DoStuff(Player player, string text, MyEventArgs args);
}

View File

@@ -0,0 +1,21 @@
// <copyright file="IExampleStrategyPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.Runtime.InteropServices;
/// <summary>
/// Interface for an example strategy plugin.
/// </summary>
[Guid("1E68B14C-9156-448A-A6AB-90E423A8E91C")]
[PlugInPoint("Strategy Plugin Test Interface", "A strategy plugin test interface")]
public interface IExampleStrategyPlugIn : IStrategyPlugIn<string>
{
/// <summary>
/// Handles the command.
/// </summary>
/// <param name="command">The command.</param>
void HandleCommand(string command);
}

View File

@@ -0,0 +1,19 @@
// <copyright file="ITestCustomPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
/// <summary>
/// A plugin interface for specific implementations for plugins managed by the <see cref="CustomTestPlugInContainer"/>.
/// </summary>
public interface ITestCustomPlugIn : ICustomTestPlugInContainer
{
}
/// <summary>
/// A plugin interface for specific implementations for plugins managed by the <see cref="CustomTestPlugInContainer"/>.
/// </summary>
public interface IAnotherCustomPlugIn : ICustomTestPlugInContainer
{
}

View File

@@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable;CS4014;VSTHRD110;VSTHRD100</WarningsAsErrors>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>bin\Debug\MUnique.OpenMU.PlugIns.Tests.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>bin\Release\MUnique.OpenMU.PlugIns.Tests.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\src\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
<Compile Include="..\..\src\SharedGlobalUsings.cs" Link="SharedGlobalUsings.cs" />
<Compile Include="..\SharedTestUsings.cs" Link="SharedTestUsings.cs" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\src\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\src\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="nunit" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\GameLogic\MUnique.OpenMU.GameLogic.csproj" />
<ProjectReference Include="..\MUnique.OpenMU.Tests\MUnique.OpenMU.Tests.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,24 @@
// <copyright file="MyEventArgs.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel;
/// <summary>
/// Event args for <see cref="IExamplePlugIn.DoStuff"/> which tell us, if the plugin got executed.
/// </summary>
/// <seealso cref="System.ComponentModel.CancelEventArgs" />
public class MyEventArgs : CancelEventArgs
{
/// <summary>
/// Gets or sets a value indicating whether the plugin instance was executed in the test.
/// </summary>
public bool WasExecuted { get; set; }
/// <summary>
/// Gets or sets a text.
/// </summary>
public string? Text { get; set; }
}

View File

@@ -0,0 +1,331 @@
// <copyright file="PlugInManagerTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel.Design;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using MUnique.OpenMU.Tests;
/// <summary>
/// Tests for the <see cref="PlugInManager"/>.
/// </summary>
[TestFixture]
public class PlugInManagerTest
{
/// <summary>
/// Tests if registering a plugin type creates a proxy for it.
/// </summary>
[Test]
public void RegisteringPlugInCreatesProxy()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.RegisterPlugIn<IExamplePlugIn, ExamplePlugIn>();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
Assert.That(point, Is.InstanceOf<IExamplePlugIn>());
Assert.That(point, Is.InstanceOf<IPlugInContainer<IExamplePlugIn>>());
}
/// <summary>
/// Tests if registered plugins are active by default.
/// </summary>
[Test]
public async ValueTask RegisteredPlugInsActiveByDefaultAsync()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var plugIn = new ExamplePlugIn();
manager.RegisterPlugInAtPlugInPoint<IExamplePlugIn>(plugIn);
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(plugIn.WasExecuted, Is.True);
}
/// <summary>
/// Tests if plugins can be deactivated and are not executed if they are.
/// </summary>
[Test]
public async ValueTask DeactivatingPlugInsAsync()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var plugIn = new ExamplePlugIn();
manager.RegisterPlugInAtPlugInPoint<IExamplePlugIn>(plugIn);
manager.DeactivatePlugIn<ExamplePlugIn>();
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(plugIn.WasExecuted, Is.False);
}
/// <summary>
/// Tests if deactivating a deactivated plugin doesn't cause issues.
/// </summary>
[Test]
public async ValueTask DeactivatingDeactivatedPlugInAsync()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var plugIn = new ExamplePlugIn();
manager.RegisterPlugInAtPlugInPoint<IExamplePlugIn>(plugIn);
manager.DeactivatePlugIn<ExamplePlugIn>();
manager.DeactivatePlugIn<ExamplePlugIn>();
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(plugIn.WasExecuted, Is.False);
}
/// <summary>
/// Tests if activating an activated plugin doesn't cause issues.
/// </summary>
[Test]
public async ValueTask ActivatingActivatedPlugInAsync()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var plugIn = new ExamplePlugIn();
manager.RegisterPlugInAtPlugInPoint<IExamplePlugIn>(plugIn);
manager.ActivatePlugIn<ExamplePlugIn>();
manager.ActivatePlugIn<ExamplePlugIn>();
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(plugIn.WasExecuted, Is.True);
}
/// <summary>
/// Tests if deactivating a plugin doesn't affect another plugin.
/// </summary>
[Test]
public async ValueTask DeactivatingOnePlugInDoesntAffectOthersAsync()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var plugIn = new ExamplePlugIn();
manager.RegisterPlugInAtPlugInPoint<IExamplePlugIn>(plugIn);
manager.RegisterPlugIn<IExamplePlugIn, ExamplePlugIn.NestedPlugIn>();
manager.DeactivatePlugIn<ExamplePlugIn.NestedPlugIn>();
manager.ActivatePlugIn<ExamplePlugIn.NestedPlugIn>();
manager.DeactivatePlugIn<ExamplePlugIn.NestedPlugIn>();
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(plugIn.WasExecuted, Is.True);
}
/// <summary>
/// Tests if the plugins are in the correct active-state if they got created with a <see cref="PlugInConfiguration"/>.
/// </summary>
/// <param name="active">If set to <c>true</c>, the <see cref="PlugInConfiguration"/> is configured to be active.</param>
[TestCase(true)]
[TestCase(false)]
public async ValueTask CreatedAndActiveByConfigurationAsync(bool active)
{
var configuration = new PlugInConfiguration
{
TypeId = typeof(ExamplePlugIn).GUID,
IsActive = active,
};
var manager = new PlugInManager(new List<PlugInConfiguration> { configuration }, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(args.WasExecuted, Is.EqualTo(active));
}
/// <summary>
/// Tests if a custom plugin in a non-existing assembly is not created and throws no errors.
/// </summary>
[Test]
public void CustomPlugInByExternalAssemblyNotFoundDoesntThrowError()
{
var configuration = new PlugInConfiguration
{
TypeId = new Guid("D88B1ACA-42B7-4A89-B3E0-3C97AA4C8578"),
IsActive = true,
ExternalAssemblyName = "DoesNotExist.dll",
};
_ = new PlugInManager(new List<PlugInConfiguration> { configuration }, new NullLoggerFactory(), this.CreateServiceProvider(), null);
}
/// <summary>
/// Tests if an unknown plugin in the configuration doesn't cause exceptions.
/// </summary>
[Test]
public void UnknownPlugInByConfigurationDoesntThrowError()
{
var configuration = new PlugInConfiguration
{
TypeId = new Guid("A9BDA3E2-4EB6-45C3-B234-37C1819C0CB6"),
IsActive = true,
};
_ = new PlugInManager(new List<PlugInConfiguration> { configuration }, new NullLoggerFactory(), this.CreateServiceProvider(), null);
}
/// <summary>
/// Tests if activating an unknown plugin doesn't cause exceptions.
/// </summary>
[Test]
public void ActivatingUnknownPlugInDoesNotThrowError()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.ActivatePlugIn(new Guid("4C38A813-F9BF-428A-8EA1-A6C90A87E583"));
}
/// <summary>
/// Tests if deactivating an unknown plugin doesn't cause exceptions.
/// </summary>
[Test]
public void DeactivatingUnknownPlugInDoesNotThrowError()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.ActivatePlugIn(new Guid("4C38A813-F9BF-428A-8EA1-A6C90A87E583"));
}
/// <summary>
/// Tests if plugins can be activated and are executed if they are.
/// </summary>
[Test]
public async ValueTask ActivatingPlugInsAsync()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var plugIn = new ExamplePlugIn();
manager.RegisterPlugInAtPlugInPoint<IExamplePlugIn>(plugIn);
manager.DeactivatePlugIn<ExamplePlugIn>();
manager.ActivatePlugIn<ExamplePlugIn>();
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var point = manager.GetPlugInPoint<IExamplePlugIn>();
point!.DoStuff(player, command, args);
Assert.That(plugIn.WasExecuted, Is.True);
}
/// <summary>
/// Tests the automatic discovery of plugins of the loaded assemblies.
/// </summary>
[Test]
public void AutoDiscovery()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.DiscoverAndRegisterPlugIns();
var examplePlugInPoint = manager.GetPlugInPoint<IExamplePlugIn>();
Assert.That(examplePlugInPoint, Is.InstanceOf<IExamplePlugIn>());
}
/// <summary>
/// Tests if registering a plug in without a unique identifier throws an error.
/// </summary>
[Test]
public void RegisteringPlugInWithoutGuidThrowsError()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
Assert.Throws<ArgumentException>(() => manager.RegisterPlugIn<IExamplePlugIn, ExamplePlugIn.NestedWithoutGuid>());
}
/// <summary>
/// Tests if the strategy provider is created for registered strategy plug in.
/// </summary>
[Test]
public void StrategyProviderCreatedForRegisteredStrategyPlugIn()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.RegisterPlugIn<IExampleStrategyPlugIn, ExampleStrategyPlugIn>();
var strategyProvider = manager.GetStrategyProvider<string, IExampleStrategyPlugIn>();
Assert.That(strategyProvider, Is.Not.Null);
}
/// <summary>
/// Tests if the strategy provider is not created when there is no registered strategy plug in yet.
/// </summary>
[Test]
public void StrategyProviderNotCreatedWithoutRegisteredStrategyPlugIn()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
var strategyProvider = manager.GetStrategyProvider<string, IExampleStrategyPlugIn>();
Assert.That(strategyProvider, Is.Null);
}
/// <summary>
/// Tests if the registered strategy plug in is available.
/// </summary>
[Test]
public void RegisteredStrategyPlugInAvailable()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.RegisterPlugIn<IExampleStrategyPlugIn, ExampleStrategyPlugIn>();
var strategy = manager.GetStrategy<IExampleStrategyPlugIn>(ExampleStrategyPlugIn.CommandKey);
Assert.That(strategy, Is.Not.Null);
Assert.That(strategy, Is.TypeOf<ExampleStrategyPlugIn>());
}
/// <summary>
/// Tests if the registered, but deactivated strategy plug in is not available.
/// </summary>
[Test]
public void DeactivatedStrategyPlugInNotAvailable()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.RegisterPlugIn<IExampleStrategyPlugIn, ExampleStrategyPlugIn>();
manager.DeactivatePlugIn<ExampleStrategyPlugIn>();
var strategy = manager.GetStrategy<IExampleStrategyPlugIn>(ExampleStrategyPlugIn.CommandKey);
Assert.That(strategy, Is.Null);
}
/// <summary>
/// Tests if registering an already registered strategy plug in does not throw an error.
/// </summary>
[Test]
public void RegisteringRegisteredStrategyPlugInDoesntThrowError()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.RegisterPlugIn<IExampleStrategyPlugIn, ExampleStrategyPlugIn>();
manager.RegisterPlugIn<IExampleStrategyPlugIn, ExampleStrategyPlugIn>();
}
/// <summary>
/// Tests if no plug in point is created and returned for strategy plug ins.
/// </summary>
[Test]
public void NoPlugInPointForStrategyPlugIn()
{
var manager = new PlugInManager(null, new NullLoggerFactory(), this.CreateServiceProvider(), null);
manager.RegisterPlugIn<IExampleStrategyPlugIn, ExampleStrategyPlugIn>();
Assert.That(manager.GetPlugInPoint<IExampleStrategyPlugIn>(), Is.Null);
}
private IServiceProvider CreateServiceProvider()
{
var provider = new ServiceContainer();
provider.AddService(typeof(ILoggerFactory), new NullLoggerFactory());
return provider;
}
}

View File

@@ -0,0 +1,190 @@
// <copyright file="PlugInProxyTypeGeneratorTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel;
using Nito.AsyncEx;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using MUnique.OpenMU.Tests;
/// <summary>
/// Tests for the <see cref="PlugInProxyTypeGenerator"/>.
/// </summary>
[TestFixture]
public class PlugInProxyTypeGeneratorTest
{
/// <summary>
/// An interface with an unsupported method signature.
/// </summary>
[PlugInPoint("Async test", "Bar")]
public interface IAsyncPlugIn
{
/// <summary>
/// An async method.
/// </summary>
ValueTask MyMethodAsync();
}
/// <summary>
/// An interface with an unsupported method signature.
/// </summary>
[PlugInPoint("Foo", "Bar")]
internal interface IUnsupportedPlugIn
{
/// <summary>
/// Unsupported method.
/// </summary>
/// <returns>Some boolean.</returns>
bool UnsupportedMethod();
}
/// <summary>
/// Tests the proxy creation for <see cref="IExamplePlugIn"/>.
/// </summary>
[Test]
public void ProxyIsCreated()
{
var generator = new PlugInProxyTypeGenerator();
var proxy = generator.GenerateProxy<IExamplePlugIn>(new PlugInManager(null, new NullLoggerFactory(), null, null));
Assert.That(proxy, Is.Not.Null);
}
/// <summary>
/// Tests if multiple plugins are executed.
/// </summary>
[Test]
public async ValueTask MultiplePlugInsAreExecutedAsync()
{
var generator = new PlugInProxyTypeGenerator();
var proxy = generator.GenerateProxy<IExamplePlugIn>(new PlugInManager(null, NullLoggerFactory.Instance, null, null));
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var firstMock = new Mock<IExamplePlugIn>();
var secondMock = new Mock<IExamplePlugIn>();
firstMock.Setup(p => p.DoStuff(player, command, args)).Verifiable();
secondMock.Setup(p => p.DoStuff(player, command, args)).Verifiable();
proxy.AddPlugIn(firstMock.Object, true);
proxy.AddPlugIn(secondMock.Object, true);
(proxy as IExamplePlugIn)?.DoStuff(player, command, args);
firstMock.VerifyAll();
secondMock.VerifyAll();
}
/// <summary>
/// Tests if multiple plugins are executed.
/// </summary>
[Test]
public async ValueTask MultipleAsyncPlugInsAreExecutedAsync()
{
var generator = new PlugInProxyTypeGenerator();
var proxy = generator.GenerateProxy<IAsyncPlugIn>(new PlugInManager(null, NullLoggerFactory.Instance, null, null));
// Forcing to load NitoEx
_ = new AsyncReaderWriterLock();
_ = new AwaitableDisposable<IDisposable>(Task.FromResult((IDisposable)null!));
var firstMock = new Mock<IAsyncPlugIn>();
var secondMock = new Mock<IAsyncPlugIn>();
firstMock.Setup(p => p.MyMethodAsync()).Verifiable();
secondMock.Setup(p => p.MyMethodAsync()).Verifiable();
proxy.AddPlugIn(firstMock.Object, true);
proxy.AddPlugIn(secondMock.Object, true);
await ((IAsyncPlugIn)proxy).MyMethodAsync().ConfigureAwait(false);
firstMock.VerifyAll();
secondMock.VerifyAll();
}
/// <summary>
/// Tests if inactive plugins are not executed.
/// </summary>
[Test]
public async ValueTask InactivePlugInsAreNotExecutedAsync()
{
var generator = new PlugInProxyTypeGenerator();
var proxy = generator.GenerateProxy<IExamplePlugIn>(new PlugInManager(null, NullLoggerFactory.Instance, null, null));
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var firstMock = new Mock<IExamplePlugIn>();
var secondMock = new Mock<IExamplePlugIn>();
secondMock.Setup(p => p.DoStuff(player, command, args)).Verifiable();
proxy.AddPlugIn(firstMock.Object, false);
proxy.AddPlugIn(secondMock.Object, true);
(proxy as IExamplePlugIn)?.DoStuff(player, command, args);
firstMock.VerifyAll();
firstMock.VerifyNoOtherCalls();
secondMock.VerifyAll();
}
/// <summary>
/// Tests if parameters of <see cref="CancelEventArgs"/> are respected, so that when <see cref="CancelEventArgs.Cancel"/> is <c>true</c>,
/// next plugins are not executed anymore.
/// </summary>
[Test]
public async ValueTask CancelEventArgsAreRespectedAsync()
{
var generator = new PlugInProxyTypeGenerator();
var proxy = generator.GenerateProxy<IExamplePlugIn>(new PlugInManager(null, NullLoggerFactory.Instance, null, null));
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var command = "test";
var args = new MyEventArgs();
var firstMock = new Mock<IExamplePlugIn>();
var secondMock = new Mock<IExamplePlugIn>();
firstMock.Setup(p => p.DoStuff(player, command, args)).Callback(() => args.Cancel = true).Verifiable();
proxy.AddPlugIn(firstMock.Object, true);
proxy.AddPlugIn(secondMock.Object, true);
(proxy as IExamplePlugIn)?.DoStuff(player, command, args);
firstMock.VerifyAll();
secondMock.VerifyAll();
secondMock.VerifyNoOtherCalls();
}
/// <summary>
/// Tests if the proxy creation fails when a class is passed as proxy interface type.
/// </summary>
[Test]
public void ErrorForClasses()
{
var generator = new PlugInProxyTypeGenerator();
Assert.Throws<ArgumentException>(() => generator.GenerateProxy<ExamplePlugIn>(new PlugInManager(null, new NullLoggerFactory(), null, null)));
}
/// <summary>
/// Tests if the proxy creation fails when an interface without <see cref="PlugInPointAttribute"/> is passed as proxy interface type.
/// </summary>
[Test]
public void ErrorForInterfaceWithoutAttribute()
{
var generator = new PlugInProxyTypeGenerator();
Assert.Throws<ArgumentException>(() => generator.GenerateProxy<ICloneable>(new PlugInManager(null, new NullLoggerFactory(), null, null)));
}
/// <summary>
/// Tests if the proxy creation fails when an interface with an unsupported method signature is passed as proxy interface type.
/// </summary>
[Test]
public void ErrorForInterfaceWithUnsupportedMethodSignature()
{
var generator = new PlugInProxyTypeGenerator();
Assert.Throws<ArgumentException>(() => generator.GenerateProxy<IUnsupportedPlugIn>(new PlugInManager(null, new NullLoggerFactory(), null, null)));
}
}

View File

@@ -0,0 +1,90 @@
// <copyright file="PlugInTypeExtensionTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using MUnique.OpenMU.GameServer;
using MUnique.OpenMU.Network.PlugIns;
/// <summary>
/// Tests for the <see cref="PlugInTypeExtensions"/>.
/// </summary>
[TestFixture]
public class PlugInTypeExtensionTest
{
/// <summary>
/// Tests the maximum client version requirement.
/// </summary>
[Test]
public void ConsiderMaximumClientVersion()
{
Assert.That(new ClientVersion(6, 3, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeUntilSeason1)), Is.False);
Assert.That(new ClientVersion(1, 1, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeUntilSeason1)), Is.False);
Assert.That(new ClientVersion(1, 0, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeUntilSeason1)), Is.True);
Assert.That(new ClientVersion(0, 99, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeUntilSeason1)), Is.True);
}
/// <summary>
/// Tests the minimum client version requirement.
/// </summary>
[Test]
public void ConsiderMinimumClientVersion()
{
Assert.That(new ClientVersion(6, 3, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeAfterSeason2)), Is.True);
Assert.That(new ClientVersion(2, 0, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeAfterSeason2)), Is.True);
Assert.That(new ClientVersion(1, 255, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeAfterSeason2)), Is.False);
}
/// <summary>
/// Tests the minimum and maximum client version requirements in combination.
/// </summary>
[Test]
public void ConsiderMinimumAndMaximumClientVersion()
{
Assert.That(new ClientVersion(6, 3, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeBetweenSeason1And2)), Is.False);
Assert.That(new ClientVersion(2, 0, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeBetweenSeason1And2)), Is.True);
Assert.That(new ClientVersion(1, 0, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeBetweenSeason1And2)), Is.True);
Assert.That(new ClientVersion(0, 255, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeBetweenSeason1And2)), Is.False);
Assert.That(new ClientVersion(1, 1, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeAtExactlySeason1)), Is.False);
Assert.That(new ClientVersion(1, 0, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeAtExactlySeason1)), Is.True);
Assert.That(new ClientVersion(0, 255, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInTypeAtExactlySeason1)), Is.False);
}
/// <summary>
/// Tests if minimum and maximum client version requirements are not inherited from base classes.
/// </summary>
[Test]
public void DontConsiderInheritedAttributes()
{
Assert.That(new ClientVersion(1, 1, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInWithInheritedAttribute)), Is.True);
Assert.That(new ClientVersion(0, 255, ClientLanguage.English).IsPlugInSuitable(typeof(PlugInWithInheritedAttribute)), Is.True);
}
[MaximumClient(1, 0, ClientLanguage.Invariant)]
private class PlugInTypeUntilSeason1
{
}
[MinimumClient(2, 0, ClientLanguage.Invariant)]
private class PlugInTypeAfterSeason2
{
}
[MinimumClient(1, 0, ClientLanguage.Invariant)]
[MaximumClient(2, 0, ClientLanguage.Invariant)]
private class PlugInTypeBetweenSeason1And2
{
}
[MaximumClient(1, 0, ClientLanguage.Invariant)]
[MinimumClient(1, 0, ClientLanguage.Invariant)]
private class PlugInTypeAtExactlySeason1
{
}
private class PlugInWithInheritedAttribute : PlugInTypeAtExactlySeason1
{
}
}

View File

@@ -0,0 +1,10 @@
// <copyright file="AssemblyInfo.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
using System.Reflection;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MUnique.OpenMU.PlugIns.Tests")]

View File

@@ -0,0 +1,16 @@
// <copyright file="TestCustomPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
/// <summary>
/// A test implementation of <see cref="ITestCustomPlugIn"/>.
/// </summary>
[PlugIn]
[Display(Name = nameof(TestCustomPlugIn))]
[Guid("77CF382A-2F87-4642-889A-85BF6D76E218")]
public class TestCustomPlugIn : ITestCustomPlugIn;

View File

@@ -0,0 +1,18 @@
// <copyright file="TestCustomPlugIn2.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.PlugIns.Tests;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
/// <summary>
/// A second test implementation of <see cref="ITestCustomPlugIn"/>.
/// </summary>
[PlugIn]
[Display(Name = nameof(TestCustomPlugIn2))]
[Guid("9431C449-1F0C-47C1-BE5D-F9E356090DAB")]
public class TestCustomPlugIn2 : ITestCustomPlugIn, IAnotherCustomPlugIn
{
}