Files
AdamuSw/tests/MUnique.OpenMU.Tests/GameMapTest.cs
2026-07-14 19:00:35 +03:00

182 lines
8.5 KiB
C#

// <copyright file="GameMapTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
using Nito.AsyncEx;
namespace MUnique.OpenMU.Tests;
using Moq;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.Pathfinding;
/// <summary>
/// Tests for the game map.
/// </summary>
[TestFixture]
public class GameMapTest
{
private const byte ChunkSize = 8;
/// <summary>
/// An interface which combines several other interfaces which are needed in combination for this test.
/// </summary>
public interface ITestPlayer : ILocateable, IBucketMapObserver, IObservable, ISupportIdUpdate
{
}
/// <summary>
/// Tests if the discovery of players works when a new player is entering the map in the view range.
/// </summary>
[Test]
public async ValueTask TestPlayerEntersMapAsync()
{
var map = new GameMap(new GameMapDefinition(), TimeSpan.FromSeconds(60), ChunkSize);
var player1 = this.GetPlayer();
player1.Object.Position = new Point(100, 100);
await map.AddAsync(player1.Object).ConfigureAwait(false);
var player2 = this.GetPlayer();
player2.Object.Position = new Point(101, 100);
await map.AddAsync(player2.Object).ConfigureAwait(false);
player1.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player2.Object))), Times.Once);
player2.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player1.Object))), Times.Once);
player1.Verify(p => p.LocateableAddedAsync(It.IsAny<ILocateable>()), Times.Once);
}
/// <summary>
/// Tests if movements of a player into the view range of another player causes
/// that the players get notified about each other as soon as they are in view range.
/// </summary>
[Test]
public async ValueTask TestPlayerMovesInMapAsync()
{
var map = new GameMap(new GameMapDefinition(), TimeSpan.FromSeconds(60), ChunkSize);
var player1 = this.GetPlayer();
await map.AddAsync(player1.Object).ConfigureAwait(false);
var player2 = this.GetPlayer();
player2.Object.Position = new Point(101, 100);
await map.AddAsync(player2.Object).ConfigureAwait(false);
await map.MoveAsync(player1.Object, new Point(100, 100), new AsyncLock(), 0).ConfigureAwait(false);
player1.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player2.Object))), Times.Once);
player1.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player1.Object))), Times.Once);
player2.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player1.Object))), Times.Once);
player2.Verify(p => p.LocateableAddedAsync(It.IsAny<ILocateable>()), Times.Once);
}
/// <summary>
/// Tests if movements of a player out of the view range of another player causes
/// that the players get notified about it.
/// </summary>
[Test]
public async ValueTask PlayerMovesOutOfRangeAsync()
{
var map = new GameMap(new GameMapDefinition(), TimeSpan.FromSeconds(60), ChunkSize);
var player1 = this.GetPlayer();
player1.Object.Position = new Point(101, 100);
await map.AddAsync(player1.Object).ConfigureAwait(false);
var player2 = this.GetPlayer();
player2.Object.Position = new Point(101, 100);
await map.AddAsync(player2.Object).ConfigureAwait(false);
await map.MoveAsync(player1.Object, new Point(100, 130), new AsyncLock(), 0).ConfigureAwait(false);
player1.Verify(p => p.LocateablesOutOfScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player2.Object))), Times.Once);
player2.Verify(p => p.LocateableRemovedAsync(It.IsAny<ILocateable>()), Times.Once);
}
/// <summary>
/// Tests if movements of a player into and out of the view range of another player causes
/// that the players get notified about it.
/// </summary>
[Test]
public async ValueTask PlayerMovesOutAndIntoTheRangeAsync()
{
var map = new GameMap(new GameMapDefinition(), TimeSpan.FromSeconds(60), ChunkSize);
var player1 = this.GetPlayer();
player1.Object.Position = new Point(101, 100);
await map.AddAsync(player1.Object).ConfigureAwait(false);
var player2 = this.GetPlayer();
player2.Object.Position = new Point(101, 100);
await map.AddAsync(player2.Object).ConfigureAwait(false);
player1.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player2.Object))), Times.Once);
player2.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player1.Object))), Times.Once);
player1.Verify(p => p.LocateableAddedAsync(It.IsAny<ILocateable>()), Times.Once);
player1.Invocations.Clear();
player2.Invocations.Clear();
await map.MoveAsync(player1.Object, new Point(100, 130), new AsyncLock(), 0).ConfigureAwait(false);
player1.Verify(p => p.LocateablesOutOfScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player2.Object))), Times.Once);
player2.Verify(p => p.LocateableRemovedAsync(It.IsAny<ILocateable>()), Times.Once);
player1.Invocations.Clear();
player2.Invocations.Clear();
await map.MoveAsync(player2.Object, new Point(101, 130), new AsyncLock(), 0).ConfigureAwait(false);
player2.Verify(p => p.NewLocateablesInScopeAsync(It.Is<IEnumerable<ILocateable>>(n => n.Contains(player1.Object))), Times.Once);
player1.Verify(p => p.LocateableAddedAsync(It.IsAny<ILocateable>()), Times.Once);
}
/// <summary>
/// Tests the performance of the movements. Not a standard test.
/// </summary>
/// [Test]
public async ValueTask TestPerformanceMoveAsync()
{
var map = new GameMap(new GameMapDefinition(), TimeSpan.FromSeconds(60), ChunkSize);
var player1 = this.GetPlayer();
await map.AddAsync(player1.Object).ConfigureAwait(false);
var player2 = this.GetPlayer();
player2.Object.Position = new Point(101, 100);
await map.AddAsync(player2.Object).ConfigureAwait(false);
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
var moveLock = new AsyncLock();
for (int i = 0; i < 1000; i++)
{
await map.MoveAsync(player1.Object, new Point((byte)(100 + (i % 30)), (byte)(100 + (i % 30))), moveLock, 0).ConfigureAwait(false);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
/// <summary>
/// Tests if the players in view range get notified when another player leaves the map.
/// </summary>
[Test]
public async ValueTask TestPlayerLeavesMapAsync()
{
var map = new GameMap(new GameMapDefinition(), TimeSpan.FromSeconds(60), ChunkSize);
var player1 = this.GetPlayer();
player1.Object.Position = new Point(100, 100);
await map.AddAsync(player1.Object).ConfigureAwait(false);
var player2 = this.GetPlayer();
player2.Object.Position = new Point(101, 100);
await map.AddAsync(player2.Object).ConfigureAwait(false);
await map.RemoveAsync(player2.Object).ConfigureAwait(false);
Assert.AreEqual(player2.Object.ObservingBuckets.Count, 0);
player1.Verify(p => p.LocateableRemovedAsync(It.IsAny<ILocateable>()), Times.Once);
player2.Verify(p => p.LocateableRemovedAsync(It.IsAny<ILocateable>()), Times.Once);
Assert.That(player1.Object.Observers.Count, Is.EqualTo(0));
Assert.That(player2.Object.Observers.Count, Is.EqualTo(0));
}
private Mock<ITestPlayer> GetPlayer()
{
var player = new Mock<ITestPlayer>();
player.SetupAllProperties();
player.As<ILocateable>().SetupGet(p => p.Id).Returns(() => (player.Object as ISupportIdUpdate).Id);
player.Setup(p => p.ObservingBuckets).Returns(new List<Bucket<ILocateable>>());
player.Setup(p => p.Observers).Returns(new HashSet<IWorldObserver>());
player.Setup(p => p.ObserverLock).Returns(new AsyncReaderWriterLock());
player.Setup(p => p.InfoRange).Returns(20);
player.Setup(p => p.AddObserverAsync(It.IsAny<IWorldObserver>())).Callback<IWorldObserver>(o => player.Object.Observers.Add(o));
player.Setup(p => p.RemoveObserverAsync(It.IsAny<IWorldObserver>())).Callback<IWorldObserver>(o => player.Object.Observers.Remove(o));
return player;
}
}