Merge pull request #164 from Fate-JH/guid2

Unique Number System
This commit is contained in:
Fate-JH 2017-10-02 19:57:32 -04:00 committed by GitHub
commit ddba33ffc7
17 changed files with 759 additions and 989 deletions

View file

@ -0,0 +1,13 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope
abstract class ActorTest(sys : ActorSystem) extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}

View file

@ -2,24 +2,11 @@
package objects
import akka.actor.{ActorSystem, Props}
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.NumberPoolHub
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import net.psforever.objects.guid.actor.{NumberPoolAccessorActor, NumberPoolActor, Register}
import net.psforever.objects.guid.actor.NumberPoolActor
import net.psforever.objects.guid.pool.ExclusivePool
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import org.specs2.specification.Scope
import scala.concurrent.duration.Duration
import scala.util.Success
abstract class ActorTest(sys : ActorSystem) extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}
class NumberPoolActorTest extends ActorTest(ActorSystem("test")) {
"NumberPoolActor" should {
@ -61,23 +48,3 @@ class NumberPoolActorTest2 extends ActorTest(ActorSystem("test")) {
}
}
}
class NumberPoolActorTest3 extends ActorTest(ActorSystem("test")) {
"NumberPoolAccessorActor" should {
class TestEntity extends IdentifiableEntity
"register" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val pool = hub.AddPool("test", (25 to 50).toList)
pool.Selector = new RandomSelector
val poolActor = system.actorOf(Props(classOf[NumberPoolActor], pool), name = "poolActor")
val poolAccessor = system.actorOf(Props(classOf[NumberPoolAccessorActor], hub, pool, poolActor), name = "accessor")
val obj : TestEntity = new TestEntity
val probe = new TestProbe(system)
poolAccessor ! Register(obj, probe.ref)
probe.expectMsg(Success(obj))
assert({obj.GUID; true}) //NoGUIDException if failure
}
}
}

View file

