From e4d607533fce0348842d3af16fc8b85ea3478b14 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Fri, 13 Dec 2019 03:00:55 -0500 Subject: [PATCH] moving ActorTest-style tests from common to pslogin (#300) --- .../scala/objects/VehicleSpawnPadTest.scala | 242 +----------------- pslogin/src/test/scala/ActorTest.scala | 67 ----- pslogin/src/test/scala/MDCTestProbe.scala | 46 ++++ .../test/scala/PacketCodingActorTest.scala | 46 ++-- .../src/test/scala/actor/base/ActorTest.scala | 27 ++ .../actor/objects/VehicleSpawnPadTest.scala | 242 ++++++++++++++++++ .../actor}/service/AvatarServiceTest.scala | 4 +- 7 files changed, 343 insertions(+), 331 deletions(-) delete mode 100644 pslogin/src/test/scala/ActorTest.scala create mode 100644 pslogin/src/test/scala/MDCTestProbe.scala create mode 100644 pslogin/src/test/scala/actor/base/ActorTest.scala create mode 100644 pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala rename {common/src/test/scala => pslogin/src/test/scala/actor}/service/AvatarServiceTest.scala (99%) diff --git a/common/src/test/scala/objects/VehicleSpawnPadTest.scala b/common/src/test/scala/objects/VehicleSpawnPadTest.scala index 0c4b5476..3d56e304 100644 --- a/common/src/test/scala/objects/VehicleSpawnPadTest.scala +++ b/common/src/test/scala/objects/VehicleSpawnPadTest.scala @@ -1,19 +1,10 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, ActorSystem, Props} -import akka.testkit.TestProbe -import base.ActorTest -import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} -import net.psforever.objects.serverobject.structures.StructureType -import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle} -import net.psforever.objects.zones.Zone -import net.psforever.packet.game.PlanetSideGUID -import net.psforever.types._ +import akka.actor.ActorRef +import net.psforever.objects.serverobject.pad.VehicleSpawnPad +import net.psforever.objects.GlobalDefinitions import org.specs2.mutable.Specification -import services.vehicle.{VehicleAction, VehicleServiceMessage} - -import scala.concurrent.duration._ class VehicleSpawnPadTest extends Specification { "VehicleSpawnPadDefinition" should { @@ -30,230 +21,3 @@ class VehicleSpawnPadTest extends Specification { } } } - -class VehicleSpawnControl1Test extends ActorTest { - "VehicleSpawnControl" should { - "construct" in { - val obj = VehicleSpawnPad(GlobalDefinitions.mb_pad_creation) - obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "mb_pad_creation") - assert(obj.Actor != ActorRef.noSender) - } - } -} - -class VehicleSpawnControl2Test extends ActorTest { - "VehicleSpawnControl" should { - "complete a vehicle order" in { - val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) - val probe = new TestProbe(system, "zone-events") - - zone.VehicleEvents = probe.ref //zone events - pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order - - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) - vehicle.Seats(0).Occupant = player - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.PlayerSeatedInVehicle]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideStart]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideEnd]) - //if we move the vehicle away from the pad, we should receive a ResetSpawnPad message - //that means that the first order has cleared and the spawn pad is now waiting for additional orders - vehicle.Position = Vector3(12,0,0) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ResetSpawnPad]) - } - } -} - -class VehicleSpawnControl3Test extends ActorTest { - "VehicleSpawnControl" should { - "block the second vehicle order until the first is completed" in { - val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) - //we can recycle the vehicle and the player for each order - val probe = new TestProbe(system, "zone-events") - val player2 = Player(Avatar("test2", player.Faction, CharacterGender.Male, 0, CharacterVoice.Mute)) - player2.GUID = PlanetSideGUID(11) - player2.Continent = zone.Id - player2.Spawn - - zone.VehicleEvents = probe.ref //zone events - pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //first order - pad.Actor ! VehicleSpawnPad.VehicleOrder(player2, vehicle) //second order (vehicle shared) - - assert(probe.receiveOne(1 seconds) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Queue, _) => true - case _ => false - }) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) - vehicle.Seats(0).Occupant = player - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.PlayerSeatedInVehicle]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideStart]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideEnd]) - assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false - }) - assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false - }) - - //if we move the vehicle away from the pad, we should receive a second ConcealPlayer message - //that means that the first order has cleared and the spawn pad is now working on the second order successfully - player.VehicleSeated = None //since shared between orders, as necessary - vehicle.Seats(0).Occupant = None - vehicle.Position = Vector3(12,0,0) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ResetSpawnPad]) - probe.expectMsgClass(3 seconds, classOf[VehicleSpawnPad.ConcealPlayer]) - } - } -} - -class VehicleSpawnControl4Test extends ActorTest { - "VehicleSpawnControl" should { - "clean up the vehicle if the driver-to-be is on the wrong continent" in { - val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) - val probe = new TestProbe(system, "zone-events") - zone.VehicleEvents = probe.ref - player.Continent = "problem" //problem - - pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order - - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DisposeVehicle]) - probe.expectNoMsg(5 seconds) - } - } -} - -class VehicleSpawnControl5Test extends ActorTest() { - "VehicleSpawnControl" should { - "abandon a destroyed vehicle on the spawn pad (blocking)" in { - val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) - - val probe = new TestProbe(system, "zone-events") - zone.VehicleEvents = probe.ref - pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order - - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) - vehicle.Health = 0 //problem - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) - assert(probe.receiveOne(1 minute) match { - case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_,_,_,_,_)) => true - case _ => false - }) - assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false - }) - } - } -} - -class VehicleSpawnControl6Test extends ActorTest() { - "VehicleSpawnControl" should { - "abandon a vehicle on the spawn pad if driver is unfit to drive (blocking)" in { - val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) - - val probe = new TestProbe(system, "zone-events") - zone.VehicleEvents = probe.ref - pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order - - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) - player.Die - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) - assert(probe.receiveOne(1 minute) match { - case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_,_,_,_,_)) => true - case _ => false - }) - assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false - }) - } - } -} - -class VehicleSpawnControl7Test extends ActorTest { - "VehicleSpawnControl" should { - "abandon a vehicle on the spawn pad if driver is unfit to drive (blocking)" in { - val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) - val probe = new TestProbe(system, "zone-events") - player.ExoSuit = ExoSuitType.MAX - - zone.VehicleEvents = probe.ref //zone events - pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order - - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) - probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) - assert(probe.receiveOne(1 minute) match { - case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_,_,_,_,_)) => true - case _ => false - }) - assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false - }) - } - } -} - -object VehicleSpawnPadControlTest { - import net.psforever.objects.zones.ZoneMap - private val map = new ZoneMap("test-map") - - def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Vehicle, Player, VehicleSpawnPad, Zone) = { - import net.psforever.objects.guid.NumberPoolHub - import net.psforever.objects.guid.source.LimitedNumberSource - import net.psforever.objects.serverobject.structures.Building - import net.psforever.objects.vehicles.VehicleControl - import net.psforever.objects.zones.ZoneActor - import net.psforever.objects.Tool - import net.psforever.types.CharacterGender - - val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) - val weapon = vehicle.WeaponControlledFromSeat(1).get.asInstanceOf[Tool] - val zone = new Zone("test-zone", map, 0) { - override def SetupNumberPools() = { - val guid : NumberPoolHub = new NumberPoolHub(LimitedNumberSource(5)) - guid.AddPool("test-pool", (0 to 2).toList) - //do not do this under normal conditions - guid.register(vehicle, "test-pool") - guid.register(weapon, "test-pool") - guid.register(weapon.AmmoSlot.Box, "test-pool") - GUID(guid) - } - } - zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), s"test-zone-${System.nanoTime()}") - zone.Actor ! Zone.Init() - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), s"vehicle-control-${System.nanoTime()}") - - val pad = VehicleSpawnPad(GlobalDefinitions.mb_pad_creation) - pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), s"test-pad-${System.nanoTime()}") - pad.Owner = new Building("Building", building_guid = 0, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) - pad.Owner.Faction = faction - val player = Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)) - player.GUID = PlanetSideGUID(10) - player.Continent = zone.Id - player.Spawn - //note: pad and vehicle are both at Vector3(1,0,0) so they count as blocking - pad.Position = Vector3(1,0,0) - vehicle.Position = Vector3(1,0,0) - (vehicle, player, pad, zone) - } -} diff --git a/pslogin/src/test/scala/ActorTest.scala b/pslogin/src/test/scala/ActorTest.scala deleted file mode 100644 index a218c649..00000000 --- a/pslogin/src/test/scala/ActorTest.scala +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2017 PSForever - -import akka.actor.{ActorRef, ActorSystem, MDCContextAware} -import akka.testkit.{ImplicitSender, TestKit, TestProbe} -import com.typesafe.config.ConfigFactory -import net.psforever.packet.{ControlPacket, GamePacket} -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} -import org.specs2.specification.Scope - -abstract class ActorTest(sys : ActorSystem = ActorSystem("system", ConfigFactory.parseMap(ActorTest.LoggingConfig))) - extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { - override def afterAll { - TestKit.shutdownActorSystem(system) - } -} - -object ActorTest { - import scala.collection.JavaConverters._ - private val LoggingConfig = Map( - "akka.loggers" -> List("akka.testkit.TestEventListener").asJava, - "akka.loglevel" -> "OFF", - "akka.stdout-loglevel" -> "OFF", - "akka.log-dead-letters" -> "OFF" - ).asJava - - final case class MDCGamePacket(packet : GamePacket) - - final case class MDCControlPacket(packet : ControlPacket) - - class MDCTestProbe(probe : TestProbe) extends MDCContextAware { - /* - The way this test mediator works needs to be explained. - - MDCContextAware objects initialize themselves in a chain of ActorRefs defined in the HelloFriend message. - As the iterator is consumed, it produces a right-neighbor (r-neighbor) that is much further along the chain. - The HelloFriend is passed to that r-neighbor and that is how subsequent neighbors are initialized and chained. - - MDCContextAware objects consume and produce internal messages called MdcMsg that wrap around the payload. - Normally inaccessible from the outside, the payload is unwrapped within the standard receive PartialFunction. - By interacting with a TestProbe constructor param, information that would be concealed by MdcMsg can be polled. - - The l-neighbor of the MDCContextAware is the system of the base.ActorTest TestKit. - The r-neighbor of the MDCContextAware is this MDCTestProbe and, indirectly, the TestProbe that was interjected. - Pass l-input into the MDCContextAware itself. - The r-output is a normal message that can be polled on that TestProbe. - Pass r-input into this MDCTestProbe directly. - The l-output is an MdcMsg that can be treated just as r-output, sending it to this Actor and polling the TestProbe. - */ - private var left : ActorRef = ActorRef.noSender - - def receive : Receive = { - case msg @ HelloFriend(_, _) => - left = sender() - probe.ref ! msg - - case MDCGamePacket(msg) => - left ! msg - - case MDCControlPacket(msg) => - left ! msg - - case msg => - left ! msg - probe.ref ! msg - } - } -} diff --git a/pslogin/src/test/scala/MDCTestProbe.scala b/pslogin/src/test/scala/MDCTestProbe.scala new file mode 100644 index 00000000..19e53fd1 --- /dev/null +++ b/pslogin/src/test/scala/MDCTestProbe.scala @@ -0,0 +1,46 @@ +// Copyright (c) 2019 PSForever +import akka.actor.{ActorRef, MDCContextAware} +import akka.testkit.TestProbe +import net.psforever.packet.{ControlPacket, GamePacket} + +final case class MDCGamePacket(packet : GamePacket) + +final case class MDCControlPacket(packet : ControlPacket) + +class MDCTestProbe(probe : TestProbe) extends MDCContextAware { + /* + The way this test mediator works needs to be explained. + + MDCContextAware objects initialize themselves in a chain of ActorRefs defined in the HelloFriend message. + As the iterator is consumed, it produces a right-neighbor (r-neighbor) that is much further along the chain. + The HelloFriend is passed to that r-neighbor and that is how subsequent neighbors are initialized and chained. + + MDCContextAware objects consume and produce internal messages called MdcMsg that wrap around the payload. + Normally inaccessible from the outside, the payload is unwrapped within the standard receive PartialFunction. + By interacting with a TestProbe constructor param, information that would be concealed by MdcMsg can be polled. + + The l-neighbor of the MDCContextAware is the system of the base.actor.base.ActorTest TestKit. + The r-neighbor of the MDCContextAware is this MDCTestProbe and, indirectly, the TestProbe that was interjected. + Pass l-input into the MDCContextAware itself. + The r-output is a normal message that can be polled on that TestProbe. + Pass r-input into this MDCTestProbe directly. + The l-output is an MdcMsg that can be treated just as r-output, sending it to this Actor and polling the TestProbe. + */ + private var left : ActorRef = ActorRef.noSender + + def receive : Receive = { + case msg @ HelloFriend(_, _) => + left = sender() + probe.ref ! msg + + case MDCGamePacket(msg) => + left ! msg + + case MDCControlPacket(msg) => + left ! msg + + case msg => + left ! msg + probe.ref ! msg + } +} diff --git a/pslogin/src/test/scala/PacketCodingActorTest.scala b/pslogin/src/test/scala/PacketCodingActorTest.scala index a1f0a6b6..71b69b5d 100644 --- a/pslogin/src/test/scala/PacketCodingActorTest.scala +++ b/pslogin/src/test/scala/PacketCodingActorTest.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever - +import actor.base.ActorTest import akka.actor.{ActorRef, Props} import akka.testkit.TestProbe import net.psforever.packet.control.{ControlSync, MultiPacketBundle, SlottedMetaPacket} @@ -36,7 +36,7 @@ class PacketCodingActor3Test extends ActorTest { "PacketCodingActor" should { "initialize (an r-neighbor)" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") val iter = List(probe2).iterator val msg = HelloFriend(135, iter) @@ -56,12 +56,12 @@ class PacketCodingActor4Test extends ActorTest { "PacketCodingActor" should { "translate r-originating game packet into l-facing hexadecimal data" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume - val msg = ActorTest.MDCGamePacket(PacketCoding.CreateGamePacket(0, string_obj)) + val msg = MDCGamePacket(PacketCoding.CreateGamePacket(0, string_obj)) probe2 ! msg val reply1 = receiveOne(300 milli) //we get a MdcMsg message back probe2 ! reply1 //by feeding the MdcMsg into the actor, we get normal output on the probe @@ -77,7 +77,7 @@ class PacketCodingActor5Test extends ActorTest { "PacketCodingActor" should { "translate l-originating hexadecimal data into r-facing game packet" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -100,7 +100,7 @@ class PacketCodingActor6Test extends ActorTest { "PacketCodingActor" should { "permit l-originating game packet to pass through as an r-facing game packet" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -119,12 +119,12 @@ class PacketCodingActor7Test extends ActorTest { "PacketCodingActor" should { "translate r-originating control packet into l-facing hexadecimal data" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume - val msg = ActorTest.MDCControlPacket(PacketCoding.CreateControlPacket(string_obj)) + val msg = MDCControlPacket(PacketCoding.CreateControlPacket(string_obj)) probe2 ! msg val reply1 = receiveOne(300 milli) //we get a MdcMsg message back probe2 ! reply1 //by feeding the MdcMsg into the actor, we get normal output on the probe @@ -140,7 +140,7 @@ class PacketCodingActor8Test extends ActorTest { "PacketCodingActor" should { "translate l-originating hexadecimal data into r-facing control packet" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -163,7 +163,7 @@ class PacketCodingActor9Test extends ActorTest { "PacketCodingActor" should { "permit l-originating control packet to pass through as an r-facing control packet" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -179,7 +179,7 @@ class PacketCodingActorATest extends ActorTest { "PacketCodingActor" should { "permit l-originating unhandled message to pass through as an r-facing unhandled message" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -194,7 +194,7 @@ class PacketCodingActorBTest extends ActorTest { "PacketCodingActor" should { "permit r-originating unhandled message to pass through as an l-facing unhandled message" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -213,7 +213,7 @@ class PacketCodingActorCTest extends ActorTest { "PacketCodingActor" should { "should split r-originating hexadecimal data if it is larger than the MTU limit" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -353,12 +353,12 @@ class PacketCodingActorDTest extends ActorTest { "PacketCodingActor" should { "should split r-originating game packet if it is larger than the MTU limit" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume - val msg = ActorTest.MDCGamePacket(PacketCoding.CreateGamePacket(0, string_obj)) + val msg = MDCGamePacket(PacketCoding.CreateGamePacket(0, string_obj)) probe2 ! msg receiveN(4) } @@ -373,7 +373,7 @@ class PacketCodingActorETest extends ActorTest { val string_obj2 = GamePacket(GamePacketOpcode.PlayerStateMessageUpstream, 0, PlayerStateMessageUpstream(PlanetSideGUID(1256),Vector3(3077.0469f,4734.258f,56.390625f),Some(Vector3(5.5f,1.1875f,0.0f)),36.5625f,-2.8125f,0.0f,867,0,false,false,false,false,168,0)) val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -394,7 +394,7 @@ class PacketCodingActorFTest extends ActorTest { val string_obj = GamePacket(GamePacketOpcode.GenericObjectStateMsg, 0, GenericObjectStateMsg(PlanetSideGUID(242), 16)) val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -415,7 +415,7 @@ class PacketCodingActorGTest extends ActorTest { val string_obj = GamePacket(GamePacketOpcode.GenericObjectStateMsg, 0, GenericObjectStateMsg(PlanetSideGUID(242), 16)) val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -437,7 +437,7 @@ class PacketCodingActorHTest extends ActorTest { val string_obj2 = GamePacket(GamePacketOpcode.ObjectDeleteMessage, 0, ObjectDeleteMessage(PlanetSideGUID(1105), 2)) val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -491,7 +491,7 @@ class PacketCodingActorITest extends ActorTest { "PacketCodingActor" should { "bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -525,7 +525,7 @@ class PacketCodingActorJTest extends ActorTest { val string_hex = hex"00090000001904194f044004195104400419530440" val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -653,7 +653,7 @@ class PacketCodingActorKTest extends ActorTest { val pkt = MultiPacketBundle(list) val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume @@ -804,7 +804,7 @@ class PacketCodingActorLTest extends ActorTest { "PacketCodingActor" should { "split, rather than bundle, r-originating packets into a number of MTU-acceptable l-facing byte streams" in { val probe1 = TestProbe() - val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") + val probe2 = system.actorOf(Props(classOf[MDCTestProbe], probe1), "mdc-probe") val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca") pca ! HelloFriend(135, List(probe2).iterator) probe1.receiveOne(100 milli) //consume diff --git a/pslogin/src/test/scala/actor/base/ActorTest.scala b/pslogin/src/test/scala/actor/base/ActorTest.scala new file mode 100644 index 00000000..e38b8360 --- /dev/null +++ b/pslogin/src/test/scala/actor/base/ActorTest.scala @@ -0,0 +1,27 @@ +package actor.base + +// Copyright (c) 2017 PSForever + +import akka.actor.{ActorRef, ActorSystem, MDCContextAware} +import akka.testkit.{ImplicitSender, TestKit, TestProbe} +import com.typesafe.config.ConfigFactory +import net.psforever.packet.{ControlPacket, GamePacket} +import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.specs2.specification.Scope + +abstract class ActorTest(sys : ActorSystem = ActorSystem("system", ConfigFactory.parseMap(ActorTest.LoggingConfig))) + extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { + override def afterAll { + TestKit.shutdownActorSystem(system) + } +} + +object ActorTest { + import scala.collection.JavaConverters._ + private val LoggingConfig = Map( + "akka.loggers" -> List("akka.testkit.TestEventListener").asJava, + "akka.loglevel" -> "OFF", + "akka.stdout-loglevel" -> "OFF", + "akka.log-dead-letters" -> "OFF" + ).asJava +} diff --git a/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala b/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala new file mode 100644 index 00000000..7d54c8db --- /dev/null +++ b/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala @@ -0,0 +1,242 @@ +// Copyright (c) 2017 PSForever +package actor.objects + +import akka.actor.{ActorRef, ActorSystem, Props} +import akka.testkit.TestProbe +import actor.base.ActorTest +import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.objects.serverobject.structures.StructureType +import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle} +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.types._ +import services.vehicle.{VehicleAction, VehicleServiceMessage} + +import scala.concurrent.duration._ + +class VehicleSpawnControl1Test extends ActorTest { + "VehicleSpawnControl" should { + "construct" in { + val obj = VehicleSpawnPad(GlobalDefinitions.mb_pad_creation) + obj.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], obj), "mb_pad_creation") + assert(obj.Actor != ActorRef.noSender) + } + } +} + +class VehicleSpawnControl2Test extends ActorTest { + "VehicleSpawnControl" should { + "complete a vehicle order" in { + val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) + val probe = new TestProbe(system, "zone-events") + + zone.VehicleEvents = probe.ref //zone events + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order + + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) + vehicle.Seats(0).Occupant = player + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.PlayerSeatedInVehicle]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideStart]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideEnd]) + //if we move the vehicle away from the pad, we should receive a ResetSpawnPad message + //that means that the first order has cleared and the spawn pad is now waiting for additional orders + vehicle.Position = Vector3(12,0,0) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ResetSpawnPad]) + } + } +} + +class VehicleSpawnControl3Test extends ActorTest { + "VehicleSpawnControl" should { + "block the second vehicle order until the first is completed" in { + val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) + //we can recycle the vehicle and the player for each order + val probe = new TestProbe(system, "zone-events") + val player2 = Player(Avatar("test2", player.Faction, CharacterGender.Male, 0, CharacterVoice.Mute)) + player2.GUID = PlanetSideGUID(11) + player2.Continent = zone.Id + player2.Spawn + + zone.VehicleEvents = probe.ref //zone events + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //first order + pad.Actor ! VehicleSpawnPad.VehicleOrder(player2, vehicle) //second order (vehicle shared) + + assert(probe.receiveOne(1 seconds) match { + case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Queue, _) => true + case _ => false + }) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) + vehicle.Seats(0).Occupant = player + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.PlayerSeatedInVehicle]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideStart]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideEnd]) + assert(probe.receiveOne(1 minute) match { + case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false + }) + assert(probe.receiveOne(1 minute) match { + case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false + }) + + //if we move the vehicle away from the pad, we should receive a second ConcealPlayer message + //that means that the first order has cleared and the spawn pad is now working on the second order successfully + player.VehicleSeated = None //since shared between orders, as necessary + vehicle.Seats(0).Occupant = None + vehicle.Position = Vector3(12,0,0) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ResetSpawnPad]) + probe.expectMsgClass(3 seconds, classOf[VehicleSpawnPad.ConcealPlayer]) + } + } +} + +class VehicleSpawnControl4Test extends ActorTest { + "VehicleSpawnControl" should { + "clean up the vehicle if the driver-to-be is on the wrong continent" in { + val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) + val probe = new TestProbe(system, "zone-events") + zone.VehicleEvents = probe.ref + player.Continent = "problem" //problem + + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order + + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DisposeVehicle]) + probe.expectNoMsg(5 seconds) + } + } +} + +class VehicleSpawnControl5Test extends ActorTest() { + "VehicleSpawnControl" should { + "abandon a destroyed vehicle on the spawn pad (blocking)" in { + val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) + + val probe = new TestProbe(system, "zone-events") + zone.VehicleEvents = probe.ref + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order + + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) + vehicle.Health = 0 //problem + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) + assert(probe.receiveOne(1 minute) match { + case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_,_,_,_,_)) => true + case _ => false + }) + assert(probe.receiveOne(1 minute) match { + case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false + }) + } + } +} + +class VehicleSpawnControl6Test extends ActorTest() { + "VehicleSpawnControl" should { + "abandon a vehicle on the spawn pad if driver is unfit to drive (blocking)" in { + val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) + + val probe = new TestProbe(system, "zone-events") + zone.VehicleEvents = probe.ref + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order + + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) + player.Die + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) + assert(probe.receiveOne(1 minute) match { + case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_,_,_,_,_)) => true + case _ => false + }) + assert(probe.receiveOne(1 minute) match { + case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false + }) + } + } +} + +class VehicleSpawnControl7Test extends ActorTest { + "VehicleSpawnControl" should { + "abandon a vehicle on the spawn pad if driver is unfit to drive (blocking)" in { + val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) + val probe = new TestProbe(system, "zone-events") + player.ExoSuit = ExoSuitType.MAX + + zone.VehicleEvents = probe.ref //zone events + pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order + + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.LoadVehicle]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) + probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) + assert(probe.receiveOne(1 minute) match { + case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_,_,_,_,_)) => true + case _ => false + }) + assert(probe.receiveOne(1 minute) match { + case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false + }) + } + } +} + +object VehicleSpawnPadControlTest { + import net.psforever.objects.zones.ZoneMap + private val map = new ZoneMap("test-map") + + def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Vehicle, Player, VehicleSpawnPad, Zone) = { + import net.psforever.objects.guid.NumberPoolHub + import net.psforever.objects.guid.source.LimitedNumberSource + import net.psforever.objects.serverobject.structures.Building + import net.psforever.objects.vehicles.VehicleControl + import net.psforever.objects.zones.ZoneActor + import net.psforever.objects.Tool + import net.psforever.types.CharacterGender + + val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) + val weapon = vehicle.WeaponControlledFromSeat(1).get.asInstanceOf[Tool] + val zone = new Zone("test-zone", map, 0) { + override def SetupNumberPools() = { + val guid : NumberPoolHub = new NumberPoolHub(LimitedNumberSource(5)) + guid.AddPool("test-pool", (0 to 2).toList) + //do not do this under normal conditions + guid.register(vehicle, "test-pool") + guid.register(weapon, "test-pool") + guid.register(weapon.AmmoSlot.Box, "test-pool") + GUID(guid) + } + } + zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), s"test-zone-${System.nanoTime()}") + zone.Actor ! Zone.Init() + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), s"vehicle-control-${System.nanoTime()}") + + val pad = VehicleSpawnPad(GlobalDefinitions.mb_pad_creation) + pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), s"test-pad-${System.nanoTime()}") + pad.Owner = new Building("Building", building_guid = 0, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) + pad.Owner.Faction = faction + val player = Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)) + player.GUID = PlanetSideGUID(10) + player.Continent = zone.Id + player.Spawn + //note: pad and vehicle are both at Vector3(1,0,0) so they count as blocking + pad.Position = Vector3(1,0,0) + vehicle.Position = Vector3(1,0,0) + (vehicle, player, pad, zone) + } +} diff --git a/common/src/test/scala/service/AvatarServiceTest.scala b/pslogin/src/test/scala/actor/service/AvatarServiceTest.scala similarity index 99% rename from common/src/test/scala/service/AvatarServiceTest.scala rename to pslogin/src/test/scala/actor/service/AvatarServiceTest.scala index 989e92dd..9fce22da 100644 --- a/common/src/test/scala/service/AvatarServiceTest.scala +++ b/pslogin/src/test/scala/actor/service/AvatarServiceTest.scala @@ -1,9 +1,9 @@ // Copyright (c) 2017 PSForever -package service +package actor.service import akka.actor.Props import akka.routing.RandomPool -import base.ActorTest +import actor.base.ActorTest import net.psforever.objects._ import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}