using System;
using System.Collections.Generic;
using System.Linq;
using Chernobyl.Mathematics.Geometry;
using NUnit.Framework;
namespace Chernobyl.Mathematics.Collision
{
///
/// The test fixture that tests
///
[TestFixture]
public class QuadTreeTests : CollidableCollectionTests
{
[Test, Description("A test that ensures that the " +
"QuadTree.QuadTree(Rectangle, uint) " +
"properly constructs a Rectangle with the proportions specified by " +
"the Rectangle passed to it and creates the appropriate number of " +
"children after the max number of collidables specified have been " +
"added to it.")]
public void ConstructorRectangleUint()
{
Rectangle area = new Rectangle(0, 0, 100, 100);
QuadTree quadTree = new QuadTree(area, 3);
TestDimensions(area, quadTree);
// now test the maxCollidablesPerNode setting in the QuadTree's
// constructor
quadTree.Add(new Rectangle(20, 20, 1, 1));
Assert.That(quadTree.Children[0] == null, "The " +
"QuadTree has children even " +
"though we've only added one item (below the maximum " +
"collidables per node specified in the constructor).");
quadTree.Add(new Rectangle(25, 25, 1, 1));
Assert.That(quadTree.Children[0] == null, "The " +
"QuadTree has children even " +
"though we've only added two items (below the maximum " +
"collidables per node specified in the constructor).");
quadTree.Add(new Rectangle(30, 30, 1, 1));
Assert.That(quadTree.Children[0] == null, "The " +
"QuadTree has children even " +
"though we've only added three items (below the maximum " +
"collidables per node specified in the constructor).");
quadTree.Add(new Rectangle(35, 35, 1, 1));
Assert.That(quadTree.Children[0] != null, "The " +
"QuadTree does NOT have " +
"children even though we've added four items (above the maximum " +
"collidables per node specified in the constructor).");
}
[Test, Description("A test that ensures that the " +
"QuadTree.QuadTree(Rectangle, uint) " +
"properly throws an ArgumentOutOfRangeException when the " +
"uint maxCollidablesPerNode argument is zero.")]
public void ConstructorRectangleUintThrowsArgumentOutOfRangeException()
{
Assert.Throws(() => new QuadTree(new Rectangle(0, 0, 100, 100), 0));
}
[Test, Description("A test that ensures that the " +
"QuadTree.QuadTree(Rectangle) " +
"properly constructs a Rectangle with the proportions specified by " +
"Rectangle passed to it.")]
public void ConstructorRectangle()
{
Rectangle area = new Rectangle(5, 6, 189, 24);
QuadTree quadTree = new QuadTree(area);
TestDimensions(area, quadTree);
}
[Test, Description("A test that ensures that the QuadTree.TryAdd(T) " +
"returns true when client code attempts to add an object that " +
"is inside the QuadTree's bounds.")]
public void TryAddReturnsTrue()
{
QuadTree collisionGroup = GenerateQuadTree();
Assert.True(collisionGroup.TryAdd(new Rectangle(1, 1, 1, 1)),
"The Rectangle was in the QuadTrees bounds and should have " +
"been added.");
}
[Test, Description("A test that ensures that the QuadTree.TryAdd(T) " +
"returns false when client code attempts to add an object that " +
"is outside the QuadTree's bounds.")]
public void TryAddReturnsFalse()
{
QuadTree collisionGroup = GenerateQuadTree();
Assert.False(collisionGroup.TryAdd(new Rectangle(-100, -100, 1, 1)),
"The Rectangle was out of the QuadTrees bounds and should not " +
"have been added.");
}
[Test, Description("A test that ensures that an ArgumentException is " +
"properly thrown when client code attempts to add an object that " +
"is outside the QuadTree's bounds.")]
public void AddThrowsException()
{
ICollidableCollection collidableCollection = CreateCollidableCollection();
Assert.Throws(() => collidableCollection.Add(new Rectangle(-100, -100, 1, 1)));
}
[Test, Description("A test that ensures that an ArgumentException is " +
"NOT thrown when client code attempts to add an object that " +
"is inside the QuadTree's bounds.")]
public void AddDoesNotThrowAnException()
{
ICollidableCollection collidableCollection = CreateCollidableCollection();
collidableCollection.Add(new Rectangle(1, 1, 1, 1));
}
[Test, Description("A test that ensures that a collidable contained within " +
"a QuadTree can translate and be properly contained within a sibling " +
"QuadTree.")]
public void ObjectCanTranslate()
{
QuadTree quadTree = new QuadTree(new Rectangle(100, 100), 1);
Rectangle collidable = new Rectangle(25, 25, 5, 5);
quadTree.Add(new Rectangle(60, 60, 1, 1));
quadTree.Add(collidable);
collidable.Translate(50, 50);
quadTree.Add(new Rectangle(45, 45, 10, 10));
Assert.AreEqual(3, quadTree.Count, "The collidable should still " +
"contain 3 Rectangles but it does not.");
AssertQuadTreeContainsCollidable(quadTree, collidable);
}
[Test, Description("A test that ensures that a collidable contained within " +
"a QuadTree can scale up and be properly contained within the QuadTree.")]
public void ObjectCanScaleUp()
{
QuadTree quadTree = new QuadTree(new Rectangle(100, 100), 1);
Rectangle collidable = new Rectangle(25, 25, 20, 20);
quadTree.Add(new Rectangle(60, 60, 1, 1));
quadTree.Add(collidable);
collidable.Scale(2);
quadTree.Add(new Rectangle(45, 45, 10, 10));
Assert.AreEqual(3, quadTree.Count, "The collidable should still " +
"contain 3 Rectangles but it does not.");
AssertQuadTreeContainsCollidable(quadTree, collidable);
}
[Test, Description("A test that ensures that a collidable contained within " +
"a QuadTree can scale down and be properly contained within the QuadTree.")]
public void ObjectCanScaleDown()
{
QuadTree quadTree = new QuadTree(new Rectangle(100, 100), 1);
Rectangle collidable = new Rectangle(25, 25, 40, 40);
quadTree.Add(new Rectangle(60, 60, 1, 1));
quadTree.Add(collidable);
collidable.Scale(.5f);
quadTree.Add(new Rectangle(45, 45, 10, 10));
Assert.AreEqual(3, quadTree.Count, "The collidable should still " +
"contain 3 Rectangles but it does not.");
AssertQuadTreeContainsCollidable(quadTree, collidable);
}
[Test, Description("A test that ensures that a collidable contained within " +
"a QuadTree can be properly moved out of the QuadTree.")]
public void ObjectCanMoveOut()
{
QuadTree quadTree = new QuadTree(new Rectangle(100, 100), 1);
Rectangle collidable = new Rectangle(25, 25, 20, 20);
quadTree.Add(new Rectangle(60, 60, 1, 1));
quadTree.Add(collidable);
quadTree.Add(new Rectangle(45, 45, 10, 10));
collidable.Translate(100, 100);
Assert.AreEqual(2, quadTree.Count, "The collidable should contain" +
"two Rectangles but it does not.");
Assert.That(quadTree.Contains(collidable) == false, "The collidable " +
"should NOT be contained within the QuadTree but is.");
Assert.That(quadTree.IsColliding(collidable) == false, "The " +
"collidable is NOT contained within the QuadTree but was " +
"collided against.");
Assert.That(quadTree.GetCollidables(collidable).Contains(collidable) == false,
"The collidable was contained within the Collision enumerable.");
}
[Test, Description("A test that ensures that a QuadTree's " +
"TreeDirty event is raised when that QuadTree has been dirtied.")]
public void TreeDirtyEventInvoked()
{
QuadTree quadTree = new QuadTree(new Rectangle(100, 100), 1);
Rectangle collidable = new Rectangle(25, 25, 20, 20);
quadTree.Add(new Rectangle(60, 60, 1, 1));
quadTree.Add(collidable);
quadTree.Add(new Rectangle(45, 45, 10, 10));
quadTree.TreeDirtied += (sender, e) => Assert.Pass("TreeDirted event was properly raised.");
collidable.Translate(10, 10);
Assert.Fail("TreeDirtied event was NOT properly raised.");
}
[Test, Description("A test that ensures that a QuadTree's " +
"CollidableMovedOut event is raised when an object moves out.")]
public void CollidableMovedOutEventInvoked()
{
QuadTree quadTree = new QuadTree(new Rectangle(100, 100), 1);
Rectangle collidable = new Rectangle(25, 25, 20, 20);
quadTree.Add(new Rectangle(60, 60, 1, 1));
quadTree.Add(collidable);
quadTree.Add(new Rectangle(45, 45, 10, 10));
quadTree.CollidableMovedOut += (sender, e) => Assert.Pass("CollidableMovedOut event was properly raised.");
collidable.Translate(100, 100);
// Quadtree won't throw the event until it's been cleaned.
quadTree.Clean();
Assert.Fail("CollidableMovedOut event was NOT properly raised.");
}
protected override ICollidableCollection CreateCollidableCollection()
{
return GenerateQuadTree();
}
///
/// Creates a
/// for this class and subclasses to use for testing.
///
/// The
/// that was created.
public static QuadTree GenerateQuadTree()
{
QuadTree quadTree =
new QuadTree(new Rectangle(100, 100))
{
CreateCollided(),
new Rectangle(47, 93, 33, 6),
new Rectangle(2, 6, 66, 23),
new Rectangle(13, 8, 0.5f, 0.1f),
new Rectangle(36, 65, 25, 5),
new Rectangle(24, 86, 3, 1),
new Rectangle(20, 12, 75, 13)
};
return quadTree;
}
protected override Rectangle CreateCollider()
{
return new Rectangle(70, 70, 2, 0.5f);
}
protected override Rectangle GetCollided()
{
return CreateCollided();
}
protected override Rectangle CreateNonCollider()
{
return new Rectangle(92, 95, 1, 3);
}
protected static Rectangle CreateCollided()
{
return new Rectangle(70, 70, 1, 1);
}
///
/// Tests the dimensions of the
/// against the
/// dimensions of the passed in are using the NUnit framework.
///
/// The area that is expected from the
///
/// The quad tree to test for the appropriate
/// dimensions.
static void TestDimensions(Rectangle area, QuadTree quadTree)
{
Assert.AreEqual(area.Position, quadTree.Position, "The Position " +
"is different from the position this test specified.");
Assert.AreEqual(area.Width, quadTree.Width, "The Width " +
"is different from the Width this test specified.");
Assert.AreEqual(area.Height, quadTree.Height, "The Height " +
"is different from the Height this test specified.");
}
///
/// Asserts if the is not contained within
/// .
///
/// The
/// to check
/// for containment of the .
/// The collidable to check for containment
/// within .
static void AssertQuadTreeContainsCollidable(QuadTree quadTree, Rectangle collidable)
{
Assert.That(quadTree.Contains(collidable), "The collidable should " +
"still be contained within the QuadTree but is not.");
Assert.That(quadTree.IsColliding(collidable), "The collidable is " +
"still contained within the QuadTree but was not properly " +
"collided against.");
Assert.That(quadTree.GetCollidables(collidable).Contains(collidable),
"The collidable was not properly contained within the Collision " +
"enumerable.");
}
[TestFixture, Description("Tests the IShape and ITransform " +
"portions of QuadTree.")]
public class QuadTreeShapeTests : ShapeTests
{
protected override IShape CreateShape()
{
return GenerateQuadTree();
}
[Test]
public override void Rotate()
{
Assert.Throws(() => base.Rotate());
}
[Test]
public override void Pitch()
{
Assert.Throws(() => base.Pitch());
}
[Test]
public override void Yaw()
{
Assert.Throws(() => base.Yaw());
}
[Test]
public override void Roll()
{
Assert.Throws(() => base.Roll());
}
protected override float ExpectedWidth
{
get { return 100; }
}
protected override float ExpectedHeight
{
get { return 100; }
}
protected override float ExpectedDepth
{
get { return 0; }
}
protected override bool ExpectedIsConvex
{
get { return true; }
}
protected override float ExpectedArea
{
get { return 10000; }
}
protected override float ExpectedVolume
{
get { return 0; }
}
protected override float ExpectedPerimeter
{
get { return 400; }
}
protected override uint ExpectedDimensions
{
get { return 2; }
}
}
[TestFixture, Description("Tests the IEnumerable> " +
"portion of QuadTree.")]
public class EnumerationTests : EnumerationTests>
{
public EnumerationTests() : base(false)
{ }
protected override IEnumerable> CreateSingleItemEnumerable()
{
return new QuadTree(new Rectangle(50.0f, 50.0f));
}
protected override IEnumerable> CreateManyItemEnumerable()
{
return GenerateQuadTree();
}
}
[TestFixture, Description("Tests the ICollection and IEnumerable " +
"portions of QuadTree.")]
public class CollectionTests : CollectionTests
{
///
/// Initializes a new instance of the
/// class.
///
public CollectionTests()
: base(false)
{ }
protected override ICollection CreateCollection()
{
return GenerateQuadTree();
}
protected override ICollection CreateReadOnlyCollection()
{
return CreateCollection();
}
protected override Rectangle CreateItem()
{
return new Rectangle(3.5f, 4.3f, 5, 5);
}
}
[TestFixture, Description("Tests the IExtendedCollidable and ICollidable " +
"portions of QuadTree.")]
public class ExtendedCollidableTests : ExtendedCollidableTests
{
protected override IExtendedCollidable CreateExtendedCollidable()
{
return GenerateQuadTree();
}
protected override Rectangle CreateCollidingCollidable()
{
return new Rectangle(40, 67, 3, 5);
}
protected override Rectangle CreateEdgeCollidingCollidable()
{
return new Rectangle(47, 7, 1, 1);
}
protected override Rectangle CreateNonCollidingCollidable()
{
return new Rectangle();
}
}
}
}