@ -9,180 +9,6 @@ class NumberSourceTest extends Specification {
import net.psforever.objects.entity.IdentifiableEntity
private class TestClass extends IdentifiableEntity
"MaxNumberSource" should {
import net.psforever.objects.guid.source.MaxNumberSource
"construct" in {
val obj = MaxNumberSource()
obj.Size mustEqual Int.MaxValue
obj.CountAvailable mustEqual Int.MaxValue
obj.CountUsed mustEqual 0
}
"get a number" in {
val obj = MaxNumberSource()
val result : Option[LoanedKey] = obj.Available(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object mustEqual None
obj.Size mustEqual Int.MaxValue
obj.CountAvailable mustEqual Int.MaxValue - 1
obj.CountUsed mustEqual 1
}
"assign the number" in {
val obj = MaxNumberSource()
val result : Option[LoanedKey] = obj.Available(5)
result.isDefined mustEqual true
result.get.Object = new TestClass()
ok
}
"return a number (unused)" in {
val obj = MaxNumberSource()
val result : Option[LoanedKey] = obj.Available(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
obj.CountUsed mustEqual 1
val ret = obj.Return(result.get)
ret mustEqual None
obj.CountUsed mustEqual 0
}
"return a number (assigned)" in {
val obj = MaxNumberSource()
val test = new TestClass()
val result : Option[LoanedKey] = obj.Available(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Object = test
obj.CountUsed mustEqual 1
val ret = obj.Return(result.get)
ret mustEqual Some(test)
obj.CountUsed mustEqual 0
}
"restrict a number (unassigned)" in {
val obj = MaxNumberSource()
val result : Option[LoanedKey] = obj.Restrict(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object mustEqual None
}
"restrict a number (assigned + multiple assignments)" in {
val obj = MaxNumberSource()
val test1 = new TestClass()
val test2 = new TestClass()
val result : Option[LoanedKey] = obj.Restrict(5)
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object mustEqual None
result.get.Object = None //assignment 1
result.get.Object mustEqual None //still unassigned
result.get.Object = test1 //assignment 2
result.get.Object mustEqual Some(test1)
result.get.Object = test2 //assignment 3
result.get.Object mustEqual Some(test1) //same as above
}
"return a restricted number (correctly fail)" in {
val obj = MaxNumberSource()
val test = new TestClass()
val result : Option[LoanedKey] = obj.Restrict(5)
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object = test
obj.Return(5)
val result2 : Option[SecureKey] = obj.Get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Restricted
result2.get.Object mustEqual Some(test)
}
"restrict a previously-assigned number" in {
val obj = MaxNumberSource()
val test = new TestClass()
val result1 : Option[LoanedKey] = obj.Available(5)
result1.isDefined mustEqual true
result1.get.Policy mustEqual AvailabilityPolicy.Leased
result1.get.Object = test
val result2 : Option[LoanedKey] = obj.Restrict(5)
result2.isDefined mustEqual true
result2.get.Policy mustEqual AvailabilityPolicy.Restricted
result2.get.Object mustEqual Some(test)
}
"check a number (not previously gotten)" in {
val obj = MaxNumberSource()
val result2 : Option[SecureKey] = obj.Get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Available
result2.get.Object mustEqual None
}
"check a number (previously gotten)" in {
val obj = MaxNumberSource()
val result : Option[LoanedKey] = obj.Available(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object mustEqual None
val result2 : Option[SecureKey] = obj.Get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object mustEqual None
}
"check a number (assigned)" in {
val obj = MaxNumberSource()
val result : Option[LoanedKey] = obj.Available(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object = new TestClass()
val result2 : Option[SecureKey] = obj.Get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object mustEqual result.get.Object
}
"check a number (assigned and returned)" in {
val obj = MaxNumberSource()
val test = new TestClass()
val result : Option[LoanedKey] = obj.Available(5)
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object = test
val result2 : Option[SecureKey] = obj.Get(5)
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object.get === test
obj.Return(5) mustEqual Some(test)
val result3 : Option[SecureKey] = obj.Get(5)
result3.get.Policy mustEqual AvailabilityPolicy.Available
result3.get.Object mustEqual None
}
"clear" in {
val obj = MaxNumberSource()
val test1 = new TestClass()
val test2 = new TestClass()
obj.Available(5) //no assignment
obj.Available(10).get.Object = test1
obj.Available(15).get.Object = test2
obj.Restrict(15)
obj.Restrict(20).get.Object = test1
obj.CountUsed mustEqual 4
val list : List[IdentifiableEntity] = obj.Clear()
obj.CountUsed mustEqual 0
list.size mustEqual 3
list.count(obj => { obj == test1 }) mustEqual 2
list.count(obj => { obj == test2 }) mustEqual 1
}
}
"LimitedNumberSource" should {
import net.psforever.objects.guid.source.LimitedNumberSource
"construct" in {

View file

@ -0,0 +1,288 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.{NumberPoolActor, Register, UniqueNumberSystem, Unregister}
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import scala.concurrent.duration.Duration
import scala.util.{Failure, Success}
class AllocateNumberPoolActors extends ActorTest(ActorSystem("test")) {
"AllocateNumberPoolActors" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList)
guid.AddPool("pool2", (3001 to 4000).toList)
guid.AddPool("pool3", (5001 to 6000).toList)
val actorMap = UniqueNumberSystemTest.AllocateNumberPoolActors(guid)
assert(actorMap.size == 4)
assert(actorMap.get("generic").isDefined) //automatically generated
assert(actorMap.get("pool1").isDefined)
assert(actorMap.get("pool2").isDefined)
assert(actorMap.get("pool3").isDefined)
}
}
class UniqueNumberSystemTest extends ActorTest(ActorSystem("test")) {
"UniqueNumberSystem" should {
"constructor" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList)
guid.AddPool("pool2", (3001 to 4000).toList)
guid.AddPool("pool3", (5001 to 6000).toList)
system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
//as long as it constructs ...
}
}
}
class UniqueNumberSystemTest1 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Register (success)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
val pool1 = (1001 to 2000).toList
val pool2 = (3001 to 4000).toList
val pool3 = (5001 to 6000).toList
guid.AddPool("pool1", pool1).Selector = new RandomSelector
guid.AddPool("pool2", pool2).Selector = new RandomSelector
guid.AddPool("pool3", pool3).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
assert(src.CountUsed == 0)
//pool1
for(_ <- 1 to 100) {
val testObj = new EntityTestClass()
uns ! Register(testObj, "pool1")
val msg = receiveOne(Duration.create(100, "ms"))
assert(msg.isInstanceOf[Success[_]])
assert(pool1.contains(testObj.GUID.guid))
}
//pool2
for(_ <- 1 to 100) {
val testObj = new EntityTestClass()
uns ! Register(testObj, "pool2")
val msg = receiveOne(Duration.create(100, "ms"))
assert(msg.isInstanceOf[Success[_]])
assert(pool2.contains(testObj.GUID.guid))
}
//pool3
for(_ <- 1 to 100) {
val testObj = new EntityTestClass()
uns ! Register(testObj, "pool3")
val msg = receiveOne(Duration.create(100, "ms"))
assert(msg.isInstanceOf[Success[_]])
assert(pool3.contains(testObj.GUID.guid))
}
assert(src.CountUsed == 300)
}
}
}
class UniqueNumberSystemTest2 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Register (success; already registered)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool1")
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 1)
val id = testObj.GUID.guid
uns ! Register(testObj, "pool2") //different pool; makes no difference
val msg2 = receiveOne(Duration.create(100, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 1)
assert(testObj.GUID.guid == id) //unchanged
}
}
//a log.warn should have been generated during this test
}
class UniqueNumberSystemTest3 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Register (failure; no pool)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool4")
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
}
}
}
class UniqueNumberSystemTest4 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Register (failure; empty pool)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
guid.AddPool("pool4", 50 :: Nil).Selector = new RandomSelector //list of one element; can not add an empty list
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj1 = new EntityTestClass()
uns ! Register(testObj1, "pool4")
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Success[_]]) //pool4 is now empty
val testObj2 = new EntityTestClass()
uns ! Register(testObj2, "pool4")
val msg2 = receiveOne(Duration.create(100, "ms"))
assert(msg2.isInstanceOf[Failure[_]])
}
}
}
class UniqueNumberSystemTest5 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Unregister (success)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
val pool2 = (3001 to 4000).toList
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", pool2).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool2")
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(pool2.contains(testObj.GUID.guid))
assert(src.CountUsed == 1)
uns ! Unregister(testObj)
val msg2 = receiveOne(Duration.create(100, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
}
}
}
class UniqueNumberSystemTest6 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Unregister (success; object not registered at all)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
uns ! Unregister(testObj)
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
}
}
}
class UniqueNumberSystemTest7 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Unregister (failure; number not in system)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj = new EntityTestClass()
testObj.GUID = net.psforever.packet.game.PlanetSideGUID(6001) //fake registering; number too high
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
uns ! Unregister(testObj)
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
}
}
}
class UniqueNumberSystemTest8 extends ActorTest(ActorSystem("test")) {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Unregister (failure; object is not registered to that number)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val testObj = new EntityTestClass()
testObj.GUID = net.psforever.packet.game.PlanetSideGUID(3500) //fake registering
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
uns ! Unregister(testObj)
val msg1 = receiveOne(Duration.create(100, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
}
}
}
object UniqueNumberSystemTest {
/**
* @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
*/
def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = {
poolSource.Pools.map({ case ((pname, pool)) =>
pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname)
}).toMap
}
}