diff options
Diffstat (limited to 'Plugins/MonoGame.Extended/tests')
87 files changed, 9309 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/tests/Directory.Build.props b/Plugins/MonoGame.Extended/tests/Directory.Build.props new file mode 100644 index 0000000..06b0a41 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/Directory.Build.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project> + + <Import Project="$(MSBuildThisFileDirectory)..\Directory.Build.props" /> + + <PropertyGroup> + <ArtifactsPath>$(SolutionDirectory).artifacts/tests</ArtifactsPath> + <ProjectCategory>tests</ProjectCategory> + <GenerateDocumentationFile>false</GenerateDocumentationFile> + <IsPackable>false</IsPackable> + <NoWarn>CA1707</NoWarn> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="MonoGame.Framework.DesktopGL" + Version="3.8.1.303" /> + + <PackageReference Include="NSubstitute" + Version="4.2.2" /> + + <PackageReference Include="xunit" + Version="2.6.2" + IsImplicitlyDefined="true" /> + + <PackageReference Include="Microsoft.NET.Test.Sdk" + Version="17.6.0" + IsImplicitlyDefined="true" /> + + <PackageReference Include="xunit.runner.visualstudio" + Version="2.5.6" + IsImplicitlyDefined="true" + IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" + PrivateAssets="all" /> + + <PackageReference Include="coverlet.collector" + Version="6.0.0" + IsImplicitlyDefined="true" + IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" + PrivateAssets="all" /> + </ItemGroup> + + <ItemGroup> + <Using Include="Xunit" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs new file mode 100644 index 0000000..39ac707 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs @@ -0,0 +1,416 @@ +using System; +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Collisions.Tests +{ + using MonoGame.Extended.Collisions.Layers; + + /// <summary> + /// Test collision of actors with various shapes. + /// </summary> + /// <remarks> + /// Uses the fact that <see cref="BasicActor"/> moves itself away from + /// <see cref="BasicWall"/> on collision. + /// </remarks> + public class CollisionComponentTests + { + private readonly CollisionComponent _collisionComponent; + private readonly GameTime _gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromMilliseconds(16)); + + public CollisionComponentTests() + { + _collisionComponent = new CollisionComponent(new RectangleF(Point2.Zero, new Point2(10, 10))); + } + + #region Circle Circle + + [Fact] + public void PenetrationVectorSameCircleTest() + { + Point2 pos1 = Point2.Zero; + Point2 pos2 = Point2.Zero; + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.Y - -4f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorSlightlyOverlappingCircleTest() + { + Point2 pos1 = new Point2(0, 1.5f); + Point2 pos2 = Point2.Zero; + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + // Actor should have moved up because the distance is shorter. + Assert.True(actor1.Position.Y > actor2.Position.Y); + // The circle centers should be about 4 units away after moving + Assert.True(Math.Abs(actor1.Position.Y - 4.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorSlightlyOverlappingOffAxisTest() + { + Point2 pos1 = new Point2(2, 2.5f); + Point2 pos2 = new Point2(2, 1); + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + // Actor should have moved up because the distance is shorter. + Assert.True(actor1.Position.Y > actor2.Position.Y); + // The circle centers should be about 4 units away after moving + Assert.True(Math.Abs(actor1.Position.Y - 5.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.X - 2.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationZeroRadiusCircleCircleTest() + { + Point2 pos1 = new Point2(0, 1.5f); + Point2 pos2 = Point2.Zero; + + IShapeF shape1 = new CircleF(pos1, 0); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + // Actor should have moved up because the distance is shorter. + Assert.True(actor1.Position.Y > actor2.Position.Y); + // The circle centers should be about 4 units away after moving + // Assert.True(Math.Abs(actor1.Position.Y - 2.0f) < float.Epsilon); + } + + #endregion + + #region Circle Rectangle + + [Fact] + public void PenetrationVectorCircleRectangleTest() + { + Point2 pos1 = new Point2(0, 1); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 0.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 3.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorCircleContainedInRectangleTest() + { + Point2 pos1 = new Point2(0, 0); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new CircleF(pos1, 1.0f); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 0.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - -2.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorCircleOffAxisRectangleTest() + { + Point2 pos1 = new Point2(2, 1); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 2.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 3.0f) < float.Epsilon); + } + + #endregion + + #region Rectangle Rectangle + + [Fact] + public void PenetrationVectorRectangleRectangleTest() + { + Point2 pos1 = new Point2(0, 0); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new RectangleF(pos1, new Size2(4, 2)); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 0.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 1.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorRectangleRectangleOffAxisTest() + { + Point2 pos1 = new Point2(4, 2); + Point2 pos2 = new Point2(3, 1); + + IShapeF shape1 = new RectangleF(pos1, new Size2(4, 2)); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 4.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 3.0f) < float.Epsilon); + } + + #endregion + + public class Behaviours : CollisionComponentTests + { + [Fact] + public void Actors_is_colliding() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var anotherStaticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + var anotherStaticActor = new CollisionIndicatingActor(anotherStaticBounds); + _collisionComponent.Insert(staticActor); + _collisionComponent.Insert(anotherStaticActor); + + _collisionComponent.Update(_gameTime); + + Assert.True(staticActor.IsColliding); + Assert.True(anotherStaticActor.IsColliding); + } + + [Fact] + public void Actors_is_not_colliding_when_dynamic_actor_is_moved_out_of_collision_bounds() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var dynamicBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + var dynamicActor = new CollisionIndicatingActor(dynamicBounds); + _collisionComponent.Insert(staticActor); + _collisionComponent.Insert(dynamicActor); + dynamicActor.MoveTo(new Point2(2, 2)); + + _collisionComponent.Update(_gameTime); + + Assert.False(staticActor.IsColliding); + Assert.False(dynamicActor.IsColliding); + } + + [Fact] + public void Actors_is_colliding_when_dynamic_actor_is_moved_after_update() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + _collisionComponent.Insert(staticActor); + for (int i = 0; i < QuadTree.QuadTree.DefaultMaxObjectsPerNode; i++) + { + var fillerBounds = new RectangleF(new Point2(0, 2), new Size2(.1f, .1f)); + var fillerActor = new CollisionIndicatingActor(fillerBounds); + _collisionComponent.Insert(fillerActor); + } + + var dynamicBounds = new RectangleF(new Point2(2, 2), new Size2(1, 1)); + var dynamicActor = new CollisionIndicatingActor(dynamicBounds); + _collisionComponent.Insert(dynamicActor); + + _collisionComponent.Update(_gameTime); + Assert.False(staticActor.IsColliding); + Assert.False(dynamicActor.IsColliding); + + dynamicActor.MoveTo(new Point2(0, 0)); + + _collisionComponent.Update(_gameTime); + Assert.True(dynamicActor.IsColliding); + Assert.True(staticActor.IsColliding); + } + + [Fact] + public void Actors_is_colliding_when_dynamic_actor_is_moved_into_collision_bounds() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var dynamicBounds = new RectangleF(new Point2(2, 2), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + var dynamicActor = new CollisionIndicatingActor(dynamicBounds); + _collisionComponent.Insert(staticActor); + _collisionComponent.Insert(dynamicActor); + dynamicActor.MoveTo(new Point2(0, 0)); + + _collisionComponent.Update(_gameTime); + + Assert.True(staticActor.IsColliding); + Assert.True(dynamicActor.IsColliding); + } + + [Fact] + public void InsertActor_ThrowsUndefinedLayerException_IfThereIsNoLayerDefined() + { + var sut = new CollisionComponent(); + + var act = () => sut.Insert(new CollisionIndicatingActor(RectangleF.Empty)); + + Assert.Throws<UndefinedLayerException>(act); + } + + private class CollisionIndicatingActor : ICollisionActor + { + private RectangleF _bounds; + + public CollisionIndicatingActor(RectangleF bounds) + { + _bounds = bounds; + } + + public IShapeF Bounds => _bounds; + + public void OnCollision(CollisionEventArgs collisionInfo) + { + IsColliding = true; + } + + public bool IsColliding { get; private set; } + + public void MoveTo(Point2 position) + { + _bounds = new RectangleF(position, _bounds.Size); + } + } + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs new file mode 100644 index 0000000..fe56fdc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Collisions.Tests +{ + public class BasicActor : ICollisionActor + { + public Vector2 Position { get; set; } + public IShapeF Bounds { get; set; } + public Vector2 Velocity { get; set; } + + public BasicActor() + { + Bounds = new RectangleF(0f, 0f, 1f, 1f); + } + public void OnCollision(CollisionEventArgs collisionInfo) + { + Bounds.Position -= collisionInfo.PenetrationVector; + Position -= collisionInfo.PenetrationVector; + + if (collisionInfo.Other is BasicActor) + { + CollisionCount++; + } + else + { + Console.WriteLine(collisionInfo.Other.GetType().Name); + } + } + + public int CollisionCount { get; set; } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs new file mode 100644 index 0000000..3492735 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Collisions.Tests +{ + public class BasicWall : ICollisionActor + { + public Vector2 Position { get; set; } + public IShapeF Bounds { get; set; } + public Vector2 Velocity { get; set; } + + public BasicWall() + { + Bounds = new RectangleF(0f, 0f, 1f, 1f); + } + public void OnCollision(CollisionEventArgs collisionInfo) + { + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj new file mode 100644 index 0000000..c62c1cd --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Collisions\MonoGame.Extended.Collisions.csproj" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs new file mode 100644 index 0000000..1013288 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs @@ -0,0 +1,446 @@ +using System.Collections.Generic; +using MonoGame.Extended.Collisions.QuadTree; +using Xunit; + +namespace MonoGame.Extended.Collisions.Tests +{ + public class QuadTreeTests + { + private QuadTree.QuadTree MakeTree() + { + // Bounds set to ensure actors will fit inside the tree with default bounds. + var bounds = _quadTreeArea; + var tree = new QuadTree.QuadTree(bounds); + + return tree; + } + + private RectangleF _quadTreeArea = new RectangleF(-10f, -15, 20.0f, 30.0f); + + [Fact] + public void ConstructorTest() + { + var bounds = new RectangleF(-10f, -15, 20.0f, 30.0f); + var tree = new QuadTree.QuadTree(bounds); + + Assert.Equal(bounds, tree.NodeBounds); + Assert.True(tree.IsLeaf); + } + + [Fact] + public void NumTargetsEmptyTest() + { + var tree = MakeTree(); + + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void NumTargetsOneTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + + tree.Insert(new QuadtreeData(actor)); + + Assert.Equal(1, tree.NumTargets()); + } + + + [Fact] + public void NumTargetsMultipleTest() + { + var tree = MakeTree(); + for (int i = 0; i < 5; i++) + { + tree.Insert(new QuadtreeData(new BasicActor())); + } + + Assert.Equal(5, tree.NumTargets()); + } + + [Fact] + public void NumTargetsManyTest() + { + var tree = MakeTree(); + for (int i = 0; i < 1000; i++) + { + tree.Insert(new QuadtreeData(new BasicActor())); + Assert.Equal(i + 1, tree.NumTargets()); + } + + Assert.Equal(1000, tree.NumTargets()); + } + + [Fact] + public void InsertOneTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + + tree.Insert(new QuadtreeData(actor)); + + Assert.Equal(1, tree.NumTargets()); + } + + [Fact] + public void InsertOneOverlappingQuadrantsTest() + { + var tree = MakeTree(); + var actor = new BasicActor + { + Bounds = new RectangleF(-2.5f, -2.5f, 5f, 5f) + }; + + tree.Insert(new QuadtreeData(actor)); + + Assert.Equal(1, tree.NumTargets()); + } + + [Fact] + public void InsertMultipleTest() + { + var tree = MakeTree(); + + for (int i = 0; i < 10; i++) + { + tree.Insert(new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + })); + } + + Assert.Equal(10, tree.NumTargets()); + } + + [Fact] + public void InsertManyTest() + { + var tree = MakeTree(); + + for (int i = 0; i < 1000; i++) + { + tree.Insert(new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + })); + } + + Assert.Equal(1000, tree.NumTargets()); + } + + [Fact] + public void InsertMultipleOverlappingQuadrantsTest() + { + var tree = MakeTree(); + + for (int i = 0; i < 10; i++) + { + var actor = new BasicActor() + { + Bounds = new RectangleF(-10f, -15, 20.0f, 30.0f) + }; + tree.Insert(new QuadtreeData(actor)); + } + + Assert.Equal(10, tree.NumTargets()); + } + + [Fact] + public void RemoveToEmptyTest() + { + var actor = new BasicActor() + { + Bounds = new RectangleF(-5f, -7f, 10.0f, 15.0f) + }; + var data = new QuadtreeData(actor); + + var tree = MakeTree(); + tree.Insert(data); + + tree.Remove(data); + + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void RemoveTwoTest() + { + var tree = MakeTree(); + var inserted = new List<QuadtreeData>(); + var numTargets = 2; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + }); + tree.Insert(data); + inserted.Add(data); + } + + + var inTree = numTargets; + Assert.Equal(inTree, tree.NumTargets()); + + foreach (var data in inserted) + { + tree.Remove(data); + Assert.Equal(--inTree, tree.NumTargets()); + } + } + + [Fact] + public void RemoveThreeTest() + { + var tree = MakeTree(); + var inserted = new List<QuadtreeData>(); + var numTargets = 3; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + }); + tree.Insert(data); + inserted.Add(data); + } + + + var inTree = numTargets; + Assert.Equal(inTree, tree.NumTargets()); + + foreach (var data in inserted) + { + tree.Remove(data); + Assert.Equal(--inTree, tree.NumTargets()); + } + } + + [Fact] + public void RemoveManyTest() + { + var tree = MakeTree(); + var inserted = new List<QuadtreeData>(); + var numTargets = 1000; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + }); + tree.Insert(data); + inserted.Add(data); + } + + + var inTree = numTargets; + Assert.Equal(inTree, tree.NumTargets()); + + foreach (var data in inserted) + { + data.RemoveFromAllParents(); + Assert.Equal(--inTree, tree.NumTargets()); + } + } + + + [Fact] + public void ShakeWhenEmptyTest() + { + var tree = MakeTree(); + tree.Shake(); + + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void ShakeAfterSplittingWhenEmptyTest() + { + var tree = MakeTree(); + + tree.Split(); + tree.Shake(); + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void ShakeAfterSplittingNotEmptyTest() + { + var tree = MakeTree(); + + tree.Split(); + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + tree.Shake(); + Assert.Equal(1, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingOneTest() + { + var tree = MakeTree(); + var numTargets = 1; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingTwoTest() + { + var tree = MakeTree(); + var numTargets = 2; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingThreeTest() + { + var tree = MakeTree(); + var numTargets = 3; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingManyTest() + { + var tree = MakeTree(); + var numTargets = QuadTree.QuadTree.DefaultMaxObjectsPerNode + 1; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void QueryWhenEmptyTest() + { + var tree = MakeTree(); + + var query = tree.Query(ref _quadTreeArea); + + Assert.Empty(query); + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void QueryNotOverlappingTest() + { + var tree = MakeTree(); + + var area = new RectangleF(100f, 100f, 1f, 1f); + var query = tree.Query(ref area); + + Assert.Empty(query); + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void QueryLeafNodeNotEmptyTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + tree.Insert(new QuadtreeData(actor)); + + var query = tree.Query(ref _quadTreeArea); + Assert.Single(query); + Assert.Equal(tree.NumTargets(), query.Count); + } + + [Fact] + public void QueryLeafNodeNoOverlapTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + tree.Insert(new QuadtreeData(actor)); + + var area = new RectangleF(100f, 100f, 1f, 1f); + var query = tree.Query(ref area); + Assert.Empty(query); + } + + [Fact] + public void QueryLeafNodeMultipleTest() + { + var tree = MakeTree(); + var numTargets = QuadTree.QuadTree.DefaultMaxObjectsPerNode; + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + + var query = tree.Query(ref _quadTreeArea); + Assert.Equal(numTargets, query.Count); + Assert.Equal(tree.NumTargets(), query.Count); + } + + [Fact] + public void QueryNonLeafManyTest() + { + var tree = MakeTree(); + var numTargets = 2*QuadTree.QuadTree.DefaultMaxObjectsPerNode; + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + + var query = tree.Query(ref _quadTreeArea); + Assert.Equal(numTargets, query.Count); + Assert.Equal(tree.NumTargets(), query.Count); + } + + [Fact] + public void QueryTwiceConsecutiveTest() + { + var tree = MakeTree(); + var numTargets = 2 * QuadTree.QuadTree.DefaultMaxObjectsPerNode; + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + + var query1 = tree.Query(ref _quadTreeArea); + var query2 = tree.Query(ref _quadTreeArea); + Assert.Equal(numTargets, query1.Count); + Assert.Equal(tree.NumTargets(), query1.Count); + Assert.Equal(query1.Count, query2.Count); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs new file mode 100644 index 0000000..7c09a9e --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace MonoGame.Extended.Collisions.Tests; + +public class SpatialHashTests +{ + private SpatialHash generateSpatialHash() => new SpatialHash(new Size2(64, 64)); + private readonly RectangleF RECT = new RectangleF(10, 10, 20, 20); + + [Fact] + public void CollisionOneTrueTest() + { + var hash = generateSpatialHash(); + hash.Insert(new BasicActor() + { + Bounds = RECT, + }); + var collisions = hash.Query(RECT); + Assert.Equal(1, collisions.Count()); + } + + [Fact] + public void CollisionTwoTest() + { + var hash = generateSpatialHash(); + hash.Insert(new BasicActor + { + Bounds = RECT, + }); + hash.Insert(new BasicActor + { + Bounds = RECT, + }); + var collisions = hash.Query(RECT); + Assert.Equal(2, collisions.Count()); + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config new file mode 100644 index 0000000..fa95cfc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="MonoGame.Framework.WindowsDX" version="3.6.0.1625" targetFramework="net452" /> + <package id="NSubstitute" version="1.10.0.0" targetFramework="net452" /> + <package id="NUnit" version="2.6.4" targetFramework="net452" /> +</packages>
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj new file mode 100644 index 0000000..9c8f57c --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj @@ -0,0 +1,24 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <PackageReference Include="MonoGame.Framework.Content.Pipeline" Version="3.8.1.303" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Content.Pipeline\MonoGame.Extended.Content.Pipeline.csproj" /> + </ItemGroup> + + <ItemGroup> + <Content Include="TestData\isometric.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\isometric_tileset.png" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\level01.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\template.tx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\test-object-layer.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\test-tileset-base64.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\test-tileset-csv.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\test-tileset-gzip.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\test-tileset-xml.tmx" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\test-tileset-zlib.tmx" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric.tmx new file mode 100644 index 0000000..3f3168c --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric.tmx @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE map SYSTEM "http://mapeditor.org/dtd/1.0/map.dtd"> +<map version="1.0" orientation="isometric" width="40" height="40" tilewidth="64" tileheight="32"> + <tileset firstgid="1" name="Isometric Tileset" tilewidth="64" tileheight="128"> + <image source="isometric_tileset.png" width="128" height="128"/> + <tile id="0"> + <properties> + <property name="obstacle" value="1"/> + </properties> + </tile> + </tileset> + <layer name="Ground" width="40" height="40"> + <data encoding="csv"> +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,1,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,2,1,1,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,2,1,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,1,1,1,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,1,1,1,2,1,1,2,1,0,0,0,0,0,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +1,1,2,2,2,2,1,1,0,0,2,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +1,1,2,2,2,2,2,2,1,0,0,0,0,0,2,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +1,1,1,2,2,2,2,2,1,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +1,1,1,1,2,2,2,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,1,0,0,1,1,1,2,2,2,0,0,0,0,0,2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,1,0,0,2,1,2,2,2,2,2,0,0,0,0,0,0,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,1,0,2,2,1,1,0,2,2,0,0,0,0,0,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,1,1,2,2,2,1,1,0,2,0,0,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,1,2,2,2,2,2,1,2,0,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +1,2,2,2,1,2,1,2,2,2,1,1,2,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +1,1,1,2,2,1,1,1,2,2,2,1,1,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,1,1,0,1,0,1,2,2,2,1,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,1,1,1,0,1,1,2,2,1,1,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,2,1,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +0,0,0,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,0,0,1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,0,1,1,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,1,1,1,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +</data> + </layer> + <objectgroup color="#ffaa00" name="Objects" width="40" height="40"> + <object x="1261" y="428" width="141" height="74"/> + <object type="npc" x="498" y="35" width="156" height="42"/> + <object x="576" y="224" width="448" height="128"/> + <object x="2183" y="747" width="228" height="96"/> + <object name="fddsadsa" type="warp" x="960" y="416" width="192" height="96"/> + <object x="1100" y="556" width="138" height="23"/> + <object x="218" y="217"> + <polyline points="0,0 14,-90 36,3"/> + </object> + <object x="243" y="174"> + <polyline points="0,0 -17,0"/> + </object> + <object x="302" y="219"> + <polyline points="0,0 -2,-91 -36,-94 28,-91"/> + </object> + <object x="357" y="125"> + <polyline points="0,0 7,96 41,98"/> + </object> + </objectgroup> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric_tileset.png b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric_tileset.png Binary files differnew file mode 100644 index 0000000..4c6f1b4 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric_tileset.png diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/level01.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/level01.tmx new file mode 100644 index 0000000..39eb900 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/level01.tmx @@ -0,0 +1,440 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="right-down" width="20" height="10" tilewidth="128" tileheight="128" backgroundcolor="#7d7d7d" nextobjectid="1"> + <properties> + <property name="awesome" value="42"/> + </properties> + <tileset firstgid="1" name="free-tileset" tilewidth="128" tileheight="128" tilecount="30" spacing="2" margin="2"> + <image source="free-tileset.png" width="652" height="783"/> + <tile id="7"> + <properties> + <property name="frog" value="dog"/> + </properties> + </tile> + <tile id="23"> + <properties> + <property name="element" value="box"/> + </properties> + </tile> + <tile id="24"> + <properties> + <property name="hp" value="55"/> + <property name="mp" value="16"/> + </properties> + </tile> + </tileset> + <layer name="Tile Layer 2" width="20" height="10"> + <data> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="26"/> + <tile gid="21"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="25"/> + <tile gid="29"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="21"/> + <tile gid="20"/> + <tile gid="27"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="20"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="21"/> + <tile gid="28"/> + <tile gid="25"/> + <tile gid="30"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="29"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="19"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + </data> + </layer> + <imagelayer name="Image Layer 1" x="100" y="100"> + <image source="hills.png"/> + </imagelayer> + <layer name="Tile Layer 1" width="20" height="10"> + <properties> + <property name="customlayerprop" value="1"/> + <property name="customlayerprop2" value="2"/> + </properties> + <data> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="14"/> + <tile gid="15"/> + <tile gid="15"/> + <tile gid="16"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="14"/> + <tile gid="15"/> + <tile gid="16"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="24"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="1"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="3"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="1"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="4"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="7"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="4"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="2"/> + <tile gid="8"/> + <tile gid="9"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="7"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="1"/> + <tile gid="2"/> + <tile gid="8"/> + <tile gid="9"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="7"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="4"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="7"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="4"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + <tile gid="6"/> + </data> + </layer> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/template.tx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/template.tx new file mode 100644 index 0000000..1f00d19 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/template.tx @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<template> + <object width="30.9247" height="20.7597"/> +</template> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-object-layer.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-object-layer.tmx new file mode 100644 index 0000000..dcdbee9 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-object-layer.tmx @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="right-down" width="25" height="15" tilewidth="64" tileheight="64" nextobjectid="12"> + <objectgroup name="Object Layer 1"> + <object id="1" x="131.345" y="65.234" width="311.111" height="311.232"> + <properties> + <property name="shape" value="circle"/> + </properties> + <ellipse/> + </object> + <object id="7" class="sprite" x="240" y="440" width="322" height="186" visible="0"/> + <object id="8" type="rectangle" x="506" y="142" width="136" height="234"> + <properties> + <property name="area" value="player-spawn"/> + </properties> + </object> + <object id="9" name="polygon" x="621" y="450"> + <polygon points="0,0 180,90 -8,275 -45,81 38,77"/> + </object> + <object id="11" x="43" y="350"> + <polyline points="0,0 28,299 326,413 461,308"/> + </object> + <object id="12" gid="23" x="169.333" y="490.909" width="345.818" height="364"/> + </objectgroup> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-base64.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-base64.tmx new file mode 100644 index 0000000..09177cb --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-base64.tmx @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="left-down" width="3" height="3" tilewidth="32" tileheight="32" nextobjectid="1"> + <tileset firstgid="1" name="test-tileset" tilewidth="32" tileheight="32" spacing="2" margin="2"> + <image source="test-tileset.png" width="104" height="104"/> + </tileset> + <layer name="Tile Layer 1" width="3" height="3"> + <data encoding="base64"> + AQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA + </data> + </layer> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-csv.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-csv.tmx new file mode 100644 index 0000000..b709f2b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-csv.tmx @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="left-down" width="3" height="3" tilewidth="32" tileheight="32" nextobjectid="1"> + <tileset firstgid="1" name="test-tileset" tilewidth="32" tileheight="32" spacing="2" margin="2"> + <image source="test-tileset.png" width="104" height="104"/> + </tileset> + <layer name="Tile Layer 1" width="3" height="3"> + <data encoding="csv"> +1,2,3, +4,5,6, +7,8,9 +</data> + </layer> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-gzip.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-gzip.tmx new file mode 100644 index 0000000..c48705f --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-gzip.tmx @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="left-down" width="3" height="3" tilewidth="32" tileheight="32" nextobjectid="1"> + <tileset firstgid="1" name="test-tileset" tilewidth="32" tileheight="32" spacing="2" margin="2"> + <image source="test-tileset.png" width="104" height="104"/> + </tileset> + <layer name="Tile Layer 1" width="3" height="3"> + <data encoding="base64" compression="gzip"> + H4sIAAAAAAAACw3Dhw0AAAjDsLLh/4eJJZskZzBZbA6Xxwdm9rUOJAAAAA== + </data> + </layer> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-xml.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-xml.tmx new file mode 100644 index 0000000..0f1a741 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-xml.tmx @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="left-down" width="3" height="3" tilewidth="32" tileheight="32" nextobjectid="1"> + <tileset firstgid="1" name="test-tileset" tilewidth="32" tileheight="32" spacing="2" margin="2"> + <image source="test-tileset.png" width="104" height="104"/> + </tileset> + <layer name="Tile Layer 1" width="3" height="3"> + <data> + <tile gid="1"/> + <tile gid="2"/> + <tile gid="3"/> + <tile gid="4"/> + <tile gid="5"/> + <tile gid="6"/> + <tile gid="7"/> + <tile gid="8"/> + <tile gid="9"/> + </data> + </layer> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-zlib.tmx b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-zlib.tmx new file mode 100644 index 0000000..7a5a548 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-zlib.tmx @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" renderorder="left-down" width="3" height="3" tilewidth="32" tileheight="32" nextobjectid="1"> + <tileset firstgid="1" name="test-tileset" tilewidth="32" tileheight="32" spacing="2" margin="2"> + <image source="test-tileset.png" width="104" height="104"/> + </tileset> + <layer name="Tile Layer 1" width="3" height="3"> + <data encoding="base64" compression="zlib"> + eJwNw4cNAAAIw7Cy4f+HiSWbJGcwWWwOl8cHArgALg== + </data> + </layer> +</map> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TiledMapImporterProcessorTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TiledMapImporterProcessorTests.cs new file mode 100644 index 0000000..352457a --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TiledMapImporterProcessorTests.cs @@ -0,0 +1,205 @@ +using System.IO; +using System.Linq; +using Microsoft.Xna.Framework.Content.Pipeline; +using MonoGame.Extended.Content.Pipeline.Tiled; +using MonoGame.Extended.Tiled.Serialization; +using NSubstitute; +using Xunit; + +namespace MonoGame.Extended.Content.Pipeline.Tests.Tiled +{ + + public class TiledMapImporterProcessorTests + { + [Fact] + public void TiledMapImporter_Import_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "level01.tmx"); + + var logger = Substitute.For<ContentBuildLogger>(); + var importer = new TiledMapImporter(); + var importerContext = Substitute.For<ContentImporterContext>(); + importerContext.Logger.Returns(logger); + + var contentItem = importer.Import(filePath, importerContext); + var map = contentItem.Data; + + Assert.Equal("1.0", map.Version); + Assert.Equal(TiledMapOrientationContent.Orthogonal, map.Orientation); + Assert.Equal(TiledMapTileDrawOrderContent.RightDown, map.RenderOrder); + Assert.Equal(20, map.Width); + Assert.Equal(10, map.Height); + Assert.Equal(128, map.TileWidth); + Assert.Equal(128, map.TileHeight); + Assert.Equal("#7d7d7d", map.BackgroundColor); + Assert.Equal("awesome", map.Properties[0].Name); + Assert.Equal("42", map.Properties[0].Value); + Assert.Single(map.Tilesets); + Assert.Equal(3, map.Layers.Count); + Assert.Equal(TiledMapOrientationContent.Orthogonal, map.Orientation); + + var tileset = map.Tilesets.First(); + Assert.Equal(1, tileset.FirstGlobalIdentifier); + Assert.Equal("free-tileset.png", Path.GetFileName(tileset.Image.Source)); + Assert.Equal(652, tileset.Image.Width); + Assert.Equal(783, tileset.Image.Height); + Assert.Equal(2, tileset.Margin); + Assert.Equal(30, tileset.TileCount); + Assert.Equal("free-tileset", tileset.Name); + Assert.Null(tileset.Source); + Assert.Equal(2, tileset.Spacing); + //Assert.Equal(0, tileset.TerrainTypes.Count); + Assert.Empty(tileset.Properties); + Assert.Equal(128, tileset.TileHeight); + Assert.Equal(128, tileset.TileWidth); + Assert.Equal(0, tileset.TileOffset.X); + Assert.Equal(0, tileset.TileOffset.Y); + + var tileLayer2 = (TiledMapTileLayerContent)map.Layers[0]; + Assert.Equal("Tile Layer 2", tileLayer2.Name); + Assert.Equal(1, tileLayer2.Opacity); + Assert.Empty(tileLayer2.Properties); + Assert.True(tileLayer2.Visible); + Assert.Equal(200, tileLayer2.Data.Tiles.Count); + Assert.Equal(0, tileLayer2.X); + Assert.Equal(0, tileLayer2.Y); + + var imageLayer = (TiledMapImageLayerContent)map.Layers[1]; + Assert.Equal("Image Layer 1", imageLayer.Name); + Assert.Equal(1, imageLayer.Opacity); + Assert.Empty(imageLayer.Properties); + Assert.True(imageLayer.Visible); + Assert.Equal("hills.png", Path.GetFileName(imageLayer.Image.Source)); + Assert.Equal(100, imageLayer.X); + Assert.Equal(100, imageLayer.Y); + + var tileLayer1 = (TiledMapTileLayerContent)map.Layers[2]; + Assert.Equal("Tile Layer 1", tileLayer1.Name); + Assert.Equal(2, tileLayer1.Properties.Count); + + Assert.Equal("customlayerprop", tileLayer1.Properties[0].Name); + Assert.Equal("1", tileLayer1.Properties[0].Value); + + Assert.Equal("customlayerprop2", tileLayer1.Properties[1].Name); + Assert.Equal("2", tileLayer1.Properties[1].Value); + } + + [Fact] + public void TiledMapImporter_Xml_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "test-tileset-xml.tmx"); + var map = ImportAndProcessMap(filePath); + var layer = map.Layers.OfType<TiledMapTileLayerContent>().First(); + var actualData = layer.Data.Tiles.Select(i => i.GlobalIdentifier).ToArray(); + + Assert.Null(layer.Data.Encoding); + Assert.Null(layer.Data.Compression); + Assert.True(new uint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.SequenceEqual(actualData)); + } + + [Fact] + public void TiledMapImporter_Csv_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "test-tileset-csv.tmx"); + var map = ImportAndProcessMap(filePath); + var layer = map.Layers.OfType<TiledMapTileLayerContent>().First(); + var data = layer.Data.Tiles.Select(i => i.GlobalIdentifier).ToArray(); + + Assert.Equal("csv", layer.Data.Encoding); + Assert.Null(layer.Data.Compression); + //Assert.True(new uint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.SequenceEqual(data)); + } + + [Fact] + public void TiledMapImporter_Base64_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "test-tileset-base64.tmx"); + var map = ImportAndProcessMap(filePath); + var layer = map.Layers.OfType<TiledMapTileLayerContent>().First(); + var data = layer.Data.Tiles.Select(i => i.GlobalIdentifier).ToArray(); + + Assert.Equal("base64", layer.Data.Encoding); + Assert.Null(layer.Data.Compression); + //Assert.True(new uint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.SequenceEqual(data)); + } + + [Fact] + public void TiledMapImporter_Gzip_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "test-tileset-gzip.tmx"); + var map = ImportAndProcessMap(filePath); + var layer = map.Layers.OfType<TiledMapTileLayerContent>().First(); + var data = layer.Data.Tiles.Select(i => i.GlobalIdentifier).ToArray(); + + Assert.Equal("base64", layer.Data.Encoding); + Assert.Equal("gzip", layer.Data.Compression); + //Assert.True(new uint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.SequenceEqual(data)); + } + + + [Fact] + public void TiledMapImporter_Zlib_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "test-tileset-zlib.tmx"); + var map = ImportAndProcessMap(filePath); + var layer = map.Layers.OfType<TiledMapTileLayerContent>().First(); + var data = layer.Data.Tiles.Select(i => i.GlobalIdentifier).ToArray(); + + Assert.Equal("base64", layer.Data.Encoding); + Assert.Equal("zlib", layer.Data.Compression); + //Assert.True(new uint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.SequenceEqual(data)); + } + + [Fact] + public void TiledMapImporter_ObjectLayer_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "test-object-layer.tmx"); + var map = ImportAndProcessMap(filePath); + + Assert.Single(map.Layers); + Assert.IsType<TiledMapObjectLayerContent>(map.Layers[0]); + var tmxObjectGroup = map.Layers[0] as TiledMapObjectLayerContent; + var tmxObject = tmxObjectGroup.Objects[0]; + var tmxPolygon = tmxObjectGroup.Objects[3].Polygon; + var tmxPolyline = tmxObjectGroup.Objects[4].Polyline; + + Assert.Equal("Object Layer 1", tmxObjectGroup.Name); + Assert.Equal(1, tmxObject.Identifier); + Assert.Equal(131.345f, tmxObject.X); + Assert.Equal(65.234f, tmxObject.Y); + Assert.Equal(311.111f, tmxObject.Width); + Assert.Equal(311.232f, tmxObject.Height); + Assert.Single(tmxObject.Properties); + Assert.Equal("shape", tmxObject.Properties[0].Name); + Assert.Equal("circle", tmxObject.Properties[0].Value); + Assert.NotNull(tmxObject.Ellipse); + Assert.False(tmxObjectGroup.Objects[1].Visible); + Assert.Equal((uint)0, tmxObjectGroup.Objects[1].GlobalIdentifier); + Assert.Equal((uint)23, tmxObjectGroup.Objects[5].GlobalIdentifier); + Assert.Equal("rectangle", tmxObjectGroup.Objects[2].Type); + Assert.Equal("sprite", tmxObjectGroup.Objects[1].Class); + Assert.NotNull(tmxPolygon); + Assert.Equal("0,0 180,90 -8,275 -45,81 38,77", tmxPolygon.Points); + Assert.NotNull(tmxPolyline); + Assert.Equal("0,0 28,299 326,413 461,308", tmxPolyline.Points); + } + + + private static TiledMapContent ImportAndProcessMap(string filename) + { + var logger = Substitute.For<ContentBuildLogger>(); + var importer = new TiledMapImporter(); + var importerContext = Substitute.For<ContentImporterContext>(); + importerContext.Logger.Returns(logger); + + var processor = new TiledMapProcessor(); + var processorContext = Substitute.For<ContentProcessorContext>(); + processorContext.Logger.Returns(logger); + + var import = importer.Import(filename, importerContext); + var result = processor.Process(import, processorContext); + + return result.Data; + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorImporterTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorImporterTests.cs new file mode 100644 index 0000000..c3af651 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorImporterTests.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using MonoGame.Extended.Content.Pipeline.Animations; +using Xunit; + +namespace MonoGame.Extended.Content.Pipeline.Tests +{ + + public class AstridAnimatorImporterTests + { + [Fact] + public void AstridAnimatorImporter_Import_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "astrid-animator.aa"); + var importer = new AstridAnimatorImporter(); + var result = importer.Import(filePath, null); + var data = result.Data; + + Assert.Equal("astrid-animator-atlas.json", data.TextureAtlas); + Assert.Equal(2, data.Animations.Count); + + Assert.Equal("appear", data.Animations[0].Name); + Assert.Equal(8, data.Animations[0].FramesPerSecond); + Assert.Equal(2, data.Animations[0].Frames.Count); + Assert.Equal("appear_01", data.Animations[0].Frames[0]); + Assert.Equal("appear_02", data.Animations[0].Frames[1]); + + Assert.Equal("die", data.Animations[1].Name); + Assert.Equal(16, data.Animations[1].FramesPerSecond); + Assert.Single(data.Animations[1].Frames); + Assert.Equal("die_01", data.Animations[1].Frames[0]); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorProcessorTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorProcessorTests.cs new file mode 100644 index 0000000..0a27412 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorProcessorTests.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using MonoGame.Extended.Content.Pipeline.Animations; +using Xunit; + +namespace MonoGame.Extended.Content.Pipeline.Tests +{ + + public class AstridAnimatorProcessorTests + { + [Fact] + public void AstridAnimatorProcessor_Process_Test() + { + var filePath = PathExtensions.GetApplicationFullPath("TestData", "astrid-animator.aa"); + + var importer = new AstridAnimatorImporter(); + var importerResult = importer.Import(filePath, null); + + var processor = new AstridAnimatorProcessor(); + var result = processor.Process(importerResult, null); + + Assert.Equal("astrid-animator-atlas", result.TextureAtlasAssetName); + Assert.Equal("TestData", Path.GetFileName(result.Directory)); + Assert.Equal(3, result.Frames.Count); + Assert.Equal("appear_01", result.Frames[0]); + Assert.Equal("appear_02", result.Frames[1]); + Assert.Equal("die_01", result.Frames[2]); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj new file mode 100644 index 0000000..cee444e --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj @@ -0,0 +1,16 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <PackageReference Include="MonoGame.Framework.Content.Pipeline" Version="3.8.1.303" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Content.Pipeline\MonoGame.Extended.Content.Pipeline.csproj" /> + </ItemGroup> + + <ItemGroup> + <None Update="TestData\astrid-animator-atlas.json" CopyToOutputDirectory="PreserveNewest" /> + <None Update="TestData\astrid-animator.aa" CopyToOutputDirectory="PreserveNewest" /> + <None Update="TestData\test-tileset.json" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator-atlas.json b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator-atlas.json new file mode 100644 index 0000000..7a9f9f0 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator-atlas.json @@ -0,0 +1,390 @@ +{ + "frames": [ + { + "filename": "appear_01.png", + "frame": {"x":494,"y":111,"w":27,"h":31}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":113,"w":27,"h":31}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_02.png", + "frame": {"x":465,"y":111,"w":27,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":102,"w":27,"h":42}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_03.png", + "frame": {"x":705,"y":158,"w":31,"h":52}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":92,"w":31,"h":52}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_04.png", + "frame": {"x":112,"y":298,"w":74,"h":17}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":16,"y":127,"w":74,"h":17}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_05.png", + "frame": {"x":564,"y":621,"w":94,"h":62}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":16,"y":82,"w":94,"h":62}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_06.png", + "frame": {"x":507,"y":544,"w":106,"h":75}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":69,"w":106,"h":75}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_07.png", + "frame": {"x":119,"y":637,"w":98,"h":93}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":51,"w":98,"h":93}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_08.png", + "frame": {"x":486,"y":621,"w":76,"h":118}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":12,"y":26,"w":76,"h":118}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_09.png", + "frame": {"x":321,"y":601,"w":73,"h":124}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":15,"y":20,"w":73,"h":124}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_10.png", + "frame": {"x":406,"y":600,"w":78,"h":140}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":10,"y":4,"w":78,"h":140}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "appear_11.png", + "frame": {"x":615,"y":469,"w":82,"h":144}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":11,"y":0,"w":82,"h":144}, + "sourceSize": {"w":110,"h":144}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_01.png", + "frame": {"x":523,"y":15,"w":112,"h":144}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":0,"w":112,"h":144}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_02.png", + "frame": {"x":113,"y":2,"w":134,"h":129}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":15,"w":134,"h":129}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_03.png", + "frame": {"x":249,"y":2,"w":139,"h":106}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":38,"w":139,"h":106}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_04.png", + "frame": {"x":465,"y":161,"w":133,"h":73}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":71,"w":133,"h":73}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_05.png", + "frame": {"x":113,"y":133,"w":147,"h":69}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":76,"w":147,"h":69}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_06.png", + "frame": {"x":564,"y":685,"w":154,"h":51}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":94,"w":154,"h":51}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_07.png", + "frame": {"x":242,"y":266,"w":188,"h":24}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":28,"y":121,"w":188,"h":24}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "die_08.png", + "frame": {"x":390,"y":2,"w":222,"h":11}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":134,"w":222,"h":11}, + "sourceSize": {"w":222,"h":146}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_01.png", + "frame": {"x":637,"y":2,"w":100,"h":154}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":1,"w":100,"h":154}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_02.png", + "frame": {"x":262,"y":110,"w":100,"h":154}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":1,"w":100,"h":154}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_03.png", + "frame": {"x":364,"y":111,"w":99,"h":153}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":99,"h":153}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_04.png", + "frame": {"x":203,"y":298,"w":99,"h":153}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":99,"h":153}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_05.png", + "frame": {"x":305,"y":447,"w":99,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":99,"h":152}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_06.png", + "frame": {"x":405,"y":292,"w":98,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":3,"w":98,"h":152}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_07.png", + "frame": {"x":304,"y":292,"w":99,"h":153}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":99,"h":153}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_08.png", + "frame": {"x":103,"y":480,"w":99,"h":155}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":99,"h":155}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_09.png", + "frame": {"x":2,"y":322,"w":99,"h":156}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":0,"w":99,"h":156}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "go_10.png", + "frame": {"x":2,"y":480,"w":99,"h":156}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":0,"w":99,"h":156}, + "sourceSize": {"w":100,"h":156}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_01.png", + "frame": {"x":2,"y":2,"w":109,"h":159}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":77,"y":0,"w":109,"h":159}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_02.png", + "frame": {"x":103,"y":322,"w":98,"h":156}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":79,"y":3,"w":98,"h":156}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_03.png", + "frame": {"x":2,"y":163,"w":108,"h":157}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":25,"y":3,"w":108,"h":157}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_04.png", + "frame": {"x":390,"y":15,"w":131,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":66,"w":131,"h":94}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_05.png", + "frame": {"x":112,"y":204,"w":128,"h":92}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":70,"w":128,"h":92}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_06.png", + "frame": {"x":2,"y":638,"w":115,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":16,"y":60,"w":115,"h":100}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "hit_07.png", + "frame": {"x":219,"y":608,"w":100,"h":132}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":28,"w":100,"h":132}, + "sourceSize": {"w":186,"h":162}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "idle_01.png", + "frame": {"x":204,"y":453,"w":99,"h":153}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":0,"w":99,"h":153}, + "sourceSize": {"w":100,"h":154}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "idle_02.png", + "frame": {"x":406,"y":446,"w":99,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":1,"w":99,"h":152}, + "sourceSize": {"w":100,"h":154}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "idle_03.png", + "frame": {"x":505,"y":236,"w":98,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":98,"h":152}, + "sourceSize": {"w":100,"h":154}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "idle_04.png", + "frame": {"x":507,"y":390,"w":98,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":98,"h":152}, + "sourceSize": {"w":100,"h":154}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "idle_05.png", + "frame": {"x":605,"y":161,"w":98,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":98,"h":152}, + "sourceSize": {"w":100,"h":154}, + "pivot": {"x":0.5,"y":1} + }, + { + "filename": "idle_06.png", + "frame": {"x":607,"y":315,"w":99,"h":152}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":1,"w":99,"h":152}, + "sourceSize": {"w":100,"h":154}, + "pivot": {"x":0.5,"y":1} + }], + "meta": { + "app": "http://www.codeandweb.com/texturepacker", + "version": "1.0", + "image": "zombie.png", + "format": "RGBA8888", + "size": {"w":742,"h":742}, + "scale": "0.5", + "smartupdate": "$TexturePacker:SmartUpdate:28fca4a18eeef90b2646ccc59eb1f593:ccb2bd5648fe15c740c647d39945c765:3e6ed6fe54c801c395eefb25aa5e45e8$" + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator.aa b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator.aa new file mode 100644 index 0000000..ba8d43f --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator.aa @@ -0,0 +1,15 @@ +{ + "TextureAtlas": "astrid-animator-atlas.json", + "Animations": [ + { + "Name": "appear", + "FramesPerSecond": 8, + "Frames": [ "appear_01", "appear_02" ] + }, + { + "Name": "die", + "FramesPerSecond": 16, + "Frames": [ "die_01" ] + } + ] +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/test-tileset.json b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/test-tileset.json new file mode 100644 index 0000000..57e46f5 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/test-tileset.json @@ -0,0 +1,93 @@ +{"frames": [ + +{ + "filename": "1.png", + "frame": {"x":2,"y":2,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "2.png", + "frame": {"x":36,"y":2,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "3.png", + "frame": {"x":70,"y":2,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "4.png", + "frame": {"x":2,"y":36,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "5.png", + "frame": {"x":36,"y":36,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "6.png", + "frame": {"x":70,"y":36,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "7.png", + "frame": {"x":2,"y":70,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "8.png", + "frame": {"x":36,"y":70,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "9.png", + "frame": {"x":70,"y":70,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}], +"meta": { + "app": "http://www.codeandweb.com/texturepacker", + "version": "1.0", + "image": "test-tileset.png", + "format": "RGBA8888", + "size": {"w":104,"h":104}, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:f5f4c00eb32fae603057f0d9dc5c7b73:ca39697f48630ecdea6d81a8fdc48cf6:c79a4cc8e4ba9657462e67dafcaf93d2$" +} +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TexturePackerJsonImporterProcessorTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TexturePackerJsonImporterProcessorTests.cs new file mode 100644 index 0000000..4172ee2 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TexturePackerJsonImporterProcessorTests.cs @@ -0,0 +1,33 @@ +using Microsoft.Xna.Framework.Content.Pipeline; +using MonoGame.Extended.Content.Pipeline.TextureAtlases; +using NSubstitute; +using Xunit; + +namespace MonoGame.Extended.Content.Pipeline.Tests +{ + + public class TexturePackerJsonImporterProcessorTests + { + [Fact] + public void TexturePackerJsonImporter_Import_Test() + { + var filePath = PathExtensions.GetApplicationFullPath(@"TestData/test-tileset.json"); + var importer = new TexturePackerJsonImporter(); + var data = importer.Import(filePath, Substitute.For<ContentImporterContext>()); + + Assert.NotNull(data); + } + + [Fact] + public void TexturePackerJsonImporter_Processor_Test() + { + var filePath = PathExtensions.GetApplicationFullPath(@"TestData/test-tileset.json"); + var importer = new TexturePackerJsonImporter(); + var input = importer.Import(filePath, Substitute.For<ContentImporterContext>()); + var processor = new TexturePackerProcessor(); + var output = processor.Process(input, Substitute.For<ContentProcessorContext>()); + + Assert.NotNull(output); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs new file mode 100644 index 0000000..1b3e971 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs @@ -0,0 +1,70 @@ +using System; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Sprites; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + public class AspectBuilderTests + { + [Fact] + public void MatchAllTypes() + { + var builder = new AspectBuilder() + .All(typeof(Transform2), typeof(Sprite)); + + Assert.Equal(2, builder.AllTypes.Count); + Assert.Contains(typeof(Transform2), builder.AllTypes); + Assert.Contains(typeof(Sprite), builder.AllTypes); + } + + [Fact] + public void MatchAllTypesIsEmpty() + { + var builder = new AspectBuilder() + .All(); + + Assert.Empty(builder.AllTypes); + Assert.Empty(builder.OneTypes); + Assert.Empty(builder.ExclusionTypes); + } + + [Fact] + public void MatchOneOfType() + { + var builder = new AspectBuilder() + .One(typeof(Transform2), typeof(Sprite)); + + Assert.Equal(2, builder.OneTypes.Count); + Assert.Contains(typeof(Transform2), builder.OneTypes); + Assert.Contains(typeof(Sprite), builder.OneTypes); + } + + [Fact] + public void ExcludeTypes() + { + var builder = new AspectBuilder() + .Exclude(typeof(Transform2), typeof(Sprite)); + + Assert.Equal(2, builder.ExclusionTypes.Count); + Assert.Contains(typeof(Transform2), builder.ExclusionTypes); + Assert.Contains(typeof(Sprite), builder.ExclusionTypes); + } + + [Fact] + public void BuildAspect() + { + var componentManager = new ComponentManager(); + var builder = new AspectBuilder() + .All(typeof(Transform2), typeof(Sprite)) + .One(typeof(string)) + .Exclude(typeof(Texture2D)); + + var aspect = builder.Build(componentManager); + + Assert.True(aspect.AllSet.Data != 0); + Assert.True(aspect.OneSet.Data != 0); + Assert.True(aspect.ExclusionSet.Data != 0); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs new file mode 100644 index 0000000..b06780b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs @@ -0,0 +1,88 @@ +using System.Collections.Specialized; +using MonoGame.Extended.Sprites; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + public class DummyComponent + { + } + + public class AspectTests + { + private readonly ComponentManager _componentManager; + private readonly BitVector32 _entityA; + private readonly BitVector32 _entityB; + + public AspectTests() + { + _componentManager = new ComponentManager(); + _entityA = new BitVector32 + { + [1 << _componentManager.GetComponentTypeId(typeof(Transform2))] = true, + [1 << _componentManager.GetComponentTypeId(typeof(Sprite))] = true, + [1 << _componentManager.GetComponentTypeId(typeof(DummyComponent))] = true + }; + _entityB = new BitVector32 + { + [1 << _componentManager.GetComponentTypeId(typeof(Transform2))] = true, + [1 << _componentManager.GetComponentTypeId(typeof(Sprite))] = true, + }; + } + + [Fact] + public void EmptyAspectMatchesAllComponents() + { + var componentManager = new ComponentManager(); + var emptyAspect = Aspect.All() + .Build(componentManager); + + Assert.True(emptyAspect.IsInterested(_entityA)); + Assert.True(emptyAspect.IsInterested(_entityB)); + } + + [Fact] + public void IsInterestedInAllComponents() + { + var allAspect = Aspect + .All(typeof(Sprite), typeof(Transform2), typeof(DummyComponent)) + .Build(_componentManager); + + Assert.True(allAspect.IsInterested(_entityA)); + Assert.False(allAspect.IsInterested(_entityB)); + } + + [Fact] + public void IsInterestedInEitherOneOfTheComponents() + { + var eitherOneAspect = Aspect + .One(typeof(Transform2), typeof(DummyComponent)) + .Build(_componentManager); + + Assert.True(eitherOneAspect.IsInterested(_entityA)); + Assert.True(eitherOneAspect.IsInterested(_entityB)); + } + + [Fact] + public void IsInterestedInJustOneComponent() + { + var oneAspect = Aspect + .One(typeof(DummyComponent)) + .Build(_componentManager); + + Assert.True(oneAspect.IsInterested(_entityA)); + Assert.False(oneAspect.IsInterested(_entityB)); + } + + [Fact] + public void IsInterestedInExcludingOneComponent() + { + var oneAspect = Aspect + .Exclude(typeof(DummyComponent)) + .Build(_componentManager); + + Assert.False(oneAspect.IsInterested(_entityA)); + Assert.True(oneAspect.IsInterested(_entityB)); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/BitArrayExtensionsTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/BitArrayExtensionsTests.cs new file mode 100644 index 0000000..44ec4af --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/BitArrayExtensionsTests.cs @@ -0,0 +1,20 @@ +using System.Collections; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + public class BitArrayExtensionsTests + { + [Fact] + public void BitArrayIsEmpty() + { + Assert.True(new BitArray(1).IsEmpty()); + Assert.False(new BitArray(new[] { true }).IsEmpty()); + Assert.True(new BitArray(new[] { false }).IsEmpty()); + + var bitArray = new BitArray(new[] { true }); + bitArray.Set(0, false); + Assert.True(bitArray.IsEmpty()); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs new file mode 100644 index 0000000..8d988e4 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs @@ -0,0 +1,47 @@ +using MonoGame.Extended.Sprites; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + public class ComponentManagerTests + { + [Fact] + public void GetMapperForType() + { + var componentManager = new ComponentManager(); + var transformMapper = componentManager.GetMapper<Transform2>(); + var spriteMapper = componentManager.GetMapper<Sprite>(); + + Assert.IsType<ComponentMapper<Transform2>>(transformMapper); + Assert.IsType<ComponentMapper<Sprite>>(spriteMapper); + Assert.Equal(0, transformMapper.Id); + Assert.Equal(1, spriteMapper.Id); + Assert.Same(spriteMapper, componentManager.GetMapper<Sprite>()); + } + + [Fact] + public void GetComponentTypeId() + { + var componentManager = new ComponentManager(); + + Assert.Equal(0, componentManager.GetComponentTypeId(typeof(Transform2))); + Assert.Equal(1, componentManager.GetComponentTypeId(typeof(Sprite))); + Assert.Equal(0, componentManager.GetComponentTypeId(typeof(Transform2))); + } + + //[Fact] + //public void GetCompositionIdentity() + //{ + // var compositionBits = new BitArray(3) + // { + // [0] = true, + // [1] = false, + // [2] = true + // }; + // var componentManager = new ComponentManager(); + // var identity = componentManager.GetCompositionIdentity(compositionBits); + + // Assert.Equal(0b101, identity); + //} + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs new file mode 100644 index 0000000..54fab04 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs @@ -0,0 +1,95 @@ +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + public class ComponentMapperTests + { + [Fact] + public void CreateComponentMapper() + { + var mapper = new ComponentMapper<object>(0, _ => {}); + + Assert.Equal(typeof(object), mapper.ComponentType); + Assert.Empty(mapper.Components); + } + + [Fact] + public void OnPut() + { + const int entityId = 3; + + var mapper = new ComponentMapper<Transform2>(1, _ => { }); + var component = new Transform2(); + + mapper.OnPut += (entId) => + { + Assert.Equal(entityId, entId); + Assert.Same(component, mapper.Get(entityId)); + }; + + mapper.Put(entityId, component); + } + + [Fact] + public void PutAndGetComponent() + { + const int entityId = 3; + + var mapper = new ComponentMapper<Transform2>(1, _ => { }); + var component = new Transform2(); + + mapper.Put(entityId, component); + + Assert.Equal(typeof(Transform2), mapper.ComponentType); + Assert.True(mapper.Components.Count >= 1); + Assert.Same(component, mapper.Get(entityId)); + } + + [Fact] + public void OnDelete() + { + const int entityId = 1; + + var mapper = new ComponentMapper<Transform2>(2, _ => { }); + var component = new Transform2(); + + mapper.OnDelete += (entId) => + { + Assert.Equal(entityId, entId); + Assert.False(mapper.Has(entityId)); + }; + + mapper.Put(entityId, component); + mapper.Delete(entityId); + } + + [Fact] + public void DeleteComponent() + { + const int entityId = 1; + + var mapper = new ComponentMapper<Transform2>(2, _ => { }); + var component = new Transform2(); + + mapper.Put(entityId, component); + mapper.Delete(entityId); + + Assert.False(mapper.Has(entityId)); + } + + [Fact] + public void HasComponent() + { + const int entityId = 0; + + var mapper = new ComponentMapper<Transform2>(3, _ => { }); + var component = new Transform2(); + + Assert.False(mapper.Has(entityId)); + + mapper.Put(entityId, component); + + Assert.True(mapper.Has(entityId)); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs new file mode 100644 index 0000000..8953804 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs @@ -0,0 +1,20 @@ +using MonoGame.Extended.Sprites; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests +{ + //public class ComponentTypeTests + //{ + // [Fact] + // public void CreateComponentType() + // { + // var type = typeof(Sprite); + // var componentType = new ComponentType(type, 3); + + // Assert.Same(type, componentType.Type); + // Assert.Equal(3, componentType.Id); + // Assert.Equal(new ComponentType(typeof(Sprite), 3), componentType); + // Assert.Equal(componentType.Id, componentType.GetHashCode()); + // } + //} +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj new file mode 100644 index 0000000..9eac8c4 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Entities\MonoGame.Extended.Entities.csproj" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs new file mode 100644 index 0000000..7620f1e --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using MonoGame.Extended.Entities.Systems; +using MonoGame.Extended.Sprites; +using Xunit; + +namespace MonoGame.Extended.Entities.Tests; + +public class WorldManagerTests +{ + private GameTime _gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromMilliseconds(16)); + + [Fact] + public void CrudEntity() + { + var dummySystem = new DummySystem(); + var worldBuilder = new WorldBuilder(); + worldBuilder.AddSystem(dummySystem); + var world = worldBuilder.Build(); + + world.Initialize(); + + var entity = world.CreateEntity(); + entity.Attach(new Transform2()); + world.Update(_gameTime); + world.Draw(_gameTime); + var otherEntity = world.GetEntity(entity.Id); + Assert.Equal(entity, otherEntity); + Assert.True(otherEntity.Has<Transform2>()); + Assert.Contains(entity.Id, dummySystem.AddedEntitiesId); + + entity.Destroy(); + world.Update(_gameTime); + world.Draw(_gameTime); + otherEntity = world.GetEntity(entity.Id); + Assert.Null(otherEntity); + Assert.Contains(entity.Id, dummySystem.RemovedEntitiesId); + } + + private class DummyComponent { } + + private class DummySystem : EntitySystem, IUpdateSystem, IDrawSystem + { + public List<int> AddedEntitiesId { get; } = new (); + public List<int> RemovedEntitiesId { get; } = new (); + + public DummySystem() : base(Aspect.All(typeof(DummyComponent))) { } + + public override void Initialize(IComponentMapperService mapperService) + { + // Do NOT initialize mapper in order to test: https://github.com/craftworkgames/MonoGame.Extended/issues/707 + } + + public void Draw(GameTime gameTime) { } + + public void Update(GameTime gameTime) { } + + protected override void OnEntityAdded(int entityId) + { + base.OnEntityAdded(entityId); + AddedEntitiesId.Add(entityId); + } + + protected override void OnEntityRemoved(int entityId) + { + base.OnEntityRemoved(entityId); + RemovedEntitiesId.Add(entityId); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiButtonTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiButtonTests.cs new file mode 100644 index 0000000..7defe1c --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiButtonTests.cs @@ -0,0 +1,122 @@ +//using System.Collections.Generic; +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.BitmapFonts; +//using MonoGame.Extended.Gui.Controls; +//using MonoGame.Extended.Tests; +//using MonoGame.Extended.TextureAtlases; +//using NSubstitute; +//using Xunit; + +//namespace MonoGame.Extended.Gui.Tests.Controls +//{ +// public class GuiButtonTests +// { +// [Fact] +// public void DesiredSizeShouldBeEmptyByDefault() +// { +// var availableSize = new Size2(800, 480); +// var context = Substitute.For<IGuiContext>(); +// var button = new Button(); +// var desiredSize = button.GetDesiredSize(context, availableSize); + +// Assert.That(desiredSize, Is.EqualTo(Size2.Empty)); +// } + +// [Fact] +// public void DesiredSizeShouldBeTheSizeOfTheBackgroundRegion() +// { +// var availableSize = new Size2(800, 480); +// var context = Substitute.For<IGuiContext>(); +// var backgroundRegion = MockTextureRegion(); +// var button = new GuiButton { BackgroundRegion = backgroundRegion }; +// var desiredSize = button.GetDesiredSize(context, availableSize); + +// Assert.That(desiredSize, Is.EqualTo(backgroundRegion.Size)); +// } + +// [Fact] +// public void DesiredSizeShouldBeTheSizeOfTheMarginsInANinePatchRegion() +// { +// var availableSize = new Size2(800, 480); +// var context = Substitute.For<IGuiContext>(); +// var texture = new Texture2D(new TestGraphicsDevice(), 512, 512); +// var backgroundRegion = new NinePatchRegion2D(new TextureRegion2D(texture), new Thickness(10, 20)); +// var button = new GuiButton() { BackgroundRegion = backgroundRegion }; +// var desiredSize = button.GetDesiredSize(context, availableSize); + +// Assert.That(desiredSize, Is.EqualTo(new Size2(20, 40))); +// } + +// [Fact] +// public void DesiredSizeShouldAtLeastBeTheSizeOfTheText() +// { +// const string text = "abcdefg"; + +// var availableSize = new Size2(800, 480); +// var context = Substitute.For<IGuiContext>(); +// var font = CreateMockFont(text, lineHeight: 32); +// var expectedSize = font.MeasureString(text); +// var button = new GuiButton {Text = text, Font = font}; + +// var desiredSize = button.GetDesiredSize(context, availableSize); + +// Assert.That(desiredSize, Is.EqualTo(expectedSize)); +// } + +// [Fact] +// public void DesiredSizeShouldAtLeastBeTheSizeOfTheIcon() +// { +// var texture = new Texture2D(new TestGraphicsDevice(), 35, 38); +// var icon = new TextureRegion2D(texture); + +// var availableSize = new Size2(800, 480); +// var context = Substitute.For<IGuiContext>(); +// var button = new GuiButton { IconRegion = icon }; +// var desiredSize = button.GetDesiredSize(context, availableSize); + +// Assert.That(desiredSize, Is.EqualTo(icon.Size)); +// } + +// [Fact] +// public void DesiredSizeShouldBeTheSizeOfTheBiggestTextOrIcon() +// { +// const string text = "abcdefg"; + +// var texture = new Texture2D(new TestGraphicsDevice(), 35, 38); +// var icon = new TextureRegion2D(texture); +// var iconExpectedSize = icon.Size; + +// var availableSize = new Size2(800, 480); +// var context = Substitute.For<IGuiContext>(); + +// var font = CreateMockFont(text, 32); +// var fontExpectedSize = font.MeasureString(text); + +// var button = new GuiButton { Text = text, Font = font, IconRegion = icon }; +// var desiredSize = button.GetDesiredSize(context, availableSize); + +// Assert.That(desiredSize.Width, Is.EqualTo(fontExpectedSize.Width)); +// Assert.That(desiredSize.Height, Is.EqualTo(iconExpectedSize.Height)); +// } + +// private static BitmapFont CreateMockFont(string text, int lineHeight) +// { +// var regions = new List<BitmapFontRegion>(); +// var xOffset = 0; + +// foreach (var character in text) +// { +// regions.Add(new BitmapFontRegion(MockTextureRegion(10, 10), character, xOffset, yOffset: 0, xAdvance: 0)); +// xOffset += 10; +// } + +// return new BitmapFont("font", regions, lineHeight); +// } + +// private static TextureRegion2D MockTextureRegion(int width = 100, int height = 200) +// { +// var texture = new Texture2D(new TestGraphicsDevice(), width, height); +// return new TextureRegion2D(texture, 0, 0, width, height); +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiControlCollectionTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiControlCollectionTests.cs new file mode 100644 index 0000000..a2145b8 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiControlCollectionTests.cs @@ -0,0 +1,58 @@ +//using MonoGame.Extended.Gui.Controls; +//using NSubstitute; +//using Xunit; + +//namespace MonoGame.Extended.Gui.Tests.Controls +//{ + +// public class GuiControlCollectionTests +// { +// [Fact] +// public void GuiControlCollection_Add_SetsTheParent_Test() +// { +// var parent = Substitute.For<GuiControl>(); +// var child = Substitute.For<GuiControl>(); + +// var controls = new GuiControlCollection(parent) { child }; +// Assert.IsTrue(controls.Contains(child)); +// Assert.AreSame(parent, child.Parent); +// } + +// [Fact] +// public void GuiControlCollection_Remove_SetsTheParentToNull_Test() +// { +// var parent = Substitute.For<GuiControl>(); +// var child = Substitute.For<GuiControl>(); + +// new GuiControlCollection(parent) { child }.Remove(child); + +// Assert.IsNull(child.Parent); +// } + +// [Fact] +// public void GuiControlCollection_Insert_SetsTheParent_Test() +// { +// var parent = Substitute.For<GuiControl>(); +// var child = Substitute.For<GuiControl>(); + +// var controls = new GuiControlCollection(parent); + +// controls.Insert(0, child); +// Assert.IsTrue(controls.Contains(child)); +// Assert.AreSame(parent, child.Parent); +// } + +// [Fact] +// public void GuiControlCollection_Clear_SetsAllTheParentsToNull_Test() +// { +// var parent = Substitute.For<GuiControl>(); +// var child0 = Substitute.For<GuiControl>(); +// var child1 = Substitute.For<GuiControl>(); + +// new GuiControlCollection(parent) { child0, child1 }.Clear(); + +// Assert.IsNull(child0.Parent); +// Assert.IsNull(child1.Parent); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/GuiRendererTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/GuiRendererTests.cs new file mode 100644 index 0000000..44b3bc3 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/GuiRendererTests.cs @@ -0,0 +1,34 @@ +using MonoGame.Extended.Gui.Controls; +using Xunit; + +namespace MonoGame.Extended.Gui.Tests +{ + // + //public class GuiRendererTests + //{ + // [Fact] + // public void GuiRenderer_TargetScreen_Test() + // { + // var screen = new GuiScreen(); + // var renderer = new GuiTestRenderer(screen); + + // Assert.That(renderer.TargetScreen, Is.SameAs(screen)); + // } + //} + + //public class GuiTestRenderer : GuiRenderer<TestDrawer> + //{ + // public GuiTestRenderer(GuiScreen targetScreen) + // : base(targetScreen) + // { + // } + + // protected override void DrawControl(TestDrawer drawer, GuiControl control) + // { + // } + //} + + //public class TestDrawer + //{ + //} +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj new file mode 100644 index 0000000..6e96c86 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Gui\MonoGame.Extended.Gui.csproj" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AngleTest.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AngleTest.cs new file mode 100644 index 0000000..2f2762b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AngleTest.cs @@ -0,0 +1,92 @@ +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests +{ + public class AngleTest + { + private const float _delta = 0.00001f; + private readonly WithinDeltaEqualityComparer _withinDeltaEqualityComparer = new(_delta); + + [Fact] + public void ConstructorTest() + { + const float value = 0.5f; + + // ReSharper disable once RedundantArgumentDefaultValue + var radians = new Angle(value, AngleType.Radian); + var degrees = new Angle(value, AngleType.Degree); + var gradians = new Angle(value, AngleType.Gradian); + var revolutions = new Angle(value, AngleType.Revolution); + + Assert.Equal(0.5f, radians.Radians, _withinDeltaEqualityComparer); + Assert.Equal(0.5f, degrees.Degrees, _withinDeltaEqualityComparer); + Assert.Equal(0.5f, gradians.Gradians, _withinDeltaEqualityComparer); + Assert.Equal(0.5f, revolutions.Revolutions, _withinDeltaEqualityComparer); + } + + [Fact] + public void ConversionTest() + { + //from radians + var radians = new Angle(MathHelper.Pi); + Assert.Equal(180f, radians.Degrees, _withinDeltaEqualityComparer); + Assert.Equal(200f, radians.Gradians, _withinDeltaEqualityComparer); + Assert.Equal(0.5f, radians.Revolutions, _withinDeltaEqualityComparer); + + //to radians + var degrees = new Angle(180f, AngleType.Degree); + var gradians = new Angle(200f, AngleType.Gradian); + var revolutions = new Angle(0.5f, AngleType.Revolution); + + Assert.Equal(MathHelper.Pi, degrees.Radians, _withinDeltaEqualityComparer); + Assert.Equal(MathHelper.Pi, gradians.Radians, _withinDeltaEqualityComparer); + Assert.Equal(MathHelper.Pi, revolutions.Radians, _withinDeltaEqualityComparer); + } + + [Fact] + public void WrapTest() + { + for (var f = -10f; f < 10f; f += 0.1f) + { + var wrappositive = new Angle(f); + wrappositive.WrapPositive(); + + var wrap = new Angle(f); + wrap.Wrap(); + + Assert.True(wrappositive.Radians >= 0); + Assert.True(wrappositive.Radians < 2d * MathHelper.Pi); + + Assert.True(wrap.Radians >= -MathHelper.Pi); + Assert.True(wrap.Radians < MathHelper.Pi); + } + } + + [Fact] + public void VectorTest() + { + var angle = Angle.FromVector(Vector2.One); + Assert.Equal(-MathHelper.Pi / 4f, angle.Radians, _withinDeltaEqualityComparer); + Assert.Equal(10f, angle.ToVector(10f).Length()); + + angle = Angle.FromVector(Vector2.UnitX); + Assert.Equal(0, angle.Radians, _withinDeltaEqualityComparer); + Assert.True(Vector2.UnitX.EqualsWithTolerence(angle.ToUnitVector())); + + angle = Angle.FromVector(-Vector2.UnitY); + Assert.Equal(MathHelper.Pi / 2f, angle.Radians, _withinDeltaEqualityComparer); + Assert.True((-Vector2.UnitY).EqualsWithTolerence(angle.ToUnitVector())); + } + + [Fact] + public void EqualsTest() + { + var angle1 = new Angle(0); + var angle2 = new Angle(MathHelper.Pi * 2f); + Assert.True(angle1 == angle2); + angle2.Radians = MathHelper.Pi * 4f; + Assert.True(angle1.Equals(angle2)); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AssertExtensions.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AssertExtensions.cs new file mode 100644 index 0000000..72347ca --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AssertExtensions.cs @@ -0,0 +1,21 @@ +using System; + +namespace MonoGame.Extended.Tests +{ + public static class AssertExtensions + { + public static bool AreApproximatelyEqual(Point2 firstPoint, Point2 secondPoint) + { + return Math.Abs(firstPoint.X - secondPoint.X) < float.Epsilon && + Math.Abs(firstPoint.Y - secondPoint.Y) < float.Epsilon; + } + + public static bool AreApproximatelyEqual(RectangleF firstRectangle, RectangleF secondRectangle) + { + return Math.Abs(firstRectangle.X - secondRectangle.X) < float.Epsilon && + Math.Abs(firstRectangle.Y - secondRectangle.Y) < float.Epsilon && + Math.Abs(firstRectangle.Width - secondRectangle.Width) < float.Epsilon && + Math.Abs(firstRectangle.Height - secondRectangle.Height) < float.Epsilon; + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs new file mode 100644 index 0000000..8f751bc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs @@ -0,0 +1,79 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.BitmapFonts; +using MonoGame.Extended.TextureAtlases; +using Xunit; + +namespace MonoGame.Extended.Tests.BitmapFonts +{ + public class BitmapFontTests + { + [Fact] + public void BitmapFont_Constructor_Test() + { + var font = CreateTestFont(); + + Assert.Equal("Impact", font.Name); + Assert.Equal(22, font.LineHeight); + } + + [Fact] + public void BitmapFont_MeasureString_SingleWord_Test() + { + var font = CreateTestFont(); + var size = font.MeasureString("fox"); + + Assert.Equal(40, size.Width); + Assert.Equal(font.LineHeight, size.Height); + } + + [Fact] + public void BitmapFont_MeasureString_WithLetterSpacing_Test() + { + var font = CreateTestFont(); + font.LetterSpacing = 3; + + var size = font.MeasureString("fox"); + + Assert.Equal(46, size.Width); + Assert.Equal(size.Height, font.LineHeight); + } + + [Fact] + public void BitmapFont_MeasureString_MultipleLines_Test() + { + var font = CreateTestFont(); + var size = font.MeasureString("box fox\nbox of fox"); + + Assert.Equal(123, size.Width); + Assert.Equal(size.Height, font.LineHeight * 2); + } + + [Fact] + public void BitmapFont_MeasureString_EmptyString_Test() + { + var font = CreateTestFont(); + var size = font.MeasureString(string.Empty); + + Assert.Equal(0, size.Width); + Assert.Equal(0, size.Height); + } + + private static BitmapFont CreateTestFont() + { + var textureRegion = new TextureRegion2D(null, x: 219, y: 61, width: 16, height: 18); + var regions = new[] + { + // extracted from 'Impact' font. 'x' is particularly interesting because it has a negative x offset + new BitmapFontRegion(textureRegion, character: ' ', xOffset: 0, yOffset: 0, xAdvance: 6), + new BitmapFontRegion(textureRegion, character: 'b', xOffset: 0, yOffset: 7, xAdvance: 17), + new BitmapFontRegion(textureRegion, character: 'f', xOffset: 0, yOffset: 7, xAdvance: 9), + new BitmapFontRegion(textureRegion, character: 'o', xOffset: 0, yOffset: 11, xAdvance: 16), + new BitmapFontRegion(textureRegion, character: 'x', xOffset: -1, yOffset: 11, xAdvance: 13), + }; + + var font = new BitmapFont("Impact", regions, lineHeight: 22); + return font; + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Camera2DTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Camera2DTests.cs new file mode 100644 index 0000000..2f188b8 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Camera2DTests.cs @@ -0,0 +1,97 @@ +//using Microsoft.Xna.Framework; +//using MonoGame.Extended.ViewportAdapters; +//using Xunit; + +//namespace MonoGame.Extended.Tests +//{ +// +// public class Camera2DTests +// { +// [Fact] +// public void Camera2D_LookAt_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var viewportAdapter = new DefaultViewportAdapter(graphicsDevice); +// var camera = new OrthographicCamera(viewportAdapter); + +// camera.LookAt(new Vector2(100, 200)); + +// Assert.Equal(new Vector2(-300, -40), camera.Position); +// } + +// [Fact] +// public void Camera2D_GetBoundingFrustum_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var camera = new OrthographicCamera(graphicsDevice); +// var boundingFrustum = camera.GetBoundingFrustum(); +// var corners = boundingFrustum.GetCorners(); + +// const float delta = 0.01f; +// TestHelper.AreEqual(new Vector3(0, 0, 1), corners[0], delta); +// TestHelper.AreEqual(new Vector3(800, 0, 1), corners[1], delta); +// TestHelper.AreEqual(new Vector3(800, 480, 1), corners[2], delta); +// TestHelper.AreEqual(new Vector3(0, 480, 1), corners[3], delta); +// TestHelper.AreEqual(new Vector3(0, 0, 0), corners[4], delta); +// TestHelper.AreEqual(new Vector3(800, 0, 0), corners[5], delta); +// TestHelper.AreEqual(new Vector3(800, 480, 0), corners[6], delta); +// TestHelper.AreEqual(new Vector3(0, 480, 0), corners[7], delta); +// } + +// [Fact] +// public void Camera2D_BoundingRectangle_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var viewport = new DefaultViewportAdapter(graphicsDevice); +// var camera = new OrthographicCamera(viewport); +// camera.Move(new Vector2(2, 0)); +// camera.Move(new Vector2(0, 3)); + +// var boundingRectangle = camera.BoundingRectangle; + +// Assert.Equal(2, boundingRectangle.Left, 0.01); +// Assert.Equal(3, boundingRectangle.Top, 0.01); +// Assert.Equal(802, boundingRectangle.Right, 0.01); +// Assert.Equal(483, boundingRectangle.Bottom, 0.01); +// } + +// [Fact] +// public void Camera2D_ContainsPoint_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var camera = new OrthographicCamera(graphicsDevice); + +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Point(1, 1))); +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Point(799, 479))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Point(-1, -1))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Point(801, 481))); +// } + +// [Fact] +// public void Camera2D_ContainsVector2_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var camera = new OrthographicCamera(graphicsDevice); + +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Vector2(799.5f, 479.5f))); +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Vector2(0.5f, 0.5f))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Vector2(-0.5f, -0.5f))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Vector2(800.5f, 480.5f))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Vector2(-0.5f, 240f))); +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Vector2(0.5f, 240f))); +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Vector2(799.5f, 240f))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Vector2(800.5f, 240f))); +// } + +// [Fact] +// public void Camera2D_ContainsRectangle_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var camera = new OrthographicCamera(graphicsDevice); + +// Assert.Equal(ContainmentType.Intersects, camera.Contains(new Rectangle(-50, -50, 100, 100))); +// Assert.Equal(ContainmentType.Contains, camera.Contains(new Rectangle(50, 50, 100, 100))); +// Assert.Equal(ContainmentType.Disjoint, camera.Contains(new Rectangle(850, 500, 100, 100))); +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/CollectionAssert.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/CollectionAssert.cs new file mode 100644 index 0000000..15c105b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/CollectionAssert.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Xunit; + +namespace MonoGame.Extended.Tests; + +public static class CollectionAssert +{ + public static void Equal<T>(IReadOnlyList<T> expected, IReadOnlyList<T> actual) + { + Assert.True(expected.Count == actual.Count, "The number of items in the collections does not match."); + + Assert.All(actual, x => Assert.Contains(x, expected)); + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/BagTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/BagTests.cs new file mode 100644 index 0000000..f89b45b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/BagTests.cs @@ -0,0 +1,48 @@ +using MonoGame.Extended.Collections; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; + +namespace MonoGame.Extended.Tests.Collections +{ + public class BagTests + { + [Fact] + public void Bag_Enumeration_Does_Not_Allocate() + { + var bag = new Bag<int>(); + for (int i = 0; i < 100; i++) bag.Add(i); + // ensure we have plenty of memory and that the heap only increases for the duration of this test + Assert.True(GC.TryStartNoGCRegion(Unsafe.SizeOf<Bag<int>.BagEnumerator>() * 1000)); + var heapSize = GC.GetAllocatedBytesForCurrentThread(); + + // this should NOT allocate + foreach (int i in bag) + { + // assert methods cause the NoGCRegion to fail, so do this manually + if (GC.GetAllocatedBytesForCurrentThread() != heapSize) + Assert.True(false); + } + + // sanity check: this SHOULD allocate + foreach (int _ in (IEnumerable<int>)bag) + { + // assert methods cause the NoGCRegion to fail, so do this manually + if (GC.GetAllocatedBytesForCurrentThread() == heapSize) + Assert.True(false); + } + + // Wrap in if statement due to exception thrown when running test through + // cake build script or when debugging test script manually. + if(GCSettings.LatencyMode == GCLatencyMode.NoGCRegion) + { + GC.EndNoGCRegion(); + } + } + + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/DequeTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/DequeTests.cs new file mode 100644 index 0000000..3196309 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/DequeTests.cs @@ -0,0 +1,408 @@ +using System; +using System.Linq; +using MonoGame.Extended.Collections; +using Xunit; + +namespace MonoGame.Extended.Tests.Collections +{ + public class DequeTests + { + private class TestDequeElement + { + public int Value { get; set; } + } + + private readonly Random _random; + + public DequeTests() + { + _random = new Random(); + } + + [Fact] + public void Deque_Constructor_Default() + { + var deque = new Deque<object>(); + Assert.True(deque.Count == 0); + Assert.True(deque.Capacity == 0); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Constructor_Collection(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + Assert.True(deque.Count == count); + Assert.True(deque.Capacity == count); + for (var index = 0; index < deque.Count; index++) + { + Assert.True(deque[index].Value == index); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Constructor_Capacity(int capacity) + { + var deque = new Deque<TestDequeElement>(capacity); + Assert.True(deque.Count == 0); + Assert.True(deque.Capacity == capacity); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Clear(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + deque.Clear(); + Assert.True(deque.Count == 0); + Assert.True(deque.Capacity >= count); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Trim_And_Clear(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + deque.Clear(); + deque.TrimExcess(); + Assert.True(deque.Count == 0); + Assert.True(deque.Capacity == 0); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Trim_Front(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + + for (var i = 0; i < count; i++) + { + deque.RemoveFromFront(out _); + deque.Capacity = deque.Count; + Assert.True(deque.Count == count - 1 - i); + Assert.True(deque.Capacity == count - 1 - i); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Trim_Back(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + + for (var i = 0; i < count; i++) + { + deque.RemoveFromBack(out _); + deque.Capacity = deque.Count; + Assert.True(deque.Count == count - 1 - i); + Assert.True(deque.Capacity == count - 1 - i); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Add_Front(int count) + { + var deque = new Deque<TestDequeElement>(); + for (var i = 0; i < count; i++) + { + deque.AddToFront(new TestDequeElement + { + Value = i + }); + } + Assert.True(deque.Count == count); + Assert.True(deque.Capacity >= count); + for (var index = 0; index < deque.Count; index++) + { + var element = deque[index]; + Assert.True(element.Value == deque.Count - 1 - index); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Add_Back(int count) + { + var deque = new Deque<TestDequeElement>(); + for (var i = 0; i < count; i++) + { + deque.AddToBack(new TestDequeElement + { + Value = i + }); + } + Assert.True(deque.Count == count); + Assert.True(deque.Capacity >= count); + for (var index = 0; index < deque.Count; index++) + { + var element = deque[index]; + Assert.True(element.Value == index); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Remove_Front(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + + var index = 0; + while (deque.RemoveFromFront(out var element)) + { + Assert.True(element.Value == index); + index++; + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Remove_Back(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + + var index = 0; + while (deque.RemoveFromBack(out var element)) + { + Assert.True(element.Value == elements.Length - 1 - index); + index++; + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Get_Front(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + var indices = Enumerable.Range(0, count); + foreach (var index in indices) + { + deque.GetFront(out var element); + deque.RemoveFromFront(); + Assert.True(element.Value == index); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Get_Back(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + var indices = Enumerable.Range(0, count); + foreach (var index in indices) + { + deque.GetBack(out var element); + deque.RemoveFromBack(); + Assert.True(element.Value == count - 1 - index); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Get_Index(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + var indices = Enumerable.Range(0, count).ToList().Shuffle(_random); + foreach (var index in indices) + { + deque.Get(index, out var element); + Assert.True(element.Value == index); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_ForEach_Iteration(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + var counter = 0; + foreach (var element in deque) + { + Assert.True(element.Value == counter); + counter++; + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_ForEach_Iteration_Modified(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + var counter = 0; + foreach (var element in deque) + { + Assert.True(element.Value == counter); + counter++; + deque.RemoveFromFront(); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(50)] + public void Deque_Remove(int count) + { + var elements = new TestDequeElement[count]; + for (var i = 0; i < count; i++) + { + elements[i] = new TestDequeElement + { + Value = i + }; + } + var deque = new Deque<TestDequeElement>(elements); + var counter = count; + while (deque.Count > 0) + { + var index = _random.Next(0, deque.Count - 1); + deque.RemoveAt(index); + counter--; + Assert.True(deque.Count == counter); + } + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Content/ContentReaderExtensionsTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Content/ContentReaderExtensionsTests.cs new file mode 100644 index 0000000..e1c701d --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Content/ContentReaderExtensionsTests.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq; +using MonoGame.Extended.Collections; +using MonoGame.Extended.Content; +using Xunit; + +namespace MonoGame.Extended.Tests.Content +{ + public class ContentReaderExtensionsTests + { + [Theory] + [InlineData("testsuperdir/testsubdir1/testsubdir2/resource", "testsuperdir/testsubdir1/testsubdir2/resource")] + [InlineData("testsuperdir/testsubdir1/../testsubdir2/resource","testsuperdir/testsubdir2/resource")] + [InlineData("testsuperdir/../resource","resource")] + [InlineData("../testsuperdir/testsubdir1/../testsubdir2/resource","../testsuperdir/testsubdir2/resource")] + [InlineData("testsuperdir/testsubdir1/testsubdir2/../../testsubdir3/resource","testsuperdir/testsubdir3/resource")] + [InlineData("testsuperdir/testsubdir1/../testsubdir2/../testsubdir3/resource", "testsuperdir/testsubdir3/resource")] + public void ContentReaderExtensions_ShortenRelativePath(string input, string expectedoutput) + { + Assert.True(ContentReaderExtensions.ShortenRelativePath(input) == expectedoutput); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MockGameWindow.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MockGameWindow.cs new file mode 100644 index 0000000..a65479b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MockGameWindow.cs @@ -0,0 +1,42 @@ +using System; +using System.Drawing; +using Microsoft.Xna.Framework; +using Point = Microsoft.Xna.Framework.Point; +using Rectangle = Microsoft.Xna.Framework.Rectangle; + +namespace MonoGame.Extended.Tests +{ + public class MockGameWindow : GameWindow + { + public override bool AllowUserResizing { get; set; } + public override Rectangle ClientBounds { get; } + public override Point Position { get; set; } + public override DisplayOrientation CurrentOrientation { get; } + public override IntPtr Handle { get; } + public override string ScreenDeviceName { get; } + + public MockGameWindow() + { + } + + public override void BeginScreenDeviceChange(bool willBeFullScreen) + { + } + + public override void EndScreenDeviceChange(string screenDeviceName, int clientWidth, int clientHeight) + { + } + + protected override void SetSupportedOrientations(DisplayOrientation orientations) + { + } + + protected override void SetTitle(string title) + { + } + +#if __MonoCS__ + public override Icon Icon { get; set; } +#endif + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj new file mode 100644 index 0000000..9e831ff --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended\MonoGame.Extended.csproj" /> + <ProjectReference Include="..\..\source\MonoGame.Extended.Particles\MonoGame.Extended.Particles.csproj" /> + </ItemGroup> + + <ItemGroup> + <Folder Include="Content\" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/OpenTK.dll.config b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/OpenTK.dll.config new file mode 100644 index 0000000..1dc6f45 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/OpenTK.dll.config @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1" /> + <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1" /> + <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1" /> + <dllmap os="linux" dll="alut.dll" target="libalut.so.0" /> + <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so" /> + <dllmap os="linux" dll="libX11" target="libX11.so.6" /> + <dllmap os="linux" dll="libXi" target="libXi.so.6" /> + <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0" /> + <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL" /> + <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" /> + <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" /> + <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" /> + <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" /> + <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" /> + <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL" /> + <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib" /> + <!-- XQuartz compatibility (X11 on Mac) --> + <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib" /> + <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib" /> + <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib" /> + <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib" /> + <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib" /> + <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib" /> +</configuration> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/AssertionModifier.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/AssertionModifier.cs new file mode 100644 index 0000000..ff23d8b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/AssertionModifier.cs @@ -0,0 +1,25 @@ +//using System; +//using MonoGame.Extended.Particles; +//using MonoGame.Extended.Particles.Modifiers; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Particles +//{ +// internal class AssertionModifier : Modifier +// { +// private readonly Predicate<Particle> _predicate; + +// public AssertionModifier(Predicate<Particle> predicate) +// { +// _predicate = predicate; +// } + +// public override unsafe void Update(float elapsedSeconds, ParticleBuffer.ParticleIterator iterator) +// { +// while (iterator.HasNext) { +// var particle = iterator.Next(); +// Assert.IsTrue(_predicate(*particle)); +// } +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ColourTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ColourTests.cs new file mode 100644 index 0000000..49b5e03 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ColourTests.cs @@ -0,0 +1,126 @@ +using System; +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests.Particles +{ + public class ColourTests + { + public class Constructor + { + [Fact] + public void WhenGivenValues_ReturnsInitializedColour() + { + var colour = new HslColor(1f, 1f, 1f); + Assert.Equal(1f, colour.H); + Assert.Equal(1f, colour.S); + Assert.Equal(1f, colour.L); + } + } + + public class AreEqualColourMethod + { + [Fact] + public void WhenGivenEqualValues_ReturnsTrue() + { + var x = new HslColor(360f, 1f, 1f); + var y = new HslColor(360f, 1f, 1f); + Assert.Equal(x, y); + } + + [Fact] + public void WhenGivenDifferentValues_ReturnsFalse() + { + var x = new HslColor(0f, 1f, 0f); + var y = new HslColor(360f, 1f, 1f); + Assert.False(x.Equals(y)); + } + } + + public class AreEqualObjectMethod + { + [Fact] + public void WhenGivenEqualColour_ReturnsTrue() + { + var x = new HslColor(360f, 1f, 1f); + + Object y = new HslColor(360f, 1f, 1f); + Assert.Equal(x, y); + } + + [Fact] + public void WhenGivenDifferentColour_ReturnsFalse() + { + var x = new HslColor(360f, 1f, 1f); + + Object y = new HslColor(0f, 1f, 0f); + Assert.False(x.Equals(y)); + } + + [Fact] + public void WhenGivenObjectOfAntotherType_ReturnsFalse() + { + var colour = new HslColor(360f, 1f, 1f); + + // ReSharper disable once SuspiciousTypeConversion.Global + Assert.False(colour.Equals(DateTime.Now)); + } + } + + public class GetHashCodeMethod + { + [Fact] + public void WhenObjectsAreDifferent_YieldsDifferentHashCodes() + { + var x = new HslColor(0f, 1f, 0f); + var y = new HslColor(360f, 1f, 1f); + Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); + } + + [Fact] + public void WhenObjectsAreSame_YieldsIdenticalHashCodes() + { + var x = new HslColor(180f, 0.5f, 0.5f); + var y = new HslColor(180f, 0.5f, 0.5f); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + } + + public class ToStringMethod + { + [Theory] + [InlineData(360f, 1f, 1f, "H:0.0° S:100.0 L:100.0")] + [InlineData(180f, 0.5f, 0.5f, "H:180.0° S:50.0 L:50.0")] + [InlineData(0f, 0f, 0f, "H:0.0° S:0.0 L:0.0")] + public void ReturnsCorrectValue(float h, float s, float l, string expected) + { + var colour = new HslColor(h, s, l); + Assert.Equal(expected, colour.ToString()); + } + } + + public class ToRgbMethod + { + [Theory] + [InlineData(0f, 1f, 0.5f, "{R:255 G:0 B:0 A:255}")] // Color.Red + [InlineData(360f, 1f, 0.5f, "{R:255 G:0 B:0 A:255}")] // Color.Red + [InlineData(120f, 1f, 0.5f, "{R:0 G:255 B:0 A:255}")] // Color.Lime + public void ReturnsCorrectValue(float h, float s, float l, string expected) + { + var hslColour = new HslColor(h, s, l); + Color rgbColor = hslColour.ToRgb(); + + Assert.Equal(expected, rgbColor.ToString()); + } + + [Fact] + public void FromRgbAndToRgbWorksCorrectly() + { + HslColor blueHsl = HslColor.FromRgb(Color.Blue); + Color blueRgb = blueHsl.ToRgb(); + + Assert.Equal(Color.Blue, blueRgb); + } + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/EmitterTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/EmitterTests.cs new file mode 100644 index 0000000..4ec9b65 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/EmitterTests.cs @@ -0,0 +1,131 @@ +//using System; +//using Microsoft.Xna.Framework; +//using MonoGame.Extended.Particles; +//using MonoGame.Extended.Particles.Modifiers; +//using MonoGame.Extended.Particles.Profiles; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Particles +//{ +// +// public class EmitterTests +// { +// public class UpdateMethod +// { +// [Fact] +// public void WhenThereAreParticlesToExpire_DecreasesActiveParticleCount() +// { +// var subject = new ParticleEmitter(null, 100, TimeSpan.FromSeconds(1), Profile.Point()) +// { +// AutoTrigger = false, +// Parameters = new ParticleReleaseParameters +// { +// Quantity = 1 +// } +// }; + +// subject.Trigger(new Vector2(0f, 0f)); +// Assert.Equal(subject.ActiveParticles, 1); + +// subject.Update(2f); +// Assert.Equal(subject.ActiveParticles, 0); +// } + +// [Fact] +// public void WhenThereAreParticlesToExpire_DoesNotPassExpiredParticlesToModifiers() +// { +// var subject = new ParticleEmitter(null, 100, TimeSpan.FromSeconds(1), Profile.Point()) +// { +// Parameters = new ParticleReleaseParameters() +// { +// Quantity = 1 +// }, +// Modifiers = +// { +// new AssertionModifier(particle => particle.Age <= 1f) +// } +// }; + +// subject.Trigger(new Vector2(0f, 0f)); +// subject.Update(0.5f); +// subject.Trigger(new Vector2(0f, 0f)); +// subject.Update(0.5f); +// subject.Trigger(new Vector2(0f, 0f)); +// subject.Update(0.5f); +// } + +// [Fact] +// public void WhenThereAreNoActiveParticles_GracefullyDoesNothing() +// { +// var subject = new ParticleEmitter(null, 100, TimeSpan.FromSeconds(1), Profile.Point()) { AutoTrigger = false }; + +// subject.Update(0.5f); +// Assert.Equal(subject.ActiveParticles, 0); +// } +// } + +// public class TriggerMethod +// { +// [Fact] +// public void WhenEnoughHeadroom_IncreasesActiveParticlesCountByReleaseQuantity() +// { +// var subject = new ParticleEmitter(null, 100, TimeSpan.FromSeconds(1), Profile.Point()) +// { +// Parameters = new ParticleReleaseParameters +// { +// Quantity = 10 +// } +// }; +// Assert.Equal(subject.ActiveParticles, 0); +// subject.Trigger(new Vector2(0f, 0f)); +// Assert.Equal(subject.ActiveParticles, 10); +// } + +// [Fact] +// public void WhenNotEnoughHeadroom_IncreasesActiveParticlesCountByRemainingParticles() +// { +// var subject = new ParticleEmitter(null, 15, TimeSpan.FromSeconds(1), Profile.Point()) +// { +// Parameters = new ParticleReleaseParameters +// { +// Quantity = 10 +// } +// }; + +// subject.Trigger(new Vector2(0f, 0f)); +// Assert.Equal(subject.ActiveParticles, 10); +// subject.Trigger(new Vector2(0f, 0f)); +// Assert.Equal(subject.ActiveParticles, 15); +// } + +// [Fact] +// public void WhenNoRemainingParticles_DoesNotIncreaseActiveParticlesCount() +// { +// var subject = new ParticleEmitter(null, 10, TimeSpan.FromSeconds(1), Profile.Point()) +// { +// Parameters = new ParticleReleaseParameters +// { +// Quantity = 10 +// } +// }; + +// subject.Trigger(new Vector2(0f, 0f)); +// Assert.Equal(subject.ActiveParticles, 10); +// subject.Trigger(new Vector2(0f, 0f)); +// Assert.Equal(subject.ActiveParticles, 10); +// } +// } + +// public class DisposeMethod +// { +// [Fact] +// public void IsIdempotent() +// { +// var subject = new ParticleEmitter(null, 10, TimeSpan.FromSeconds(1), Profile.Point()); + +// subject.Dispose(); +// subject.Dispose(); +// } +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ParticleBufferTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ParticleBufferTests.cs new file mode 100644 index 0000000..ef2e159 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ParticleBufferTests.cs @@ -0,0 +1,184 @@ +//using System; +//using MonoGame.Extended.Particles; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Particles +//{ +// +// public class ParticleBufferTests +// { +// public class AvailableProperty +// { +// [Fact] +// public void WhenNoParticlesReleased_ReturnsBufferSize() +// { +// var subject = new ParticleBuffer(100); + +// Assert.Equal(subject.Available, 100); +// } + +// [Fact] +// public void WhenSomeParticlesReleased_ReturnsAvailableCount() +// { +// var subject = new ParticleBuffer(100); + +// subject.Release(10); +// Assert.Equal(subject.Available, 90); +// } + +// [Fact] +// public void WhenAllParticlesReleased_ReturnsZero() +// { +// var subject = new ParticleBuffer(100); + +// subject.Release(100); +// Assert.Equal(subject.Available, 0); + +// } +// } + +// public class CountProperty +// { +// [Fact] +// public void WhenNoParticlesReleased_ReturnsZero() +// { +// var subject = new ParticleBuffer(100); +// Assert.Equal(subject.Count, 0); +// } + +// [Fact] +// public void WhenSomeParticlesReleased_ReturnsCount() +// { +// var subject = new ParticleBuffer(100); + +// subject.Release(10); +// Assert.Equal(subject.Count, 10); + +// } + +// [Fact] +// public void WhenAllParticlesReleased_ReturnsZero() +// { +// var subject = new ParticleBuffer(100); + +// subject.Release(100); +// Assert.Equal(subject.Count, 100); + +// } +// } + +// public class ReleaseMethod +// { +// [Fact] +// public void WhenPassedReasonableQuantity_ReturnsNumberReleased() +// { +// var subject = new ParticleBuffer(100); + +// var count = subject.Release(50); + +// Assert.Equal(count.Total, 50); +// } + +// [Fact] +// public void WhenPassedImpossibleQuantity_ReturnsNumberActuallyReleased() +// { +// var subject = new ParticleBuffer(100); + +// var count = subject.Release(200); +// Assert.Equal(count.Total, 100); +// } +// } + +// public class ReclaimMethod +// { +// [Fact] +// public void WhenPassedReasonableNumber_ReclaimsParticles() +// { +// var subject = new ParticleBuffer(100); + +// subject.Release(100); +// Assert.Equal(subject.Count, 100); + +// subject.Reclaim(50); +// Assert.Equal(subject.Count, 50); +// } +// } + +// //public class CopyToMethod +// //{ +// // [Fact] +// // public void WhenBufferIsSequential_CopiesParticlesInOrder() +// // { +// // unsafe +// // { +// // var subject = new ParticleBuffer(10); +// // var iterator = subject.Release(5); + +// // do +// // { +// // var particle = iterator.Next(); +// // particle->Age = 1f; +// // } +// // while (iterator.HasNext); + +// // var destination = new Particle[10]; + +// // fixed (Particle* buffer = destination) +// // { +// // subject.CopyTo((IntPtr)buffer); +// // } + +// // Assert.Equal(destination[0].Age, 1f, 0.0001); +// // Assert.Equal(destination[1].Age, 1f, 0.0001); +// // Assert.Equal(destination[2].Age, 1f, 0.0001); +// // Assert.Equal(destination[3].Age, 1f, 0.0001); +// // Assert.Equal(destination[4].Age, 1f, 0.0001); +// // } +// // } +// //} + +// //public class CopyToReverseMethod +// //{ +// // [Fact] +// // public void WhenBufferIsSequential_CopiesParticlesInReverseOrder() +// // { +// // unsafe +// // { +// // var subject = new ParticleBuffer(10); +// // var iterator = subject.Release(5); + +// // do +// // { +// // var particle = iterator.Next(); +// // particle->Age = 1f; +// // } +// // while (iterator.HasNext); + +// // var destination = new Particle[10]; + +// // fixed (Particle* buffer = destination) +// // { +// // subject.CopyToReverse((IntPtr)buffer); +// // } + +// // Assert.Equal(destination[0].Age, 1f, 0.0001); +// // Assert.Equal(destination[1].Age, 1f, 0.0001); +// // Assert.Equal(destination[2].Age, 1f, 0.0001); +// // Assert.Equal(destination[3].Age, 1f, 0.0001); +// // Assert.Equal(destination[4].Age, 1f, 0.0001); +// // } +// // } +// //} + +// public class DisposeMethod +// { +// [Fact] +// public void IsIdempotent() +// { +// var subject = new ParticleBuffer(100); +// subject.Dispose(); +// subject.Dispose(); +// } +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/PointProfileTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/PointProfileTests.cs new file mode 100644 index 0000000..cb22b3b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/PointProfileTests.cs @@ -0,0 +1,33 @@ +using System; +using MonoGame.Extended.Particles.Profiles; +using Xunit; + +namespace MonoGame.Extended.Tests.Particles.Profiles +{ + + public class PointProfileTests + { + [Fact] + public void ReturnsZeroOffset() + { + var subject = new PointProfile(); + + subject.GetOffsetAndHeading(out var offset, out _); + + Assert.Equal(0f, offset.X); + Assert.Equal(0f, offset.Y); + } + + [Fact] + public void ReturnsHeadingAsUnitVector() + { + var subject = new PointProfile(); + + subject.GetOffsetAndHeading(out _, out var heading); + + var length = Math.Sqrt(heading.X * heading.X + heading.Y * heading.Y); + Assert.Equal(1f, length, 6); + } + + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/RingProfileTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/RingProfileTests.cs new file mode 100644 index 0000000..a18f5b7 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/RingProfileTests.cs @@ -0,0 +1,38 @@ +using System; +using MonoGame.Extended.Particles.Profiles; +using Xunit; + +namespace MonoGame.Extended.Tests.Particles.Profiles +{ + public class RingProfileTests + { + [Fact] + public void ReturnsOffsetEqualToRadius() + { + var subject = new RingProfile + { + Radius = 10f + }; + subject.GetOffsetAndHeading(out var offset, out _); + + var length = Math.Sqrt(offset.X * offset.X + offset.Y * offset.Y); + Assert.Equal(10f, length, 6); + } + + [Fact] + public void WhenRadiateIsTrue_HeadingIsEqualToNormalizedOffset() + { + var subject = new RingProfile + { + Radius = 10f, + Radiate = Profile.CircleRadiation.Out + }; + subject.GetOffsetAndHeading(out var offset, out var heading); + + Assert.Equal(heading.X, offset.X / 10, 6); + Assert.Equal(heading.Y, offset.Y / 10, 6); + + } + + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs new file mode 100644 index 0000000..6f95cf5 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs @@ -0,0 +1,396 @@ +//using System.Collections.Generic; +//using System.Globalization; +//using Microsoft.Xna.Framework; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Primitives +//{ +// +// public class BoundingRectangleTests +// { +// public IEnumerable<TestCaseData> ConstructorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Vector2()).SetName( +// "The empty bounding rectangle has the expected position and radii."); +// yield return +// new TestCaseData(new Point2(5, 5), new Vector2(15, 15)).SetName( +// "A non-empty bounding rectangle has the expected position and radii."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ConstructorTestCases))] +// public void Constructor(Point2 centre, Vector2 radii) +// { +// var boundingRectangle = new BoundingRectangle(centre, radii); +// Assert.Equal(centre, boundingRectangle.Center); +// Assert.Equal(radii, boundingRectangle.HalfExtents); +// } + +// public IEnumerable<TestCaseData> CreateFromMinimumMaximumTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), new BoundingRectangle()).SetName( +// "The bounding rectangle created from the zero minimum point and zero maximum point is the empty bounding rectangle.") +// ; +// yield return +// new TestCaseData(new Point2(5, 5), new Point2(15, 15), +// new BoundingRectangle(new Point2(10, 10), new Size2(5, 5))).SetName( +// "The bounding rectangle created from the non-zero minimum point and the non-zero maximum point is the expected bounding rectangle.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(CreateFromMinimumMaximumTestCases))] +// public void CreateFromMinimumMaximum(Point2 minimum, Point2 maximum, BoundingRectangle expectedBoundingRectangle) +// { +// var actualBoundingRectangle = BoundingRectangle.CreateFrom(minimum, maximum); +// Assert.Equal(expectedBoundingRectangle, actualBoundingRectangle); +// } + +// public IEnumerable<TestCaseData> CreateFromPointsTestCases +// { +// get +// { +// yield return +// new TestCaseData(null, new BoundingRectangle()).SetName( +// "The bounding rectangle created from null points is the empty bounding rectangle."); +// yield return +// new TestCaseData(new Point2[0], new BoundingRectangle()).SetName( +// "The bounding rectangle created from the empty set of points is the empty bounding rectangle."); +// yield return +// new TestCaseData( +// new[] +// { +// new Point2(5, 5), new Point2(10, 10), new Point2(15, 15), new Point2(-5, -5), +// new Point2(-15, -15) +// }, new BoundingRectangle(new Point2(0, 0), new Size2(15, 15))).SetName( +// "The bounding rectangle created from a non-empty set of points is the expected bounding rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(CreateFromPointsTestCases))] +// public void CreateFromPoints(Point2[] points, BoundingRectangle expectedBoundingRectangle) +// { +// var actualBoundingRectangle = BoundingRectangle.CreateFrom(points); +// Assert.Equal(expectedBoundingRectangle, actualBoundingRectangle); +// } + +// public IEnumerable<TestCaseData> CreateFromTransformedTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), Matrix2.Identity, new BoundingRectangle()).SetName( +// "The bounding rectangle created from the empty bounding rectangle transformed by the identity matrix is the empty bounding rectangle.") +// ; +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(20, 20)), Matrix2.CreateScale(2), new BoundingRectangle(new Point2(0, 0), new Size2(40, 40))).SetName( +// "The bounding rectangle created from a non-empty bounding rectangle transformed by a non-identity matrix is the expected bounding rectangle.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(CreateFromTransformedTestCases))] +// public void CreateFromTransformed(BoundingRectangle boundingRectangle, Matrix2 transformMatrix, +// BoundingRectangle expectedBoundingRectangle) +// { +// var actualBoundingRectangle = BoundingRectangle.Transform(boundingRectangle, ref transformMatrix); +// Assert.Equal(expectedBoundingRectangle, actualBoundingRectangle); +// } + +// public IEnumerable<TestCaseData> UnionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), new BoundingRectangle()).SetName( +// "The union of two empty bounding rectangles is the empty bounding rectangle."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(15, 15)), +// new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)), new BoundingRectangle(new Point2(20, 20), new Size2(40, 40))) +// .SetName( +// "The union of two non-empty bounding rectangles is the expected bounding rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(UnionTestCases))] +// public void Union(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, BoundingRectangle expectedBoundingRectangle) +// { +// Assert.Equal(expectedBoundingRectangle, boundingRectangle1.Union(boundingRectangle2)); +// Assert.Equal(expectedBoundingRectangle, BoundingRectangle.Union(boundingRectangle1, boundingRectangle2)); +// } + +// public IEnumerable<TestCaseData> IntersectionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), new BoundingRectangle()).SetName( +// "The intersection of two empty bounding rectangles is the empty bounding box."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(-10, -10), new Size2(15, 15)), +// new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)), +// new BoundingRectangle(new Point2(-7.5f, -7.5f), new Size2(12.5f, 12.5f))).SetName( +// "The intersection of two overlapping non-empty bounding rectangles is the expected bounding rectangle."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(-30, -30), new Size2(15, 15)), +// new BoundingRectangle(new Point2(20, 20), new Size2(10, 10)), +// BoundingRectangle.Empty).SetName( +// "The intersection of two non-overlapping non-empty bounding rectangles is the empty bounding rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(IntersectionTestCases))] +// public void Intersection(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, +// BoundingRectangle? expectedBoundingRectangle) +// { +// Assert.Equal(expectedBoundingRectangle, boundingRectangle1.Intersection(boundingRectangle2)); +// Assert.Equal(expectedBoundingRectangle, BoundingRectangle.Intersection(boundingRectangle1, boundingRectangle2)); +// } + +// public IEnumerable<TestCaseData> IntersectsTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), true).SetName( +// "Two empty bounding rectangles intersect."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(-10, -10), new Size2(15, 15)), +// new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)), true).SetName( +// "Two overlapping non-empty bounding rectangles intersect."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(-40, -50), new Size2(15, 15)), +// new BoundingRectangle(new Point2(20, 20), new Size2(15, 15)), false).SetName( +// "Two non-overlapping non-empty bounding rectangles do not intersect."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(IntersectsTestCases))] +// public void Intersects(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, bool expectedToIntersect) +// { +// Assert.Equal(expectedToIntersect, boundingRectangle1.Intersects(boundingRectangle2)); +// Assert.Equal(expectedToIntersect, BoundingRectangle.Intersects(boundingRectangle1, boundingRectangle2)); +// } + +// public IEnumerable<TestCaseData> ContainsPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new Point2(), true).SetName( +// "The empty bounding rectangle contains the zero point."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(15, 15)), new Point2(-15, -15), true) +// .SetName( +// "A non-empty bounding rectangle contains a point inside it."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(15, 15)), new Point2(-16, 15), false) +// .SetName( +// "A non-empty bounding rectangle does not contain a point outside it."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ContainsPointTestCases))] +// public void ContainsPoint(BoundingRectangle boundingRectangle, Point2 point, bool expectedToContainPoint) +// { +// Assert.Equal(expectedToContainPoint, boundingRectangle.Contains(point)); +// Assert.Equal(expectedToContainPoint, BoundingRectangle.Contains(boundingRectangle, point)); +// } + +// public IEnumerable<TestCaseData> ClosestPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new Point2(), new Point2()).SetName( +// "The closest point on the empty bounding rectangle to the zero point is the zero point."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Point2(50, 50)), new Point2(25, 25), +// new Point2(25, 25)).SetName( +// "The closest point on a non-empty bounding rectangle to a point which is inside the bounding rectangle is that point.") +// ; +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Point2(50, 50)), new Point2(400, 0), +// new Point2(50, 0)).SetName( +// "The closest point on a non-empty bounding rectangle to a point which is outside the bounding rectangle is the expected point.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ClosestPointTestCases))] +// public void ClosestPoint(BoundingRectangle boundingRectangle, Point2 point, Point2 expectedClosestPoint) +// { +// var actualClosestPoint = boundingRectangle.ClosestPointTo(point); +// Assert.Equal(expectedClosestPoint, actualClosestPoint); +// } + +// public IEnumerable<TestCaseData> EqualityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), true).SetName( +// "Empty bounding rectangles are equal.") +// ; +// yield return +// new TestCaseData( +// new BoundingRectangle(new Point2(0, 0), new Size2(float.MaxValue, float.MinValue)), +// new BoundingRectangle(new Point2(0, 0), +// new Point2(float.MinValue, float.MaxValue)), false).SetName( +// "Two different non-empty bounding rectangles are not equal."); +// yield return +// new TestCaseData( +// new BoundingRectangle(new Point2(0, 0), new Size2(float.MinValue, float.MaxValue)), +// new BoundingRectangle(new Point2(0, 0), +// new Size2(float.MinValue, float.MaxValue)), true).SetName( +// "Two identical non-empty bounding rectangles are equal."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(EqualityTestCases))] +// public void Equality(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, bool expectedToBeEqual) +// { +// Assert.IsTrue(Equals(boundingRectangle1, boundingRectangle2) == expectedToBeEqual); +// Assert.IsTrue(boundingRectangle1 == boundingRectangle2 == expectedToBeEqual); +// Assert.IsFalse(boundingRectangle1 == boundingRectangle2 != expectedToBeEqual); +// Assert.IsTrue(boundingRectangle1.Equals(boundingRectangle2) == expectedToBeEqual); + +// if (expectedToBeEqual) +// Assert.Equal(boundingRectangle1.GetHashCode(), boundingRectangle2.GetHashCode()); +// } + +// public IEnumerable<TestCaseData> InequalityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), null, false).SetName( +// "A bounding rectangle is not equal to a null object."); +// yield return +// new TestCaseData(new BoundingRectangle(), new object(), false).SetName( +// "A bounding rectangle is not equal to an instantiated object."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(InequalityTestCases))] +// public void Inequality(BoundingRectangle boundingRectangle, object obj, bool expectedToBeEqual) +// { +// Assert.IsTrue(boundingRectangle.Equals(obj) == expectedToBeEqual); +// } + +// public IEnumerable<TestCaseData> HashCodeTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), true).SetName( +// "Two empty bounding rectangles have the same hash code."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(50, 50)), +// new BoundingRectangle(new Point2(0, 0), new Size2(50, 50)), true).SetName( +// "Two indentical non-empty bounding rectangles have the same hash code."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(50, 50)), +// new BoundingRectangle(new Point2(50, 50), new Size2(50, 50)), false).SetName( +// "Two different non-empty bounding rectangles do not have the same hash code."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(HashCodeTestCases))] +// public void HashCode(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, bool expectedThatHashCodesAreEqual) +// { +// var hashCode1 = boundingRectangle1.GetHashCode(); +// var hashCode2 = boundingRectangle2.GetHashCode(); +// if (expectedThatHashCodesAreEqual) +// Assert.Equal(hashCode1, hashCode2); +// else +// Assert.AreNotEqual(hashCode1, hashCode2); +// } + +// public IEnumerable<TestCaseData> ToRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), new Rectangle()).SetName( +// "The empty bounding rectangle point converted to a rectangle is the empty rectangle."); +// yield return +// new TestCaseData(new BoundingRectangle(new Point2(25, 25), new Size2(25, 25)), +// new Rectangle(0, 0, 50, 50)).SetName( +// "A non-empty bounding rectangle converted to a rectangle is the expected rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ToRectangleTestCases))] +// public void ToRectangle(BoundingRectangle boundingRectangle, Rectangle expectedRectangle) +// { +// var actualRectangle = (Rectangle)boundingRectangle; +// Assert.Equal(expectedRectangle, actualRectangle); +// } + +// public IEnumerable<TestCaseData> FromRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Rectangle(), new BoundingRectangle()).SetName( +// "The empty rectangle converted to a bounding rectangle is the empty bounding rectangle."); +// yield return +// new TestCaseData(new Rectangle(0, 0, 50, 50), +// new BoundingRectangle(new Point2(25, 25), new Size2(25, 25))).SetName( +// "A non-empty rectangle converted to a bounding rectangle is the expected bounding rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(FromRectangleTestCases))] +// public void FromRectangle(Rectangle rectangle, BoundingRectangle expectedBoundingRectangle) +// { +// var actualBoundingRectangle = (BoundingRectangle)rectangle; +// Assert.Equal(expectedBoundingRectangle, actualBoundingRectangle); +// } + +// public IEnumerable<TestCaseData> StringCases +// { +// get +// { +// yield return +// new TestCaseData(new BoundingRectangle(), +// string.Format(CultureInfo.CurrentCulture, "Centre: {0}, Radii: {1}", new Point2(), +// new Vector2())).SetName( +// "The empty bounding rectangle has the expected string representation using the current culture."); +// yield return new TestCaseData(new BoundingRectangle(new Point2(5.1f, -5.123f), new Size2(5.4f, -5.4123f)), +// string.Format(CultureInfo.CurrentCulture, "Centre: {0}, Radii: {1}", new Point2(5.1f, -5.123f), +// new Vector2(5.4f, -5.4123f))).SetName( +// "A non-empty bounding rectangle has the expected string representation using the current culture."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(StringCases))] +// public void String(BoundingRectangle boundingRectangle, string expectedString) +// { +// var actualString = boundingRectangle.ToString(); +// Assert.Equal(expectedString, actualString); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/CircleFTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/CircleFTests.cs new file mode 100644 index 0000000..f88155f --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/CircleFTests.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests.Primitives +{ + +public class CircleFTests +{ + + [Fact] + public void CircCircIntersectionDiagonalCircleTest() + { + var circle = new CircleF(new Point2(16.0f, 16.0f), 16.0f); + var point = new Point2(0, 0); + + Assert.False(circle.Contains(point)); + } +// public IEnumerable<TestCaseData> ConstructorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), 0.0f).SetName( +// "The empty circle has the expected position and radius."); +// yield return +// new TestCaseData(new Point2(5, 5), 15f).SetName( +// "A non-empty circle has the expected position and radius."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ConstructorTestCases))] +// public void Constructor(Point2 centre, float radius) +// { +// var circle = new CircleF(centre, radius); +// Assert.Equal(centre, circle.Center); +// Assert.Equal(radius, circle.Radius); +// } + +// public IEnumerable<TestCaseData> CreateFromMinimumMaximumTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), new CircleF()).SetName( +// "The bounding circle created from the zero minimum point and zero maximum point is the empty bounding circle.") +// ; +// yield return +// new TestCaseData(new Point2(5, 5), new Point2(15, 15), +// new CircleF(new Point2(10, 10), 5f)).SetName( +// "The bounding circle created from the non-zero minimum point and the non-zero maximum point is the expected bounding circle.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(CreateFromMinimumMaximumTestCases))] +// public void CreateFromMinimumMaximum(Point2 minimum, Point2 maximum, CircleF expectedBoundingCircle) +// { +// var actualBoundingCircle = CircleF.CreateFrom(minimum, maximum); +// Assert.Equal(expectedBoundingCircle, actualBoundingCircle); +// } + +// public IEnumerable<TestCaseData> CreateFromPointsTestCases +// { +// get +// { +// yield return +// new TestCaseData(null, new CircleF()).SetName( +// "The bounding circle created from null points is the empty bounding circle."); +// yield return +// new TestCaseData(new Point2[0], new CircleF()).SetName( +// "The bounding circle created from the empty set of points is the empty bounding circle."); +// yield return +// new TestCaseData( +// new[] +// { +// new Point2(5, 5), new Point2(10, 10), new Point2(15, 15), new Point2(-5, -5), +// new Point2(-15, -15) +// }, new CircleF(new Point2(0, 0), 15)).SetName( +// "The bounding circle created from a non-empty set of points is the expected bounding circle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(CreateFromPointsTestCases))] +// public void CreateFromPoints(Point2[] points, CircleF expectedCircle) +// { +// var actualCircle = CircleF.CreateFrom(points); +// Assert.Equal(expectedCircle, actualCircle); +// } + +// public IEnumerable<TestCaseData> IntersectsCircleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new CircleF(), true).SetName( +// "Two empty circles intersect."); +// yield return +// new TestCaseData(new CircleF(new Point2(-10, -10), 15), +// new CircleF(new Point2(20, 20), 40), true).SetName( +// "Two overlapping non-empty circles intersect."); +// yield return +// new TestCaseData(new CircleF(new Point2(-40, -50), 15), +// new CircleF(new Point2(20, 20), 15), false).SetName( +// "Two non-overlapping non-empty circles do not intersect."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(IntersectsCircleTestCases))] +// public void Intersects(CircleF circle, CircleF circle2, bool expectedToIntersect) +// { +// Assert.Equal(expectedToIntersect, circle.Intersects(circle2)); +// Assert.Equal(expectedToIntersect, CircleF.Intersects(circle, circle2)); +// } + +// public IEnumerable<TestCaseData> IntersectsRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new RectangleF(), true).SetName( +// "The empty circle and the empty rectangle intersect."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 15), +// new RectangleF(new Point2(0, 0), new Size2(40, 40)), true).SetName( +// "The non-empty circle and a non-empty overlapping rectangle intersect."); +// yield return +// new TestCaseData(new CircleF(new Point2(-40, -50), 15), +// new RectangleF(new Point2(20, 20), new Size2(15, 15)), false).SetName( +// "The non-empty circle and a non-empty non-overlapping rectangle do not intersect."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(IntersectsRectangleTestCases))] +// public void Intersects(CircleF circle, RectangleF rectangle, bool expectedToIntersect) +// { +// Assert.Equal(expectedToIntersect, circle.Intersects((BoundingRectangle)rectangle)); +// Assert.Equal(expectedToIntersect, CircleF.Intersects(circle, (BoundingRectangle)rectangle)); +// } + +// public IEnumerable<TestCaseData> ContainsPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new Point2(), true).SetName( +// "The empty circle contains the zero point."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 15), new Point2(-15, -15), true) +// .SetName( +// "A non-empty circle contains a point inside it."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 15), new Point2(-16, 15), false) +// .SetName( +// "A non-empty circle does not contain a point outside it."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ContainsPointTestCases))] +// public void ContainsPoint(CircleF circle, Point2 point, bool expectedToContainPoint) +// { +// Assert.Equal(expectedToContainPoint, circle.Contains(point)); +// Assert.Equal(expectedToContainPoint, CircleF.Contains(circle, point)); +// } + +// public IEnumerable<TestCaseData> ClosestPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new Point2(), new Point2()).SetName( +// "The closest point on the empty circle to the zero point is the zero point."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 50), new Point2(25, 25), +// new Point2(25, 25)).SetName( +// "The closest point on a non-empty circle to a point which is inside the circle is that point.") +// ; +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 50), new Point2(400, 0), +// new Point2(50, 0)).SetName( +// "The closest point on a non-empty circle to a point which is outside the circle is the expected point.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ClosestPointTestCases))] +// public void ClosestPoint(CircleF circle, Point2 point, Point2 expectedClosestPoint) +// { +// var actualClosestPoint = circle.ClosestPointTo(point); +// Assert.Equal(expectedClosestPoint, actualClosestPoint); +// } + +// public IEnumerable<TestCaseData> BoundaryPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), 0.0f, new Point2()).SetName( +// "The boundary point on the empty circle at an angle is the zero point."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 50), MathHelper.PiOver2, +// new Point2(0, 50)).SetName( +// "The boundary point on a non-empty circle at an angle is the expected point."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(BoundaryPointTestCases))] +// public void BoundaryPointAt(CircleF circle, float angle, Point2 expectedPoint) +// { +// var actualPoint = circle.BoundaryPointAt(angle); +// AssertExtensions.AreApproximatelyEqual(expectedPoint, actualPoint); +// } + +// public IEnumerable<TestCaseData> EqualityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new CircleF(), true).SetName( +// "Empty circles are equal.") +// ; +// yield return +// new TestCaseData( +// new CircleF(new Point2(0, 0), float.MaxValue), +// new CircleF(new Point2(0, 0), float.MinValue), false).SetName( +// "Two different non-empty circles are not equal."); +// yield return +// new TestCaseData( +// new CircleF(new Point2(0, 0), float.MinValue), +// new CircleF(new Point2(0, 0), float.MinValue), true).SetName( +// "Two identical non-empty circles are equal."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(EqualityTestCases))] +// public void Equality(CircleF circle1, CircleF circle2, bool expectedToBeEqual) +// { +// Assert.IsTrue(Equals(circle1, circle2) == expectedToBeEqual); +// Assert.IsTrue(circle1 == circle2 == expectedToBeEqual); +// Assert.IsFalse(circle1 == circle2 != expectedToBeEqual); +// Assert.IsTrue(circle1.Equals(circle2) == expectedToBeEqual); + +// if (expectedToBeEqual) +// Assert.Equal(circle1.GetHashCode(), circle2.GetHashCode()); +// } + +// public IEnumerable<TestCaseData> InequalityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), null, false).SetName( +// "A circle is not equal to a null object."); +// yield return +// new TestCaseData(new CircleF(), new object(), false).SetName( +// "A circle is not equal to an instantiated object."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(InequalityTestCases))] +// public void Inequality(CircleF circle, object obj, bool expectedToBeEqual) +// { +// Assert.IsTrue(circle.Equals(obj) == expectedToBeEqual); +// } + +// public IEnumerable<TestCaseData> HashCodeTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new CircleF(), true).SetName( +// "Two empty circles have the same hash code."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 50), +// new CircleF(new Point2(0, 0), 50), true).SetName( +// "Two indentical non-empty circles have the same hash code."); +// yield return +// new TestCaseData(new CircleF(new Point2(0, 0), 50), +// new CircleF(new Point2(50, 50), 50), false).SetName( +// "Two different non-empty circles do not have the same hash code."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(HashCodeTestCases))] +// public void HashCode(CircleF circle1, CircleF circle2, bool expectedThatHashCodesAreEqual) +// { +// var hashCode1 = circle1.GetHashCode(); +// var hashCode2 = circle2.GetHashCode(); +// if (expectedThatHashCodesAreEqual) +// Assert.Equal(hashCode1, hashCode2); +// else +// Assert.AreNotEqual(hashCode1, hashCode2); +// } + +// public IEnumerable<TestCaseData> ToRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new Rectangle()).SetName( +// "The empty circle converted to a rectangle is the empty integer rectangle."); +// yield return +// new TestCaseData(new CircleF(new Point2(25, 25), 25), +// new Rectangle(0, 0, 50, 50)).SetName( +// "A non-empty circle converted to a rectangle is the expected integer rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ToRectangleTestCases))] +// public void ToRectangle(CircleF circle, Rectangle expectedRectangle) +// { +// var actualRectangle = (Rectangle)circle; +// Assert.Equal(expectedRectangle, actualRectangle); +// } + +// public IEnumerable<TestCaseData> ToRectangleFTestCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), new RectangleF()).SetName( +// "The empty circle converted to a rectangle is the empty float rectangle."); +// yield return +// new TestCaseData(new CircleF(new Point2(25, 25), 25), +// new RectangleF(0, 0, 50, 50)).SetName( +// "A non-empty circle converted to a rectangle is the expected float rectangle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ToRectangleFTestCases))] +// public void ToRectangleF(CircleF circle, RectangleF expectedRectangle) +// { +// var actualRectangle = (RectangleF)circle; +// Assert.Equal(expectedRectangle, actualRectangle); +// } + +// public IEnumerable<TestCaseData> FromRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Rectangle(), new CircleF()).SetName( +// "The empty rectangle converted to a circle is the empty circle."); +// yield return +// new TestCaseData(new Rectangle(0, 0, 50, 50), +// new CircleF(new Point2(25, 25), 25)).SetName( +// "A non-empty rectangle converted to a circle is the expected circle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(FromRectangleTestCases))] +// public void FromRectangle(Rectangle rectangle, CircleF expectedCircle) +// { +// var actualCircle = (CircleF)rectangle; +// Assert.Equal(expectedCircle, actualCircle); +// } + +// public IEnumerable<TestCaseData> FromRectangleFTestCases +// { +// get +// { +// yield return +// new TestCaseData(new RectangleF(), new CircleF()).SetName( +// "The empty rectangle converted to a circle is the empty circle."); +// yield return +// new TestCaseData(new RectangleF(0, 0, 50, 50), +// new CircleF(new Point2(25, 25), 25)).SetName( +// "A non-empty rectangle converted to a circle is the expected circle."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(FromRectangleFTestCases))] +// public void FromRectangleF(RectangleF rectangle, CircleF expectedCircle) +// { +// var actualCircle = (CircleF)rectangle; +// Assert.Equal(expectedCircle, actualCircle); +// } + +// public IEnumerable<TestCaseData> StringCases +// { +// get +// { +// yield return +// new TestCaseData(new CircleF(), +// string.Format(CultureInfo.CurrentCulture, "Centre: {0}, Radius: {1}", new Point2(), +// 0)).SetName( +// "The empty circle has the expected string representation using the current culture."); +// yield return new TestCaseData(new CircleF(new Point2(5.1f, -5.123f), 5.4f), +// string.Format(CultureInfo.CurrentCulture, "Centre: {0}, Radius: {1}", new Point2(5.1f, -5.123f), +// 5.4f)).SetName( +// "A non-empty circle has the expected string representation using the current culture."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(StringCases))] +// public void String(CircleF circle, string expectedString) +// { +// var actualString = circle.ToString(); +// Assert.Equal(expectedString, actualString); +// } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/EllipseFTest.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/EllipseFTest.cs new file mode 100644 index 0000000..4a4ecdf --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/EllipseFTest.cs @@ -0,0 +1,38 @@ +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests.Primitives +{ + + public class EllipseFTest + { + [Theory] + [InlineData(-1, -1, false)] + [InlineData(110, 300, true)] + [InlineData(200, 300, true)] + [InlineData(290, 300, true)] + [InlineData(400, 400, false)] + public void ContainsPoint_Circle(int x, int y, bool expected) + { + var ellipse = new EllipseF(new Vector2(200.0f, 300.0f), 100.0f, 100.0f); + + Assert.Equal(expected, ellipse.Contains(x, y)); + } + + [Theory] + [InlineData(299, 400, false)] + [InlineData(501, 400, false)] + [InlineData(400, 199, false)] + [InlineData(400, 601, false)] + [InlineData(301, 400, true)] + [InlineData(499, 400, true)] + [InlineData(400, 201, true)] + [InlineData(400, 599, true)] + public void ContainsPoint_NonCircle(int x, int y, bool expected) + { + var ellipse = new EllipseF(new Vector2(400.0f, 400.0f), 100.0f, 200.0f); + + Assert.Equal(expected, ellipse.Contains(x, y)); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs new file mode 100644 index 0000000..e3b40c8 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs @@ -0,0 +1,234 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Xunit; +using Vector2 = Microsoft.Xna.Framework.Vector2; + +namespace MonoGame.Extended.Tests.Primitives; + +public class OrientedRectangleTests +{ + [Fact] + public void Initializes_oriented_rectangle() + { + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)); + + Assert.Equal(new Point2(1, 2), rectangle.Center); + Assert.Equal(new Vector2(3, 4), rectangle.Radii); + Assert.Equal(new Matrix2(5, 6, 7, 8, 9, 10), rectangle.Orientation); + CollectionAssert.Equal( + new List<Vector2> + { + new(-3, -2), + new(-33, -38), + new(23, 26), + new(53, 62) + }, + rectangle.Points); + } + + public static readonly IEnumerable<object[]> _equalsComparisons = new[] + { + new object[] + { + "empty compared with empty is true", + new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix2.Identity), + new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix2.Identity) + }, + new object[] + { + "initialized compared with initialized true", + new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)), + new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)) + } + }; + + [Theory] + [MemberData(nameof(_equalsComparisons))] +#pragma warning disable xUnit1026 + public void Equals_comparison(string name, OrientedRectangle first, OrientedRectangle second) +#pragma warning restore xUnit1026 + { + Assert.True(first == second); + Assert.False(first != second); + } + + public class Transform + { + [Fact] + public void Center_point_is_not_translated() + { + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity); + var transform = Matrix2.Identity; + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(new Point2(1, 2), result.Center); + } + + [Fact] + public void Center_point_is_translated() + { + var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(), new Matrix2()); + var transform = Matrix2.CreateTranslation(1, 2); + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(new Point2(1, 2), result.Center); + } + + [Fact] + public void Radii_is_not_changed_by_identity_transform() + { + var rectangle = new OrientedRectangle(new Point2(), new Size2(10, 20), new Matrix2()); + var transform = Matrix2.Identity; + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(new Vector2(10, 20), result.Radii); + } + + [Fact] + public void Radii_is_not_changed_by_translation() + { + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(10, 20), new Matrix2()); + var transform = Matrix2.CreateTranslation(1, 2); + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(new Vector2(10, 20), result.Radii); + } + + [Fact] + public void Orientation_is_rotated_45_degrees_left() + { + var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix2.Identity); + var transform = Matrix2.CreateRotationZ(MathHelper.PiOver4); + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(new Point2(), result.Center); + Assert.Equal(new Vector2(), result.Radii); + Assert.Equal(Matrix2.CreateRotationZ(MathHelper.PiOver4), result.Orientation); + } + + [Fact] + public void Orientation_is_rotated_to_45_degrees_from_180() + { + var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix2.CreateRotationZ(MathHelper.Pi)); + var transform = Matrix2.CreateRotationZ(-3 * MathHelper.PiOver4); + var expectedOrientation = Matrix2.CreateRotationZ(MathHelper.PiOver4); + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(new Point2(), result.Center); + Assert.Equal(new Vector2(), result.Radii); + Assert.Equal(expectedOrientation.M11, result.Orientation.M11, 6); + Assert.Equal(expectedOrientation.M12, result.Orientation.M12, 6); + Assert.Equal(expectedOrientation.M21, result.Orientation.M21, 6); + Assert.Equal(expectedOrientation.M22, result.Orientation.M22, 6); + Assert.Equal(expectedOrientation.M31, result.Orientation.M31, 6); + Assert.Equal(expectedOrientation.M32, result.Orientation.M32, 6); + } + + [Fact] + public void Points_are_same_as_center() + { + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity); + var transform = Matrix2.Identity; + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + CollectionAssert.Equal( + new List<Vector2> + { + new(1, 2), + new(1, 2), + new(1, 2), + new(1, 2) + }, + result.Points); + } + + [Fact] + public void Points_are_translated() + { + var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(2, 4), Matrix2.Identity); + var transform = Matrix2.CreateTranslation(10, 20); + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + CollectionAssert.Equal( + new List<Vector2> + { + new(8, 16), + new(8, 24), + new(12, 24), + new(12, 16) + }, + result.Points); + } + + [Fact] + public void Applies_rotation_and_translation() + { + /* Rectangle with center point p, aligned in coordinate system with origin 0. + * + * : + * : + * +-+ + * | | + * |p| + * | | + * ...............0-+............ + * : + * : + * : + * + * Rotate around center point p, 90 degrees around origin 0. + * + * : + * : + * +---+ + * | p | + * ...........+---0.............. + * : + * : + * : + * + * Then translate rectangle by x=10 and y=20. + * : + * : +---+ + * : | p | + * y=21 - - - - - - - -> +---+ + * . + * : + * ...............0.............. + * : + * : + * : + */ + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(2, 4), Matrix2.Identity); + var transform = + Matrix2.CreateRotationZ(MathHelper.PiOver2) + * + Matrix2.CreateTranslation(10, 20); + + var result = OrientedRectangle.Transform(rectangle, ref transform); + + Assert.Equal(8, result.Center.X, 6); + Assert.Equal(21, result.Center.Y, 6); + Assert.Equal(2, result.Radii.X, 6); + Assert.Equal(4, result.Radii.Y, 6); + Assert.Equal(Matrix2.CreateRotationZ(MathHelper.PiOver2), result.Orientation); + CollectionAssert.Equal( + new List<Vector2> + { + new(4, 23), + new(4, 19), + new(12, 19), + new(12, 23) + }, + result.Points); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Point2Tests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Point2Tests.cs new file mode 100644 index 0000000..ce5f89b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Point2Tests.cs @@ -0,0 +1,356 @@ +//using System.Collections.Generic; +//using System.Globalization; +//using Microsoft.Xna.Framework; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Primitives +//{ +// +// public class Point2Tests +// { +// public IEnumerable<TestCaseData> ConstructorTestCases +// { +// get +// { +// yield return +// new TestCaseData(0, 0).SetName( +// "The zero point has the expected coordinates."); +// yield return +// new TestCaseData(float.MinValue, float.MaxValue).SetName +// ( +// "A non-zero point has the expected coordinates."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ConstructorTestCases))] +// public void Constructor(float x, float y) +// { +// var point = new Point2(x, y); +// Assert.Equal(x, point.X); +// Assert.Equal(y, point.Y); +// } + +// public IEnumerable<TestCaseData> CoordinatesTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), 0, 0).SetName( +// "The zero point has the expected coordinates."); +// yield return +// new TestCaseData(new Point2(float.MinValue, float.MaxValue), float.MinValue, float.MaxValue).SetName +// ( +// "A non-zero point has the expected coordinates."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(CoordinatesTestCases))] +// public void Coordinates(Point2 point, float expectedX, float expecetedY) +// { +// Assert.Equal(expectedX, point.X); +// Assert.Equal(expecetedY, point.Y); + +// point.X = 10; +// Assert.Equal(10, point.X); + +// point.Y = -10.123f; +// Assert.Equal(-10.123f, point.Y); +// } + +// public IEnumerable<TestCaseData> VectorAdditionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Vector2(), new Point2()).SetName( +// "The addition of the zero point and the zero vector is the zero point."); +// yield return +// new TestCaseData(new Point2(5, 5), new Vector2(15, 15), new Point2(20, 20)).SetName( +// "The addition of a non-zero point and a non-zero vector is the expected point."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(VectorAdditionTestCases))] +// public void VectorAddition(Point2 point, Vector2 vector, Point2 expectedPoint) +// { +// Assert.Equal(expectedPoint, point + vector); +// Assert.Equal(expectedPoint, Point2.Add(point, vector)); +// } + +// public IEnumerable<TestCaseData> VectorSubtractionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Vector2(), new Point2()).SetName( +// "The vector subtraction of two zero points is the zero vector."); +// yield return +// new TestCaseData(new Point2(5, 5), new Vector2(15, 15), new Point2(-10, -10)).SetName( +// "The vector subtraction of two non-zero points is the expected vector."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(VectorSubtractionTestCases))] +// public void VectorSubtraction(Point2 point, Vector2 vector, Point2 expectedPoint) +// { +// Assert.Equal(expectedPoint, point - vector); +// Assert.Equal(expectedPoint, Point2.Subtract(point, vector)); +// } + + +// public IEnumerable<TestCaseData> DisplacementTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), new Vector2()).SetName( +// "The displacement between two zero points is the zero vector."); +// yield return +// new TestCaseData(new Point2(5, 5), new Point2(15, 15), new Vector2(10, 10)).SetName( +// "The displacement between two non-zero points is the expected vector."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(DisplacementTestCases))] +// public void Displacement(Point2 point1, Point2 point2, Vector2 expectedVector) +// { +// Assert.Equal(expectedVector, point2 - point1); +// Assert.Equal(expectedVector, Point2.Displacement(point2, point1)); +// } + +// public IEnumerable<TestCaseData> SizeAdditionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Size2(), new Point2()).SetName( +// "The size addition of the zero point with the empty size is the zero point."); +// yield return +// new TestCaseData(new Point2(5, 5), new Size2(15, 15), new Point2(20, 20)).SetName( +// "The size addition of a non-zero point with a non-empty size is the expected point."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(SizeAdditionTestCases))] +// public void SizeAdditon(Point2 point, Size2 size, Point2 expectedPoint) +// { +// Assert.Equal(expectedPoint, point + size); +// Assert.Equal(expectedPoint, Point2.Add(point, size)); +// } + +// public IEnumerable<TestCaseData> SizeSubtractionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Size2(), new Point2()).SetName( +// "The size substraction of the zero point with the empty size is the zero point."); +// yield return +// new TestCaseData(new Point2(5, 5), new Size2(15, 15), new Point2(-10, -10)).SetName( +// "The size subscration of a non-zero point with a non-empty size is the expected point."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(SizeSubtractionTestCases))] +// public void SizeSubtraction(Point2 point, Size2 size, Point2 expectedPoint) +// { +// Assert.Equal(expectedPoint, point - size); +// Assert.Equal(expectedPoint, Point2.Subtract(point, size)); +// } + +// public IEnumerable<TestCaseData> MinimumTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), new Point2()).SetName( +// "The minimum coordinates of two zero points is the coordinates of the zero point."); +// yield return +// new TestCaseData(new Point2(float.MaxValue, float.MinValue), new Point2(int.MaxValue, int.MinValue), +// new Point2(int.MaxValue, float.MinValue)).SetName( +// "The minimum coordaintes of two non-zero points is the expected coordinates."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(MinimumTestCases))] +// public void Minimum(Point2 point1, Point2 point2, Point2 expectedPoint) +// { +// var actualPoint = Point2.Minimum(point1, point2); +// Assert.Equal(expectedPoint, actualPoint); +// } + +// public IEnumerable<TestCaseData> MaximumTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), new Point2()).SetName( +// "The maximum coordinates of two zero points is the coordinates of the zero point."); +// yield return +// new TestCaseData(new Point2(float.MaxValue, float.MinValue), new Point2(int.MaxValue, int.MinValue), +// new Point2(float.MaxValue, int.MinValue)).SetName( +// "The maximum coordaintes of two non-zero points is the expected coordinates."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(MaximumTestCases))] +// public void Maximum(Point2 point1, Point2 point2, Point2 expectedPoint) +// { +// var actualPoint = Point2.Maximum(point1, point2); +// Assert.Equal(expectedPoint, actualPoint); +// } + +// public IEnumerable<TestCaseData> EqualityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), true).SetName("Two zero points are equal."); +// yield return +// new TestCaseData(new Point2(float.MinValue, float.MaxValue), +// new Point2(float.MaxValue, float.MinValue), false).SetName( +// "Two different non-zero points are not equal."); +// yield return +// new TestCaseData( +// new Point2(float.MinValue, float.MaxValue), new Point2(float.MinValue, float.MaxValue), true) +// .SetName( +// "Two identical non-zero points are equal."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(EqualityTestCases))] +// public void Equality(Point2 point1, Point2 point2, bool expectedToBeEqual) +// { +// Assert.IsTrue(Equals(point1, point2) == expectedToBeEqual); +// Assert.IsTrue(point1 == point2 == expectedToBeEqual); +// Assert.IsFalse(point1 == point2 != expectedToBeEqual); +// Assert.IsTrue(point1.Equals(point2) == expectedToBeEqual); + +// if (expectedToBeEqual) +// Assert.Equal(point1.GetHashCode(), point2.GetHashCode()); +// } + +// public IEnumerable<TestCaseData> InequalityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), null, false).SetName("A point is not equal to a null object."); +// yield return +// new TestCaseData(new Point2(), new object(), false).SetName( +// "A point is not equal to an instantiated object."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(InequalityTestCases))] +// public void Inequality(Point2 point, object obj, bool expectedToBeEqual) +// { +// Assert.IsTrue(point.Equals(obj) == expectedToBeEqual); +// } + +// public IEnumerable<TestCaseData> HashCodeTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2(), true).SetName( +// "Two zero points have the same hash code."); +// yield return +// new TestCaseData(new Point2(50, 50), new Point2(50, 50), true).SetName( +// "Two indentical non-zero points have the same hash code."); +// yield return +// new TestCaseData(new Point2(0, 0), new Point2(50, 50), false).SetName( +// "Two different non-zero points do not have the same hash code."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(HashCodeTestCases))] +// public void HashCode(Point2 point1, Point2 point2, bool expectedThatHashCodesAreEqual) +// { +// var hashCode1 = point1.GetHashCode(); +// var hashCode2 = point2.GetHashCode(); +// if (expectedThatHashCodesAreEqual) +// Assert.Equal(hashCode1, hashCode2); +// else +// Assert.AreNotEqual(hashCode1, hashCode2); +// } + +// public IEnumerable<TestCaseData> ToVectorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Vector2()).SetName( +// "The zero point converted to a vector is the zero vector."); +// yield return +// new TestCaseData(new Point2(float.MinValue, float.MaxValue), +// new Vector2(float.MinValue, float.MaxValue)).SetName( +// "A non-zero point converted to a vector is the expected vector."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ToVectorTestCases))] +// public void ToVector(Point2 point, Vector2 expectedVector) +// { +// var actualVector = (Vector2)point; +// Assert.Equal(expectedVector, actualVector); +// } + +// public IEnumerable<TestCaseData> FromVectorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Vector2(), new Point2()).SetName( +// "The zero vector converted to a point is the zero point."); +// yield return +// new TestCaseData(new Vector2(float.MinValue, float.MaxValue), +// new Point2(float.MinValue, float.MaxValue)).SetName( +// "A non-zero vector converted to a point is the expected point."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(FromVectorTestCases))] +// public void FromVector(Vector2 vector, Point2 expectedPoint) +// { +// var actualPoint = (Point2)vector; +// Assert.Equal(expectedPoint, actualPoint); +// } + +// public IEnumerable<TestCaseData> StringCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), +// string.Format(CultureInfo.CurrentCulture, "({0}, {1})", 0, 0)).SetName( +// "The zero point has the expected string representation using the current culture."); +// yield return new TestCaseData(new Point2(5.1f, -5.123f), +// string.Format(CultureInfo.CurrentCulture, "({0}, {1})", 5.1f, -5.123f)).SetName( +// "A non-zero point has the expected string representation using the current culture."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(StringCases))] +// public void String(Point2 point, string expectedString) +// { +// var actualString = point.ToString(); +// Assert.Equal(expectedString, actualString); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Ray2DTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Ray2DTests.cs new file mode 100644 index 0000000..04e8b00 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Ray2DTests.cs @@ -0,0 +1,217 @@ +//using System.Collections.Generic; +//using System.Globalization; +//using Microsoft.Xna.Framework; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Primitives +//{ +// +// public class Ray2Tests +// { +// public IEnumerable<TestCaseData> ConstructorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Vector2()).SetName( +// "The degenerate ray has the expected position and direction."); +// yield return +// new TestCaseData(new Point2(5, 5), new Vector2(15, 15)).SetName( +// "A non-degenerate ray has the expected position and direction."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ConstructorTestCases))] +// public void Constructor(Point2 position, Vector2 direction) +// { +// var ray = new Ray2(position, direction); +// Assert.Equal(position, ray.Position); +// Assert.Equal(direction, ray.Direction); +// } + +// public IEnumerable<TestCaseData> PositionDirectionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Ray2(), new Point2(), new Vector2()).SetName( +// "The degenerate ray has the expected position and direction."); +// yield return +// new TestCaseData(new Ray2(new Point2(5, 5), new Vector2(15, 15)), new Point2(5, 5), +// new Vector2(15, 15)).SetName +// ( +// "A non-degenerate ray has the expected position and direction."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(PositionDirectionTestCases))] +// public void PositionDirection(Ray2 ray, Point2 expectedPosition, Vector2 expecetedDirection) +// { +// Assert.Equal(expectedPosition, ray.Position); +// Assert.Equal(expecetedDirection, ray.Direction); + +// ray.Position.X = 10; +// ray.Position.Y = 10; +// Assert.Equal(new Point2(10, 10), ray.Position); + +// ray.Direction.X = -10.123f; +// ray.Direction.Y = 10.123f; +// Assert.Equal(new Vector2(-10.123f, 10.123f), ray.Direction); +// } + +// public IEnumerable<TestCaseData> IntersectsBoundingRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Ray2(), new BoundingRectangle(), true, Point2.Zero, Point2.Zero).SetName( +// "The degenerate ray intersects the empty bounding box."); +// yield return +// new TestCaseData(new Ray2(new Point2(-75, -75), new Vector2(75, -75)), +// new BoundingRectangle(new Point2(), new Size2(50, 50)), false, Point2.NaN, Point2.NaN).SetName( +// "A non-degenerate ray that does not cross a non-empty bounding box does not intersect the bounding box."); +// yield return +// new TestCaseData(new Ray2(new Point2(0, 0), new Vector2(25, 0)), new BoundingRectangle(new Point2(), new Size2(50, 50)), +// true, new Point2(0, 0), new Point2(50, 0)).SetName( +// "A non-degenerate ray starting from inside a non-empty bounding box intersects the bounding box."); +// yield return +// new TestCaseData(new Ray2(new Point2(-100, 0), new Vector2(100, 0)), +// new BoundingRectangle(new Point2(), new Size2(50, 50)), +// true, new Point2(-50, 0), new Point2(50, 0)).SetName( +// "A non-degenerate ray crossing a non-empty bounding box intersects the bounding box."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(IntersectsBoundingRectangleTestCases))] +// public void IntersectsBoundingRectangle(Ray2 ray, BoundingRectangle boundingRectangle, bool expectedResult, +// Point2 firstExpectedIntersectionPoint, Point2 secondExpectedIntersectionPoint) +// { +// float rayNearDistance, rayFarDistance; +// var actualResult = ray.Intersects(boundingRectangle, out rayNearDistance, out rayFarDistance); +// Assert.Equal(expectedResult, actualResult); + +// if (actualResult) +// { +// var firstActualIntersectionPoint = ray.Position + ray.Direction * rayNearDistance; +// Assert.Equal(firstExpectedIntersectionPoint, firstActualIntersectionPoint); +// var secondActualIntersectionPoint = ray.Position + ray.Direction * rayFarDistance; +// Assert.Equal(secondExpectedIntersectionPoint, secondActualIntersectionPoint); +// } +// else +// { +// Assert.IsTrue(float.IsNaN(rayNearDistance)); +// Assert.IsTrue(float.IsNaN(rayFarDistance)); +// } +// } + +// public IEnumerable<TestCaseData> EqualityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Ray2(), new Ray2(), true).SetName("Two degenerate rays are equal."); +// yield return +// new TestCaseData(new Ray2(new Point2(float.MinValue, float.MaxValue), +// new Vector2(float.MaxValue, float.MinValue)), new Ray2(new Point2(float.MaxValue, float.MinValue), +// new Vector2(float.MaxValue, float.MinValue)), false).SetName( +// "Two different non-degenerate rays are not equal."); +// yield return +// new TestCaseData( +// new Ray2(new Point2(float.MinValue, float.MaxValue), +// new Vector2(float.MinValue, float.MaxValue)), new Ray2(new Point2(float.MinValue, float.MaxValue), +// new Vector2(float.MinValue, float.MaxValue)), true) +// .SetName( +// "Two identical non-degenerate rays are equal."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(EqualityTestCases))] +// public void Equality(Ray2 ray1, Ray2 ray2, bool expectedToBeEqual) +// { +// Assert.IsTrue(Equals(ray1, ray2) == expectedToBeEqual); +// Assert.IsTrue(ray1 == ray2 == expectedToBeEqual); +// Assert.IsFalse(ray1 == ray2 != expectedToBeEqual); +// Assert.IsTrue(ray1.Equals(ray2) == expectedToBeEqual); + +// if (expectedToBeEqual) +// Assert.Equal(ray1.GetHashCode(), ray2.GetHashCode()); +// } + +// public IEnumerable<TestCaseData> InequalityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Ray2(), null, false).SetName("A ray is not equal to a null object."); +// yield return +// new TestCaseData(new Ray2(), new object(), false).SetName( +// "A ray is not equal to an instantiated object."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(InequalityTestCases))] +// public void Inequality(Ray2 ray, object obj, bool expectedToBeEqual) +// { +// Assert.IsTrue(ray.Equals(obj) == expectedToBeEqual); +// } + +// public IEnumerable<TestCaseData> HashCodeTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Ray2(), new Ray2(), true).SetName( +// "Two degenerate rays have the same hash code."); +// yield return +// new TestCaseData(new Ray2(new Point2(50, 50), new Vector2(50, 50)), +// new Ray2(new Point2(50, 50), new Vector2(50, 50)), true).SetName( +// "Two indentical non-zero points have the same hash code."); +// yield return +// new TestCaseData(new Ray2(new Point2(0, 0), new Vector2(50, 50)), +// new Ray2(new Point2(50, 50), new Vector2(50, 50)), false).SetName( +// "Two different non-zero points do not have the same hash code."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(HashCodeTestCases))] +// public void HashCode(Ray2 ray1, Ray2 ray2, bool expectedThatHashCodesAreEqual) +// { +// var hashCode1 = ray1.GetHashCode(); +// var hashCode2 = ray2.GetHashCode(); +// if (expectedThatHashCodesAreEqual) +// Assert.Equal(hashCode1, hashCode2); +// else +// Assert.AreNotEqual(hashCode1, hashCode2); +// } + +// public IEnumerable<TestCaseData> StringCases +// { +// get +// { +// yield return +// new TestCaseData(new Ray2(), +// string.Format(CultureInfo.CurrentCulture, "Position: {0}, Direction: {1}", new Point2(), +// new Vector2())).SetName( +// "The degenerate ray has the expected string representation using the current culture."); +// yield return new TestCaseData(new Ray2(new Point2(5.1f, -5.123f), new Vector2(0, 1)), +// string.Format(CultureInfo.CurrentCulture, "Position: {0}, Direction: {1}", new Point2(5.1f, -5.123f), +// new Vector2(0, 1))).SetName( +// "A non-degenerate ray has the expected string representation using the current culture."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(StringCases))] +// public void String(Ray2 ray, string expectedString) +// { +// var actualString = ray.ToString(); +// Assert.Equal(expectedString, actualString); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs new file mode 100644 index 0000000..9121d6a --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs @@ -0,0 +1,135 @@ +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests.Primitives +{ + public class RectangleFTests + { + [Fact] + public void Rectangle_Intersects_Test() + { + var rect1 = new Rectangle(0, 0, 32, 32); + var rect2 = new Rectangle(32, 32, 32, 32); + + Assert.False(rect1.Intersects(rect2)); + } + + [Fact] + public void PassVector2AsConstructorParameter_Test() + { + var rect1 = new RectangleF(new Vector2(0, 0), new Size2(12.34f, 56.78f)); + var rect2 = new RectangleF(new Vector2(0, 0), new Vector2(12.34f, 56.78f)); + + Assert.Equal(rect1, rect2); + } + + [Fact] + public void PassPointAsConstructorParameter_Test() + { + var rect1 = new RectangleF(new Vector2(0, 0), new Size2(12, 56)); + var rect2 = new RectangleF(new Vector2(0, 0), new Size2(12, 56)); + + Assert.Equal(rect1, rect2); + } + + public class Transform + { + [Fact] + public void Center_point_is_not_translated() + { + var rectangle = new RectangleF(new Point2(0, 0), new Size2(20, 30)); + var transform = Matrix2.Identity; + + var result = RectangleF.Transform(rectangle, ref transform); + + Assert.Equal(new Point2(10, 15), result.Center); + } + + [Fact] + public void Center_point_is_translated() + { + var rectangleF = new RectangleF(new Point2(0, 0), new Size2(20, 30)); + var transform = Matrix2.CreateTranslation(1, 2); + + var result = RectangleF.Transform(rectangleF, ref transform); + + Assert.Equal(new Point2(11, 17), result.Center); + } + + [Fact] + public void Size_is_not_changed_by_identity_transform() + { + var rectangle = new RectangleF(new Point2(0, 0), new Size2(20, 30)); + var transform = Matrix2.Identity; + + var result = RectangleF.Transform(rectangle, ref transform); + + Assert.Equal(new Size2(20, 30), result.Size); + } + + [Fact] + public void Size_is_not_changed_by_translation() + { + var rectangle = new RectangleF(new Point2(0, 0), new Size2(20, 30)); + var transform = Matrix2.CreateTranslation(1, 2); + + var result = RectangleF.Transform(rectangle, ref transform); + + Assert.Equal(new Size2(20, 30), result.Size); + } + + [Fact] + public void Applies_rotation_and_translation() + { + /* Rectangle with center point aligned in coordinate system with origin 0. + * + * : + * : + * +-+ + * | | + * |p| + * | | + * ...............0-+............ + * : + * : + * : + * + * Rotate center point p, 90 degrees around origin 0. + * + * : + * : + * +---+ + * | p | + * ...........+---0.............. + * : + * : + * : + * + * Then translate rectangle by x=10 and y=20. + * : + * : +---+ + * : | p | + * y=21 - - - - - - - -> +---+ + * . + * : + * ...............0.............. + * : + * : + * : + */ + var rectangle = new RectangleF(new Point2(0, 0), new Size2(2, 4)); + var transform = + Matrix2.CreateRotationZ(MathHelper.PiOver2) + * + Matrix2.CreateTranslation(10, 20); + + var result = RectangleF.Transform(rectangle, ref transform); + + Assert.Equal(-2 + 10, result.Center.X, 6); + Assert.Equal(1 + 20, result.Center.Y, 6); + Assert.Equal(4, result.Size.Width, 6); + Assert.Equal(2, result.Size.Height, 6); + } + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Segment2DTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Segment2DTests.cs new file mode 100644 index 0000000..b994527 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Segment2DTests.cs @@ -0,0 +1,251 @@ +//using System.Collections.Generic; +//using System.Globalization; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Primitives +//{ +// public class Segment2DTests +// { +// public IEnumerable<TestCaseData> ConstructorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Point2()).SetName( +// "The empty segment has expected starting and ending points."); +// yield return +// new TestCaseData( +// new Point2(float.MaxValue, float.MinValue), +// new Point2(int.MaxValue, int.MinValue)).SetName( +// "A non-empty segment has the expected starting and ending points."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ConstructorTestCases))] +// public void Constructor(Point2 startingPoint, Point2 endingPoint) +// { +// var segment = new Segment2(startingPoint, endingPoint); +// Assert.Equal(startingPoint, segment.Start); +// Assert.Equal(endingPoint, segment.End); +// } + +// public IEnumerable<TestCaseData> ClosestPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), new Point2(), new Point2()).SetName( +// "The closest point on the empty segment to the zero point is the zero point."); +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(200, 0)), new Point2(-100, 200), +// new Point2(0, 0)).SetName( +// "The closest point on a non-empty segment to a point which is projected beyond the start of the segment is the segment's starting point.") +// ; +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(200, 0)), new Point2(400, 200), +// new Point2(200, 0)).SetName( +// "The closest point on a non-empty segment to a point which is projected beyond the end of the segment is the segment's ending point.") +// ; +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(200, 0)), new Point2(100, 200), +// new Point2(100, 0)).SetName( +// "The closest point on a non-empty segment to a point which is projected inside the segment is the projected point.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ClosestPointTestCases))] +// public void ClosestPoint(Segment2 segment, Point2 point, Point2 expectedClosestPoint) +// { +// var actualClosestPoint = segment.ClosestPointTo(point); +// Assert.Equal(expectedClosestPoint, actualClosestPoint); +// } + +// public IEnumerable<TestCaseData> SquaredDistanceToPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), new Point2(), 0).SetName( +// "The squared distance of the zero point to the empty segment is 0."); +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(20, 0)), new Point2(-10, 20), 500) +// .SetName( +// "The squared distance of a point projected beyond the start of a non-empty segment is the squared distance from the segment's starting point to the point.") +// ; +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(20, 0)), new Point2(40, 20), 400) +// .SetName( +// "The squared distance of a point projected beyond the end of a non-empty segment is the squared distance from the segment's ending point to the point.") +// ; +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(20, 0)), new Point2(10, 25), 625).SetName +// ( +// "The squared distance of a point projected inside a non-empty segment is the squared distance from the projected point to the point.") +// ; +// } +// } + +// [Fact] +// [TestCaseSource(nameof(SquaredDistanceToPointTestCases))] +// public void SquaredDistanceToPoint(Segment2 segment, Point2 point, +// float expectedDistance) +// { +// var actualDistance = segment.SquaredDistanceTo(point); +// Assert.Equal(expectedDistance, actualDistance); +// } + +// public IEnumerable<TestCaseData> IntersectsBoundingRectangleTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), new BoundingRectangle(), true, Point2.Zero).SetName( +// "The empty segment intersects the empty bounding box."); +// yield return +// new TestCaseData(new Segment2(new Point2(-75, -75), new Point2(75, -75)), +// new BoundingRectangle(new Point2(), new Size2(50, 50)), false, Point2.NaN).SetName( +// "A non-empty segment outside a non-empty bounding box does not intersect the bounding box."); +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(25, 0)), new BoundingRectangle(new Point2(), new Size2(50, 50)), +// true, new Point2(0, 0)).SetName( +// "A non-empty segment inside a non-empty bounding box intersects the bounding box."); +// yield return +// new TestCaseData(new Segment2(new Point2(-100, 0), new Point2(100, 0)), +// new BoundingRectangle(new Point2(), new Size2(50, 50)), +// true, new Point2(-50, 0)).SetName( +// "A non-empty segment crossing a non-empty bounding box intersects the bounding box."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(IntersectsBoundingRectangleTestCases))] +// public void IntersectsBoundingRectangle(Segment2 segment, BoundingRectangle boundingRectangle, bool expectedResult, +// Point2 expectedIntersectionPoint) +// { +// Point2 actualIntersectionPoint; +// var actualResult = segment.Intersects(boundingRectangle, out actualIntersectionPoint); +// Assert.Equal(expectedResult, actualResult); + +// if (actualResult) +// { +// Assert.Equal(expectedIntersectionPoint, actualIntersectionPoint); +// } +// else +// { +// Assert.IsTrue(float.IsNaN(actualIntersectionPoint.X)); +// Assert.IsTrue(float.IsNaN(actualIntersectionPoint.Y)); +// } +// } + +// public IEnumerable<TestCaseData> EqualityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), new Segment2(), true).SetName("Empty segments are equal.") +// ; +// yield return +// new TestCaseData( +// new Segment2(new Point2(0, 0), new Point2(float.MaxValue, float.MinValue)), +// new Segment2(new Point2(0, 0), +// new Point2(float.MinValue, float.MaxValue)), false).SetName( +// "Two different non-empty segments are not equal."); +// yield return +// new TestCaseData( +// new Segment2(new Point2(0, 0), new Point2(float.MinValue, float.MaxValue)), +// new Segment2(new Point2(0, 0), +// new Point2(float.MinValue, float.MaxValue)), true).SetName( +// "Two identical non-empty segments are equal."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(EqualityTestCases))] +// public void Equality(Segment2 segment1, Segment2 segment2, bool expectedToBeEqual) +// { +// Assert.IsTrue(Equals(segment1, segment2) == expectedToBeEqual); +// Assert.IsTrue(segment1 == segment2 == expectedToBeEqual); +// Assert.IsFalse(segment1 == segment2 != expectedToBeEqual); +// Assert.IsTrue(segment1.Equals(segment2) == expectedToBeEqual); + +// if (expectedToBeEqual) +// Assert.Equal(segment1.GetHashCode(), segment2.GetHashCode()); +// } + +// public IEnumerable<TestCaseData> InequalityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), null, false).SetName("A segment is not equal to a null object."); +// yield return +// new TestCaseData(new Segment2(), new object(), false).SetName( +// "A segment is not equal to an instantiated object."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(InequalityTestCases))] +// public void Inequality(Segment2 segment, object obj, bool expectedToBeEqual) +// { +// Assert.IsTrue(segment.Equals(obj) == expectedToBeEqual); +// } + +// public IEnumerable<TestCaseData> HashCodeTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), new Segment2(), true).SetName( +// "Two empty segments have the same hash code."); +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(50, 50)), +// new Segment2(new Point2(0, 0), new Point2(50, 50)), true).SetName( +// "Two indentical non-empty segments have the same hash code."); +// yield return +// new TestCaseData(new Segment2(new Point2(0, 0), new Point2(50, 50)), +// new Segment2(new Point2(50, 50), new Point2(50, 50)), false).SetName( +// "Two different non-empty segments do not have the same hash code."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(HashCodeTestCases))] +// public void HashCode(Segment2 segment1, Segment2 segment2, bool expectedThatHashCodesAreEqual) +// { +// var hashCode1 = segment1.GetHashCode(); +// var hashCode2 = segment2.GetHashCode(); +// if (expectedThatHashCodesAreEqual) +// Assert.Equal(hashCode1, hashCode2); +// else +// Assert.AreNotEqual(hashCode1, hashCode2); +// } + +// public IEnumerable<TestCaseData> StringCases +// { +// get +// { +// yield return +// new TestCaseData(new Segment2(), +// string.Format(CultureInfo.CurrentCulture, "{0} -> {1}", new Point2(), +// new Point2())).SetName( +// "The empty segment has the expected string representation using the current culture."); +// yield return new TestCaseData(new Segment2(new Point2(5.1f, -5.123f), new Point2(5.4f, -5.4123f)), +// string.Format(CultureInfo.CurrentCulture, "{0} -> {1}", new Point2(5.1f, -5.123f), +// new Point2(5.4f, -5.4123f))).SetName( +// "A non-empty segment has the expected string representation using the current culture."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(StringCases))] +// public void String(Segment2 segment, string expectedString) +// { +// var actualString = segment.ToString(); +// Assert.Equal(expectedString, actualString); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs new file mode 100644 index 0000000..37b2fc4 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs @@ -0,0 +1,180 @@ +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests.Primitives; + +public class ShapeTests +{ + public class CircleFTests + { + [Fact] + public void CircCircIntersectionSameCircleTest() + { + IShapeF shape1 = new CircleF(Point2.Zero, 2.0f); + IShapeF shape2 = new CircleF(Point2.Zero, 2.0f); + + Assert.True(shape1.Intersects(shape2)); + } + + [Fact] + public void CircCircIntersectionOverlappingTest() + { + IShapeF shape1 = new CircleF(new Point2(1, 2), 2.0f); + IShapeF shape2 = new CircleF(Point2.Zero, 2.0f); + + Assert.True(shape1.Intersects(shape2)); + } + + [Fact] + public void CircleCircleNotIntersectingTest() + { + IShapeF shape1 = new CircleF(new Point2(5, 5), 2.0f); + IShapeF shape2 = new CircleF(Point2.Zero, 2.0f); + + Assert.False(shape1.Intersects(shape2)); + } + } + + public class RectangleFTests + { + [Fact] + public void RectRectSameRectTest() + { + IShapeF shape1 = new RectangleF(Point2.Zero, new Size2(5, 5)); + IShapeF shape2 = new RectangleF(Point2.Zero, new Size2(5, 5)); + + Assert.True(shape1.Intersects(shape2)); + } + + [Fact] + public void RectRectIntersectingTest() + { + IShapeF shape1 = new RectangleF(Point2.Zero, new Size2(5, 5)); + IShapeF shape2 = new RectangleF(new Point2(3, 3), new Size2(5, 5)); + + Assert.True(shape1.Intersects(shape2)); + } + + [Fact] + public void RectRectNotIntersectingTest() + { + IShapeF shape1 = new RectangleF(Point2.Zero, new Size2(5, 5)); + IShapeF shape2 = new RectangleF(new Point2(10, 10), new Size2(5, 5)); + + Assert.False(shape1.Intersects(shape2)); + } + + [Fact] + public void RectCircContainedTest() + { + IShapeF shape1 = new RectangleF(Point2.Zero, new Size2(5, 5)); + IShapeF shape2 = new CircleF(Point2.Zero, 4); + + Assert.True(shape1.Intersects(shape2)); + Assert.True(shape2.Intersects(shape1)); + } + + [Fact] + public void RectCircOnEdgeTest() + { + IShapeF shape1 = new RectangleF(Point2.Zero, new Size2(5, 5)); + IShapeF shape2 = new CircleF(new Point2(5, 0), 4); + + Assert.True(shape1.Intersects(shape2)); + Assert.True(shape2.Intersects(shape1)); + } + } + + public class OrientedRectangleTests + { + [Fact] + public void Axis_aligned_rectangle_intersects_circle() + { + /* + * : + * : + * +*+ + * ...........* *......... + * +*+ + * : + * : + */ + IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity); + var circle = new CircleF(Point2.Zero, 1); + + Assert.True(rectangle.Intersects(circle)); + } + + [Fact] + public void Axis_aligned_rectangle_does_not_intersect_circle_in_top_left() + { + /* + * * : + * * *: + * *+-+ + * ...........| |......... + * +-+ + * : + * : + */ + IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity); + var circle = new CircleF(new Point2(-2, 1), .99f); + + Assert.False(rectangle.Intersects(circle)); + } + + [Fact] + public void Rectangle_rotated_45_degrees_does_not_intersect_circle_in_bottom_right() + { + /* + * : + * : + * +-. + * .........../ / ........ + * +./* * + * * * + * :* * + */ + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 1), new Size2(1.42f, 1.42f), Matrix2.CreateRotationZ(-MathHelper.PiOver4)); + var circle = new CircleF(new Point2(1, -1), 1.4f); + + Assert.False(rectangle.Intersects(circle)); + } + + [Fact] + public void Axis_aligned_rectangle_does_not_intersect_rectangle() + { + /* + * : + * : + * +-+ + * ..........| |**....... + * +-+ * + * :** + * : + */ + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix2.Identity); + var rect = new RectangleF(new Point2(0.001f, 0), new Size2(2, 2)); + + Assert.False(rectangle.Intersects(rect)); + } + + [Fact] + public void Axis_aligned_rectangle_intersects_rectangle() + { + /* + * : + * : + * +-+ + * ..........| |**....... + * +-+ * + * :** + * : + */ + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix2.Identity); + var rect = new RectangleF(new Point2(0, 0), new Size2(2, 2)); + + Assert.True(rectangle.Intersects(rect)); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Size2Tests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Size2Tests.cs new file mode 100644 index 0000000..faaa426 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Size2Tests.cs @@ -0,0 +1,304 @@ +//using System.Collections.Generic; +//using System.Globalization; +//using Microsoft.Xna.Framework; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Primitives +//{ +// +// public class Size2Tests +// { +// public IEnumerable<TestCaseData> ConstructorTestCases +// { +// get +// { +// yield return +// new TestCaseData(0, 0).SetName( +// "The empty size has the expected dimensions."); +// yield return +// new TestCaseData(float.MinValue, float.MaxValue).SetName +// ( +// "A non-empty size has the expected dimensions."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ConstructorTestCases))] +// public void Constructor(float width, float height) +// { +// var size = new Size2(width, height); +// Assert.Equal(width, size.Width); +// Assert.Equal(height, size.Height); +// } + +// public IEnumerable<TestCaseData> DimensionsTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), 0, 0).SetName( +// "The empty size has the expected dimensions."); +// yield return +// new TestCaseData(new Size2(float.MinValue, float.MaxValue), float.MinValue, float.MaxValue).SetName +// ( +// "A non-empty size has the expected dimensions."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(DimensionsTestCases))] +// public void Dimensions(Size2 size, float expectedWidth, float expecetedHeight) +// { +// Assert.Equal(expectedWidth, size.Width); +// Assert.Equal(expecetedHeight, size.Height); + +// size.Width = 10; +// Assert.Equal(10, size.Width); + +// size.Height = -10.123f; +// Assert.Equal(-10.123f, size.Height); +// } + +// public IEnumerable<TestCaseData> AdditionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), new Size2(), new Size2()).SetName( +// "The addition of two empty sizes is the empty size."); +// yield return +// new TestCaseData(new Size2(5, 5), new Size2(15, 15), new Size2(20, 20)).SetName( +// "The addition of two non-empty sizes is the expected size."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(AdditionTestCases))] +// public void Addition(Size2 size1, Size2 size2, Size2 expectedSize) +// { +// Assert.Equal(expectedSize, size1 + size2); +// Assert.Equal(expectedSize, Size2.Add(size1, size2)); +// } + +// public IEnumerable<TestCaseData> SubtractionTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), new Size2(), new Size2()).SetName( +// "The subtraction of two empty sizes is the empty size."); +// yield return +// new TestCaseData(new Size2(5, 5), new Size2(15, 15), new Size2(-10, -10)).SetName( +// "The subtraction of two non-empty sizes is the expected size."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(SubtractionTestCases))] +// public void Subtraction(Size2 size1, Size2 size2, Size2 expectedSize) +// { +// Assert.Equal(expectedSize, size1 - size2); +// Assert.Equal(expectedSize, Size2.Subtract(size1, size2)); +// } + +// public IEnumerable<TestCaseData> EqualityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), new Size2(), true).SetName("Two empty sizes are equal."); +// yield return +// new TestCaseData(new Size2(float.MinValue, float.MaxValue), +// new Size2(float.MaxValue, float.MinValue), false).SetName( +// "Two different non-empty sizes are not equal."); +// yield return +// new TestCaseData( +// new Size2(float.MinValue, float.MaxValue), new Size2(float.MinValue, float.MaxValue), true) +// .SetName( +// "Two identical non-empty sizes are equal."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(EqualityTestCases))] +// public void Equality(Size2 size1, Size2 size2, bool expectedToBeEqual) +// { +// Assert.IsTrue(Equals(size1, size2) == expectedToBeEqual); +// Assert.IsTrue(size1 == size2 == expectedToBeEqual); +// Assert.IsFalse(size1 == size2 != expectedToBeEqual); +// Assert.IsTrue(size1.Equals(size2) == expectedToBeEqual); + +// if (expectedToBeEqual) +// Assert.Equal(size1.GetHashCode(), size2.GetHashCode()); +// } + +// public IEnumerable<TestCaseData> InequalityTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), null, false).SetName("A size is not equal to a null object."); +// yield return +// new TestCaseData(new Size2(), new object(), false).SetName( +// "A size is not equal to an instantiated object."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(InequalityTestCases))] +// public void Inequality(Size2 size, object obj, bool expectedToBeEqual) +// { +// Assert.IsTrue(size.Equals(obj) == expectedToBeEqual); +// } + +// public IEnumerable<TestCaseData> HashCodeTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), new Size2(), true).SetName( +// "Two empty sizes have the same hash code."); +// yield return +// new TestCaseData(new Size2(50, 50), new Size2(50, 50), true).SetName( +// "Two indentical non-empty sizes have the same hash code."); +// yield return +// new TestCaseData(new Size2(0, 0), new Size2(50, 50), false).SetName( +// "Two different non-empty sizes do not have the same hash code."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(HashCodeTestCases))] +// public void HashCode(Size2 size1, Size2 size2, bool expectedThatHashCodesAreEqual) +// { +// var hashCode1 = size1.GetHashCode(); +// var hashCode2 = size2.GetHashCode(); +// if (expectedThatHashCodesAreEqual) +// Assert.Equal(hashCode1, hashCode2); +// else +// Assert.AreNotEqual(hashCode1, hashCode2); +// } + +// public IEnumerable<TestCaseData> ToPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), new Point2()).SetName("The empty size converted to a point is the zero point."); +// yield return +// new TestCaseData(new Size2(float.MinValue, float.MaxValue), new Point2(float.MinValue, float.MaxValue)).SetName( +// "A non-empty size converted to a point is the expected point."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ToPointTestCases))] +// public void ToPoint(Size2 size, Point2 expectedPoint) +// { +// var actualPoint = (Point2)size; +// Assert.Equal(expectedPoint, actualPoint); +// } + +// public IEnumerable<TestCaseData> FromPointTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Point2(), new Size2()).SetName("The zero point converted to a size is the empty size."); +// yield return +// new TestCaseData(new Point2(float.MinValue, float.MaxValue), new Size2(float.MinValue, float.MaxValue)).SetName( +// "A non-zero point converted to a size is the expected size."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(FromPointTestCases))] +// public void FromPoint(Point2 point, Size2 expectedSize) +// { +// var actualSize = (Size2)point; +// Assert.Equal(expectedSize, actualSize); +// } + +// public IEnumerable<TestCaseData> ToVectorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), new Vector2()).SetName("The empty size converted to a vector is the zero vector."); +// yield return +// new TestCaseData(new Size2(float.MinValue, float.MaxValue), new Vector2(float.MinValue, float.MaxValue)).SetName( +// "A non-empty size converted to a vector is the expected vector."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(ToVectorTestCases))] +// public void ToVector(Size2 size, Vector2 expectedVector) +// { +// var actualVector = (Vector2)size; +// Assert.Equal(expectedVector, actualVector); +// } + +// public IEnumerable<TestCaseData> FromVectorTestCases +// { +// get +// { +// yield return +// new TestCaseData(new Vector2(), new Size2()).SetName("The zero vector converted to a size is the empty size."); +// yield return +// new TestCaseData(new Vector2(float.MinValue, float.MaxValue), new Size2(float.MinValue, float.MaxValue)).SetName( +// "A non-zero vector converted to a size is the expected size."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(FromVectorTestCases))] +// public void FromVector(Vector2 vector, Size2 expectedSize) +// { +// var actualSize = (Size2)vector; +// Assert.Equal(expectedSize, actualSize); +// } + +// //public IEnumerable<TestCaseData> FromSizeTestCases +// //{ +// // get +// // { +// // yield return +// // new TestCaseData(new Size2(), new Size2()).SetName("The empty size converted to a size is the empty size."); +// // yield return +// // new TestCaseData(new Size2(int.MinValue, int.MaxValue), new Size2(int.MinValue, int.MaxValue)).SetName( +// // "A non-zero size converted to a size is the expected size."); +// // } +// //} + +// //[Fact] +// //[TestCaseSource(nameof(FromSizeTestCases))] +// //public void FromSize(Size2 size, Size2 expectedSize) +// //{ +// // var actualSize = (Size2)size; +// // Assert.Equal(expectedSize, actualSize); +// //} + +// public IEnumerable<TestCaseData> StringCases +// { +// get +// { +// yield return +// new TestCaseData(new Size2(), +// string.Format(CultureInfo.CurrentCulture, "Width: {0}, Height: {1}", 0, 0)).SetName( +// "The empty size has the expected string representation using the current culture."); +// yield return new TestCaseData(new Size2(5.1f, -5.123f), +// string.Format(CultureInfo.CurrentCulture, "Width: {0}, Height: {1}", 5.1f, -5.123f)).SetName( +// "A non-empty size has the expected string representation using the current culture."); +// } +// } + +// [Fact] +// [TestCaseSource(nameof(StringCases))] +// public void String(Size2 size, string expectedString) +// { +// var actualString = size.ToString(); +// Assert.Equal(expectedString, actualString); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/RangeTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/RangeTests.cs new file mode 100644 index 0000000..d022dcc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/RangeTests.cs @@ -0,0 +1,102 @@ +using System; +using Xunit; + +namespace MonoGame.Extended.Tests +{ + + public class RangeTests + { + [Fact] + public void ConstructorTest() + { + //can pass min < max + var unused = new Range<int>(10, 100); + //can't pass min > max + Assert.Throws<ArgumentException>(() => new Range<int>(100, 10)); + //can pass min == max + var unused1 = new Range<int>(10, 10); + } + + [Fact] + public void DegenerateTest() + { + var proper = new Range<double>(0, 1); + Assert.True(proper.IsProper); + Assert.False(proper.IsDegenerate); + + var degenerate = new Range<double>(1, 1); + Assert.False(degenerate.IsProper); + Assert.True(degenerate.IsDegenerate); + } + + [Fact] + public void IntegerTest() + { + var range = new Range<int>(10, 100); + + Assert.Equal(10, range.Min); + Assert.Equal(100, range.Max); + + for (var i = 10; i <= 100; i++) + { + Assert.True(range.IsInBetween(i)); + } + + Assert.False(range.IsInBetween(9)); + Assert.False(range.IsInBetween(101)); + Assert.False(range.IsInBetween(10, true)); + Assert.False(range.IsInBetween(100, maxValueExclusive: true)); + } + + [Fact] + public void FloatTest() + { + var range = new Range<float>(0f, 1f); + + Assert.Equal(0f, range.Min); + Assert.Equal(1f, range.Max); + + for (float i = 0; i <= 1f; i += 0.001f) + { + Assert.True(range.IsInBetween(i)); + } + + Assert.False(range.IsInBetween(-float.Epsilon)); + Assert.False(range.IsInBetween(1.00001f)); + + Assert.False(range.IsInBetween(0f, true)); + Assert.False(range.IsInBetween(1f, maxValueExclusive: true)); + } + + [Fact] + public void OperatorTest() + { + var rangeA = new Range<int>(0, 1); + var rangeB = new Range<int>(0, 1); + var rangeC = new Range<int>(1, 2); + var rangeD = new Range<double>(0, 1); + + Assert.True(rangeA == rangeB); + Assert.False(rangeA == rangeC); + + Assert.False(rangeA != rangeB); + Assert.True(rangeA != rangeC); + + Assert.True(rangeA.Equals(rangeB)); + Assert.False(rangeA.Equals(rangeC)); + Assert.False(rangeA.Equals(rangeD)); + + Range<int> implict = 1; + Assert.Equal(1, implict.Max); + Assert.Equal(1, implict.Min); + } + + [Fact] + public void ToStringTest() + { + var range = new Range<float>(0, 1); + + Assert.Equal("Range<Single> [0 1]", range.ToString()); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/ColorJsonConverterTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/ColorJsonConverterTests.cs new file mode 100644 index 0000000..ae8e1e7 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/ColorJsonConverterTests.cs @@ -0,0 +1,66 @@ +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using Microsoft.Xna.Framework; +using MonoGame.Extended.Serialization; + +namespace MonoGame.Extended.Tests.Serialization; + +public sealed class ColorJsonConverterTests +{ + private readonly ColorJsonConverter _converter = new ColorJsonConverter(); + + [Fact] + public void CanConvert_ColorType_ReturnsTrue() + { + var colorType = typeof(Color); + var result = _converter.CanConvert(colorType); + Assert.True(result); + } + + [Fact] + public void CanConvert_NonColorType_ReturnsFalse() + { + var otherType = typeof(string); + var result = _converter.CanConvert(otherType); + Assert.False(result); + } + + [Theory] + [InlineData("Red", 255, 0, 0, 255)] + [InlineData("#FF0000FF", 255, 0, 0, 255)] + public void Read_ValidColorJson_ReturnsExpectedColor(string jsonValue, byte r, byte g, byte b, byte a) + { + var json = $"\"{jsonValue}\""; + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + + reader.Read(); + var actual = _converter.Read(ref reader, typeof(Color), new JsonSerializerOptions()); + + var expected = new Color(r, g, b, a); + Assert.Equal(expected, actual); + } + + [Fact] + public void Write_ValidColor_WritesExpectedJson() + { + var expected = "#ff000000"; + var color = ColorHelper.FromHex(expected); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + _converter.Write(writer, color, new JsonSerializerOptions()); + writer.Flush(); + var actual = Encoding.UTF8.GetString(stream.ToArray()); + + Assert.Equal($"\"{expected}\"", actual); + } + + [Fact] + public void Write_NullWrier_ThrowArgumentNullException() + { + var color = Color.MonoGameOrange; + Assert.Throws<ArgumentNullException>(() => _converter.Write(null, color, new JsonSerializerOptions())); + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs new file mode 100644 index 0000000..96f9c4f --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Text.Json; +using MonoGame.Extended.Serialization; +using Xunit; + +namespace MonoGame.Extended.Tests.Serialization; + +public class RectangleFJsonConverterTest +{ + + public class TestContent + { + public RectangleF Box { get; set; } + } + + [Fact] + public void ConstructorTest() + { + var jsonData = @" +{ + ""box"": ""1 1 10 10"" +} +"; + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + options.Converters.Add(new RectangleFJsonConverter()); + var content = JsonSerializer.Deserialize<TestContent>(jsonData, options); + + Assert.Equal(1, content.Box.Left); + Assert.Equal(1, content.Box.Top); + Assert.Equal(10, content.Box.Width); + Assert.Equal(10, content.Box.Height); + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Shapes/PolygonFTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Shapes/PolygonFTests.cs new file mode 100644 index 0000000..d052908 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Shapes/PolygonFTests.cs @@ -0,0 +1,87 @@ +using Microsoft.Xna.Framework; +using MonoGame.Extended.Shapes; +using Xunit; + +namespace MonoGame.Extended.Tests.Shapes +{ + public class PolygonFTests + { + [Fact] + public void Polygon_Contains_Point_Test() + { + var vertices = new[] + { + new Vector2(0, 0), + new Vector2(10, 0), + new Vector2(10, 10), + new Vector2(0, 10) + }; + + var polygon = new Polygon(vertices); + + Assert.True(polygon.Contains(new Vector2(5, 5))); + Assert.True(polygon.Contains(new Vector2(0.01f, 0.01f))); + Assert.True(polygon.Contains(new Vector2(9.99f, 9.99f))); + Assert.False(polygon.Contains(new Vector2(-1f, -1f))); + Assert.False(polygon.Contains(new Vector2(-11f, -11f))); + } + + [Fact] + public void Polygon_Transform_Translation_Test() + { + var vertices = new[] + { + new Vector2(0, 0), + new Vector2(10, 0), + new Vector2(10, 10), + new Vector2(0, 10) + }; + + var polygon = new Polygon(vertices); + polygon.Offset(new Vector2(2, 3)); + + Assert.Equal(new Vector2(2, 3), polygon.Vertices[0]); + Assert.Equal(new Vector2(12, 3), polygon.Vertices[1]); + Assert.Equal(new Vector2(12, 13), polygon.Vertices[2]); + Assert.Equal(new Vector2(2, 13), polygon.Vertices[3]); + } + + [Fact] + public void Polygon_Transform_Rotation_Test() + { + var vertices = new[] + { + new Vector2(-5, -5), + new Vector2(5, 10), + new Vector2(-5, 10) + }; + + var polygon = new Polygon(vertices); + polygon.Rotate(MathHelper.ToRadians(90)); + + const float tolerance = 0.01f; + Assert.True(new Vector2(5, -5).EqualsWithTolerence(polygon.Vertices[0], tolerance)); + Assert.True(new Vector2(-10, 5).EqualsWithTolerence(polygon.Vertices[1], tolerance)); + Assert.True(new Vector2(-10, -5).EqualsWithTolerence(polygon.Vertices[2], tolerance)); + } + + [Fact] + public void Polygon_Transform_Scale_Test() + { + var vertices = new[] + { + new Vector2(0, -1), + new Vector2(1, 1), + new Vector2(-1, 1) + }; + + var polygon = new Polygon(vertices); + polygon.Scale(new Vector2(1, -0.5f)); + + const float tolerance = 0.01f; + Assert.True(new Vector2(0, -0.5f).EqualsWithTolerence(polygon.Vertices[0], tolerance), "0"); + Assert.True(new Vector2(2f, 0.5f).EqualsWithTolerence(polygon.Vertices[1], tolerance), "1"); + Assert.True(new Vector2(-2f, 0.5f).EqualsWithTolerence(polygon.Vertices[2], tolerance), "2"); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs new file mode 100644 index 0000000..35ad3e3 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs @@ -0,0 +1,910 @@ +using System; +using Microsoft.Xna.Framework; +using MonoGame.Extended.Sprites; +using MonoGame.Extended.TextureAtlases; +using Xunit; + +namespace MonoGame.Extended.Tests.Sprites +{ + public class SpriteSheetAnimationTests + { + [Theory] + [InlineData(0, 0)] + [InlineData(0, 0.9f)] + [InlineData(1, 1f)] + [InlineData(1, 1.9f)] + [InlineData(0, 2f)] + [InlineData(0, 2.9f)] + [InlineData(1, 3f)] + [InlineData(0, 4f)] + [InlineData(1, 5f)] + public void Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Fact] + public void Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(0, 0.9f)] + [InlineData(1, 1f)] + [InlineData(1, 1.1f)] + [InlineData(1, 1.9f)] + public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(1, 2f)] + [InlineData(1, 3f)] + [InlineData(1, 4f)] + public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + } + + [Fact] + public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + + isCompleteFired = false; // Reset isCompleteFired for next execution + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); // Event is not fired again as animation was already completed + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 0.9f)] + [InlineData(0, 1f)] + [InlineData(0, 1.9f)] + [InlineData(1, 2f)] + [InlineData(1, 2.9f)] + [InlineData(0, 3f)] + [InlineData(1, 4f)] + [InlineData(0, 5f)] + public void Reversed_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Fact] + public void Reversed_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(1, 0.9f)] + [InlineData(0, 1f)] + [InlineData(0, 1.1f)] + [InlineData(0, 1.9f)] + public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(0, 2f)] + [InlineData(0, 3f)] + [InlineData(0, 4f)] + public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + } + + [Fact] + public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + + isCompleteFired = false; // Reset isCompleteFired for next execution + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); // Event is not fired again as animation was already completed; + } + + [Theory] + [InlineData(0, 0)] + [InlineData(0, 0.9f)] + [InlineData(1, 1f)] + [InlineData(1, 1.9f)] + [InlineData(0, 2f)] + [InlineData(0, 2.9f)] + [InlineData(1, 3f)] + [InlineData(0, 4f)] + [InlineData(1, 5f)] + [InlineData(0, 6f)] + [InlineData(1, 7f)] + [InlineData(0, 8f)] + public void PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Fact] + public void PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(0, 0.9f)] + [InlineData(1, 1f)] + [InlineData(1, 1.9f)] + [InlineData(0, 2f)] + [InlineData(0, 2.9f)] + public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(0, 3f)] + [InlineData(0, 4f)] + [InlineData(0, 5f)] + public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + } + + [Fact] + public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D3 = new TextureRegion2D("Region 3", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2, textureRegion2D3 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1, 2 }, + 1, + false, + false, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + + isCompleteFired = false; // Reset isCompleteFired for next execution + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); // Event is not fired again as animation was already completed + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 0.9f)] + [InlineData(0, 1f)] + [InlineData(0, 1.9f)] + [InlineData(1, 2f)] + [InlineData(1, 2.9f)] + [InlineData(0, 3f)] + [InlineData(1, 4f)] + [InlineData(0, 5f)] + [InlineData(1, 6f)] + [InlineData(0, 7f)] + [InlineData(1, 8f)] + public void Reversed_PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Fact] + public void Reversed_PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + true, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(1, 0.9f)] + [InlineData(0, 1f)] + [InlineData(0, 1.9f)] + [InlineData(1, 2f)] + [InlineData(1, 2.9f)] + public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + } + + [Theory] + [InlineData(1, 3f)] + [InlineData(1, 4f)] + [InlineData(1, 5f)] + public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1 }, + 1, + false, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); + + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + } + + [Fact] + public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() + { + var textureRegion2D1 = new TextureRegion2D("Region 1", null, new Rectangle()); + var textureRegion2D2 = new TextureRegion2D("Region 2", null, new Rectangle()); + var textureRegion2D3 = new TextureRegion2D("Region 3", null, new Rectangle()); + + var textureRegions = new[] { textureRegion2D1, textureRegion2D2, textureRegion2D3 }; + + var spriteSheetAnimationData = new SpriteSheetAnimationData( + new[] { 0, 1, 2 }, + 1, + false, + true, + true + ); + + var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); + + var isCompleteFired = false; + spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; + + spriteSheetAnimation.Play(); + + var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); + Assert.False(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); + + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.True(isCompleteFired); + + isCompleteFired = false; // Reset isCompleteFired for next execution + gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + spriteSheetAnimation.Update(gameTime); + + Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); + Assert.True(spriteSheetAnimation.IsComplete); + Assert.False(isCompleteFired); // Event is not fired again as animation was already completed + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs new file mode 100644 index 0000000..307e272 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs @@ -0,0 +1,91 @@ +//using Microsoft.Xna.Framework; +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.Sprites; +//using MonoGame.Extended.TextureAtlases; +//using NSubstitute; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Sprites +//{ +// +// public class SpriteTests +// { +// [Fact] +// public void Sprite_BoundingRectangleAfterPosition_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 50, 200); +// var sprite = new Sprite(texture); + +// Assert.Equal(new RectangleF(375, 140, 50, 200), sprite.GetBoundingRectangle(new Vector2(400, 240), 0, Vector2.One)); +// } + +// [Fact] +// public void Sprite_BoundingRectangleAfterOrigin_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 50, 200); +// var sprite = new Sprite(texture) { OriginNormalized = new Vector2(1.0f, 1.0f) }; + +// Assert.Equal(new RectangleF(-50, -200, 50, 200), sprite.GetBoundingRectangle(Vector2.Zero, 0, Vector2.One)); +// } + +// [Fact] +// public void Sprite_BoundingRectangleAfterScale_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 50, 200); +// var sprite = new Sprite(texture); + +// Assert.Equal(new RectangleF(-50, -200, 100, 400), sprite.GetBoundingRectangle(Vector2.Zero, 0, Vector2.One * 2.0f)); +// } + +// [Fact] +// public void Sprite_BoundingRectangleAfterRotation_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 50, 200); +// var sprite = new Sprite(texture); + +// AssertExtensions.AreApproximatelyEqual(new RectangleF(-100, -25, 200, 50), sprite.GetBoundingRectangle(Vector2.Zero, MathHelper.ToRadians(90), Vector2.One * 2.0f)); +// } + +// [Fact] +// public void Sprite_TextureRegionIsFullTextureWhenTextureConstructorIsUsed_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 100, 200); +// var sprite = new Sprite(texture); + +// Assert.Equal(new Rectangle(0, 0, 100, 200), sprite.TextureRegion.Bounds); +// } + +// [Fact] +// public void Sprite_DefaultOriginIsCentre_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 100, 200); +// var sprite = new Sprite(texture); + +// Assert.Equal(new Vector2(0.5f, 0.5f), sprite.OriginNormalized); +// Assert.Equal(new Vector2(50, 100), sprite.Origin); +// } + +// [Fact] +// public void Sprite_PreserveNormalizedOriginWhenTextureRegionChanges_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(graphicsDevice, 100, 100); +// var textureRegion = new TextureRegion2D(texture, 10, 20, 30, 40); +// var sprite = new Sprite(textureRegion); + +// Assert.Equal(new Vector2(0.5f, 0.5f), sprite.OriginNormalized); +// Assert.Equal(new Vector2(15, 20), sprite.Origin); + +// sprite.TextureRegion = new TextureRegion2D(texture, 30, 40, 50, 60); + +// Assert.Equal(new Vector2(0.5f, 0.5f), sprite.OriginNormalized); +// Assert.Equal(new Vector2(25, 30), sprite.Origin); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGame.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGame.cs new file mode 100644 index 0000000..dad6e8a --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGame.cs @@ -0,0 +1,21 @@ +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Tests +{ + public class TestGame : Game + { + private readonly GraphicsDeviceManager _graphicsDeviceManager; + + public TestGame() + { + _graphicsDeviceManager = new GraphicsDeviceManager(this); + RunOneFrame(); + } + + protected override void Dispose(bool disposing) + { + _graphicsDeviceManager.Dispose(); + base.Dispose(disposing); + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs new file mode 100644 index 0000000..8a8741b --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs @@ -0,0 +1,12 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace MonoGame.Extended.Tests +{ + public class TestGraphicsDevice : GraphicsDevice + { + public TestGraphicsDevice() + : base(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, new PresentationParameters()) + { + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestHelper.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestHelper.cs new file mode 100644 index 0000000..b6a02bb --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestHelper.cs @@ -0,0 +1,27 @@ +//using Microsoft.Xna.Framework; +//using Microsoft.Xna.Framework.Graphics; +//using Xunit; + +//namespace MonoGame.Extended.Tests +//{ +// public static class TestHelper +// { +// public static void AreEqual(Vector3 a, Vector3 b, double delta) +// { +// Assert.Equal(a.X, b.X, delta); +// Assert.Equal(a.Y, b.Y, delta); +// Assert.Equal(a.Z, b.Z, delta); +// } + +// public static GraphicsDevice CreateGraphicsDevice() +// { +// return new GraphicsDevice( +// GraphicsAdapter.DefaultAdapter, +// GraphicsProfile.HiDef, +// new PresentationParameters()) +// { +// Viewport = new Viewport(0, 0, 800, 480) +// }; +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs new file mode 100644 index 0000000..13d9dfc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.TextureAtlases; +using Xunit; + +namespace MonoGame.Extended.Tests.TextureAtlases +{ + //public class TextureAtlasTests : IDisposable + //{ + // private readonly TestGame _game; + + // public TextureAtlasTests() + // { + // _game = new TestGame(); + // } + + // public void Dispose() + // { + // _game.Dispose(); + // } + + // [Fact] + // public void TextureAtlas_CreateRegion_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // var region = atlas.CreateRegion("region0", 10, 20, 30, 40); + + // Assert.Same(texture, region.Texture); + // Assert.Equal(10, region.X); + // Assert.Equal(20, region.Y); + // Assert.Equal(30, region.Width); + // Assert.Equal(40, region.Height); + // } + // } + + // [Fact] + // public void TextureAtlas_GetRegionsByIndex_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // var region0 = atlas.CreateRegion("region0", 10, 20, 30, 40); + // var region1 = atlas.CreateRegion("region1", 50, 60, 35, 45); + + // Assert.Same(region0, atlas[0]); + // Assert.Same(region1, atlas[1]); + // Assert.Same(region0, atlas.GetRegion(0)); + // Assert.Same(region1, atlas.GetRegion(1)); + // } + // } + + + // [Fact] + // public void TextureAtlas_GetRegionsByName_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // var region0 = atlas.CreateRegion("region0", 10, 20, 30, 40); + // var region1 = atlas.CreateRegion("region1", 50, 60, 35, 45); + + // Assert.Same(region0, atlas["region0"]); + // Assert.Same(region1, atlas["region1"]); + // Assert.Same(region0, atlas.GetRegion("region0")); + // Assert.Same(region1, atlas.GetRegion("region1")); + // } + // } + + // [Fact] + // public void TextureAtlas_RemoveRegions_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // var region0 = atlas.CreateRegion("region0", 10, 20, 30, 40); + // var region1 = atlas.CreateRegion("region1", 50, 60, 35, 45); + // var region2 = atlas.CreateRegion("region2", 32, 33, 34, 35); + + // Assert.Same(texture, atlas.Texture); + // Assert.Equal(3, atlas.RegionCount); + // Assert.Equal(atlas.RegionCount, atlas.Regions.Count()); + // Assert.Same(region1, atlas[1]); + + // atlas.RemoveRegion(1); + + // Assert.Equal(2, atlas.Regions.Count()); + // Assert.Same(region0, atlas[0]); + // Assert.Same(region2, atlas[1]); + + // atlas.RemoveRegion("region0"); + + // Assert.Single(atlas.Regions); + // Assert.Same(region2, atlas[0]); + // } + // } + + // [Fact] + // public void TextureAtlas_CreateRegionThatAlreadyExistsThrowsException_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // atlas.CreateRegion("region0", 10, 20, 30, 40); + // Assert.Throws<InvalidOperationException>(() => atlas.CreateRegion("region0", 50, 60, 35, 45)); + // } + // } + + // [Fact] + // public void TextureAtlas_GetRegion_InvalidIndexThrowsException_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // atlas.CreateRegion("region0", 10, 20, 30, 40); + // Assert.Throws<IndexOutOfRangeException>(() => atlas.GetRegion(-1)); + // } + // } + + // [Fact] + // public void TextureAtlas_GetRegion_InvalidNameThrowsException_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // atlas.CreateRegion("region0", 10, 20, 30, 40); + // Assert.Throws<KeyNotFoundException>(() => atlas.GetRegion("region1")); + // } + // } + + // [Fact] + // public void TextureAtlas_EnumerateRegions_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) + // { + // var atlas = new TextureAtlas(null, texture); + + // var regions = new TextureRegion2D[3]; + // regions[0] = atlas.CreateRegion("region0", 10, 20, 30, 40); + // regions[1] = atlas.CreateRegion("region1", 50, 60, 35, 45); + // regions[2] = atlas.CreateRegion("region2", 32, 33, 34, 35); + // var index = 0; + + // foreach (var region in atlas) + // { + // Assert.Same(region, regions[index]); + // index++; + // } + // } + // } + + // [Fact] + // public void TextureAtlas_Create_WithDefaultParameters_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 50, 100) {Name = "testTexture"}) + // { + // var atlas = TextureAtlas.Create(null, texture, 25, 50); + + // Assert.Equal(4, atlas.RegionCount); + // Assert.True(atlas.Regions.All(i => i.Width == 25)); + // Assert.True(atlas.Regions.All(i => i.Height == 50)); + // Assert.True(atlas.Regions.All(i => ReferenceEquals(i.Texture, texture))); + // Assert.True(atlas.Regions.All(i => i.Name.StartsWith(texture.Name))); + // } + // } + + // [Fact] + // public void TextureAtlas_Create_WithMaxRegionCount_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 64, 64)) + // { + // var atlas = TextureAtlas.Create(null, texture, 32, 32, maxRegionCount: 3); + + // Assert.Equal(3, atlas.RegionCount); + // } + // } + + // [Fact] + // public void TextureAtlas_Create_WithMargin_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 24, 24)) + // { + // var atlas = TextureAtlas.Create(null, texture, 10, 10, margin: 2); + + // Assert.Equal(4, atlas.RegionCount); + // Assert.True(atlas.Regions.All(i => i.Width == 10 && i.Height == 10)); + // Assert.Equal(2, atlas[0].X); + // Assert.Equal(2, atlas[0].Y); + // Assert.Equal(12, atlas[3].X); + // Assert.Equal(12, atlas[3].Y); + // } + // } + + // [Fact] + // public void TextureAtlas_Create_WithSpacing_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 24, 24)) + // { + // var atlas = TextureAtlas.Create(null, texture, 10, 10, spacing: 2); + + // Assert.Equal(4, atlas.RegionCount); + // Assert.True(atlas.Regions.All(i => i.Width == 10 && i.Height == 10)); + // Assert.Equal(0, atlas[0].X); + // Assert.Equal(0, atlas[0].Y); + // Assert.Equal(12, atlas[3].X); + // Assert.Equal(12, atlas[3].Y); + // } + // } + + // [Fact] + // public void TextureAtlas_Create_WithMarginAndSpacing_Test() + // { + // using (var texture = new Texture2D(_game.GraphicsDevice, 28, 28)) + // { + // var atlas = TextureAtlas.Create(null, texture, 10, 10, margin: 3, spacing: 2); + + // Assert.Equal(4, atlas.RegionCount); + // Assert.True(atlas.Regions.All(i => i.Width == 10 && i.Height == 10)); + // Assert.Equal(3, atlas[0].X); + // Assert.Equal(3, atlas[0].Y); + // Assert.Equal(15, atlas[3].X); + // Assert.Equal(15, atlas[3].Y); + // } + // } + //} +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs new file mode 100644 index 0000000..1274b10 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs @@ -0,0 +1,40 @@ +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.TextureAtlases; +//using Xunit; + +//namespace MonoGame.Extended.Tests.TextureAtlases +//{ +// +// public class TextureRegion2DTests +// { +// [Fact] +// public void TextureRegion2D_FromTexture_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 100, 200); +// var textureRegion = new TextureRegion2D(texture); + +// Assert.AreSame(texture, textureRegion.Texture); +// Assert.Equal(0, textureRegion.X); +// Assert.Equal(0, textureRegion.Y); +// Assert.Equal(100, textureRegion.Width); +// Assert.Equal(200, textureRegion.Height); +// Assert.IsNull(textureRegion.Tag); +// } + +// [Fact] +// public void TextureRegion2D_Specified_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 100, 200); +// var textureRegion = new TextureRegion2D(texture, 10, 20, 30, 40); + +// Assert.AreSame(texture, textureRegion.Texture); +// Assert.Equal(10, textureRegion.X); +// Assert.Equal(20, textureRegion.Y); +// Assert.Equal(30, textureRegion.Width); +// Assert.Equal(40, textureRegion.Height); +// Assert.IsNull(textureRegion.Tag); +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Vector2ExtensionsTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Vector2ExtensionsTests.cs new file mode 100644 index 0000000..aa1d889 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Vector2ExtensionsTests.cs @@ -0,0 +1,101 @@ +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tests +{ + public class Vector2ExtensionsTests + { + [Fact] + public void Vector2_EqualsWithTolerence_Test() + { + var a = new Vector2(1f, 1f); + var b = new Vector2(1.0000001f, 1.0000001f); + + Assert.False(a.Equals(b)); + Assert.True(a.EqualsWithTolerence(b)); + } + + [Fact] + public void Vector2_NormalizedCopy_Test() + { + var a = new Vector2(5, -10); + var b = a.NormalizedCopy(); + + Assert.True(new Vector2(0.4472136f, -0.8944272f).EqualsWithTolerence(b)); + } + + [Fact] + public void Vector2_Perpendicular_Test() + { + // http://mathworld.wolfram.com/PerpendicularVector.html + var a = new Vector2(5, -10); + var b = a.PerpendicularClockwise(); + var c = a.PerpendicularCounterClockwise(); + + Assert.Equal(new Vector2(-10, -5), b); + Assert.Equal(new Vector2(10, 5), c); + } + + [Fact] + public void Vector2_Rotate_90_Degrees_Test() + { + var a = new Vector2(0, -10); + var b = a.Rotate(MathHelper.ToRadians(90)); + + Assert.True(new Vector2(10, 0).EqualsWithTolerence(b)); + } + + [Fact] + public void Vector2_Rotate_360_Degrees_Test() + { + var a = new Vector2(0, 10); + var b = a.Rotate(MathHelper.ToRadians(360)); + + Assert.True(new Vector2(0, 10).EqualsWithTolerence(b)); + } + + [Fact] + public void Vector2_Rotate_45_Degrees_Test() + { + var a = new Vector2(0, -10); + var b = a.Rotate(MathHelper.ToRadians(45)); + + Assert.True(new Vector2(7.071068f, -7.071068f).EqualsWithTolerence(b)); + } + + [Fact] + public void Vector2_Truncate_Test() + { + var a = new Vector2(10, 10); + var b = a.Truncate(5); + + Assert.Equal(5f, b.Length(), 3); + } + + [Fact] + public void Vector2_IsNaN_Test() + { + var a = new Vector2(float.NaN, 10); + var b = new Vector2(10, float.NaN); + var c = new Vector2(float.NaN, float.NaN); + var d = new Vector2(10, 10); + + Assert.True(a.IsNaN()); + Assert.True(b.IsNaN()); + Assert.True(c.IsNaN()); + Assert.False(d.IsNaN()); + } + + [Fact] + public void Vector2_ToAngle_Test() + { + var a = new Vector2(0, -10); + var b = new Vector2(10, 0); + var c = -Vector2.UnitY.Rotate(MathHelper.ToRadians(45)); + + Assert.Equal(MathHelper.ToRadians(0), a.ToAngle()); + Assert.Equal(MathHelper.ToRadians(90), b.ToAngle()); + Assert.Equal(MathHelper.ToRadians(45), c.ToAngle()); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/BoxingViewportAdapterTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/BoxingViewportAdapterTests.cs new file mode 100644 index 0000000..665fea4 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/BoxingViewportAdapterTests.cs @@ -0,0 +1,41 @@ +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.ViewportAdapters; +//using Xunit; + +//namespace MonoGame.Extended.Tests.ViewportAdapters +//{ +// +// public class BoxingViewportAdapterTests +// { +// [Fact] +// public void BoxingViewportAdapter_Letterbox_Test() +// { +// var gameWindow = new MockGameWindow(); +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var viewportAdapter = new BoxingViewportAdapter(gameWindow, graphicsDevice, 800, 480); + +// graphicsDevice.Viewport = new Viewport(0, 0, 1024, 768); +// viewportAdapter.Reset(); + +// Assert.Equal(1024, graphicsDevice.Viewport.Width); +// Assert.Equal(614, graphicsDevice.Viewport.Height); +// Assert.Equal(BoxingMode.Letterbox, viewportAdapter.BoxingMode); +// } + +// [Fact] +// public void BoxingViewportAdapter_Pillarbox_Test() +// { +// var gameWindow = new MockGameWindow(); +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var viewportAdapter = new BoxingViewportAdapter(gameWindow, graphicsDevice, 800, 480); + +// graphicsDevice.Viewport = new Viewport(0, 0, 900, 500); +// viewportAdapter.Reset(); + +// Assert.Equal(833, graphicsDevice.Viewport.Width); +// Assert.Equal(500, graphicsDevice.Viewport.Height); +// Assert.Equal(BoxingMode.Pillarbox, viewportAdapter.BoxingMode); +// } + +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/DefaultViewportAdapterTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/DefaultViewportAdapterTests.cs new file mode 100644 index 0000000..1262119 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/DefaultViewportAdapterTests.cs @@ -0,0 +1,26 @@ +//using Microsoft.Xna.Framework; +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.ViewportAdapters; +//using Xunit; + +//namespace MonoGame.Extended.Tests.ViewportAdapters +//{ +// +// public class DefaultViewportAdapterTests +// { +// [Fact] +// public void DefaultViewportAdapter_Test() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var viewportAdapter = new DefaultViewportAdapter(graphicsDevice); + +// graphicsDevice.Viewport = new Viewport(0, 0, 1024, 768); + +// Assert.Equal(1024, viewportAdapter.ViewportWidth); +// Assert.Equal(768, viewportAdapter.ViewportHeight); +// Assert.Equal(viewportAdapter.ViewportWidth, viewportAdapter.VirtualWidth); +// Assert.Equal(viewportAdapter.ViewportHeight, viewportAdapter.VirtualHeight); +// Assert.Equal(Matrix.Identity, viewportAdapter.GetScaleMatrix()); +// } +// } +//}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/WithinDeltaEqualityComparer.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/WithinDeltaEqualityComparer.cs new file mode 100644 index 0000000..6ab17de --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/WithinDeltaEqualityComparer.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace MonoGame.Extended.Tests; + +public class WithinDeltaEqualityComparer : IEqualityComparer<float> +{ + private readonly float _delta; + + public WithinDeltaEqualityComparer(float delta) + { + _delta = delta; + } + + public bool Equals(float x, float y) + { + return Math.Abs(x - y) < _delta; + } + + public int GetHashCode(float obj) + { + return obj.GetHashCode(); + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/FullMapRendererTest.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/FullMapRendererTest.cs new file mode 100644 index 0000000..3f8ae1d --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/FullMapRendererTest.cs @@ -0,0 +1,291 @@ +//using Microsoft.Xna.Framework; +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.Shapes; +//using MonoGame.Extended.TextureAtlases; +//using MonoGame.Extended.Tiled; +//using MonoGame.Extended.Tiled.Graphics; +//using NSubstitute; +//using Xunit; + +//namespace MonoGame.Extended.Tests.Tiled.Renderers +//{ +// +// public class FullMapRendererTest +// { +// [Fact] +// public void Draw_MapObjectLayer_MissingGID_NoGroups() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); + +// IShapeF shape = new RectangleF(1, 1, 1, 1); +// TiledObject[] objs = +// { +// new TiledObject(TiledObjectType.Tile, 1, null, shape, 1, 1) { IsVisible = true }, +// }; + +// var layer = new TiledObjectLayer("object", objs); +// m.AddLayer(layer); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void Draw_MapObjectLayer_ShapeObject_NoGroups() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); + +// IShapeF shape = new RectangleF(1, 1, 1, 1); +// TiledObject[] objs = +// { +// new TiledObject(TiledObjectType.Rectangle, 1, 1, shape, 1, 1) { IsVisible = true }, +// }; + +// var layer = new TiledObjectLayer("object", objs); +// m.AddLayer(layer); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void Draw_MapObjectLayer_TileObject_OneGroup() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); + +// IShapeF shape = new RectangleF(1, 1, 1, 1); +// TiledObject[] objs = +// { +// new TiledObject(TiledObjectType.Tile, 1, 1, shape, 1, 1) { IsVisible = true }, +// }; + +// var layer = new TiledObjectLayer("object", objs); +// m.AddLayer(layer); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.NotNull(gd.Indices); +// Assert.Equal(6, gd.Indices.IndexCount); +// } + +// [Fact] +// public void Draw_MapObjectLayer_NotVisible_NoGroups() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); + +// IShapeF shape = new RectangleF(1, 1, 1, 1); +// TiledObject[] objs = +// { +// new TiledObject(TiledObjectType.Tile, 1, 1, shape, 1, 1) { IsVisible = false }, +// }; + +// var layer = new TiledObjectLayer("object", objs); +// m.AddLayer(layer); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void Draw_MapObjectLayer_NoObjects_NoGroups() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); + +// TiledObject[] objs = {}; + +// var layer = new TiledObjectLayer("object", objs); +// m.AddLayer(layer); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void Draw_MapTileLayer_TwoVisible_OneGroup() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); +// m.CreateTileLayer("tile", 2, 2, new int[] { 1, 0, 1, 0 }); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.NotNull(gd.Indices); +// Assert.Equal(12, gd.Indices.IndexCount); +// } + +// [Fact] +// public void Draw_MapTileLayer_AllBlank_NoGroups() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 2, 2, 32, 32); +// m.CreateTileset(texture, 0, 32, 32, 4); +// m.CreateTileLayer("tile", 2, 2, new int[] { 0, 0, 0, 0 }); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void Draw_MapImageLayer_OneGroup() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); + +// var m = new TiledMap("test", 10, 10, 32, 32); +// m.CreateImageLayer("img", texture, new Vector2(100, 100)); + +// r.Map = m; + +// r.Draw(new Matrix()); + +// Assert.NotNull(gd.Indices); +// Assert.Equal(6, gd.Indices.IndexCount); +// } + +// [Fact] +// public void Draw_MapNoGroups() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); +// r.Map = new TiledMap("test", 10, 10, 32, 32); + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void Draw_NoMap() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var r = new MockRenderer(gd); + +// r.Draw(new Matrix()); + +// Assert.IsNull(gd.Indices); +// } + +// [Fact] +// public void CreatePrimatives() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); +// var region = Substitute.For<TextureRegion2D>(texture, 1, 1, 32, 32); + +// VertexPositionTexture[] vertices; +// ushort[] indexes; + +// var r = new MockRenderer(gd); +// r.CreatePrimitives(new Point(0, 0), region, 0, 0.5f, out vertices, out indexes); + +// Assert.Equal(4, vertices.Length); +// Assert.Equal(new Vector3(0, 0, .5f), vertices[0].Position); +// Assert.Equal(new Vector2(0.0234375f, 0.0234375f), vertices[0].TextureCoordinate); +// Assert.Equal(new Vector3(32, 0, .5f), vertices[1].Position); +// Assert.Equal(new Vector2(0.515625f, 0.0234375f), vertices[1].TextureCoordinate); +// Assert.Equal(new Vector3(0, 32, .5f), vertices[2].Position); +// Assert.Equal(new Vector2(0.0234375f, 0.515625f), vertices[2].TextureCoordinate); +// Assert.Equal(new Vector3(32, 32, .5f), vertices[3].Position); +// Assert.Equal(new Vector2(0.515625f, 0.515625f), vertices[3].TextureCoordinate); + +// CollectionAssert.Equal(new[] { 0, 1, 2, 1, 3, 2 }, indexes); +// } + +// [Fact] +// public void CreatePrimatives_Offset10() +// { +// var gd = TestHelper.CreateGraphicsDevice(); +// var texture = Substitute.For<Texture2D>(gd, 64, 64); +// var region = Substitute.For<TextureRegion2D>(texture, 1, 1, 32, 32); + +// VertexPositionTexture[] vertices; +// ushort[] indexes; + +// var r = new MockRenderer(gd); +// r.CreatePrimitives(new Point(0, 0), region, 10, 0.5f, out vertices, out indexes); + +// Assert.Equal(4, vertices.Length); +// Assert.Equal(new Vector3(0, 0, .5f), vertices[0].Position); +// Assert.Equal(new Vector2(0.0234375f, 0.0234375f), vertices[0].TextureCoordinate); +// Assert.Equal(new Vector3(32, 0, .5f), vertices[1].Position); +// Assert.Equal(new Vector2(0.515625f, 0.0234375f), vertices[1].TextureCoordinate); +// Assert.Equal(new Vector3(0, 32, .5f), vertices[2].Position); +// Assert.Equal(new Vector2(0.0234375f, 0.515625f), vertices[2].TextureCoordinate); +// Assert.Equal(new Vector3(32, 32, .5f), vertices[3].Position); +// Assert.Equal(new Vector2(0.515625f, 0.515625f), vertices[3].TextureCoordinate); + +// CollectionAssert.Equal(new[] { 40, 41, 42, 41, 43, 42 }, indexes); +// } +// } + +// internal class MockRenderer : TiledMapRenderer +// { +// public MockRenderer(GraphicsDevice graphicsDevice) +// : base(graphicsDevice) +// { +// } + +// public void CreatePrimitives(Point point, TextureRegion2D region, int offset, float depth, +// out VertexPositionTexture[] vertices, out ushort[] indexes) +// { +// base.CreatePrimitives(point, region, offset, depth, out vertices, out indexes); +// } + +// public new void Draw(Matrix viewMatrix) +// { +// base.Draw(viewMatrix); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj new file mode 100644 index 0000000..32a97f2 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Tiled\MonoGame.Extended.Tiled.csproj" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/TiledTilesetTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/TiledTilesetTests.cs new file mode 100644 index 0000000..e3e5084 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/TiledTilesetTests.cs @@ -0,0 +1,130 @@ +//#region + +//using Microsoft.Xna.Framework.Content.Pipeline; +//using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler; +//using Microsoft.Xna.Framework.Graphics; +//using MonoGame.Extended.Tests; +//using Xunit; + +//#endregion + +//namespace MonoGame.Extended.Tiled.Tests +//{ +// +// public class TiledTilesetTests +// { +// [Fact] +// public void Constructor() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 64, 64); + +// var tiledTileset = new TiledMapTileset(texture, 10, 32, 32, 4, 0, 0); + + +// //Assert.IsNull(tiledTileset.GetTileRegion(0)); +// } + +// [Fact] +// public void GetTileRegion_BlankTile() +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 64, 64); + +// var tiledTileset = new TiledTileset(texture, 10, 32, 32, 4, 0, 0); + + +// //Assert.IsNull(tiledTileset.GetTileRegion(0)); +// } + +// [Fact] +// [TestCase(9, Result = false, Description = "Too low")] +// [TestCase(10, Result = true, Description = "Min tile")] +// [TestCase(11, Result = true, Description = "Middle tile")] +// [TestCase(13, Result = true, Description = "Last tile")] +// [TestCase(14, Result = false, Description = "Too high")] +// public bool ContainsTileId(int id) +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 64, 64); + +// var tiledTileset = new TiledTileset(texture, 10, 32, 32, 4, 0, 0); + +// return tiledTileset.ContainsTileId(id); +// } + +// [Fact] +// public void Constructor_NoMargin([Values(0, 2)] int spacing) +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 64, 64); + +// var tiledTileset = new TiledTileset(texture, 1, 32, 32, 4, spacing, 0); + +// var region = tiledTileset.GetTileRegion(1); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(0, region.X); +// Assert.Equal(0, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); + +// region = tiledTileset.GetTileRegion(2); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(spacing + 32, region.X); +// Assert.Equal(0, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); + +// region = tiledTileset.GetTileRegion(3); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(0, region.X); +// Assert.Equal(spacing + 32, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); + +// region = tiledTileset.GetTileRegion(4); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(spacing + 32, region.X); +// Assert.Equal(spacing + 32, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); +// } + +// [Fact] +// public void Constructor_NoSpacing([Values(0, 2)] int margin) +// { +// var graphicsDevice = TestHelper.CreateGraphicsDevice(); +// var texture = new Texture2D(graphicsDevice, 64, 64); + +// var tileset = new TiledTileset(texture, 1, 32, 32, 4, 0, margin); + +// var region = tileset.GetTileRegion(1); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(margin, region.X); +// Assert.Equal(margin, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); + +// region = tileset.GetTileRegion(2); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(margin + 32, region.X); +// Assert.Equal(margin, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); + +// region = tileset.GetTileRegion(3); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(margin, region.X); +// Assert.Equal(margin + 32, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); + +// region = tileset.GetTileRegion(4); +// Assert.Equal(texture, region.Texture); +// Assert.Equal(margin + 32, region.X); +// Assert.Equal(margin + 32, region.Y); +// Assert.Equal(32, region.Width); +// Assert.Equal(32, region.Height); +// } +// } +//} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/ColorHandler.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/ColorHandler.cs new file mode 100644 index 0000000..a4cddc0 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/ColorHandler.cs @@ -0,0 +1,8 @@ +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Tweening.Tests; + +public class ColorHandler +{ + public Color Color { get; set; } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj new file mode 100644 index 0000000..88ce44f --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Tweening\MonoGame.Extended.Tweening.csproj" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/TweenerTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/TweenerTests.cs new file mode 100644 index 0000000..0fbfded --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/TweenerTests.cs @@ -0,0 +1,15 @@ +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Tweening.Tests; + +public class TweenerTests +{ + [Fact] + public void TweenerTweenToSuccessTest() + { + var tweener = new Tweener(); + var obj = new ColorHandler(); + tweener.TweenTo(obj, x => x.Color, Color.Red, 2f); + } +} |