Add start of test framework for plugins

Adds a basic testing framework suitable for plugins that start simple
Actions for players, which can be expanded on in the future.  The
banking and training dummy tests have been updated to use this framework
and serve as samples.
This commit is contained in:
Gary Tierney
2017-06-19 02:50:50 +01:00
parent d0fec15a84
commit b536b2ed9d
8 changed files with 248 additions and 1 deletions
+15
View File
@@ -24,6 +24,19 @@ task testPlugins {
}
}
sourceSets {
pluginTesting {
kotlin {
srcDir 'src/pluginTesting/kotlin'
}
}
}
dependencies {
pluginTestingCompile sourceSets.test.output
pluginTestingCompile sourceSets.test.compileClasspath
}
check.dependsOn testPlugins
class PluginBuildData {
@@ -65,6 +78,8 @@ def configurePluginDependencies(SourceSet mainSources, SourceSet testSources,
dependencies.add(testConfiguration, mainSources.output)
dependencies.add(testConfiguration, configurations.testCompile)
dependencies.add(testConfiguration, sourceSets.test.output)
dependencies.add(testConfiguration, sourceSets.test.compileClasspath)
dependencies.add(testConfiguration, sourceSets.pluginTesting.output)
}
def configurePluginTasks(String name, SourceSet mainSources, SourceSet testSources,
@@ -71,7 +71,7 @@ public abstract class ScheduledTask {
/**
* Pulses this task: updates the delay and calls {@link #execute()} if necessary.
*/
final void pulse() {
public final void pulse() {
if (running && --pulses <= 0) {
execute();
pulses = delay;
@@ -0,0 +1,68 @@
package org.apollo.game.plugins.testing
import org.apollo.game.message.handler.MessageHandler
import org.apollo.game.message.handler.MessageHandlerChainSet
import org.apollo.game.model.Position
import org.apollo.game.model.World
import org.apollo.game.model.entity.Player
import org.apollo.game.plugin.*
import org.apollo.net.message.Message
import org.apollo.util.security.PlayerCredentials
import org.junit.After
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Matchers.any
import org.mockito.Mockito.mock
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
import org.powermock.api.mockito.PowerMockito.spy
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import java.util.*
@RunWith(PowerMockRunner::class)
@PrepareForTest(World::class, PluginContext::class, Player::class)
abstract class KotlinPluginTest {
private var ctx: KotlinPluginTestContext? = null
protected fun context(): KotlinPluginTestContext {
return ctx ?: throw IllegalStateException("Setup method not called")
}
@Before
fun setup() {
val messageHandlerChainSet = MessageHandlerChainSet()
val world = spy<World>(World())
val answer = Answer<Any?> { invocation: InvocationOnMock ->
messageHandlerChainSet.putHandler(
invocation.arguments[0] as Class<Message>,
invocation.arguments[1] as MessageHandler<*>)
}
val pluginContext = mock<PluginContext>(PluginContext::class.java, answer)
val pluginEnvironment = KotlinPluginEnvironment(world)
pluginEnvironment.setContext(pluginContext)
pluginEnvironment.load(ArrayList<PluginMetaData>())
val credentials = PlayerCredentials("test", "test", 1, 1, "0.0.0.0")
val position = Position(3200, 3200, 0)
val player = spy<Player>(Player(world, credentials, position))
org.powermock.api.mockito.PowerMockito.doNothing().`when`(player).send(any<Message>())
world.register(player)
val region = world.regionRepository.fromPosition(position)
region.addEntity(player)
ctx = KotlinPluginTestContext(world, player, messageHandlerChainSet)
}
@After
fun destroy() {
ctx!!.shutdown()
}
}
@@ -0,0 +1,74 @@
package org.apollo.game.plugins.testing
import org.apollo.cache.def.NpcDefinition
import org.apollo.game.action.Action
import org.apollo.game.message.handler.MessageHandlerChainSet
import org.apollo.game.message.impl.*
import org.apollo.game.model.*
import org.apollo.game.model.entity.*
import org.apollo.game.model.entity.obj.GameObject
import org.apollo.game.model.entity.obj.StaticGameObject
import org.apollo.net.message.Message
import org.junit.Assert
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
class KotlinPluginTestContext(val world: World, val activePlayer: Player, val messageHandlers: MessageHandlerChainSet) {
fun interactWith(entity: Entity, option: Int = 1) {
activePlayer.position = entity.position.step(1, Direction.NORTH)
when (entity) {
is GameObject -> notify(ObjectActionMessage(option, entity.id, entity.position))
is Npc -> notify(NpcActionMessage(option, entity.index))
is Player -> notify(PlayerActionMessage(option, entity.index))
}
}
fun spawnNpc(id: Int, position: Position): Npc {
val definition = NpcDefinition(id)
val npc = Npc(world, position, definition, arrayOfNulls(4))
val region = world.regionRepository.fromPosition(position)
val npcs = world.npcRepository
world.register(npc)
npcs.add(npc)
region.addEntity(npc)
return npc
}
fun spawnObject(id: Int, position: Position): GameObject {
val obj = StaticGameObject(world, id, position, 0, 0)
world.spawn(obj)
return obj
}
fun notify(message: Message) {
messageHandlers.notify(activePlayer, message)
}
fun shutdown() {
}
fun waitForActionCompletion(predicate: (Action<Player>) -> Boolean = { _ -> true }, timeout: Int = 15) {
val actionCaptor: ArgumentCaptor<Action<*>> = ArgumentCaptor.forClass(Action::class.java)
Mockito.verify(activePlayer).startAction(actionCaptor.capture())
val action: Action<Player> = actionCaptor.value as Action<Player>
Assert.assertTrue("Found wrong action type", predicate.invoke(action))
var pulses = 0
do {
action.pulse()
} while (action.isRunning && pulses++ < timeout)
Assert.assertFalse("Exceeded timeout waiting for action completion", pulses > timeout)
}
}
@@ -0,0 +1,38 @@
import org.apollo.game.model.Position
import org.apollo.game.plugins.testing.KotlinPluginTest
import org.junit.Test
import org.mockito.Mockito.verify
class OpenBankTest() : KotlinPluginTest() {
companion object {
const val BANK_BOOTH_ID = 2213
const val BANK_TELLER_ID = 166
val BANK_POSITION = Position(3200, 3200, 0)
}
@Test
fun `Interacting with a bank teller should open the players bank`() {
val ctx = context()
val bankTeller = ctx.spawnNpc(BANK_TELLER_ID, BANK_POSITION)
// @todo - these option numbers only match by coincidence, we should be looking up the correct ones
ctx.interactWith(bankTeller, option = 2)
ctx.waitForActionCompletion()
verify(ctx.activePlayer).openBank()
}
@Test
fun `Interacting with a bank booth object should open the players bank`() {
val ctx = context()
val bankBooth = ctx.spawnObject(BANK_BOOTH_ID, BANK_POSITION)
ctx.interactWith(bankBooth, option = 2)
ctx.waitForActionCompletion()
verify(ctx.activePlayer).openBank()
}
}
+7
View File
@@ -0,0 +1,7 @@
name = "training-dummy"
package = "org.apollo.game.plugin.entity"
authors = [ "Gary Tierney" ]
[config]
srcDir = "src/"
testDir = "test/"
@@ -0,0 +1,45 @@
import org.apollo.game.model.Position
import org.apollo.game.model.entity.Skill
import org.apollo.game.model.entity.SkillSet
import org.apollo.game.plugins.testing.KotlinPluginTest
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mockito.Matchers
import org.mockito.Matchers.*
import org.mockito.Mockito.verify
class TrainingDummyTest : KotlinPluginTest() {
companion object {
const val DUMMY_ID = 823
val DUMMY_POSITION = Position(3200, 3230, 0)
}
@Test fun `Hitting the training dummy should give the player attack experience`() {
val ctx = context()
val dummy = ctx.spawnObject(DUMMY_ID, DUMMY_POSITION)
val skills = ctx.activePlayer.skillSet
val attackExp = skills.getExperience(Skill.ATTACK)
ctx.interactWith(dummy, option = 2)
ctx.waitForActionCompletion()
assertTrue("Did not gain exp after hitting dummy", skills.getExperience(Skill.ATTACK) > attackExp)
}
@Test fun `The player should stop getting attack experience from the training dummy at level 8`() {
val ctx = context()
val dummy = ctx.spawnObject(DUMMY_ID, DUMMY_POSITION)
val skills = ctx.activePlayer.skillSet
skills.setMaximumLevel(Skill.ATTACK, 8)
val attackExp = skills.getExperience(Skill.ATTACK)
ctx.interactWith(dummy, option = 2)
ctx.waitForActionCompletion()
verify(ctx.activePlayer).sendMessage(contains("nothing more you can learn"))
assertTrue("Attack exp has changed since hitting the dummy", attackExp == skills.getExperience(Skill.ATTACK))
}
}