diff --git a/game/build.gradle b/game/build.gradle index d421fc93..7c5c8f0f 100644 --- a/game/build.gradle +++ b/game/build.gradle @@ -33,6 +33,8 @@ dependencies { compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jre8', version: "$kotlinVersion" compile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler-embeddable', version: "$kotlinVersion" + // https://mvnrepository.com/artifact/org.hamcrest/hamcrest-all + testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3' testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" } diff --git a/game/plugins.gradle b/game/plugins.gradle index 01759226..47a1f793 100644 --- a/game/plugins.gradle +++ b/game/plugins.gradle @@ -17,11 +17,7 @@ buildscript { } task testPlugins { - group = "plugin-verification" - doLast { - println("Finished executing plugin tests") - } } sourceSets { @@ -95,7 +91,6 @@ def configurePluginTasks(String name, SourceSet mainSources, SourceSet testSourc binResultsDir = file("$buildDir/plugin-test-results/binary/$name") reports { - html.destination = "$buildDir/reports/plugin-tests/$name" junitXml.destination = "$buildDir/plugin-tests/$name" } @@ -206,3 +201,10 @@ pluginMap.values().each { configurePluginDependencies(it.mainSources, it.testSources, dependencies) configurePluginTasks(it.normalizedName, it.mainSources, it.testSources, it.scriptFiles, dependencies) } + +task testPluginsReport(type: TestReport) { + destinationDir = file("$buildDir/reports/plugin-tests") + reportOn tasks.findAll { it.group.equals("plugin-verification"); } +} + +testPlugins.finalizedBy testPluginsReport \ No newline at end of file diff --git a/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTest.kt b/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTest.kt index cb2e620e..827d8804 100644 --- a/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTest.kt +++ b/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTest.kt @@ -1,21 +1,26 @@ package org.apollo.game.plugins.testing +import org.apollo.cache.def.NpcDefinition +import org.apollo.game.action.Action 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.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.game.plugin.* import org.apollo.net.message.Message import org.apollo.util.security.PlayerCredentials -import org.junit.After +import org.junit.Assert import org.junit.Before import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.Matchers.any -import org.mockito.Mockito.mock +import org.mockito.Mockito import org.mockito.invocation.InvocationOnMock import org.mockito.stubbing.Answer -import org.powermock.api.mockito.PowerMockito.spy +import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.modules.junit4.PowerMockRunner import java.util.* @@ -23,24 +28,22 @@ 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") - } + lateinit var world: World + lateinit var player: Player + lateinit var messageHandlers: MessageHandlerChainSet @Before fun setup() { - val messageHandlerChainSet = MessageHandlerChainSet() - val world = spy(World()) + messageHandlers = MessageHandlerChainSet() + world = PowerMockito.spy(World()) val answer = Answer { invocation: InvocationOnMock -> - messageHandlerChainSet.putHandler( + messageHandlers.putHandler( invocation.arguments[0] as Class, invocation.arguments[1] as MessageHandler<*>) } - val pluginContext = mock(PluginContext::class.java, answer) + val pluginContext = PowerMockito.mock(PluginContext::class.java, answer) val pluginEnvironment = KotlinPluginEnvironment(world) pluginEnvironment.setContext(pluginContext) @@ -48,21 +51,63 @@ abstract class KotlinPluginTest { val credentials = PlayerCredentials("test", "test", 1, 1, "0.0.0.0") val position = Position(3200, 3200, 0) - - val player = spy(Player(world, credentials, position)) - org.powermock.api.mockito.PowerMockito.doNothing().`when`(player).send(any()) - - world.register(player) - val region = world.regionRepository.fromPosition(position) + + player = PowerMockito.spy(Player(world, credentials, position)) + world.register(player) region.addEntity(player) - ctx = KotlinPluginTestContext(world, player, messageHandlerChainSet) + PowerMockito.doNothing().`when`(player).send(any()) } - @After - fun destroy() { - ctx!!.shutdown() + fun interactWith(entity: Entity, option: Int = 1) { + player.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 + + 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(player, message) + } + + fun waitForActionCompletion(predicate: (Action) -> Boolean = { _ -> true }, timeout: Int = 15) { + val actionCaptor: ArgumentCaptor> = ArgumentCaptor.forClass(Action::class.java) + Mockito.verify(player).startAction(actionCaptor.capture()) + + val action: Action = actionCaptor.value as Action + 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) } } diff --git a/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTestContext.kt b/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTestContext.kt deleted file mode 100644 index 2b1af62e..00000000 --- a/game/src/pluginTesting/kotlin/org/apollo/game/plugins/testing/KotlinPluginTestContext.kt +++ /dev/null @@ -1,74 +0,0 @@ -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) -> Boolean = { _ -> true }, timeout: Int = 15) { - val actionCaptor: ArgumentCaptor> = ArgumentCaptor.forClass(Action::class.java) - Mockito.verify(activePlayer).startAction(actionCaptor.capture()) - - val action: Action = actionCaptor.value as Action - 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) - } - -} diff --git a/game/src/plugins/bank/test/OpenBankTest.kt b/game/src/plugins/bank/test/OpenBankTest.kt index aba9b67e..8bede6be 100644 --- a/game/src/plugins/bank/test/OpenBankTest.kt +++ b/game/src/plugins/bank/test/OpenBankTest.kt @@ -14,25 +14,23 @@ class OpenBankTest() : KotlinPluginTest() { @Test fun `Interacting with a bank teller should open the players bank`() { - val ctx = context() - val bankTeller = ctx.spawnNpc(BANK_TELLER_ID, BANK_POSITION) + val bankTeller = 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() + interactWith(bankTeller, option = 2) + waitForActionCompletion() - verify(ctx.activePlayer).openBank() + verify(player).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) + val bankBooth = spawnObject(BANK_BOOTH_ID, BANK_POSITION) - ctx.interactWith(bankBooth, option = 2) - ctx.waitForActionCompletion() + interactWith(bankBooth, option = 2) + waitForActionCompletion() - verify(ctx.activePlayer).openBank() + verify(player).openBank() } } \ No newline at end of file diff --git a/game/src/plugins/dummy/test/TrainingDummyTest.kt b/game/src/plugins/dummy/test/TrainingDummyTest.kt index d8a7ddaa..0fc2da24 100644 --- a/game/src/plugins/dummy/test/TrainingDummyTest.kt +++ b/game/src/plugins/dummy/test/TrainingDummyTest.kt @@ -1,12 +1,10 @@ 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.hamcrest.Matchers.* +import org.junit.Assert.* import org.junit.Test -import org.mockito.Matchers -import org.mockito.Matchers.* -import org.mockito.Mockito.verify +import org.mockito.Mockito.* class TrainingDummyTest : KotlinPluginTest() { @@ -16,30 +14,30 @@ class TrainingDummyTest : KotlinPluginTest() { } @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) + val dummy = spawnObject(DUMMY_ID, DUMMY_POSITION) + val skills = player.skillSet + val beforeExp = skills.getExperience(Skill.ATTACK) - ctx.interactWith(dummy, option = 2) - ctx.waitForActionCompletion() + interactWith(dummy, option = 2) + waitForActionCompletion() - assertTrue("Did not gain exp after hitting dummy", skills.getExperience(Skill.ATTACK) > attackExp) + val afterExp = skills.getExperience(Skill.ATTACK) + assertThat(afterExp, greaterThan(beforeExp)) } @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 + val dummy = spawnObject(DUMMY_ID, DUMMY_POSITION) + val skills = player.skillSet skills.setMaximumLevel(Skill.ATTACK, 8) - val attackExp = skills.getExperience(Skill.ATTACK) + val beforeExp = skills.getExperience(Skill.ATTACK) - ctx.interactWith(dummy, option = 2) - ctx.waitForActionCompletion() + interactWith(dummy, option = 2) + 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)) + val afterExp = skills.getExperience(Skill.ATTACK) + + verify(player).sendMessage(contains("nothing more you can learn")) + assertThat(afterExp, equalTo(beforeExp)) } } \ No newline at end of file