From acea7b2e728787a0d83bbf83c8c1f042d2c32e7e Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Mon, 3 Jun 2024 10:15:45 +0800
Subject: + plugins project
---
.../MonoGame.Extended/tests/Directory.Build.props | 46 ++
.../CollisionComponentTests.cs | 416 ++++++++++
.../Implementation/BasicActor.cs | 33 +
.../Implementation/BasicWall.cs | 20 +
.../MonoGame.Extended.Collisions.Tests.csproj | 7 +
.../QuadTreeTests.cs | 446 ++++++++++
.../SpatialHashTests.cs | 39 +
.../packages.config | 6 +
...me.Extended.Content.Pipeline.Tests.Tiled.csproj | 24 +
.../TestData/isometric.tmx | 76 ++
.../TestData/isometric_tileset.png | Bin 0 -> 4599 bytes
.../TestData/level01.tmx | 440 ++++++++++
.../TestData/template.tx | 4 +
.../TestData/test-object-layer.tmx | 24 +
.../TestData/test-tileset-base64.tmx | 11 +
.../TestData/test-tileset-csv.tmx | 13 +
.../TestData/test-tileset-gzip.tmx | 11 +
.../TestData/test-tileset-xml.tmx | 19 +
.../TestData/test-tileset-zlib.tmx | 11 +
.../TiledMapImporterProcessorTests.cs | 205 +++++
.../AstridAnimatorImporterTests.cs | 34 +
.../AstridAnimatorProcessorTests.cs | 30 +
...MonoGame.Extended.Content.Pipeline.Tests.csproj | 16 +
.../TestData/astrid-animator-atlas.json | 390 +++++++++
.../TestData/astrid-animator.aa | 15 +
.../TestData/test-tileset.json | 93 +++
.../TexturePackerJsonImporterProcessorTests.cs | 33 +
.../AspectBuilderTests.cs | 70 ++
.../AspectTests.cs | 88 ++
.../BitArrayExtensionsTests.cs | 20 +
.../ComponentManagerTests.cs | 47 ++
.../ComponentMapperTests.cs | 95 +++
.../ComponentTypeTests.cs | 20 +
.../MonoGame.Extended.Entities.Tests.csproj | 7 +
.../WorldManagerTests.cs | 71 ++
.../Controls/GuiButtonTests.cs | 122 +++
.../Controls/GuiControlCollectionTests.cs | 58 ++
.../GuiRendererTests.cs | 34 +
.../MonoGame.Extended.Gui.Tests.csproj | 7 +
.../tests/MonoGame.Extended.Tests/AngleTest.cs | 92 +++
.../MonoGame.Extended.Tests/AssertExtensions.cs | 21 +
.../BitmapFonts/BitmapFontTests.cs | 79 ++
.../tests/MonoGame.Extended.Tests/Camera2DTests.cs | 97 +++
.../MonoGame.Extended.Tests/CollectionAssert.cs | 14 +
.../Collections/BagTests.cs | 48 ++
.../Collections/DequeTests.cs | 408 +++++++++
.../Content/ContentReaderExtensionsTests.cs | 23 +
.../MonoGame.Extended.Tests/MockGameWindow.cs | 42 +
.../MonoGame.Extended.Tests.csproj | 12 +
.../MonoGame.Extended.Tests/OpenTK.dll.config | 26 +
.../Particles/AssertionModifier.cs | 25 +
.../Particles/ColourTests.cs | 126 +++
.../Particles/EmitterTests.cs | 131 +++
.../Particles/ParticleBufferTests.cs | 184 +++++
.../Particles/Profiles/PointProfileTests.cs | 33 +
.../Particles/Profiles/RingProfileTests.cs | 38 +
.../Primitives/BoundingRectangleTests.cs | 396 +++++++++
.../Primitives/CircleFTests.cs | 420 ++++++++++
.../Primitives/EllipseFTest.cs | 38 +
.../Primitives/OrientedRectangleTests.cs | 234 ++++++
.../Primitives/Point2Tests.cs | 356 ++++++++
.../Primitives/Ray2DTests.cs | 217 +++++
.../Primitives/RectangleFTests.cs | 135 +++
.../Primitives/Segment2DTests.cs | 251 ++++++
.../Primitives/ShapeTests.cs | 180 ++++
.../Primitives/Size2Tests.cs | 304 +++++++
.../tests/MonoGame.Extended.Tests/RangeTests.cs | 102 +++
.../Serialization/ColorJsonConverterTests.cs | 66 ++
.../Serialization/RectangleFJsonConverterTest.cs | 36 +
.../Shapes/PolygonFTests.cs | 87 ++
.../Sprites/SpriteSheetAnimationTests.cs | 910 +++++++++++++++++++++
.../MonoGame.Extended.Tests/Sprites/SpriteTests.cs | 91 +++
.../tests/MonoGame.Extended.Tests/TestGame.cs | 21 +
.../MonoGame.Extended.Tests/TestGraphicsDevice.cs | 12 +
.../tests/MonoGame.Extended.Tests/TestHelper.cs | 27 +
.../TextureAtlases/TextureAtlasTests.cs | 236 ++++++
.../TextureAtlases/TextureRegion2DTests.cs | 40 +
.../Vector2ExtensionsTests.cs | 101 +++
.../ViewportAdapters/BoxingViewportAdapterTests.cs | 41 +
.../DefaultViewportAdapterTests.cs | 26 +
.../WithinDeltaEqualityComparer.cs | 24 +
.../FullMapRendererTest.cs | 291 +++++++
.../MonoGame.Extended.Tiled.Tests.csproj | 7 +
.../TiledTilesetTests.cs | 130 +++
.../ColorHandler.cs | 8 +
.../MonoGame.Extended.Tweening.Tests.csproj | 7 +
.../TweenerTests.cs | 15 +
87 files changed, 9309 insertions(+)
create mode 100644 Plugins/MonoGame.Extended/tests/Directory.Build.props
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric_tileset.png
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/level01.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/template.tx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-object-layer.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-base64.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-csv.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-gzip.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-xml.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/test-tileset-zlib.tmx
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TiledMapImporterProcessorTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorImporterTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/AstridAnimatorProcessorTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator-atlas.json
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/astrid-animator.aa
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TestData/test-tileset.json
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests/TexturePackerJsonImporterProcessorTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/BitArrayExtensionsTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentMapperTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiButtonTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/Controls/GuiControlCollectionTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/GuiRendererTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AngleTest.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/AssertExtensions.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/BitmapFonts/BitmapFontTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Camera2DTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/CollectionAssert.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/BagTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Collections/DequeTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Content/ContentReaderExtensionsTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MockGameWindow.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/OpenTK.dll.config
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/AssertionModifier.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ColourTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/EmitterTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/ParticleBufferTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/PointProfileTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Particles/Profiles/RingProfileTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/CircleFTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/EllipseFTest.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Point2Tests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Ray2DTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Segment2DTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Primitives/Size2Tests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/RangeTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/ColorJsonConverterTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Shapes/PolygonFTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGame.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TestHelper.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/Vector2ExtensionsTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/BoxingViewportAdapterTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/ViewportAdapters/DefaultViewportAdapterTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tests/WithinDeltaEqualityComparer.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/FullMapRendererTest.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tiled.Tests/TiledTilesetTests.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/ColorHandler.cs
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj
create mode 100644 Plugins/MonoGame.Extended/tests/MonoGame.Extended.Tweening.Tests/TweenerTests.cs
(limited to 'Plugins/MonoGame.Extended/tests')
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 @@
+
+
+
+
+
+
+ $(SolutionDirectory).artifacts/tests
+ tests
+ false
+ false
+ CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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;
+
+ ///
+ /// Test collision of actors with various shapes.
+ ///
+ ///
+ /// Uses the fact that moves itself away from
+ /// on collision.
+ ///
+ 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(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 @@
+
+
+
+
+
+
+
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();
+ 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();
+ 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();
+ 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 @@
+
+
+
+
+
+
\ 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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
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
new file mode 100644
index 0000000..4c6f1b4
Binary files /dev/null and b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/TestData/isometric_tileset.png differ
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 @@
+
+
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 @@
+
+
+
+
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 @@
+
+
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 @@
+
+
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 @@
+
+
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 @@
+
+
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 @@
+
+
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 @@
+
+
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();
+ var importer = new TiledMapImporter();
+ var importerContext = Substitute.For();
+ 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().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().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().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().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().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(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();
+ var importer = new TiledMapImporter();
+ var importerContext = Substitute.For();
+ importerContext.Logger.Returns(logger);
+
+ var processor = new TiledMapProcessor();
+ var processorContext = Substitute.For();
+ 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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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());
+
+ 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());
+ var processor = new TexturePackerProcessor();
+ var output = processor.Process(input, Substitute.For());
+
+ 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();
+ var spriteMapper = componentManager.GetMapper();
+
+ Assert.IsType>(transformMapper);
+ Assert.IsType>(spriteMapper);
+ Assert.Equal(0, transformMapper.Id);
+ Assert.Equal(1, spriteMapper.Id);
+ Assert.Same(spriteMapper, componentManager.GetMapper());
+ }
+
+ [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