using System; using Amphora; using Azimuth; using UnitTestSharp; namespace Konis.UnitTests { public class ParticleWorldTests : TestFixture { private static ParticleWorld NewWorld() { return new ParticleWorld(new Rand(1)); } private static PlainEmitterDef Def( int capacity = 100, double rate = 8, double lifespan = 0, ClockBase clock = ClockBase.Game) { return new PlainEmitterDef { Capacity = capacity, Rate = rate, Lifespan = lifespan, Clock = clock }; } private static FxTime Frame(double gameSeconds, double realSeconds = 0) { return new FxTime(Duration.FromSeconds(realSeconds), Duration.FromSeconds(gameSeconds), 1); } private static bool Near(double expected, double actual) { return Math.Abs(expected - actual) < 1e-5; } public class CtorTests : TestFixture { public void NullRandom_Throws() { CheckThrow(typeof(ArgumentNullException)); new ParticleWorld(null); } public void StartsEmptyWithAQuarterSecondStepAndNoParticleLimit() { ParticleWorld world = NewWorld(); CheckEqual(0, world.Count); CheckEqual(0, world.ParticleCount); CheckEqual(Duration.FromSeconds(0.25), world.MaxRealStep); CheckEqual(int.MaxValue, world.MaxParticles); } } public class MaxRealStepTests : TestFixture { public void IsWhatItWasSet() { ParticleWorld world = NewWorld(); world.MaxRealStep = Duration.FromSeconds(1); CheckEqual(Duration.FromSeconds(1), world.MaxRealStep); } public void Zero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NewWorld().MaxRealStep = Duration.Zero; } public void Negative_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NewWorld().MaxRealStep = Duration.FromSeconds(-1); } public void Infinite_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NewWorld().MaxRealStep = Duration.Forever; } } public class MaxParticlesTests : TestFixture { public void IsWhatItWasSet() { ParticleWorld world = NewWorld(); world.MaxParticles = 5; CheckEqual(5, world.MaxParticles); } public void Zero_IsAllowed() { ParticleWorld world = NewWorld(); world.MaxParticles = 0; CheckEqual(0, world.MaxParticles); } public void Negative_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NewWorld().MaxParticles = -1; } } public class AddTests : TestFixture { public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); NewWorld().Add(null); } public void KeepsEmittersInTheOrderAdded() { ParticleWorld world = NewWorld(); var first = new EmitterTests.MovingEmitter(Def(), new Rand(1)); var second = new EmitterTests.MovingEmitter(Def(), new Rand(2)); world.Add(first); world.Add(second); CheckEqual(2, world.Count); CheckTrue(ReferenceEquals(first, world[0])); CheckTrue(ReferenceEquals(second, world[1])); } } public class SpawnTests : TestFixture { public void NullDef_Throws() { CheckThrow(typeof(ArgumentNullException)); NewWorld().Spawn(null, new Vector3(0, 0, 0)); } public void MakesAnEmitterOfTheDefAtThePositionAndAddsIt() { ParticleWorld world = NewWorld(); PlainEmitterDef def = Def(); Emitter emitter = world.Spawn(def, new Vector3(1, 2, 3)); CheckTrue(ReferenceEquals(def, emitter.Def)); CheckEqual(new Vector3(1, 2, 3), emitter.Position); CheckEqual(1, world.Count); CheckTrue(ReferenceEquals(emitter, world[0])); } public void WithoutAPreWarm_StartsAtAgeZero() { Emitter emitter = NewWorld().Spawn(Def(), new Vector3(0, 0, 0)); CheckEqual(0.0, emitter.Age); } public void PreWarmsByTheTimeAsked() { Emitter emitter = NewWorld().Spawn(Def(), new Vector3(0, 0, 0), preWarmSeconds: 2.05f); CheckTrue(Near(2.05, emitter.Age)); CheckEqual(16, emitter.Store.Count); } public void ANegativePreWarm_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); NewWorld().Spawn(Def(), new Vector3(0, 0, 0), preWarmSeconds: -1f); } } public class IndexerTests : TestFixture { public void OutOfRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Emitter ignored = NewWorld()[0]; } } public class ParticleCountTests : TestFixture { public void SumsTheParticlesOfEveryEmitter() { ParticleWorld world = NewWorld(); world.Spawn(Def(rate: 8), new Vector3(0, 0, 0), preWarmSeconds: 1.05f); world.Spawn(Def(rate: 4), new Vector3(0, 0, 0), preWarmSeconds: 1.05f); CheckEqual(12, world.ParticleCount); } } public class ClearTests : TestFixture { public void RemovesEveryEmitterAndItsParticles() { ParticleWorld world = NewWorld(); world.Spawn(Def(), new Vector3(0, 0, 0), preWarmSeconds: 1.05f); world.Spawn(Def(), new Vector3(0, 0, 0), preWarmSeconds: 1.05f); world.Clear(); CheckEqual(0, world.Count); CheckEqual(0, world.ParticleCount); } public void LeavesTheWorldAbleToTakeNewEmitters() { ParticleWorld world = NewWorld(); world.Spawn(Def(), new Vector3(0, 0, 0)); world.Clear(); world.Spawn(Def(), new Vector3(0, 0, 0)); CheckEqual(1, world.Count); } } public class UpdateTests : TestFixture { public void AdvancesEachEmitterByTheTimeOfItsOwnClock() { ParticleWorld world = NewWorld(); Emitter game = world.Spawn(Def(clock: ClockBase.Game), new Vector3(0, 0, 0)); Emitter real = world.Spawn(Def(clock: ClockBase.Real), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 0.5, realSeconds: 0.125)); CheckTrue(Near(0.5, game.Age)); CheckTrue(Near(0.125, real.Age)); } public void ClampsTheRealTimeToTheMaxRealStep() { ParticleWorld world = NewWorld(); Emitter real = world.Spawn(Def(clock: ClockBase.Real), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 0, realSeconds: 10)); CheckTrue(Near(0.25, real.Age)); } public void LeavesTheGameTimeAlone() { ParticleWorld world = NewWorld(); Emitter game = world.Spawn(Def(clock: ClockBase.Game), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 10, realSeconds: 10)); CheckTrue(Near(10.0, game.Age)); } public void AShortFrame_IsNotClamped() { ParticleWorld world = NewWorld(); Emitter real = world.Spawn(Def(clock: ClockBase.Real), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 0, realSeconds: 0.125)); CheckTrue(Near(0.125, real.Age)); } public void DropsAFinishedEmitterAndKeepsTheOrderOfTheRest() { ParticleWorld world = NewWorld(); Emitter first = world.Spawn(Def(), new Vector3(0, 0, 0)); world.Spawn(Def(lifespan: 0.25, rate: 4), new Vector3(0, 0, 0)); Emitter third = world.Spawn(Def(), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 0.25)); CheckEqual(3, world.Count); world.Update(Frame(gameSeconds: 20)); CheckEqual(2, world.Count); CheckTrue(ReferenceEquals(first, world[0])); CheckTrue(ReferenceEquals(third, world[1])); } public void KeepsAnEmitterThatHasStoppedBirthingButStillHasParticles() { ParticleWorld world = NewWorld(); world.Spawn(Def(lifespan: 0.25, rate: 8), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 0.5)); CheckEqual(1, world.Count); } public void LimitsTheParticlesBetweenEmitters() { ParticleWorld world = NewWorld(); world.MaxParticles = 5; world.Spawn(Def(rate: 8), new Vector3(0, 0, 0)); world.Spawn(Def(rate: 8), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 1)); CheckEqual(5, world.ParticleCount); CheckEqual(5, world[0].Store.Count); CheckEqual(0, world[1].Store.Count); } public void CountsParticlesAnEarlierEmitterBornThisFrameAgainstTheLimit() { ParticleWorld world = NewWorld(); world.MaxParticles = 10; world.Spawn(Def(rate: 4), new Vector3(0, 0, 0)); world.Spawn(Def(rate: 8), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 1)); CheckEqual(4, world[0].Store.Count); CheckEqual(6, world[1].Store.Count); } public void ALimitOfZero_BirthsNothing() { ParticleWorld world = NewWorld(); world.MaxParticles = 0; world.Spawn(Def(rate: 8), new Vector3(0, 0, 0)); world.Update(Frame(gameSeconds: 1)); CheckEqual(0, world.ParticleCount); } public void AnEmptyWorld_DoesNothing() { ParticleWorld world = NewWorld(); world.Update(Frame(gameSeconds: 1)); CheckEqual(0, world.Count); } } } }