diff options
Diffstat (limited to 'sw-engine/src/test/kotlin')
38 files changed, 4034 insertions, 0 deletions
diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt new file mode 100644 index 00000000..a63ccdd6 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt @@ -0,0 +1,123 @@ +package org.luxons.sevenwonders.game + +import org.junit.Test +import org.luxons.sevenwonders.game.api.Action +import org.luxons.sevenwonders.game.cards.HandCard +import org.luxons.sevenwonders.game.api.PlayedMove +import org.luxons.sevenwonders.game.api.PlayerMove +import org.luxons.sevenwonders.game.api.PlayerTurnInfo +import org.luxons.sevenwonders.game.cards.TableCard +import org.luxons.sevenwonders.game.data.GameDefinition +import org.luxons.sevenwonders.game.data.LAST_AGE +import org.luxons.sevenwonders.game.moves.MoveType +import org.luxons.sevenwonders.game.resources.ResourceTransactions +import org.luxons.sevenwonders.game.resources.noTransactions +import org.luxons.sevenwonders.game.test.testCustomizableSettings +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class GameTest { + + @Test + fun testFullGame3Players() = playGame(nbPlayers = 3) + + @Test + fun testFullGame5Players() = playGame(nbPlayers = 6) + + @Test + fun testFullGame7Players() = playGame(nbPlayers = 7) + + private fun playGame(nbPlayers: Int) { + val game = createGame(nbPlayers) + + (1..LAST_AGE).forEach { playAge(nbPlayers, game, it) } + + game.computeScore() + } + + private fun playAge(nbPlayers: Int, game: Game, age: Int) { + repeat(6) { + playTurn(nbPlayers, game, age, 7 - it) + } + } + + private fun createGame(nbPlayers: Int): Game = + GameDefinition.load().initGame(0, testCustomizableSettings(), nbPlayers) + + private fun playTurn(nbPlayers: Int, game: Game, ageToCheck: Int, handSize: Int) { + val turnInfos = game.getCurrentTurnInfo() + assertEquals(nbPlayers, turnInfos.size) + turnInfos.forEach { + assertEquals(ageToCheck, it.currentAge) + assertEquals(handSize, it.hand.size) + } + + val moveExpectations = turnInfos.mapNotNull { it.firstAvailableMove() } + + moveExpectations.forEach { game.prepareMove(it.playerIndex, it.moveToSend) } + assertTrue(game.allPlayersPreparedTheirMove()) + + val table = game.playTurn() + + val expectedMoves = moveExpectations.map { it.expectedPlayedMove } + assertEquals(expectedMoves, table.lastPlayedMoves) + } + + private fun PlayerTurnInfo.firstAvailableMove(): MoveExpectation? = when (action) { + Action.PLAY, Action.PLAY_2, Action.PLAY_LAST -> createPlayCardMove(this) + Action.PICK_NEIGHBOR_GUILD -> createPickGuildMove(this) + Action.WAIT -> null + } + + private fun createPlayCardMove(turnInfo: PlayerTurnInfo): MoveExpectation { + val wonderBuildability = turnInfo.wonderBuildability + if (wonderBuildability.isBuildable) { + val transactions = wonderBuildability.cheapestTransactions.first() + return planMove(turnInfo, MoveType.UPGRADE_WONDER, turnInfo.hand.first(), transactions) + } + val playableCard = turnInfo.hand.firstOrNull { it.playability.isPlayable } + return if (playableCard != null) { + planMove(turnInfo, MoveType.PLAY, playableCard, playableCard.playability.cheapestTransactions.first()) + } else { + planMove(turnInfo, MoveType.DISCARD, turnInfo.hand.first(), noTransactions()) + } + } + + private fun createPickGuildMove(turnInfo: PlayerTurnInfo): MoveExpectation { + val neighbourGuilds = turnInfo.neighbourGuildCards + + // the game should send action WAIT if no guild cards are available around + assertFalse(neighbourGuilds.isEmpty()) + return MoveExpectation( + turnInfo.playerIndex, + PlayerMove(MoveType.COPY_GUILD, neighbourGuilds.first().name), + PlayedMove(turnInfo.playerIndex, MoveType.COPY_GUILD, neighbourGuilds.first(), noTransactions()) + ) + } + + data class MoveExpectation(val playerIndex: Int, val moveToSend: PlayerMove, val expectedPlayedMove: PlayedMove) + + private fun planMove( + turnInfo: PlayerTurnInfo, + moveType: MoveType, + card: HandCard, + transactions: ResourceTransactions + ): MoveExpectation = MoveExpectation( + turnInfo.playerIndex, + PlayerMove(moveType, card.name, transactions), + PlayedMove(turnInfo.playerIndex, moveType, card.toPlayedCard(), transactions) + ) + + private fun HandCard.toPlayedCard(): TableCard = + TableCard( + name, + color, + requirements, + chainParent, + chainChildren, + image, + back, + true + ) +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/api/BoardsKtTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/api/BoardsKtTest.kt new file mode 100644 index 00000000..244c30a8 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/api/BoardsKtTest.kt @@ -0,0 +1,84 @@ +package org.luxons.sevenwonders.game.api + +import org.luxons.sevenwonders.game.cards.Color +import org.luxons.sevenwonders.game.cards.TableCard +import org.luxons.sevenwonders.game.test.testCard +import kotlin.test.Test +import kotlin.test.assertEquals + +class BoardsKtTest { + + @Test + fun `toColumns on empty list should return no cols`() { + val cols = emptyList<TableCard>().toColumns() + assertEquals(emptyList<List<TableCard>>(), cols) + } + + @Test + fun `toColumns with single resource should return a single column`() { + val card = testCard(color = Color.BROWN).toTableCard() + val cols = listOf(card).toColumns() + assertEquals(listOf(listOf(card)), cols) + } + + @Test + fun `toColumns with single non-resource should return a single column`() { + val card = testCard(color = Color.BLUE).toTableCard() + val cols = listOf(card).toColumns() + assertEquals(listOf(listOf(card)), cols) + } + + @Test + fun `toColumns with two same-color cards should return a single column`() { + val card1 = testCard(color = Color.BLUE).toTableCard() + val card2 = testCard(color = Color.BLUE).toTableCard() + val cards = listOf(card1, card2) + val cols = cards.toColumns() + assertEquals(listOf(cards), cols) + } + + @Test + fun `toColumns with two resource cards should return a single column`() { + val card1 = testCard(color = Color.BROWN).toTableCard() + val card2 = testCard(color = Color.GREY).toTableCard() + val cards = listOf(card1, card2) + val cols = cards.toColumns() + assertEquals(listOf(cards), cols) + } + + @Test + fun `toColumns with 2 different non-resource cards should return 2 columns`() { + val card1 = testCard(color = Color.BLUE).toTableCard() + val card2 = testCard(color = Color.GREEN).toTableCard() + val cards = listOf(card1, card2) + val cols = cards.toColumns() + assertEquals(listOf(listOf(card1), listOf(card2)), cols) + } + + @Test + fun `toColumns with 1 res and 1 non-res card should return 2 columns`() { + val card1 = testCard(color = Color.BROWN).toTableCard() + val card2 = testCard(color = Color.GREEN).toTableCard() + val cards = listOf(card1, card2) + val cols = cards.toColumns() + assertEquals(listOf(listOf(card1), listOf(card2)), cols) + } + + @Test + fun `toColumns should return 1 col for res cards and 1 for each other color`() { + val res1 = testCard(color = Color.BROWN).toTableCard() + val res2 = testCard(color = Color.BROWN).toTableCard() + val res3 = testCard(color = Color.GREY).toTableCard() + val blue1 = testCard(color = Color.BLUE).toTableCard() + val green1 = testCard(color = Color.GREEN).toTableCard() + val green2 = testCard(color = Color.GREEN).toTableCard() + val cards = listOf(res1, green1, green2, res2, blue1, res3) + val cols = cards.toColumns() + val expectedCols = listOf( + listOf(res1, res2, res3), + listOf(blue1), + listOf(green1, green2) + ) + assertEquals(expectedCols, cols) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/api/TableTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/api/TableTest.kt new file mode 100644 index 00000000..19e4e8e8 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/api/TableTest.kt @@ -0,0 +1,71 @@ +package org.luxons.sevenwonders.game.api + +import org.junit.Assume.assumeTrue +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.boards.RelativeBoardPosition +import org.luxons.sevenwonders.game.test.createGuildCards +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class TableTest { + + @Theory + fun getBoard_wrapLeft(nbPlayers: Int) { + assumeTrue(nbPlayers >= 2) + val table = testTable(nbPlayers) + val last = nbPlayers - 1 + assertEquals(table.getBoard(last), table.getBoard(0, RelativeBoardPosition.LEFT)) + assertEquals(table.getBoard(0), table.getBoard(0, RelativeBoardPosition.SELF)) + assertEquals(table.getBoard(1), table.getBoard(0, RelativeBoardPosition.RIGHT)) + } + + @Theory + fun getBoard_wrapRight(nbPlayers: Int) { + assumeTrue(nbPlayers >= 2) + val table = testTable(nbPlayers) + val last = nbPlayers - 1 + assertEquals(table.getBoard(last - 1), table.getBoard(last, RelativeBoardPosition.LEFT)) + assertEquals(table.getBoard(last), table.getBoard(last, RelativeBoardPosition.SELF)) + assertEquals(table.getBoard(0), table.getBoard(last, RelativeBoardPosition.RIGHT)) + } + + @Theory + fun getBoard_noWrap(nbPlayers: Int) { + assumeTrue(nbPlayers >= 3) + val table = testTable(nbPlayers) + assertEquals(table.getBoard(0), table.getBoard(1, RelativeBoardPosition.LEFT)) + assertEquals(table.getBoard(1), table.getBoard(1, RelativeBoardPosition.SELF)) + assertEquals(table.getBoard(2), table.getBoard(1, RelativeBoardPosition.RIGHT)) + } + + @Theory + fun getNeighbourGuildCards(nbPlayers: Int) { + assumeTrue(nbPlayers >= 4) + val table = testTable(nbPlayers) + val guildCards = createGuildCards(4) + table.getBoard(0).addCard(guildCards[0]) + table.getBoard(0).addCard(guildCards[1]) + table.getBoard(1).addCard(guildCards[2]) + table.getBoard(2).addCard(guildCards[3]) + + val neighbourCards0 = table.getNeighbourGuildCards(0) + assertEquals(listOf(guildCards[2]), neighbourCards0) + + val neighbourCards1 = table.getNeighbourGuildCards(1) + assertEquals(guildCards - guildCards[2], neighbourCards1) + + val neighbourCards2 = table.getNeighbourGuildCards(2) + assertEquals(listOf(guildCards[2]), neighbourCards2) + } + + companion object { + + @JvmStatic + @DataPoints + fun nbPlayers(): IntArray = intArrayOf(2, 3, 4, 5, 6, 7, 8) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/BoardTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/BoardTest.kt new file mode 100644 index 00000000..d1b7c239 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/BoardTest.kt @@ -0,0 +1,211 @@ +package org.luxons.sevenwonders.game.boards + +import junit.framework.TestCase.assertEquals +import org.junit.Assume.assumeTrue +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.FromDataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.boards.Board.InsufficientFundsException +import org.luxons.sevenwonders.game.cards.Color +import org.luxons.sevenwonders.game.effects.RawPointsIncrease +import org.luxons.sevenwonders.game.effects.SpecialAbility +import org.luxons.sevenwonders.game.effects.SpecialAbilityActivation +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.resourcesOf +import org.luxons.sevenwonders.game.score.ScoreCategory +import org.luxons.sevenwonders.game.test.addCards +import org.luxons.sevenwonders.game.test.getDifferentColorFrom +import org.luxons.sevenwonders.game.test.playCardWithEffect +import org.luxons.sevenwonders.game.test.singleBoardPlayer +import org.luxons.sevenwonders.game.test.testBoard +import org.luxons.sevenwonders.game.test.testCard +import org.luxons.sevenwonders.game.test.testSettings +import org.luxons.sevenwonders.game.test.testWonder +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertSame +import kotlin.test.assertTrue + +@RunWith(Theories::class) +class BoardTest { + + @Theory + fun initialGold_respectsSettings(@FromDataPoints("gold") goldAmountInSettings: Int) { + val settings = testSettings(initialGold = goldAmountInSettings) + val board = Board(testWonder(), 0, settings) + assertEquals(goldAmountInSettings, board.gold) + } + + @Theory + fun initialProduction_containsInitialResource(type: ResourceType) { + val board = Board(testWonder(type), 0, testSettings()) + val resources = resourcesOf(type) + assertTrue(board.production.contains(resources)) + assertTrue(board.publicProduction.contains(resources)) + } + + @Theory + fun removeGold_successfulWhenNotTooMuch( + @FromDataPoints("gold") initialGold: Int, + @FromDataPoints("gold") goldRemoved: Int + ) { + assumeTrue(goldRemoved >= 0) + assumeTrue(initialGold >= goldRemoved) + + val board = Board(testWonder(), 0, testSettings(initialGold = initialGold)) + board.removeGold(goldRemoved) + assertEquals(initialGold - goldRemoved, board.gold) + } + + @Theory + fun removeGold_failsWhenTooMuch( + @FromDataPoints("gold") initialGold: Int, + @FromDataPoints("gold") goldRemoved: Int + ) { + assumeTrue(goldRemoved >= 0) + assumeTrue(initialGold < goldRemoved) + + assertFailsWith<InsufficientFundsException> { + val board = Board(testWonder(), 0, testSettings(initialGold = initialGold)) + board.removeGold(goldRemoved) + } + } + + @Theory + fun getNbCardsOfColor_properCount_singleColor( + type: ResourceType, + @FromDataPoints("nbCards") nbCards: Int, + @FromDataPoints("nbCards") nbOtherCards: Int, + color: Color + ) { + val board = testBoard(initialResource = type) + addCards(board, nbCards, nbOtherCards, color) + assertEquals(nbCards, board.getNbCardsOfColor(listOf(color))) + } + + @Theory + fun getNbCardsOfColor_properCount_multiColors( + type: ResourceType, + @FromDataPoints("nbCards") nbCards1: Int, + @FromDataPoints("nbCards") nbCards2: Int, + @FromDataPoints("nbCards") nbOtherCards: Int, + color1: Color, + color2: Color + ) { + val board = testBoard(initialResource = type) + addCards(board, nbCards1, color1) + addCards(board, nbCards2, color2) + addCards(board, nbOtherCards, getDifferentColorFrom(color1, color2)) + assertEquals(nbCards1 + nbCards2, board.getNbCardsOfColor(listOf(color1, color2))) + } + + @Test + fun setCopiedGuild_succeedsOnPurpleCard() { + val board = testBoard() + val card = testCard(color = Color.PURPLE) + + board.copiedGuild = card + assertSame(card, board.copiedGuild) + } + + @Theory + fun setCopiedGuild_failsOnNonPurpleCard(color: Color) { + assumeTrue(color !== Color.PURPLE) + val board = testBoard() + val card = testCard(color = color) + + assertFailsWith<IllegalArgumentException> { + board.copiedGuild = card + } + } + + @Theory + fun hasSpecial(applied: SpecialAbility, tested: SpecialAbility) { + val board = testBoard() + val special = SpecialAbilityActivation(applied) + + special.applyTo(singleBoardPlayer(board)) + + assertEquals(applied === tested, board.hasSpecial(tested)) + } + + @Test + fun canPlayFreeCard() { + val board = testBoard() + val special = SpecialAbilityActivation(SpecialAbility.ONE_FREE_PER_AGE) + + special.applyTo(singleBoardPlayer(board)) + + assertTrue(board.canPlayFreeCard(0)) + assertTrue(board.canPlayFreeCard(1)) + assertTrue(board.canPlayFreeCard(2)) + + board.consumeFreeCard(0) + + assertFalse(board.canPlayFreeCard(0)) + assertTrue(board.canPlayFreeCard(1)) + assertTrue(board.canPlayFreeCard(2)) + + board.consumeFreeCard(1) + + assertFalse(board.canPlayFreeCard(0)) + assertFalse(board.canPlayFreeCard(1)) + assertTrue(board.canPlayFreeCard(2)) + + board.consumeFreeCard(2) + + assertFalse(board.canPlayFreeCard(0)) + assertFalse(board.canPlayFreeCard(1)) + assertFalse(board.canPlayFreeCard(2)) + } + + @Theory + fun computePoints_gold(@FromDataPoints("gold") gold: Int) { + assumeTrue(gold >= 0) + val board = testBoard(initialGold = gold) + + val score = board.computeScore(singleBoardPlayer(board)) + assertEquals(gold / 3, score.pointsByCategory[ScoreCategory.GOLD]) + assertEquals(gold / 3, score.totalPoints) + } + + @Theory + fun computePoints_(@FromDataPoints("gold") gold: Int) { + assumeTrue(gold >= 0) + val board = testBoard(initialGold = gold) + + val effect = RawPointsIncrease(5) + playCardWithEffect(singleBoardPlayer(board), Color.BLUE, effect) + + val score = board.computeScore(singleBoardPlayer(board)) + assertEquals(gold / 3, score.pointsByCategory[ScoreCategory.GOLD]) + assertEquals(5, score.pointsByCategory[ScoreCategory.CIVIL]) + assertEquals(5 + gold / 3, score.totalPoints) + } + + companion object { + + @JvmStatic + @DataPoints("gold") + fun goldAmounts(): IntArray = intArrayOf(-3, -1, 0, 1, 2, 3) + + @JvmStatic + @DataPoints("nbCards") + fun nbCards(): IntArray = intArrayOf(0, 1, 2) + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + + @JvmStatic + @DataPoints + fun colors(): Array<Color> = Color.values() + + @JvmStatic + @DataPoints + fun specialAbilities(): Array<SpecialAbility> = SpecialAbility.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/MilitaryTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/MilitaryTest.kt new file mode 100644 index 00000000..248d43dd --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/MilitaryTest.kt @@ -0,0 +1,57 @@ +package org.luxons.sevenwonders.game.boards + +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.FromDataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.boards.Military.UnknownAgeException +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +@RunWith(Theories::class) +class MilitaryTest { + + @Theory + fun victory_addsCorrectPoints( + @FromDataPoints("ages") age: Int, + @FromDataPoints("points") nbPointsPerVictory: Int + ) { + val military = createMilitary(age, nbPointsPerVictory, 0) + val initialPoints = military.totalPoints + + military.victory(age) + assertEquals(initialPoints + nbPointsPerVictory, military.totalPoints) + } + + @Theory + fun victory_failsIfUnknownAge(@FromDataPoints("points") nbPointsPerVictory: Int) { + val military = createMilitary(0, nbPointsPerVictory, 0) + assertFailsWith<UnknownAgeException> { + military.victory(1) + } + } + + @Theory + fun defeat_removesCorrectPoints(@FromDataPoints("points") nbPointsLostPerDefeat: Int) { + val military = createMilitary(0, 0, nbPointsLostPerDefeat) + val initialPoints = military.totalPoints + + military.defeat() + assertEquals(initialPoints - nbPointsLostPerDefeat, military.totalPoints) + } + + companion object { + + @JvmStatic + @DataPoints("points") + fun points(): IntArray = intArrayOf(0, 1, 3, 5) + + @JvmStatic + @DataPoints("ages") + fun ages(): IntArray = intArrayOf(1, 2, 3) + + private fun createMilitary(age: Int, nbPointsPerVictory: Int, nbPointsPerDefeat: Int): Military = + Military(nbPointsPerDefeat, mapOf(age to nbPointsPerVictory)) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPositionTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPositionTest.kt new file mode 100644 index 00000000..2038a676 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPositionTest.kt @@ -0,0 +1,45 @@ +package org.luxons.sevenwonders.game.boards + +import org.junit.Assume.assumeTrue +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class RelativeBoardPositionTest { + + @Theory + fun getIndexFrom_wrapLeft(nbPlayers: Int) { + assumeTrue(nbPlayers >= 2) + val last = nbPlayers - 1 + assertEquals(last, RelativeBoardPosition.LEFT.getIndexFrom(0, nbPlayers)) + assertEquals(0, RelativeBoardPosition.SELF.getIndexFrom(0, nbPlayers)) + assertEquals(1, RelativeBoardPosition.RIGHT.getIndexFrom(0, nbPlayers)) + } + + @Theory + fun getIndexFrom_wrapRight(nbPlayers: Int) { + assumeTrue(nbPlayers >= 2) + val last = nbPlayers - 1 + assertEquals(last - 1, RelativeBoardPosition.LEFT.getIndexFrom(last, nbPlayers)) + assertEquals(last, RelativeBoardPosition.SELF.getIndexFrom(last, nbPlayers)) + assertEquals(0, RelativeBoardPosition.RIGHT.getIndexFrom(last, nbPlayers)) + } + + @Theory + fun getIndexFrom_noWrap(nbPlayers: Int) { + assumeTrue(nbPlayers >= 3) + assertEquals(0, RelativeBoardPosition.LEFT.getIndexFrom(1, nbPlayers)) + assertEquals(1, RelativeBoardPosition.SELF.getIndexFrom(1, nbPlayers)) + assertEquals(2, RelativeBoardPosition.RIGHT.getIndexFrom(1, nbPlayers)) + } + + companion object { + + @JvmStatic + @DataPoints + fun nbPlayers(): IntArray = intArrayOf(1, 2, 3, 5, 7, 9) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/ScienceTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/ScienceTest.kt new file mode 100644 index 00000000..80d6773d --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/boards/ScienceTest.kt @@ -0,0 +1,114 @@ +package org.luxons.sevenwonders.game.boards + +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.test.createScience +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class ScienceTest { + + @Test + fun addAll_empty() { + val initial = createScience(3, 4, 5, 1) + val empty = Science() + initial.addAll(empty) + assertEquals(3, initial.getQuantity(ScienceType.COMPASS)) + assertEquals(4, initial.getQuantity(ScienceType.WHEEL)) + assertEquals(5, initial.getQuantity(ScienceType.TABLET)) + assertEquals(1, initial.jokers) + } + + @Test + fun addAll_noJoker() { + val initial = createScience(3, 4, 5, 1) + val other = createScience(1, 2, 3, 0) + initial.addAll(other) + assertEquals(4, initial.getQuantity(ScienceType.COMPASS)) + assertEquals(6, initial.getQuantity(ScienceType.WHEEL)) + assertEquals(8, initial.getQuantity(ScienceType.TABLET)) + assertEquals(1, initial.jokers) + } + + @Test + fun addAll_withJokers() { + val initial = createScience(3, 4, 5, 1) + val other = createScience(0, 0, 0, 3) + initial.addAll(other) + assertEquals(3, initial.getQuantity(ScienceType.COMPASS)) + assertEquals(4, initial.getQuantity(ScienceType.WHEEL)) + assertEquals(5, initial.getQuantity(ScienceType.TABLET)) + assertEquals(4, initial.jokers) + } + + @Test + fun addAll_mixed() { + val initial = createScience(3, 4, 5, 1) + val other = createScience(1, 2, 3, 4) + initial.addAll(other) + assertEquals(4, initial.getQuantity(ScienceType.COMPASS)) + assertEquals(6, initial.getQuantity(ScienceType.WHEEL)) + assertEquals(8, initial.getQuantity(ScienceType.TABLET)) + assertEquals(5, initial.jokers) + } + + @Theory + fun computePoints_compassesOnly_noJoker(compasses: Int) { + val science = createScience(compasses, 0, 0, 0) + assertEquals(compasses * compasses, science.computePoints()) + } + + @Theory + fun computePoints_wheelsOnly_noJoker(wheels: Int) { + val science = createScience(0, wheels, 0, 0) + assertEquals(wheels * wheels, science.computePoints()) + } + + @Theory + fun computePoints_tabletsOnly_noJoker(tablets: Int) { + val science = createScience(0, 0, tablets, 0) + assertEquals(tablets * tablets, science.computePoints()) + } + + @Theory + fun computePoints_allSameNoJoker(eachSymbol: Int) { + val science = createScience(eachSymbol, eachSymbol, eachSymbol, 0) + assertEquals(3 * eachSymbol * eachSymbol + 7 * eachSymbol, science.computePoints()) + } + + @Theory + fun computePoints_expectation(expectation: IntArray) { + val science = createScience(expectation[0], expectation[1], expectation[2], expectation[3]) + assertEquals(expectation[4], science.computePoints()) + } + + companion object { + + @JvmStatic + @DataPoints + fun quantitiesWithExpectedPoints(): Array<IntArray> = arrayOf( + // compasses, wheels, tablets, jokers, expected points + intArrayOf(0, 0, 0, 1, 1), + intArrayOf(0, 0, 1, 0, 1), + intArrayOf(0, 0, 0, 2, 4), + intArrayOf(0, 0, 1, 1, 4), + intArrayOf(0, 0, 2, 0, 4), + intArrayOf(0, 0, 0, 3, 10), + intArrayOf(0, 0, 1, 2, 10), + intArrayOf(0, 1, 1, 1, 10), + intArrayOf(1, 1, 1, 0, 10), + intArrayOf(0, 0, 0, 4, 16), + intArrayOf(0, 0, 1, 3, 16), + intArrayOf(0, 0, 2, 2, 16), + intArrayOf(0, 0, 3, 1, 16), + intArrayOf(0, 0, 4, 0, 16) + ) + + @JvmStatic + @DataPoints + fun quantitiesDataPoints(): IntArray = intArrayOf(0, 1, 3, 5, 8) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/CardBackTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/CardBackTest.kt new file mode 100644 index 00000000..66ff7a0e --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/CardBackTest.kt @@ -0,0 +1,14 @@ +package org.luxons.sevenwonders.game.cards + +import org.junit.Test +import kotlin.test.assertEquals + +class CardBackTest { + + @Test + fun initializedWithImage() { + val imagePath = "whateverimage.png" + val (image) = CardBack(imagePath) + assertEquals(imagePath, image) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/CardTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/CardTest.kt new file mode 100644 index 00000000..b6fecbd0 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/CardTest.kt @@ -0,0 +1,42 @@ +package org.luxons.sevenwonders.game.cards + +import org.junit.Test +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.boards.Board +import org.luxons.sevenwonders.game.boards.Table +import org.luxons.sevenwonders.game.effects.ProductionIncrease +import org.luxons.sevenwonders.game.resources.Production +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.noTransactions +import org.luxons.sevenwonders.game.test.testCard +import org.luxons.sevenwonders.game.test.testSettings +import org.luxons.sevenwonders.game.wonders.Wonder +import kotlin.test.assertEquals + +class CardTest { + + @Test + fun playCardCostingMoney() { + val initialGold = 3 + val price = 1 + val settings = testSettings(3, initialGold) + + val boards = listOf( + Board(Wonder("TestWonder", ResourceType.WOOD, emptyList(), ""), 0, settings), + Board(Wonder("TestWonder", ResourceType.STONE, emptyList(), ""), 1, settings), + Board(Wonder("TestWonder", ResourceType.PAPYRUS, emptyList(), ""), 2, settings) + ) + val table = Table(boards) + + val treeFarmRequirements = Requirements(gold = price) + val treeFarmProduction = Production().apply { addChoice(ResourceType.WOOD, ResourceType.CLAY) } + val treeFarmEffect = ProductionIncrease(treeFarmProduction, false) + val treeFarmCard = testCard("Tree Farm", Color.BROWN, treeFarmRequirements, treeFarmEffect) + + treeFarmCard.applyTo(SimplePlayer(0, table), noTransactions()) + + assertEquals(initialGold - price, table.getBoard(0).gold) + assertEquals(initialGold, table.getBoard(1).gold) + assertEquals(initialGold, table.getBoard(2).gold) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/DecksTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/DecksTest.kt new file mode 100644 index 00000000..f6c45720 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/DecksTest.kt @@ -0,0 +1,104 @@ +package org.luxons.sevenwonders.game.cards + +import org.junit.Assume.assumeTrue +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.cards.Decks.CardNotFoundException +import org.luxons.sevenwonders.game.test.sampleCards +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +@RunWith(Theories::class) +class DecksTest { + + @Test + fun getCard_failsOnEmptyNameWhenDeckIsEmpty() { + val decks = createDecks(0, 0) + assertFailsWith<IllegalArgumentException> { + decks.getCard(0, "") + } + } + + @Test + fun getCard_failsWhenDeckIsEmpty() { + val decks = createDecks(0, 0) + assertFailsWith<IllegalArgumentException> { + decks.getCard(0, "Any name") + } + } + + @Test + fun getCard_failsWhenCardIsNotFound() { + val decks = createDecks(3, 20) + assertFailsWith<CardNotFoundException> { + decks.getCard(1, "Unknown name") + } + } + + @Test + fun getCard_succeedsWhenCardIsFound() { + val decks = createDecks(3, 20) + val (name) = decks.getCard(1, "Test Card 3") + assertEquals("Test Card 3", name) + } + + @Test + fun deal_failsOnZeroPlayers() { + val decks = createDecks(3, 20) + assertFailsWith<IllegalArgumentException> { + decks.deal(1, 0) + } + } + + @Test + fun deal_failsOnMissingAge() { + val decks = createDecks(2, 0) + assertFailsWith<IllegalArgumentException> { + decks.deal(4, 10) + } + } + + @Theory + fun deal_failsWhenTooFewPlayers(nbPlayers: Int, nbCards: Int) { + assumeTrue(nbCards % nbPlayers != 0) + val decks = createDecks(1, nbCards) + assertFailsWith<IllegalArgumentException> { + decks.deal(1, nbPlayers) + } + } + + @Theory + fun deal_succeedsOnZeroCards(nbPlayers: Int) { + val decks = createDecks(1, 0) + val hands = decks.deal(1, nbPlayers) + repeat(nbPlayers) { i -> + assertNotNull(hands[i]) + assertTrue(hands[i].isEmpty()) + } + } + + @Theory + fun deal_evenDistribution(nbPlayers: Int, nbCardsPerPlayer: Int) { + val nbCardsPerAge = nbPlayers * nbCardsPerPlayer + val decks = createDecks(1, nbCardsPerAge) + val hands = decks.deal(1, nbPlayers) + repeat(nbPlayers) { i -> + assertEquals(nbCardsPerPlayer, hands[i].size) + } + } + + companion object { + + @JvmStatic + @DataPoints + fun dataPoints(): IntArray = intArrayOf(1, 2, 3, 5, 10) + + private fun createDecks(nbAges: Int, nbCardsPerAge: Int): Decks = + Decks((1..nbAges).map { it to sampleCards(nbCardsPerAge) }.toMap()) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirectionTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirectionTest.kt new file mode 100644 index 00000000..4582c4a1 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirectionTest.kt @@ -0,0 +1,14 @@ +package org.luxons.sevenwonders.game.cards + +import org.junit.Test +import kotlin.test.assertEquals + +class HandRotationDirectionTest { + + @Test + fun testAgesDirections() { + assertEquals(HandRotationDirection.LEFT, HandRotationDirection.forAge(1)) + assertEquals(HandRotationDirection.RIGHT, HandRotationDirection.forAge(2)) + assertEquals(HandRotationDirection.LEFT, HandRotationDirection.forAge(3)) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/HandsTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/HandsTest.kt new file mode 100644 index 00000000..c7ff9106 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/HandsTest.kt @@ -0,0 +1,127 @@ +package org.luxons.sevenwonders.game.cards + +import org.junit.Assume.assumeTrue +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.FromDataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.test.sampleCards +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@RunWith(Theories::class) +class HandsTest { + + @Test + fun get_failsOnMissingPlayer() { + val hands = createHands(4, 7) + + assertFailsWith<IndexOutOfBoundsException> { hands[5] } + } + + @Test + fun get_retrievesCorrectCards() { + val hand0 = sampleCards(5, 0) + val hand1 = sampleCards(10, 5) + val hands = Hands(listOf(hand0, hand1)) + assertEquals(hand0, hands[0]) + assertEquals(hand1, hands[1]) + } + + @Theory + fun isEmpty_falseWhenAtLeast1_allSame( + @FromDataPoints("nbPlayers") nbPlayers: Int, + @FromDataPoints("nbCardsPerPlayer") nbCardsPerPlayer: Int + ) { + assumeTrue(nbCardsPerPlayer >= 1) + val hands = createHands(nbPlayers, nbCardsPerPlayer) + assertFalse(hands.isEmpty) + } + + @Theory + fun isEmpty_trueWhenAllEmpty(@FromDataPoints("nbPlayers") nbPlayers: Int) { + val hands = createHands(nbPlayers, 0) + assertTrue(hands.isEmpty) + } + + @Theory + fun maxOneCardRemains_falseWhenAtLeast2_allSame( + @FromDataPoints("nbPlayers") nbPlayers: Int, + @FromDataPoints("nbCardsPerPlayer") nbCardsPerPlayer: Int + ) { + assumeTrue(nbCardsPerPlayer >= 2) + val hands = createHands(nbPlayers, nbCardsPerPlayer) + assertFalse(hands.maxOneCardRemains()) + } + + @Theory + fun maxOneCardRemains_trueWhenAtMost1_allSame( + @FromDataPoints("nbPlayers") nbPlayers: Int, + @FromDataPoints("nbCardsPerPlayer") nbCardsPerPlayer: Int + ) { + assumeTrue(nbCardsPerPlayer <= 1) + val hands = createHands(nbPlayers, nbCardsPerPlayer) + assertTrue(hands.maxOneCardRemains()) + } + + @Theory + fun maxOneCardRemains_trueWhenAtMost1_someZero(@FromDataPoints("nbPlayers") nbPlayers: Int) { + val hands = createHands(nbPlayers, 1) + assertTrue(hands.maxOneCardRemains()) + } + + @Test + fun rotate_movesOfCorrectOffset_right() { + val hands = createHands(3, 7) + val rotated = hands.rotate(HandRotationDirection.RIGHT) + assertEquals(rotated[1], hands[0]) + assertEquals(rotated[2], hands[1]) + assertEquals(rotated[0], hands[2]) + } + + @Test + fun rotate_movesOfCorrectOffset_left() { + val hands = createHands(3, 7) + val rotated = hands.rotate(HandRotationDirection.LEFT) + assertEquals(rotated[2], hands[0]) + assertEquals(rotated[0], hands[1]) + assertEquals(rotated[1], hands[2]) + } + + @Test + fun createHand_containsAllCards() { + val hand0 = sampleCards(5, 0) + val hand1 = sampleCards(10, 5) + val hands = Hands(listOf(hand0, hand1)) + + val table = testTable(2) + val hand = hands.createHand(SimplePlayer(0, table)) + + assertEquals(hand0.map { it.name }, hand.map { it.name }) + } + + companion object { + + @JvmStatic + @DataPoints("nbCardsPerPlayer") + fun nbCardsPerPlayer(): IntArray { + return intArrayOf(0, 1, 2, 3, 4, 5, 6, 7) + } + + @JvmStatic + @DataPoints("nbPlayers") + fun nbPlayers(): IntArray { + return intArrayOf(3, 4, 5, 6, 7) + } + + private fun createHands(nbPlayers: Int, nbCardsPerPlayer: Int): Hands { + return sampleCards(nbCardsPerPlayer * nbPlayers, 0).deal(nbPlayers) + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt new file mode 100644 index 00000000..eccca3e7 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt @@ -0,0 +1,162 @@ +package org.luxons.sevenwonders.game.cards + +import org.junit.Assume.assumeTrue +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.boards.Table +import org.luxons.sevenwonders.game.resources.Provider +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.emptyResources +import org.luxons.sevenwonders.game.resources.noTransactions +import org.luxons.sevenwonders.game.test.createRequirements +import org.luxons.sevenwonders.game.test.createTransactions +import org.luxons.sevenwonders.game.test.singleBoardPlayer +import org.luxons.sevenwonders.game.test.testBoard +import kotlin.test.assertEquals +import kotlin.test.assertSame +import kotlin.test.assertTrue + +@RunWith(Theories::class) +class RequirementsTest { + + @Test + fun getResources_emptyAfterInit() { + val (_, resources) = Requirements() + assertTrue(resources.isEmpty()) + } + + @Test + fun setResources_success() { + val resources = emptyResources() + val requirements = Requirements(0, resources) + assertSame(resources, requirements.resources) + } + + @Theory + fun goldRequirement(boardGold: Int, requiredGold: Int) { + val requirements = Requirements(requiredGold) + + val board = testBoard(ResourceType.CLAY, boardGold) + val player = singleBoardPlayer(board) + + assertEquals(boardGold >= requiredGold, requirements.areMetWithHelpBy(board, noTransactions())) + + val satisfaction = requirements.assess(player) + if (boardGold >= requiredGold) { + if (requiredGold == 0) { + assertEquals(RequirementsSatisfaction.noRequirements(), satisfaction) + } else { + assertEquals(RequirementsSatisfaction.enoughGold(requiredGold), satisfaction) + } + } else { + assertEquals(RequirementsSatisfaction.missingRequiredGold(requiredGold), satisfaction) + } + } + + @Theory + fun resourceRequirement_initialResource(initialResource: ResourceType, requiredResource: ResourceType) { + val requirements = createRequirements(requiredResource) + + val board = testBoard(initialResource, 0) + val player = singleBoardPlayer(board) + + assertEquals(initialResource == requiredResource, requirements.areMetWithHelpBy(board, noTransactions())) + + if (initialResource == requiredResource) { + val satisfaction = requirements.assess(player) + assertEquals(RequirementsSatisfaction.enoughResources(), satisfaction) + } + } + + @Theory + fun resourceRequirement_ownProduction( + initialResource: ResourceType, + producedResource: ResourceType, + requiredResource: ResourceType + ) { + assumeTrue(initialResource != requiredResource) + + val requirements = createRequirements(requiredResource) + + val board = testBoard(initialResource, 0) + board.production.addFixedResource(producedResource, 1) + val player = singleBoardPlayer(board) + + assertEquals(producedResource == requiredResource, requirements.areMetWithHelpBy(board, noTransactions())) + + if (producedResource == requiredResource) { + val satisfaction = requirements.assess(player) + assertEquals(RequirementsSatisfaction.enoughResources(), satisfaction) + } + } + + @Theory + fun resourceRequirement_boughtResource( + initialResource: ResourceType, + boughtResource: ResourceType, + requiredResource: ResourceType + ) { + assumeTrue(initialResource != requiredResource) + + val requirements = createRequirements(requiredResource) + + val board = testBoard(initialResource, 2) + val neighbourBoard = testBoard(initialResource, 0) + neighbourBoard.publicProduction.addFixedResource(boughtResource, 1) + val table = Table(listOf(board, neighbourBoard)) + val player = SimplePlayer(0, table) + + val resources = createTransactions(Provider.RIGHT_PLAYER, boughtResource) + + val neighbourHasResource = boughtResource == requiredResource + assertEquals(neighbourHasResource, requirements.areMetWithHelpBy(board, resources)) + + val satisfaction = requirements.assess(player) + if (neighbourHasResource) { + val transactions = setOf( + createTransactions(Provider.LEFT_PLAYER, requiredResource), + createTransactions(Provider.RIGHT_PLAYER, requiredResource) + ) + assertEquals(RequirementsSatisfaction.metWithHelp(2, transactions), satisfaction) + } else { + assertEquals(RequirementsSatisfaction.unavailableResources(), satisfaction) + } + } + + @Theory + fun pay_boughtResource(initialResource: ResourceType, requiredResource: ResourceType) { + assumeTrue(initialResource != requiredResource) + + val requirements = createRequirements(requiredResource) + + val board = testBoard(initialResource, 2) + val neighbourBoard = testBoard(requiredResource, 0) + val table = Table(listOf(board, neighbourBoard)) + val player = SimplePlayer(0, table) + + val transactions = createTransactions(Provider.RIGHT_PLAYER, requiredResource) + + assertTrue(requirements.areMetWithHelpBy(board, transactions)) + assertTrue(requirements.assess(player).satisfied) + + requirements.pay(player, transactions) + + assertEquals(0, board.gold) + assertEquals(2, neighbourBoard.gold) + } + + companion object { + + @JvmStatic + @DataPoints + fun goldAmounts(): IntArray = intArrayOf(0, 1, 2, 5) + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionTest.kt new file mode 100644 index 00000000..4317a933 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionTest.kt @@ -0,0 +1,20 @@ +package org.luxons.sevenwonders.game.data + +import org.junit.Test +import org.luxons.sevenwonders.game.api.CustomizableSettings +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class GameDefinitionTest { + + @Test + fun successfulGameInit() { + val gameDefinition = GameDefinition.load() + assertNotNull(gameDefinition) + assertEquals(3, gameDefinition.minPlayers) + assertEquals(7, gameDefinition.maxPlayers) + + val game = gameDefinition.initGame(0, CustomizableSettings(), 7) + assertNotNull(game) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.kt new file mode 100644 index 00000000..0b561938 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.kt @@ -0,0 +1,96 @@ +package org.luxons.sevenwonders.game.data.definitions + +import org.junit.Before +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.api.WonderSidePickMethod +import java.util.Random +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class WonderSidePickMethodTest { + + private lateinit var random: Random + + private lateinit var random2: Random + + @Before + fun setUp() { + random = Random(123) // starts with TRUE + random2 = Random(123456) // starts with FALSE + } + + @Test + fun pick_allA() { + var side: WonderSide? = null + repeat(10) { + side = WonderSidePickMethod.ALL_A.pickSide(random, side) + assertEquals(WonderSide.A, side) + } + } + + @Test + fun pick_allB() { + var side: WonderSide? = null + repeat(10) { + side = WonderSidePickMethod.ALL_B.pickSide(random, side) + assertEquals(WonderSide.B, side) + } + } + + @Test + fun pick_eachRandom() { + var side = WonderSidePickMethod.EACH_RANDOM.pickSide(random, null) + assertEquals(WonderSide.A, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random, side) + assertEquals(WonderSide.B, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random, side) + assertEquals(WonderSide.A, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random, side) + assertEquals(WonderSide.B, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random, side) + assertEquals(WonderSide.B, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random, side) + assertEquals(WonderSide.A, side) + } + + @Test + fun pick_eachRandom2() { + var side = WonderSidePickMethod.EACH_RANDOM.pickSide(random2, null) + assertEquals(WonderSide.B, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random2, side) + assertEquals(WonderSide.A, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random2, side) + assertEquals(WonderSide.A, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random2, side) + assertEquals(WonderSide.B, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random2, side) + assertEquals(WonderSide.B, side) + side = WonderSidePickMethod.EACH_RANDOM.pickSide(random2, side) + assertEquals(WonderSide.B, side) + } + + @Theory + fun pick_allSameRandom_sameAsFirst(firstSide: WonderSide) { + var side = firstSide + repeat(10) { + side = WonderSidePickMethod.SAME_RANDOM_FOR_ALL.pickSide(random, side) + assertEquals(firstSide, side) + } + } + + @Test + fun pick_allSameRandom_firstIsRandom() { + assertEquals(WonderSide.A, WonderSidePickMethod.SAME_RANDOM_FOR_ALL.pickSide(random, null)) + assertEquals(WonderSide.B, WonderSidePickMethod.SAME_RANDOM_FOR_ALL.pickSide(random2, null)) + } + + companion object { + + @DataPoints + fun sides(): Array<WonderSide> = WonderSide.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializerTest.kt new file mode 100644 index 00000000..9b44fad2 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializerTest.kt @@ -0,0 +1,147 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import org.junit.Before +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.effects.GoldIncrease +import org.luxons.sevenwonders.game.effects.MilitaryReinforcements +import org.luxons.sevenwonders.game.effects.ProductionIncrease +import org.luxons.sevenwonders.game.effects.RawPointsIncrease +import org.luxons.sevenwonders.game.resources.Production +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +@RunWith(Theories::class) +class NumericEffectSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + gson = GsonBuilder().registerTypeAdapter(MilitaryReinforcements::class.java, NumericEffectSerializer()) + .registerTypeAdapter(RawPointsIncrease::class.java, NumericEffectSerializer()) + .registerTypeAdapter(GoldIncrease::class.java, NumericEffectSerializer()) + // ProductionIncrease is not a numeric effect, it is here for negative testing purpose + .registerTypeAdapter(ProductionIncrease::class.java, NumericEffectSerializer()).create() + } + + @Test + fun serialize_militaryReinforcements_null() { + assertEquals("null", gson.toJson(null, MilitaryReinforcements::class.java)) + } + + @Test + fun serialize_rawPointsIncrease_null() { + assertEquals("null", gson.toJson(null, RawPointsIncrease::class.java)) + } + + @Test + fun serialize_goldIncrease_null() { + assertEquals("null", gson.toJson(null, GoldIncrease::class.java)) + } + + @Test + fun serialize_failOnUnknownType() { + assertFailsWith<IllegalArgumentException> { + gson.toJson(ProductionIncrease(Production(), false)) + } + } + + @Theory + fun serialize_militaryReinforcements(count: Int) { + val reinforcements = MilitaryReinforcements(count) + assertEquals(count.toString(), gson.toJson(reinforcements)) + } + + @Theory + fun serialize_rawPointsIncrease(count: Int) { + val points = RawPointsIncrease(count) + assertEquals(count.toString(), gson.toJson(points)) + } + + @Theory + fun serialize_goldIncrease(count: Int) { + val goldIncrease = GoldIncrease(count) + assertEquals(count.toString(), gson.toJson(goldIncrease)) + } + + @Theory + fun deserialize_militaryReinforcements(count: Int) { + val reinforcements = MilitaryReinforcements(count) + assertEquals(reinforcements, gson.fromJson<MilitaryReinforcements>(count.toString())) + } + + @Theory + fun deserialize_rawPointsIncrease(count: Int) { + val points = RawPointsIncrease(count) + assertEquals(points, gson.fromJson<RawPointsIncrease>(count.toString())) + } + + @Theory + fun deserialize_goldIncrease(count: Int) { + val goldIncrease = GoldIncrease(count) + assertEquals(goldIncrease, gson.fromJson<GoldIncrease>(count.toString())) + } + + @Test + fun deserialize_militaryReinforcements_failOnEmptyString() { + assertFailsWith<NumberFormatException> { + gson.fromJson<MilitaryReinforcements>("\"\"") + } + } + + @Test + fun deserialize_rawPointsIncrease_failOnEmptyString() { + assertFailsWith<NumberFormatException> { + gson.fromJson<RawPointsIncrease>("\"\"") + } + } + + @Test + fun deserialize_goldIncrease_failOnEmptyString() { + assertFailsWith<NumberFormatException> { + gson.fromJson<GoldIncrease>("\"\"") + } + } + + @Test + fun deserialize_militaryReinforcements_failOnNonNumericString() { + assertFailsWith<NumberFormatException> { + gson.fromJson<MilitaryReinforcements>("\"abc\"") + } + } + + @Test + fun deserialize_rawPointsIncrease_failOnNonNumericString() { + assertFailsWith<NumberFormatException> { + gson.fromJson<RawPointsIncrease>("\"abc\"") + } + } + + @Test + fun deserialize_goldIncrease_failOnNonNumericString() { + assertFailsWith<NumberFormatException> { + gson.fromJson<GoldIncrease>("\"abc\"") + } + } + + @Test + fun deserialize_failOnUnknownType() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<ProductionIncrease>("\"2\"") + } + } + + companion object { + + @JvmStatic + @DataPoints + fun dataPoints(): IntArray = intArrayOf(-2, -1, 0, 1, 2, 5) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializerTest.kt new file mode 100644 index 00000000..31d695e8 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializerTest.kt @@ -0,0 +1,192 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.effects.ProductionIncrease +import org.luxons.sevenwonders.game.resources.MutableResources +import org.luxons.sevenwonders.game.resources.Production +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.Resources +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull + +class ProductionIncreaseSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + val resourceTypeList = object : TypeToken<List<ResourceType>>() {}.type + gson = GsonBuilder().registerTypeAdapter(Resources::class.java, ResourcesSerializer()) + .registerTypeAdapter(MutableResources::class.java, ResourcesSerializer()) + .registerTypeAdapter(ResourceType::class.java, ResourceTypeSerializer()) + .registerTypeAdapter(resourceTypeList, ResourceTypesSerializer()) + .registerTypeAdapter(Production::class.java, ProductionSerializer()) + .registerTypeAdapter(ProductionIncrease::class.java, ProductionIncreaseSerializer()) + .create() + } + + private fun create(sellable: Boolean, wood: Int, stone: Int, clay: Int): ProductionIncrease { + val production = Production() + if (wood > 0) { + production.addFixedResource(ResourceType.WOOD, wood) + } + if (stone > 0) { + production.addFixedResource(ResourceType.STONE, stone) + } + if (clay > 0) { + production.addFixedResource(ResourceType.CLAY, clay) + } + return ProductionIncrease(production, sellable) + } + + private fun createChoice(sellable: Boolean, vararg types: ResourceType): ProductionIncrease { + val production = Production() + production.addChoice(*types) + return ProductionIncrease(production, sellable) + } + + @Test + fun serialize_nullAsNull() { + assertEquals("null", gson.toJson(null, ProductionIncrease::class.java)) + } + + @Test + fun serialize_emptyProdIncreaseAsNull() { + val prodIncrease = ProductionIncrease(Production(), false) + assertEquals("null", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_singleType() { + val prodIncrease = create(true, 1, 0, 0) + assertEquals("\"W\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_mixedTypes() { + val prodIncrease = create(true, 1, 1, 1) + assertEquals("\"WSC\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_mixedTypes_notSellable() { + val prodIncrease = create(false, 1, 1, 1) + assertEquals("\"(WSC)\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice2() { + val prodIncrease = createChoice(true, ResourceType.WOOD, ResourceType.CLAY) + assertEquals("\"W/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice3() { + val prodIncrease = createChoice(true, ResourceType.WOOD, ResourceType.ORE, ResourceType.CLAY) + assertEquals("\"W/O/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice3_notSellable() { + val prodIncrease = createChoice(false, ResourceType.WOOD, ResourceType.ORE, ResourceType.CLAY) + assertEquals("\"(W/O/C)\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice2_unordered() { + val prodIncrease = createChoice(true, ResourceType.CLAY, ResourceType.WOOD) + assertEquals("\"W/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice3_unordered() { + val prodIncrease = createChoice(true, ResourceType.WOOD, ResourceType.CLAY, ResourceType.ORE) + assertEquals("\"W/O/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_failIfMultipleChoices() { + val prodIncrease = createChoice(true, ResourceType.WOOD, ResourceType.CLAY) + prodIncrease.production.addChoice(ResourceType.ORE, ResourceType.GLASS) + assertFailsWith<IllegalArgumentException> { + gson.toJson(prodIncrease) + } + } + + @Test + fun serialize_failIfMixedFixedAndChoices() { + val prodIncrease = create(true, 1, 0, 0) + prodIncrease.production.addChoice(ResourceType.WOOD, ResourceType.CLAY) + assertFailsWith<IllegalArgumentException> { + gson.toJson(prodIncrease) + } + } + + @Test + fun deserialize_nullFromNull() { + assertNull(gson.fromJson("null", ProductionIncrease::class.java)) + } + + @Test + fun deserialize_emptyList() { + val prodIncrease = ProductionIncrease(Production(), true) + assertEquals(prodIncrease, gson.fromJson("\"\"")) + } + + @Test + fun deserialize_failOnGarbageString() { + assertFailsWith(IllegalArgumentException::class) { + gson.fromJson<ProductionIncrease>("\"this is garbage\"") + } + } + + @Test + fun deserialize_failOnGarbageStringWithSlashes() { + assertFailsWith(IllegalArgumentException::class) { + gson.fromJson<ProductionIncrease>("\"this/is/garbage\"") + } + } + + @Test + fun deserialize_singleType() { + val prodIncrease = create(true, 1, 0, 0) + assertEquals(prodIncrease, gson.fromJson("\"W\"")) + } + + @Test + fun deserialize_multipleTimesSameType_notSellable() { + val prodIncrease = create(false, 3, 0, 0) + assertEquals(prodIncrease, gson.fromJson("\"(WWW)\"")) + } + + @Test + fun deserialize_mixedTypes() { + val prodIncrease = create(true, 1, 1, 1) + assertEquals(prodIncrease, gson.fromJson("\"WCS\"")) + } + + @Test + fun deserialize_choice2() { + val prodIncrease = createChoice(true, ResourceType.WOOD, ResourceType.CLAY) + assertEquals(prodIncrease, gson.fromJson("\"W/C\"")) + } + + @Test + fun deserialize_choice3_notSellable() { + val prodIncrease = createChoice(false, ResourceType.WOOD, ResourceType.ORE, ResourceType.CLAY) + assertEquals(prodIncrease, gson.fromJson("\"(W/O/C)\"")) + } + + @Test + fun deserialize_failOnMultipleResourcesInChoice() { + assertFailsWith(IllegalArgumentException::class) { + gson.fromJson<ProductionIncrease>("\"W/SS/C\"") + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializerTest.kt new file mode 100644 index 00000000..265087ba --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializerTest.kt @@ -0,0 +1,207 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.resources.MutableResources +import org.luxons.sevenwonders.game.resources.Production +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.Resources +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull + +class ProductionSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + val resourceTypeList = object : TypeToken<List<ResourceType>>() {}.type + gson = GsonBuilder().registerTypeAdapter(Resources::class.java, ResourcesSerializer()) + .registerTypeAdapter(MutableResources::class.java, ResourcesSerializer()) + .registerTypeAdapter(ResourceType::class.java, ResourceTypeSerializer()) + .registerTypeAdapter(resourceTypeList, ResourceTypesSerializer()) + .registerTypeAdapter(Production::class.java, ProductionSerializer()).create() + } + + private fun create(wood: Int, stone: Int, clay: Int): Production { + val production = Production() + if (wood > 0) { + production.addFixedResource(ResourceType.WOOD, wood) + } + if (stone > 0) { + production.addFixedResource(ResourceType.STONE, stone) + } + if (clay > 0) { + production.addFixedResource(ResourceType.CLAY, clay) + } + return production + } + + private fun createChoice(vararg types: ResourceType): Production { + val production = Production() + production.addChoice(*types) + return production + } + + @Test + fun serialize_nullAsNull() { + assertEquals("null", gson.toJson(null, Production::class.java)) + } + + @Test + fun serialize_emptyProdIncreaseAsNull() { + val prodIncrease = Production() + assertEquals("null", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_singleType() { + val prodIncrease = create(1, 0, 0) + assertEquals("\"W\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_multipleTimesSameType() { + val prodIncrease = create(3, 0, 0) + assertEquals("\"WWW\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_mixedTypes() { + val prodIncrease = create(1, 1, 1) + assertEquals("\"WSC\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_mixedTypesMultiple() { + val prodIncrease = create(2, 1, 2) + assertEquals("\"WWSCC\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice2() { + val prodIncrease = createChoice(ResourceType.WOOD, ResourceType.CLAY) + assertEquals("\"W/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice3() { + val prodIncrease = createChoice(ResourceType.WOOD, ResourceType.ORE, ResourceType.CLAY) + assertEquals("\"W/O/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice2_unordered() { + val prodIncrease = createChoice(ResourceType.CLAY, ResourceType.WOOD) + assertEquals("\"W/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_choice3_unordered() { + val prodIncrease = createChoice(ResourceType.WOOD, ResourceType.CLAY, ResourceType.ORE) + assertEquals("\"W/O/C\"", gson.toJson(prodIncrease)) + } + + @Test + fun serialize_failIfMultipleChoices() { + val production = createChoice(ResourceType.WOOD, ResourceType.CLAY) + production.addChoice(ResourceType.ORE, ResourceType.GLASS) + assertFailsWith<IllegalArgumentException> { + gson.toJson(production) + } + } + + @Test + fun serialize_failIfMixedFixedAndChoices() { + val production = create(1, 0, 0) + production.addChoice(ResourceType.WOOD, ResourceType.CLAY) + assertFailsWith<IllegalArgumentException> { + gson.toJson(production) + } + } + + @Test + fun deserialize_nullFromNull() { + assertNull(gson.fromJson("null", Production::class.java)) + } + + @Test + fun deserialize_emptyList() { + val prodIncrease = Production() + assertEquals(prodIncrease, gson.fromJson("\"\"")) + } + + @Test + fun deserialize_failOnGarbageString() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<Production>("\"this is garbage\"") + } + } + + @Test + fun deserialize_failOnGarbageStringWithSlashes() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<Production>("\"this/is/garbage\"") + } + } + + @Test + fun deserialize_singleType() { + val prodIncrease = create(1, 0, 0) + assertEquals(prodIncrease, gson.fromJson("\"W\"")) + } + + @Test + fun deserialize_multipleTimesSameType() { + val prodIncrease = create(3, 0, 0) + assertEquals(prodIncrease, gson.fromJson("\"WWW\"")) + } + + @Test + fun deserialize_mixedTypes() { + val prodIncrease = create(1, 1, 1) + assertEquals(prodIncrease, gson.fromJson("\"WCS\"")) + } + + @Test + fun deserialize_mixedTypes_unordered() { + val prodIncrease = create(1, 3, 2) + assertEquals(prodIncrease, gson.fromJson("\"SCWCSS\"")) + } + + @Test + fun deserialize_choice2() { + val prodIncrease = createChoice(ResourceType.WOOD, ResourceType.CLAY) + assertEquals(prodIncrease, gson.fromJson("\"W/C\"")) + } + + @Test + fun deserialize_choice3() { + val prodIncrease = createChoice(ResourceType.WOOD, ResourceType.ORE, ResourceType.CLAY) + assertEquals(prodIncrease, gson.fromJson("\"W/O/C\"")) + } + + @Test + fun deserialize_choice2_unordered() { + val prodIncrease = createChoice(ResourceType.CLAY, ResourceType.WOOD) + assertEquals(prodIncrease, gson.fromJson("\"W/C\"")) + } + + @Test + fun deserialize_choice3_unordered() { + val prodIncrease = createChoice(ResourceType.WOOD, ResourceType.CLAY, ResourceType.ORE) + assertEquals(prodIncrease, gson.fromJson("\"W/O/C\"")) + } + + @Test + fun deserialize_failOnMultipleResourcesInChoice() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<Production>("\"W/SS/C\"") + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializerTest.kt new file mode 100644 index 00000000..f2b07e84 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializerTest.kt @@ -0,0 +1,56 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.resources.ResourceType +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull + +class ResourceTypeSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + gson = GsonBuilder().registerTypeAdapter(ResourceType::class.java, ResourceTypeSerializer()).create() + } + + @Test + fun serialize_useSymbolForEachType() { + ResourceType.values().forEach { type -> + val expectedJson = "\"" + type.symbol + "\"" + assertEquals(expectedJson, gson.toJson(type)) + } + } + + @Test + fun deserialize_useSymbolForEachType() { + ResourceType.values().forEach { type -> + val typeInJson = "\"" + type.symbol + "\"" + assertEquals(type, gson.fromJson(typeInJson)) + } + } + + @Test + fun deserialize_nullFromNull() { + assertNull(gson.fromJson("null", ResourceType::class.java)) + } + + @Test + fun deserialize_failsOnEmptyString() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<ResourceType>("\"\"") + } + } + + @Test + fun deserialize_failsOnGarbageString() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<ResourceType>("\"thisisgarbage\"") + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializerTest.kt new file mode 100644 index 00000000..8c1b421d --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializerTest.kt @@ -0,0 +1,80 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.github.salomonbrys.kotson.typeToken +import com.github.salomonbrys.kotson.typedToJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.resources.ResourceType +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class ResourceTypesSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + gson = GsonBuilder().registerTypeAdapter(typeToken<List<ResourceType>>(), ResourceTypesSerializer()).create() + } + + @Test + fun serialize_null() { + assertEquals("null", gson.toJson(null, typeToken<List<ResourceType>>())) + } + + @Test + fun serialize_emptyList() { + val types = emptyList<ResourceType>() + assertEquals("\"\"", gson.typedToJson(types)) + } + + @Test + fun serialize_singleType() { + val types = listOf(ResourceType.WOOD) + assertEquals("\"W\"", gson.typedToJson(types)) + } + + @Test + fun serialize_multipleTimesSameType() { + val types = List(3) { ResourceType.WOOD } + assertEquals("\"WWW\"", gson.typedToJson(types)) + } + + @Test + fun serialize_mixedTypes() { + val types = listOf(ResourceType.WOOD, ResourceType.CLAY, ResourceType.STONE) + assertEquals("\"WCS\"", gson.typedToJson(types)) + } + + @Test + fun deserialize_null() { + assertNull(gson.fromJson("null", typeToken<List<ResourceType>>())) + } + + @Test + fun deserialize_emptyList() { + val types = emptyList<ResourceType>() + assertEquals(types, gson.fromJson("\"\"")) + } + + @Test + fun deserialize_singleType() { + val types = listOf(ResourceType.WOOD) + assertEquals(types, gson.fromJson("\"W\"")) + } + + @Test + fun deserialize_multipleTimesSameType() { + val types = List(3) { ResourceType.WOOD } + assertEquals(types, gson.fromJson("\"WWW\"")) + } + + @Test + fun deserialize_mixedTypes() { + val types = listOf(ResourceType.WOOD, ResourceType.CLAY, ResourceType.STONE) + assertEquals(types, gson.fromJson("\"WCS\"")) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializerTest.kt new file mode 100644 index 00000000..c146a948 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializerTest.kt @@ -0,0 +1,99 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.resources.MutableResources +import org.luxons.sevenwonders.game.resources.ResourceType.CLAY +import org.luxons.sevenwonders.game.resources.ResourceType.STONE +import org.luxons.sevenwonders.game.resources.ResourceType.WOOD +import org.luxons.sevenwonders.game.resources.Resources +import org.luxons.sevenwonders.game.resources.emptyResources +import org.luxons.sevenwonders.game.resources.resourcesOf +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class ResourcesSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + gson = GsonBuilder() + .registerTypeAdapter(Resources::class.java, ResourcesSerializer()) + .registerTypeAdapter(MutableResources::class.java, ResourcesSerializer()) + .create() + } + + @Test + fun serialize_null() { + assertEquals("null", gson.toJson(null, Resources::class.java)) + } + + @Test + fun serialize_emptyResourcesToNull() { + val resources = emptyResources() + assertEquals("null", gson.toJson(resources)) + } + + @Test + fun serialize_singleType() { + val resources = resourcesOf(WOOD) + assertEquals("\"W\"", gson.toJson(resources)) + } + + @Test + fun serialize_multipleTimesSameType() { + val resources = resourcesOf(WOOD to 3) + assertEquals("\"WWW\"", gson.toJson(resources)) + } + + @Test + fun serialize_mixedTypes() { + val resources = resourcesOf(WOOD, STONE, CLAY) + assertEquals("\"WSC\"", gson.toJson(resources)) + } + + @Test + fun serialize_mixedTypes_unordered() { + val resources = resourcesOf(CLAY to 1, WOOD to 2, CLAY to 1, STONE to 1) + assertEquals("\"CCWWS\"", gson.toJson(resources)) + } + + @Test + fun deserialize_null() { + assertNull(gson.fromJson("null", Resources::class.java)) + } + + @Test + fun deserialize_emptyList() { + val resources = emptyResources() + assertEquals(resources, gson.fromJson("\"\"")) + } + + @Test + fun deserialize_singleType() { + val resources = resourcesOf(WOOD) + assertEquals(resources, gson.fromJson("\"W\"")) + } + + @Test + fun deserialize_multipleTimesSameType() { + val resources = resourcesOf(WOOD to 3) + assertEquals(resources, gson.fromJson("\"WWW\"")) + } + + @Test + fun deserialize_mixedTypes() { + val resources = resourcesOf(WOOD, CLAY, STONE) + assertEquals(resources, gson.fromJson("\"WCS\"")) + } + + @Test + fun deserialize_mixedTypes_unordered() { + val resources = resourcesOf(WOOD to 1, CLAY to 2, STONE to 3) + assertEquals(resources, gson.fromJson("\"SCWCSS\"")) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializerTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializerTest.kt new file mode 100644 index 00000000..95d72517 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializerTest.kt @@ -0,0 +1,157 @@ +package org.luxons.sevenwonders.game.data.serializers + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.boards.ScienceType +import org.luxons.sevenwonders.game.effects.ScienceProgress +import org.luxons.sevenwonders.game.test.createScienceProgress +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNotNull + +private const val TABLET_STR = "\"TABLET\"" +private const val COMPASS_STR = "\"COMPASS\"" +private const val WHEEL_STR = "\"WHEEL\"" +private const val JOKER_STR = "\"any\"" + +class ScienceProgressSerializerTest { + + private lateinit var gson: Gson + + @Before + fun setUp() { + gson = GsonBuilder().registerTypeAdapter(ScienceProgress::class.java, ScienceProgressSerializer()).create() + } + + @Test + fun serialize_emptyToNull() { + val progress = createScienceProgress(0, 0, 0, 0) + val json = gson.toJson(progress) + assertEquals("null", json) + } + + @Test + fun serialize_oneCompass() { + val progress = createScienceProgress(1, 0, 0, 0) + val json = gson.toJson(progress) + assertEquals(COMPASS_STR, json) + } + + @Test + fun serialize_oneWheel() { + val progress = createScienceProgress(0, 1, 0, 0) + val json = gson.toJson(progress) + assertEquals(WHEEL_STR, json) + } + + @Test + fun serialize_oneTablet() { + val progress = createScienceProgress(0, 0, 1, 0) + val json = gson.toJson(progress) + assertEquals(TABLET_STR, json) + } + + @Test + fun serialize_oneJoker() { + val progress = createScienceProgress(0, 0, 0, 1) + val json = gson.toJson(progress) + assertEquals(JOKER_STR, json) + } + + @Test + fun serialize_failOnMultipleCompasses() { + assertFailsWith<UnsupportedOperationException> { + val progress = createScienceProgress(2, 0, 0, 0) + gson.toJson(progress) + } + } + + @Test + fun serialize_failOnMultipleWheels() { + assertFailsWith<UnsupportedOperationException> { + val progress = createScienceProgress(0, 2, 0, 0) + gson.toJson(progress) + } + } + + @Test + fun serialize_failOnMultipleTablets() { + assertFailsWith<UnsupportedOperationException> { + val progress = createScienceProgress(0, 0, 2, 0) + gson.toJson(progress) + } + } + + @Test + fun serialize_failOnMultipleJokers() { + assertFailsWith<UnsupportedOperationException> { + val progress = createScienceProgress(0, 0, 0, 2) + gson.toJson(progress) + } + } + + @Test + fun serialize_failOnMixedElements() { + assertFailsWith<UnsupportedOperationException> { + val progress = createScienceProgress(1, 1, 0, 0) + gson.toJson(progress) + } + } + + @Test + fun deserialize_failOnEmptyString() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<ScienceProgress>("\"\"") + } + } + + @Test + fun deserialize_failOnGarbageString() { + assertFailsWith<IllegalArgumentException> { + gson.fromJson<ScienceProgress>("thisisgarbage") + } + } + + @Test + fun deserialize_compass() { + val progress = gson.fromJson<ScienceProgress>(COMPASS_STR) + assertNotNull(progress.science) + assertEquals(1, progress.science.getQuantity(ScienceType.COMPASS)) + assertEquals(0, progress.science.getQuantity(ScienceType.WHEEL)) + assertEquals(0, progress.science.getQuantity(ScienceType.TABLET)) + assertEquals(0, progress.science.jokers) + } + + @Test + fun deserialize_wheel() { + val progress = gson.fromJson<ScienceProgress>(WHEEL_STR) + assertNotNull(progress.science) + assertEquals(0, progress.science.getQuantity(ScienceType.COMPASS)) + assertEquals(1, progress.science.getQuantity(ScienceType.WHEEL)) + assertEquals(0, progress.science.getQuantity(ScienceType.TABLET)) + assertEquals(0, progress.science.jokers) + } + + @Test + fun deserialize_tablet() { + val progress = gson.fromJson<ScienceProgress>(TABLET_STR) + assertNotNull(progress.science) + assertEquals(0, progress.science.getQuantity(ScienceType.COMPASS)) + assertEquals(0, progress.science.getQuantity(ScienceType.WHEEL)) + assertEquals(1, progress.science.getQuantity(ScienceType.TABLET)) + assertEquals(0, progress.science.jokers) + } + + @Test + fun deserialize_joker() { + val progress = gson.fromJson<ScienceProgress>(JOKER_STR) + assertNotNull(progress.science) + assertEquals(0, progress.science.getQuantity(ScienceType.COMPASS)) + assertEquals(0, progress.science.getQuantity(ScienceType.WHEEL)) + assertEquals(0, progress.science.getQuantity(ScienceType.TABLET)) + assertEquals(1, progress.science.jokers) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/BonusPerBoardElementTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/BonusPerBoardElementTest.kt new file mode 100644 index 00000000..700eddb1 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/BonusPerBoardElementTest.kt @@ -0,0 +1,142 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.Before +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.Player +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.boards.RelativeBoardPosition +import org.luxons.sevenwonders.game.boards.Table +import org.luxons.sevenwonders.game.cards.CardBack +import org.luxons.sevenwonders.game.cards.Color +import org.luxons.sevenwonders.game.test.addCards +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class BonusPerBoardElementTest { + + private lateinit var table: Table + private lateinit var player0: Player + + @Before + fun setUp() { + table = testTable(4) + player0 = SimplePlayer(0, table) + } + + @Theory + fun computePoints_countsCards( + boardPosition: RelativeBoardPosition, + nbCards: Int, + nbOtherCards: Int, + points: Int, + gold: Int, + color: Color + ) { + val board = table.getBoard(0, boardPosition) + addCards(board, nbCards, nbOtherCards, color) + + val bonus = BonusPerBoardElement(listOf(boardPosition), BoardElementType.CARD, gold, points, listOf(color)) + + assertEquals(nbCards * points, bonus.computePoints(player0)) + } + + @Theory + fun computePoints_countsDefeatTokens( + boardPosition: RelativeBoardPosition, + nbDefeatTokens: Int, + points: Int, + gold: Int + ) { + val board = table.getBoard(0, boardPosition) + repeat(nbDefeatTokens) { + board.military.defeat() + } + + val bonus = BonusPerBoardElement(listOf(boardPosition), BoardElementType.DEFEAT_TOKEN, gold, points, listOf()) + + assertEquals(nbDefeatTokens * points, bonus.computePoints(player0)) + } + + @Theory + fun computePoints_countsWonderStages(boardPosition: RelativeBoardPosition, nbStages: Int, points: Int, gold: Int) { + val board = table.getBoard(0, boardPosition) + repeat(nbStages) { + board.wonder.placeCard(CardBack("")) + } + + val bonus = + BonusPerBoardElement(listOf(boardPosition), BoardElementType.BUILT_WONDER_STAGES, gold, points, listOf()) + + assertEquals(nbStages * points, bonus.computePoints(player0)) + } + + @Theory + fun apply_countsCards( + boardPosition: RelativeBoardPosition, + nbCards: Int, + nbOtherCards: Int, + points: Int, + gold: Int, + color: Color + ) { + val board = table.getBoard(0, boardPosition) + addCards(board, nbCards, nbOtherCards, color) + + val bonus = BonusPerBoardElement(listOf(boardPosition), BoardElementType.CARD, gold, points, listOf(color)) + + val selfBoard = table.getBoard(0) + val initialGold = selfBoard.gold + bonus.applyTo(player0) + assertEquals(initialGold + nbCards * gold, selfBoard.gold) + } + + @Theory + fun apply_countsDefeatTokens(boardPosition: RelativeBoardPosition, nbDefeatTokens: Int, points: Int, gold: Int) { + val board = table.getBoard(0, boardPosition) + repeat(nbDefeatTokens) { + board.military.defeat() + } + + val bonus = BonusPerBoardElement(listOf(boardPosition), BoardElementType.DEFEAT_TOKEN, gold, points, listOf()) + + val selfBoard = table.getBoard(0) + val initialGold = selfBoard.gold + bonus.applyTo(player0) + assertEquals(initialGold + nbDefeatTokens * gold, selfBoard.gold) + } + + @Theory + fun apply_countsWonderStages(boardPosition: RelativeBoardPosition, nbStages: Int, points: Int, gold: Int) { + val board = table.getBoard(0, boardPosition) + repeat(nbStages) { + board.wonder.placeCard(CardBack("")) + } + + val bonus = + BonusPerBoardElement(listOf(boardPosition), BoardElementType.BUILT_WONDER_STAGES, gold, points, listOf()) + + val selfBoard = table.getBoard(0) + val initialGold = selfBoard.gold + bonus.applyTo(player0) + assertEquals(initialGold + nbStages * gold, selfBoard.gold) + } + + companion object { + + @JvmStatic + @DataPoints + fun values(): IntArray = intArrayOf(0, 1, 2, 3) + + @JvmStatic + @DataPoints + fun colors(): Array<Color> = Color.values() + + @JvmStatic + @DataPoints + fun positions(): Array<RelativeBoardPosition> = RelativeBoardPosition.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/DiscountTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/DiscountTest.kt new file mode 100644 index 00000000..d92c8d24 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/DiscountTest.kt @@ -0,0 +1,69 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.Assume +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.resources.Provider +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.test.createTransactions +import org.luxons.sevenwonders.game.test.testBoard +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class DiscountTest { + + @Theory + fun apply_givesDiscountedPrice(discountedPrice: Int, discountedType: ResourceType, provider: Provider) { + val board = testBoard(ResourceType.CLAY, 3) + val discount = Discount(listOf(discountedType), listOf(provider), discountedPrice) + discount.applyTo(board) + + val transactions = createTransactions(provider, discountedType) + assertEquals(discountedPrice, board.tradingRules.computeCost(transactions)) + } + + @Theory + fun apply_doesNotAffectOtherResources( + discountedPrice: Int, + discountedType: ResourceType, + provider: Provider, + otherType: ResourceType, + otherProvider: Provider + ) { + Assume.assumeTrue(otherProvider != provider) + Assume.assumeTrue(otherType != discountedType) + + val board = testBoard(ResourceType.CLAY, 3) + val discount = Discount(listOf(discountedType), listOf(provider), discountedPrice) + discount.applyTo(board) + + // this is the default in the settings used by TestUtilsKt.testBoard() + val normalPrice = 2 + + val fromOtherType = createTransactions(provider, otherType) + assertEquals(normalPrice, board.tradingRules.computeCost(fromOtherType)) + + val fromOtherProvider = createTransactions(otherProvider, discountedType) + assertEquals(normalPrice, board.tradingRules.computeCost(fromOtherProvider)) + + val fromOtherProviderAndType = createTransactions(otherProvider, otherType) + assertEquals(normalPrice, board.tradingRules.computeCost(fromOtherProviderAndType)) + } + + companion object { + + @JvmStatic + @DataPoints + fun discountedPrices(): IntArray = intArrayOf(0, 1, 2) + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + + @JvmStatic + @DataPoints + fun providers(): Array<Provider> = Provider.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/GoldIncreaseTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/GoldIncreaseTest.kt new file mode 100644 index 00000000..993cc273 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/GoldIncreaseTest.kt @@ -0,0 +1,43 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.test.testBoard +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class GoldIncreaseTest { + + @Theory + fun apply_increaseGoldWithRightAmount(initialAmount: Int, goldIncreaseAmount: Int, type: ResourceType) { + val board = testBoard(type, initialAmount) + val goldIncrease = GoldIncrease(goldIncreaseAmount) + + goldIncrease.applyTo(board) + + assertEquals(initialAmount + goldIncreaseAmount, board.gold) + } + + @Theory + fun computePoints_isAlwaysZero(gold: Int) { + val goldIncrease = GoldIncrease(gold) + val player = SimplePlayer(0, testTable(5)) + assertEquals(0, goldIncrease.computePoints(player)) + } + + companion object { + + @JvmStatic + @DataPoints + fun goldAmounts(): IntArray = intArrayOf(-5, -1, 0, 1, 2, 5, 10) + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/MilitaryReinforcementsTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/MilitaryReinforcementsTest.kt new file mode 100644 index 00000000..0d5765da --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/MilitaryReinforcementsTest.kt @@ -0,0 +1,44 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.test.testBoard +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class MilitaryReinforcementsTest { + + @Theory + fun apply_increaseGoldWithRightAmount(initialShields: Int, additionalShields: Int, type: ResourceType) { + val board = testBoard(type) + board.military.addShields(initialShields) + + val reinforcements = MilitaryReinforcements(additionalShields) + reinforcements.applyTo(board) + + assertEquals(initialShields + additionalShields, board.military.nbShields) + } + + @Theory + fun computePoints_isAlwaysZero(shields: Int) { + val reinforcements = MilitaryReinforcements(shields) + val player = SimplePlayer(0, testTable(5)) + assertEquals(0, reinforcements.computePoints(player)) + } + + companion object { + + @JvmStatic + @DataPoints + fun shieldCounts(): IntArray = intArrayOf(0, 1, 2, 3, 5) + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/ProductionIncreaseTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/ProductionIncreaseTest.kt new file mode 100644 index 00000000..c016ccc9 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/ProductionIncreaseTest.kt @@ -0,0 +1,73 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.resourcesOf +import org.luxons.sevenwonders.game.test.fixedProduction +import org.luxons.sevenwonders.game.test.testBoard +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@RunWith(Theories::class) +class ProductionIncreaseTest { + + @Theory + fun apply_boardContainsAddedResourceType( + initialType: ResourceType, + addedType: ResourceType, + extraType: ResourceType + ) { + val board = testBoard(initialType) + val effect = ProductionIncrease(fixedProduction(addedType), false) + + effect.applyTo(board) + + val resources = resourcesOf(initialType, addedType) + assertTrue(board.production.contains(resources)) + assertFalse(board.publicProduction.contains(resources)) + + val moreResources = resourcesOf(initialType, addedType, extraType) + assertFalse(board.production.contains(moreResources)) + assertFalse(board.publicProduction.contains(moreResources)) + } + + @Theory + fun apply_boardContainsAddedResourceType_sellable( + initialType: ResourceType, + addedType: ResourceType, + extraType: ResourceType + ) { + val board = testBoard(initialType) + val effect = ProductionIncrease(fixedProduction(addedType), true) + + effect.applyTo(board) + + val resources = resourcesOf(initialType, addedType) + assertTrue(board.production.contains(resources)) + assertTrue(board.publicProduction.contains(resources)) + + val moreResources = resourcesOf(initialType, addedType, extraType) + assertFalse(board.production.contains(moreResources)) + assertFalse(board.publicProduction.contains(moreResources)) + } + + @Theory + fun computePoints_isAlwaysZero(addedType: ResourceType) { + val effect = ProductionIncrease(fixedProduction(addedType), false) + val player = SimplePlayer(0, testTable(5)) + assertEquals(0, effect.computePoints(player)) + } + + companion object { + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/RawPointsIncreaseTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/RawPointsIncreaseTest.kt new file mode 100644 index 00000000..9cb10562 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/RawPointsIncreaseTest.kt @@ -0,0 +1,29 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class RawPointsIncreaseTest { + + @Theory + fun computePoints_equalsNbOfPoints(points: Int) { + val rawPointsIncrease = RawPointsIncrease(points) + val player = SimplePlayer(0, testTable(5)) + assertEquals(points, rawPointsIncrease.computePoints(player)) + } + + companion object { + + @JvmStatic + @DataPoints + fun points(): IntArray { + return intArrayOf(0, 1, 2, 3, 5) + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/ScienceProgressTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/ScienceProgressTest.kt new file mode 100644 index 00000000..7e566a8c --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/ScienceProgressTest.kt @@ -0,0 +1,49 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.boards.ScienceType +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.test.createScience +import org.luxons.sevenwonders.game.test.createScienceProgress +import org.luxons.sevenwonders.game.test.testBoard +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class ScienceProgressTest { + + @Theory + fun apply_initContainsAddedScience( + initCompasses: Int, + initWheels: Int, + initTablets: Int, + initJokers: Int, + compasses: Int, + wheels: Int, + tablets: Int, + jokers: Int + ) { + val board = testBoard(ResourceType.ORE) + val initialScience = createScience(initCompasses, initWheels, initTablets, initJokers) + board.science.addAll(initialScience) + + val effect = createScienceProgress(compasses, wheels, tablets, jokers) + effect.applyTo(board) + + assertEquals(initCompasses + compasses, board.science.getQuantity(ScienceType.COMPASS)) + assertEquals(initWheels + wheels, board.science.getQuantity(ScienceType.WHEEL)) + assertEquals(initTablets + tablets, board.science.getQuantity(ScienceType.TABLET)) + assertEquals(initJokers + jokers, board.science.jokers) + } + + companion object { + + @JvmStatic + @DataPoints + fun elementsCount(): IntArray { + return intArrayOf(0, 1, 2) + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/SpecialAbilityActivationTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/SpecialAbilityActivationTest.kt new file mode 100644 index 00000000..aae3be8e --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/effects/SpecialAbilityActivationTest.kt @@ -0,0 +1,93 @@ +package org.luxons.sevenwonders.game.effects + +import org.junit.Assume +import org.junit.Test +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.boards.RelativeBoardPosition +import org.luxons.sevenwonders.game.cards.Card +import org.luxons.sevenwonders.game.cards.Color +import org.luxons.sevenwonders.game.test.createGuildCard +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +@RunWith(Theories::class) +class SpecialAbilityActivationTest { + + @Theory + fun apply_addsAbility(ability: SpecialAbility) { + val effect = SpecialAbilityActivation(ability) + val player = SimplePlayer(0, testTable(5)) + + effect.applyTo(player) + + assertTrue(player.board.hasSpecial(ability)) + } + + @Theory + fun computePoints_zeroExceptForCopyGuild(ability: SpecialAbility) { + Assume.assumeTrue(ability !== SpecialAbility.COPY_GUILD) + + val effect = SpecialAbilityActivation(ability) + val player = SimplePlayer(0, testTable(5)) + + assertEquals(0, effect.computePoints(player)) + } + + @Theory + internal fun computePoints_copiedGuild(guildCard: Card, neighbour: RelativeBoardPosition) { + val effect = SpecialAbilityActivation(SpecialAbility.COPY_GUILD) + val player = SimplePlayer(0, testTable(5)) + + val neighbourBoard = player.getBoard(neighbour) + neighbourBoard.addCard(guildCard) + + player.board.copiedGuild = guildCard + + val directPointsFromGuildCard = guildCard.effects.stream().mapToInt { e -> e.computePoints(player) }.sum() + assertEquals(directPointsFromGuildCard, effect.computePoints(player)) + } + + @Test + fun computePoints_copyGuild_failWhenNoChosenGuild() { + val effect = SpecialAbilityActivation(SpecialAbility.COPY_GUILD) + val player = SimplePlayer(0, testTable(5)) + assertFailsWith<IllegalStateException> { + effect.computePoints(player) + } + } + + companion object { + + @JvmStatic + @DataPoints + fun abilities(): Array<SpecialAbility> = SpecialAbility.values() + + @JvmStatic + @DataPoints + fun neighbours(): Array<RelativeBoardPosition> = + arrayOf(RelativeBoardPosition.LEFT, RelativeBoardPosition.RIGHT) + + @JvmStatic + @DataPoints + internal fun guilds(): Array<Card> { + val bonus = BonusPerBoardElement( + listOf(RelativeBoardPosition.LEFT, RelativeBoardPosition.RIGHT), + BoardElementType.CARD, + points = 1, + colors = listOf(Color.GREY, Color.BROWN) + ) + val bonus2 = BonusPerBoardElement( + listOf(RelativeBoardPosition.LEFT, RelativeBoardPosition.SELF, RelativeBoardPosition.RIGHT), + BoardElementType.BUILT_WONDER_STAGES, + points = 1 + ) + return arrayOf(createGuildCard(1, bonus), createGuildCard(2, bonus2)) + } + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/moves/BuildWonderMoveTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/moves/BuildWonderMoveTest.kt new file mode 100644 index 00000000..21b92872 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/moves/BuildWonderMoveTest.kt @@ -0,0 +1,82 @@ +package org.luxons.sevenwonders.game.moves + +import org.junit.Test +import org.luxons.sevenwonders.game.PlayerContext +import org.luxons.sevenwonders.game.Settings +import org.luxons.sevenwonders.game.boards.Table +import org.luxons.sevenwonders.game.cards.Card +import org.luxons.sevenwonders.game.test.createMove +import org.luxons.sevenwonders.game.test.sampleCards +import org.luxons.sevenwonders.game.test.testCard +import org.luxons.sevenwonders.game.test.testSettings +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.fail + +class BuildWonderMoveTest { + + @Test + fun init_failsWhenCardNotInHand() { + val table = testTable(3) + val hand = sampleCards(7) + val playerContext = PlayerContext(0, table, hand) + val anotherCard = testCard("Card that is not in the hand") + + assertFailsWith<InvalidMoveException> { + createMove(playerContext, anotherCard, MoveType.UPGRADE_WONDER) + } + } + + @Test + fun init_failsWhenWonderIsCompletelyBuilt() { + val settings = testSettings(3) + val table = testTable(settings) + val hand = sampleCards(7) + + fillPlayerWonderLevels(settings, table, hand) + + // should fail because the wonder is already full + assertFailsWith<InvalidMoveException> { + buildOneWonderLevel(settings, table, hand, 4) + } + } + + private fun fillPlayerWonderLevels(settings: Settings, table: Table, hand: List<Card>) { + try { + val nbLevels = table.getBoard(0).wonder.stages.size + repeat(nbLevels) { + buildOneWonderLevel(settings, table, hand, it) + } + } catch (e: InvalidMoveException) { + fail("Building wonder levels should not fail before being full") + } + } + + private fun buildOneWonderLevel(settings: Settings, table: Table, hand: List<Card>, cardIndex: Int) { + val card = hand[cardIndex] + val playerContext = PlayerContext(0, table, hand) + val move = createMove(playerContext, card, MoveType.UPGRADE_WONDER) + move.place(mutableListOf(), settings) + move.activate(emptyList(), settings) + } + + @Test + fun place_increasesWonderLevel() { + val settings = testSettings(3) + val table = testTable(settings) + val hand = sampleCards(7) + val cardToUse = hand[0] + val playerContext = PlayerContext(0, table, hand) + val move = createMove(playerContext, cardToUse, MoveType.UPGRADE_WONDER) + + val initialStage = table.getBoard(0).wonder.nbBuiltStages + + move.place(mutableListOf(), settings) + + val newStage = table.getBoard(0).wonder.nbBuiltStages + + // we need to see the level increase before activation so that other players + assertEquals(initialStage + 1, newStage) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.kt new file mode 100644 index 00000000..b4c3b886 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.kt @@ -0,0 +1,137 @@ +package org.luxons.sevenwonders.game.resources + +import org.junit.Test +import org.luxons.sevenwonders.game.SimplePlayer +import org.luxons.sevenwonders.game.boards.Table +import org.luxons.sevenwonders.game.resources.Provider.LEFT_PLAYER +import org.luxons.sevenwonders.game.resources.Provider.RIGHT_PLAYER +import org.luxons.sevenwonders.game.resources.ResourceType.CLAY +import org.luxons.sevenwonders.game.resources.ResourceType.GLASS +import org.luxons.sevenwonders.game.resources.ResourceType.ORE +import org.luxons.sevenwonders.game.resources.ResourceType.STONE +import org.luxons.sevenwonders.game.resources.ResourceType.WOOD +import org.luxons.sevenwonders.game.test.createTransaction +import org.luxons.sevenwonders.game.test.createTransactions +import org.luxons.sevenwonders.game.test.testBoard +import org.luxons.sevenwonders.game.test.testTable +import kotlin.test.assertEquals + +class BestPriceCalculatorTest { + + private fun solutions(price: Int, vararg resourceTransactions: ResourceTransactions) = + TransactionPlan(price, setOf(*resourceTransactions)) + + @Test + fun bestPrice_0forEmptyResources() { + val table = testTable(3) + val player0 = SimplePlayer(0, table) + val emptyResources = emptyResources() + val emptyTransactions = noTransactions() + assertEquals(solutions(0, emptyTransactions), bestSolution(emptyResources, player0)) + } + + @Test + fun bestPrice_fixedResources_defaultCost() { + val left = testBoard(STONE) + val main = testBoard(STONE) + val right = testBoard(WOOD) + val table = Table(listOf(main, right, left)) + + val player0 = SimplePlayer(0, table) + val player1 = SimplePlayer(1, table) + val player2 = SimplePlayer(2, table) + + val resources = resourcesOf(STONE, STONE) + + val stoneLeftSingle = createTransaction(LEFT_PLAYER, STONE) + val stoneRightSingle = createTransaction(RIGHT_PLAYER, STONE) + + val stoneLeft = createTransactions(stoneLeftSingle) + val stoneRight = createTransactions(stoneRightSingle) + val stoneLeftAndRight = createTransactions(stoneLeftSingle, stoneRightSingle) + + assertEquals(solutions(2, stoneLeft), bestSolution(resources, player0)) + assertEquals(solutions(4, stoneLeftAndRight), bestSolution(resources, player1)) + assertEquals(solutions(2, stoneRight), bestSolution(resources, player2)) + } + + @Test + fun bestPrice_fixedResources_overridenCost() { + val main = testBoard(STONE) + main.tradingRules.setCost(WOOD, RIGHT_PLAYER, 1) + + val left = testBoard(WOOD) + val right = testBoard(WOOD) + val opposite = testBoard(GLASS) + val table = Table(listOf(main, right, opposite, left)) + + val player0 = SimplePlayer(0, table) + val player1 = SimplePlayer(1, table) + val player2 = SimplePlayer(2, table) + val player3 = SimplePlayer(3, table) + + val resources = resourcesOf(WOOD) + + val woodLeft = createTransactions(LEFT_PLAYER, WOOD) + val woodRight = createTransactions(RIGHT_PLAYER, WOOD) + + assertEquals(solutions(1, woodRight), bestSolution(resources, player0)) + assertEquals(solutions(0, noTransactions()), bestSolution(resources, player1)) + assertEquals(solutions(2, woodLeft, woodRight), bestSolution(resources, player2)) + assertEquals(solutions(0, noTransactions()), bestSolution(resources, player3)) + } + + @Test + fun bestPrice_mixedResources_overridenCost() { + val left = testBoard(WOOD) + + val main = testBoard(STONE) + main.tradingRules.setCost(WOOD, RIGHT_PLAYER, 1) + + val right = testBoard(ORE) + right.production.addChoice(WOOD, CLAY) + right.publicProduction.addChoice(WOOD, CLAY) + + val table = Table(listOf(main, right, left)) + + val player0 = SimplePlayer(0, table) + val player1 = SimplePlayer(1, table) + val player2 = SimplePlayer(2, table) + + val resources = resourcesOf(WOOD) + val woodRight = createTransactions(RIGHT_PLAYER, WOOD) + + assertEquals(solutions(1, woodRight), bestSolution(resources, player0)) + assertEquals(solutions(0, noTransactions()), bestSolution(resources, player1)) + assertEquals(solutions(0, noTransactions()), bestSolution(resources, player2)) + } + + @Test + fun bestPrice_chooseCheapest() { + val left = testBoard(WOOD) + + val main = testBoard(WOOD) + main.production.addChoice(CLAY, ORE) + main.tradingRules.setCost(CLAY, RIGHT_PLAYER, 1) + + val right = testBoard(WOOD) + right.production.addFixedResource(ORE, 1) + right.production.addFixedResource(CLAY, 1) + right.publicProduction.addFixedResource(ORE, 1) + right.publicProduction.addFixedResource(CLAY, 1) + + val table = Table(listOf(main, right, left)) + + val player0 = SimplePlayer(0, table) + val player1 = SimplePlayer(1, table) + val player2 = SimplePlayer(2, table) + + val resources = resourcesOf(ORE, CLAY) + val oreAndClayLeft = createTransactions(LEFT_PLAYER, ORE, CLAY) + val clayRight = createTransactions(RIGHT_PLAYER, CLAY) + + assertEquals(solutions(1, clayRight), bestSolution(resources, player0)) + assertEquals(solutions(0, noTransactions()), bestSolution(resources, player1)) + assertEquals(solutions(4, oreAndClayLeft), bestSolution(resources, player2)) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ProductionTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ProductionTest.kt new file mode 100644 index 00000000..0e865921 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ProductionTest.kt @@ -0,0 +1,292 @@ +package org.luxons.sevenwonders.game.resources + +import org.junit.Before +import org.junit.Test +import org.luxons.sevenwonders.game.resources.ResourceType.CLAY +import org.luxons.sevenwonders.game.resources.ResourceType.GLASS +import org.luxons.sevenwonders.game.resources.ResourceType.LOOM +import org.luxons.sevenwonders.game.resources.ResourceType.ORE +import org.luxons.sevenwonders.game.resources.ResourceType.PAPYRUS +import org.luxons.sevenwonders.game.resources.ResourceType.STONE +import org.luxons.sevenwonders.game.resources.ResourceType.WOOD +import java.util.EnumSet +import java.util.HashSet +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ProductionTest { + + private lateinit var emptyResources: Resources + private lateinit var resources1Wood: Resources + private lateinit var resources1Stone: Resources + private lateinit var resources1Stone1Wood: Resources + private lateinit var resources2Stones: Resources + private lateinit var resources2Stones3Clay: Resources + + @Before + fun init() { + emptyResources = emptyResources() + resources1Wood = resourcesOf(WOOD) + resources1Stone = resourcesOf(STONE) + resources1Stone1Wood = resourcesOf(STONE to 1, WOOD to 1) + resources2Stones = resourcesOf(STONE to 2) + resources2Stones3Clay = resourcesOf(STONE to 2, CLAY to 3) + } + + @Test + fun contains_newProductionContainsEmpty() { + val production = Production() + assertTrue(production.contains(emptyResources)) + } + + @Test + fun contains_singleFixedResource_noneAtAll() { + val production = Production() + assertFalse(production.contains(resources2Stones)) + } + + @Test + fun contains_singleFixedResource_notEnough() { + val production = Production() + production.addFixedResource(STONE, 1) + assertFalse(production.contains(resources2Stones)) + } + + @Test + fun contains_singleFixedResource_justEnough() { + val production = Production() + production.addFixedResource(STONE, 2) + assertTrue(production.contains(resources2Stones)) + } + + @Test + fun contains_singleFixedResource_moreThanEnough() { + val production = Production() + production.addFixedResource(STONE, 3) + assertTrue(production.contains(resources2Stones)) + } + + @Test + fun contains_singleFixedResource_moreThanEnough_amongOthers() { + val production = Production() + production.addFixedResource(STONE, 3) + production.addFixedResource(CLAY, 2) + assertTrue(production.contains(resources2Stones)) + } + + @Test + fun contains_multipleFixedResources_notEnoughOfOne() { + val production = Production() + production.addFixedResource(STONE, 3) + production.addFixedResource(CLAY, 1) + assertFalse(production.contains(resources2Stones3Clay)) + } + + @Test + fun contains_multipleFixedResources_notEnoughOfBoth() { + val production = Production() + production.addFixedResource(STONE, 1) + production.addFixedResource(CLAY, 1) + assertFalse(production.contains(resources2Stones3Clay)) + } + + @Test + fun contains_multipleFixedResources_moreThanEnough() { + val production = Production() + production.addFixedResource(STONE, 3) + production.addFixedResource(CLAY, 5) + assertTrue(production.contains(resources2Stones3Clay)) + } + + @Test + fun contains_singleChoice_containsEmpty() { + val production = Production() + production.addChoice(STONE, CLAY) + assertTrue(production.contains(emptyResources)) + } + + @Test + fun contains_singleChoice_enough() { + val production = Production() + production.addChoice(STONE, WOOD) + assertTrue(production.contains(resources1Wood)) + assertTrue(production.contains(resources1Stone)) + } + + @Test + fun contains_multipleChoices_notBoth() { + val production = Production() + production.addChoice(STONE, CLAY) + production.addChoice(STONE, CLAY) + production.addChoice(STONE, CLAY) + assertFalse(production.contains(resources2Stones3Clay)) + } + + @Test + fun contains_multipleChoices_enough() { + val production = Production() + production.addChoice(STONE, ORE) + production.addChoice(STONE, WOOD) + assertTrue(production.contains(resources1Stone1Wood)) + } + + @Test + fun contains_multipleChoices_enoughReverseOrder() { + val production = Production() + production.addChoice(STONE, WOOD) + production.addChoice(STONE, ORE) + assertTrue(production.contains(resources1Stone1Wood)) + } + + @Test + fun contains_multipleChoices_moreThanEnough() { + val production = Production() + production.addChoice(LOOM, GLASS, PAPYRUS) + production.addChoice(STONE, ORE) + production.addChoice(STONE, WOOD) + assertTrue(production.contains(resources1Stone1Wood)) + } + + @Test + fun contains_mixedFixedAndChoice_enough() { + val production = Production() + production.addFixedResource(WOOD, 1) + production.addChoice(STONE, WOOD) + assertTrue(production.contains(resources1Stone1Wood)) + } + + @Test + fun addAll_empty() { + val production = Production() + production.addAll(emptyResources) + assertTrue(production.contains(emptyResources)) + } + + @Test + fun addAll_singleResource() { + val production = Production() + production.addAll(resources1Stone) + assertTrue(production.contains(resources1Stone)) + } + + @Test + fun addAll_multipleResources() { + val production = Production() + production.addAll(resources2Stones3Clay) + assertTrue(production.contains(resources2Stones3Clay)) + } + + @Test + fun addAll_production_multipleFixedResources() { + val production = Production() + production.addAll(resources2Stones3Clay) + + val production2 = Production() + production2.addAll(production) + + assertTrue(production2.contains(resources2Stones3Clay)) + } + + @Test + fun addAll_production_multipleChoices() { + val production = Production() + production.addChoice(STONE, WOOD) + production.addChoice(STONE, ORE) + + val production2 = Production() + production2.addAll(production) + assertTrue(production.contains(resources1Stone1Wood)) + } + + @Test + fun addAll_production_mixedFixedResourcesAndChoices() { + val production = Production() + production.addFixedResource(WOOD, 1) + production.addChoice(STONE, WOOD) + + val production2 = Production() + production2.addAll(production) + + assertTrue(production.contains(resources1Stone1Wood)) + } + + @Test + fun asChoices_empty() { + val production = Production() + assertTrue(production.asChoices().isEmpty()) + } + + @Test + fun asChoices_onlyChoices() { + val production = Production() + production.addChoice(STONE, WOOD) + production.addChoice(STONE, ORE) + production.addChoice(CLAY, LOOM, GLASS) + assertEquals(production.getAlternativeResources(), production.asChoices()) + } + + @Test + fun asChoices_onlyFixed() { + val production = Production() + production.addFixedResource(WOOD, 1) + production.addFixedResource(CLAY, 2) + + val expected = HashSet<Set<ResourceType>>() + expected.add(EnumSet.of(WOOD)) + expected.add(EnumSet.of(CLAY)) + expected.add(EnumSet.of(CLAY)) + + assertEquals(expected, production.asChoices()) + } + + @Test + fun asChoices_mixed() { + val production = Production() + production.addChoice(STONE, ORE) + production.addChoice(CLAY, LOOM, GLASS) + production.addFixedResource(WOOD, 1) + production.addFixedResource(CLAY, 2) + + val expected = HashSet<Set<ResourceType>>() + expected.add(EnumSet.of(STONE, ORE)) + expected.add(EnumSet.of(CLAY, LOOM, GLASS)) + expected.add(EnumSet.of(WOOD)) + expected.add(EnumSet.of(CLAY)) + expected.add(EnumSet.of(CLAY)) + + assertEquals(expected, production.asChoices()) + } + + @Test + fun equals_trueWhenSame() { + val production = Production() + assertEquals(production, production) + } + + @Test + fun equals_trueWhenSameContent() { + val production1 = Production() + val production2 = Production() + assertTrue(production1 == production2) + production1.addFixedResource(GLASS, 1) + production2.addFixedResource(GLASS, 1) + assertTrue(production1 == production2) + production1.addChoice(ORE, WOOD) + production2.addChoice(ORE, WOOD) + assertTrue(production1 == production2) + } + + @Test + fun hashCode_sameWhenSameContent() { + val production1 = Production() + val production2 = Production() + assertEquals(production1.hashCode(), production2.hashCode()) + production1.addFixedResource(GLASS, 1) + production2.addFixedResource(GLASS, 1) + assertEquals(production1.hashCode(), production2.hashCode()) + production1.addChoice(ORE, WOOD) + production2.addChoice(ORE, WOOD) + assertEquals(production1.hashCode(), production2.hashCode()) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactionsTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactionsTest.kt new file mode 100644 index 00000000..7e6d7816 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactionsTest.kt @@ -0,0 +1,27 @@ +package org.luxons.sevenwonders.game.resources + +import org.junit.Test +import org.luxons.sevenwonders.game.resources.ResourceType.CLAY +import org.luxons.sevenwonders.game.resources.ResourceType.WOOD +import org.luxons.sevenwonders.game.test.createTransaction +import kotlin.test.assertEquals + +class ResourceTransactionsTest { + + @Test + fun toTransactions() { + val transactionMap = mapOf( + Provider.LEFT_PLAYER to (1 of WOOD) + (1 of CLAY), + Provider.RIGHT_PLAYER to (1 of WOOD) + ) + + val expectedNormalized = setOf( + createTransaction(Provider.LEFT_PLAYER, WOOD, CLAY), + createTransaction(Provider.RIGHT_PLAYER, WOOD) + ) + + assertEquals(expectedNormalized, transactionMap.toTransactions().toSet()) + } + + private infix fun Int.of(type: ResourceType): Resources = resourcesOf(type to this) +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ResourcesTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ResourcesTest.kt new file mode 100644 index 00000000..634a25c7 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/ResourcesTest.kt @@ -0,0 +1,435 @@ +package org.luxons.sevenwonders.game.resources + +import org.junit.Test +import org.luxons.sevenwonders.game.resources.ResourceType.CLAY +import org.luxons.sevenwonders.game.resources.ResourceType.GLASS +import org.luxons.sevenwonders.game.resources.ResourceType.LOOM +import org.luxons.sevenwonders.game.resources.ResourceType.ORE +import org.luxons.sevenwonders.game.resources.ResourceType.PAPYRUS +import org.luxons.sevenwonders.game.resources.ResourceType.STONE +import org.luxons.sevenwonders.game.resources.ResourceType.WOOD +import java.util.NoSuchElementException +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ResourcesTest { + + @Test + fun init_shouldBeEmpty() { + val resources = emptyResources() + for (resourceType in ResourceType.values()) { + assertEquals(0, resources[resourceType]) + } + assertEquals(0, resources.size) + assertTrue(resources.isEmpty()) + } + + @Test + fun add_zero() { + val resources = mutableResourcesOf() + resources.add(CLAY, 0) + assertEquals(0, resources[CLAY]) + assertEquals(0, resources.size) + assertTrue(resources.isEmpty()) + } + + @Test + fun add_simple() { + val resources = mutableResourcesOf() + resources.add(WOOD, 3) + assertEquals(3, resources[WOOD]) + assertEquals(3, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun add_multipleCallsStacked() { + val resources = mutableResourcesOf() + resources.add(ORE, 3) + resources.add(ORE, 2) + assertEquals(5, resources[ORE]) + assertEquals(5, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun add_interlaced() { + val resources = mutableResourcesOf() + resources.add(GLASS, 3) + resources.add(STONE, 1) + resources.add(WOOD, 4) + resources.add(GLASS, 2) + assertEquals(5, resources[GLASS]) + assertEquals(10, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun plus_zero() { + val resources = resourcesOf(CLAY to 2) + val resourcesPlusZero = resources + emptyResources() + val zeroPlusResources = emptyResources() + resources + + assertEquals(2, resourcesPlusZero[CLAY]) + assertEquals(2, resourcesPlusZero.size) + assertEquals(2, zeroPlusResources[CLAY]) + assertEquals(2, zeroPlusResources.size) + } + + @Test + fun plus_sameResource() { + val resources1 = resourcesOf(WOOD to 1) + val resources2 = resourcesOf(WOOD to 3) + val sum = resources1 + resources2 + + assertEquals(1, resources1.size) + assertEquals(3, resources2.size) + assertEquals(4, sum[WOOD]) + assertEquals(4, sum.size) + } + + @Test + fun plus_differentemptyResources() { + val resources1 = resourcesOf(WOOD to 1) + val resources2 = resourcesOf(ORE to 3) + val sum = resources1 + resources2 + + assertEquals(1, resources1.size) + assertEquals(3, resources2.size) + assertEquals(1, sum[WOOD]) + assertEquals(3, sum[ORE]) + assertEquals(4, sum.size) + } + + @Test + fun plus_overlappingemptyResources() { + val resources1 = resourcesOf(WOOD to 1) + val resources2 = resourcesOf(WOOD to 2, ORE to 4) + val sum = resources1 + resources2 + + assertEquals(1, resources1.size) + assertEquals(6, resources2.size) + assertEquals(3, sum[WOOD]) + assertEquals(4, sum[ORE]) + assertEquals(7, sum.size) + } + + @Test + fun remove_some() { + val resources = mutableResourcesOf(WOOD to 3) + resources.remove(WOOD, 2) + assertEquals(1, resources[WOOD]) + assertEquals(1, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun remove_all() { + val resources = mutableResourcesOf(WOOD to 3) + resources.remove(WOOD, 3) + assertEquals(0, resources[WOOD]) + assertEquals(0, resources.size) + assertTrue(resources.isEmpty()) + } + + @Test + fun remove_tooMany() { + val resources = mutableResourcesOf(WOOD to 2) + + assertFailsWith<NoSuchElementException> { + resources.remove(WOOD, 3) + } + } + + @Test + fun addAll_empty() { + val resources = mutableResourcesOf(STONE to 1, CLAY to 3) + + val emptyResources = emptyResources() + + resources.add(emptyResources) + assertEquals(1, resources[STONE]) + assertEquals(3, resources[CLAY]) + assertEquals(0, resources[ORE]) + assertEquals(0, resources[GLASS]) + assertEquals(0, resources[LOOM]) + assertEquals(4, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun addAll_zeros() { + val resources = mutableResourcesOf(STONE to 1, CLAY to 3) + + val emptyResources = resourcesOf(STONE to 0, CLAY to 0) + + resources.add(emptyResources) + assertEquals(1, resources[STONE]) + assertEquals(3, resources[CLAY]) + assertEquals(0, resources[ORE]) + assertEquals(0, resources[GLASS]) + assertEquals(0, resources[LOOM]) + assertEquals(4, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun addAll_same() { + val resources = mutableResourcesOf(STONE to 1, CLAY to 3) + val resources2 = resourcesOf(STONE to 2, CLAY to 6) + + resources.add(resources2) + assertEquals(3, resources[STONE]) + assertEquals(9, resources[CLAY]) + assertEquals(0, resources[ORE]) + assertEquals(0, resources[GLASS]) + assertEquals(0, resources[LOOM]) + assertEquals(12, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun addAll_overlap() { + val resources = mutableResourcesOf(STONE to 1, CLAY to 3) + val resources2 = resourcesOf(CLAY to 6, ORE to 4) + + resources.add(resources2) + assertEquals(1, resources[STONE]) + assertEquals(9, resources[CLAY]) + assertEquals(4, resources[ORE]) + assertEquals(0, resources[GLASS]) + assertEquals(0, resources[LOOM]) + assertEquals(14, resources.size) + assertFalse(resources.isEmpty()) + } + + @Test + fun contains_emptyContainsEmpty() { + val emptyResources = emptyResources() + val emptyResources2 = emptyResources() + assertTrue(emptyResources.containsAll(emptyResources2)) + } + + @Test + fun contains_singleTypeContainsEmpty() { + val resources = resourcesOf(STONE to 1) + val emptyResources = emptyResources() + + assertTrue(resources.containsAll(emptyResources)) + } + + @Test + fun contains_multipleTypesContainsEmpty() { + val resources = resourcesOf(STONE to 1, CLAY to 3) + val emptyResources = emptyResources() + + assertTrue(resources.containsAll(emptyResources)) + } + + @Test + fun contains_self() { + val resources = resourcesOf(STONE to 1, CLAY to 3) + + assertTrue(resources.containsAll(resources)) + } + + @Test + fun contains_allOfEachType() { + val resources = resourcesOf(STONE to 1, CLAY to 3) + val resources2 = resourcesOf(STONE to 1, CLAY to 3) + + assertTrue(resources.containsAll(resources2)) + } + + @Test + fun contains_someOfEachType() { + val resources = resourcesOf(STONE to 2, CLAY to 4) + val resources2 = resourcesOf(STONE to 1, CLAY to 3) + + assertTrue(resources.containsAll(resources2)) + } + + @Test + fun contains_someOfSomeTypes() { + val resources = resourcesOf(STONE to 2, CLAY to 4) + val resources2 = resourcesOf(CLAY to 3) + + assertTrue(resources.containsAll(resources2)) + } + + @Test + fun contains_allOfSomeTypes() { + val resources = resourcesOf(STONE to 2, CLAY to 4) + val resources2 = resourcesOf(CLAY to 4) + + assertTrue(resources.containsAll(resources2)) + } + + @Test + fun minus_empty() { + val resources = resourcesOf(STONE to 1, CLAY to 3) + val emptyResources = emptyResources() + + val diff = resources.minus(emptyResources) + assertEquals(1, diff[STONE]) + assertEquals(3, diff[CLAY]) + assertEquals(0, diff[ORE]) + assertEquals(0, diff[GLASS]) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_self() { + val resources = resourcesOf(STONE to 1, CLAY to 3) + + val diff = resources.minus(resources) + assertEquals(0, diff[STONE]) + assertEquals(0, diff[CLAY]) + assertEquals(0, diff[ORE]) + assertEquals(0, diff[GLASS]) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_allOfEachType() { + val resources = resourcesOf(STONE to 1, CLAY to 3) + val resources2 = resourcesOf(STONE to 1, CLAY to 3) + + val diff = resources.minus(resources2) + assertEquals(0, diff[STONE]) + assertEquals(0, diff[CLAY]) + assertEquals(0, diff[ORE]) + assertEquals(0, diff[GLASS]) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_someOfEachType() { + val resources = resourcesOf(STONE to 2, CLAY to 4) + val resources2 = resourcesOf(STONE to 1, CLAY to 3) + + val diff = resources.minus(resources2) + assertEquals(1, diff[STONE]) + assertEquals(1, diff[CLAY]) + assertEquals(0, diff[ORE]) + assertEquals(0, diff[GLASS]) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_someOfSomeTypes() { + val resources = resourcesOf(STONE to 2, CLAY to 4) + val resources2 = resourcesOf(CLAY to 3) + + val diff = resources.minus(resources2) + assertEquals(2, diff[STONE]) + assertEquals(1, diff[CLAY]) + assertEquals(0, diff[ORE]) + assertEquals(0, diff[GLASS]) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_allOfSomeTypes() { + val resources = resourcesOf(STONE to 2, CLAY to 4) + val resources2 = resourcesOf(CLAY to 4) + + val diff = resources.minus(resources2) + assertEquals(2, diff[STONE]) + assertEquals(0, diff[CLAY]) + assertEquals(0, diff[ORE]) + assertEquals(0, diff[GLASS]) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_tooMuchOfExistingType() { + val resources = resourcesOf(CLAY to 4) + val resources2 = resourcesOf(CLAY to 5) + + val diff = resources.minus(resources2) + assertEquals(0, diff[CLAY]) + } + + @Test + fun minus_someOfAnAbsentType() { + val resources = emptyResources() + val resources2 = resourcesOf(LOOM to 5) + + val diff = resources.minus(resources2) + assertEquals(0, diff[LOOM]) + } + + @Test + fun minus_someOfATypeWithZero() { + val resources = resourcesOf(LOOM to 0) + val resources2 = resourcesOf(LOOM to 5) + + val diff = resources.minus(resources2) + assertEquals(0, diff[LOOM]) + } + + @Test + fun isEmpty_noElement() { + val resources = emptyResources() + assertTrue(resources.isEmpty()) + } + + @Test + fun isEmpty_singleZeroElement() { + val resources = resourcesOf(LOOM to 0) + assertTrue(resources.isEmpty()) + } + + @Test + fun isEmpty_multipleZeroElements() { + val resources = resourcesOf(WOOD to 0, ORE to 0, LOOM to 0) + assertTrue(resources.isEmpty()) + } + + @Test + fun isEmpty_singleElementMoreThanZero() { + val resources = resourcesOf(LOOM to 3) + assertFalse(resources.isEmpty()) + } + + @Test + fun isEmpty_mixedZeroAndNonZeroElements() { + val resources = resourcesOf(WOOD to 0, LOOM to 3) + assertFalse(resources.isEmpty()) + } + + @Test + fun isEmpty_mixedZeroAndNonZeroElements_reverseOrder() { + val resources = resourcesOf(ORE to 3, PAPYRUS to 0) + assertFalse(resources.isEmpty()) + } + + @Test + fun equals_trueWhenSame() { + val resources = emptyResources() + assertEquals(resources, resources) + } + + @Test + fun equals_trueWhenSameContent() { + val resources1 = mutableResourcesOf() + val resources2 = mutableResourcesOf() + assertTrue(resources1 == resources2) + resources1.add(GLASS, 1) + resources2.add(GLASS, 1) + assertTrue(resources1 == resources2) + } + + @Test + fun hashCode_sameWhenSameContent() { + val resources1 = mutableResourcesOf() + val resources2 = mutableResourcesOf() + assertEquals(resources1.hashCode(), resources2.hashCode()) + resources1.add(GLASS, 1) + resources2.add(GLASS, 1) + assertEquals(resources1.hashCode(), resources2.hashCode()) + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/TradingRulesTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/TradingRulesTest.kt new file mode 100644 index 00000000..38953529 --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/resources/TradingRulesTest.kt @@ -0,0 +1,126 @@ +package org.luxons.sevenwonders.game.resources + +import org.junit.Assume.assumeTrue +import org.junit.experimental.theories.DataPoints +import org.junit.experimental.theories.Theories +import org.junit.experimental.theories.Theory +import org.junit.runner.RunWith +import org.luxons.sevenwonders.game.test.createTransaction +import org.luxons.sevenwonders.game.test.createTransactions +import kotlin.test.assertEquals + +@RunWith(Theories::class) +class TradingRulesTest { + + @Theory + fun setCost_overridesCost( + defaultCost: Int, + overriddenCost: Int, + overriddenProvider: Provider, + provider: Provider, + type: ResourceType + ) { + assumeTrue(defaultCost != overriddenCost) + assumeTrue(overriddenProvider != provider) + + val rules = TradingRules(defaultCost) + rules.setCost(type, overriddenProvider, overriddenCost) + + assertEquals(overriddenCost, rules.getCost(type, overriddenProvider)) + assertEquals(defaultCost, rules.getCost(type, provider)) + } + + @Theory + fun computeCost_zeroForNoResources(defaultCost: Int) { + val rules = TradingRules(defaultCost) + assertEquals(0, rules.computeCost(noTransactions())) + } + + @Theory + fun computeCost_defaultCostWhenNoOverride(defaultCost: Int, provider: Provider, type: ResourceType) { + val rules = TradingRules(defaultCost) + val transactions = createTransactions(provider, type) + assertEquals(defaultCost, rules.computeCost(transactions)) + } + + @Theory + fun computeCost_twiceDefaultFor2Resources(defaultCost: Int, provider: Provider, type: ResourceType) { + val rules = TradingRules(defaultCost) + val transactions = createTransactions(provider, type, type) + assertEquals(2 * defaultCost, rules.computeCost(transactions)) + } + + @Theory + fun computeCost_overriddenCost(defaultCost: Int, overriddenCost: Int, provider: Provider, type: ResourceType) { + val rules = TradingRules(defaultCost) + rules.setCost(type, provider, overriddenCost) + val transactions = createTransactions(provider, type) + assertEquals(overriddenCost, rules.computeCost(transactions)) + } + + @Theory + fun computeCost_defaultCostWhenOverrideOnOtherProviderOrType( + defaultCost: Int, + overriddenCost: Int, + overriddenProvider: Provider, + overriddenType: ResourceType, + provider: Provider, + type: ResourceType + ) { + assumeTrue(overriddenProvider != provider || overriddenType != type) + val rules = TradingRules(defaultCost) + rules.setCost(overriddenType, overriddenProvider, overriddenCost) + val transactions = createTransactions(provider, type) + assertEquals(defaultCost, rules.computeCost(transactions)) + } + + @Theory + fun computeCost_oneDefaultAndOneOverriddenType( + defaultCost: Int, + overriddenCost: Int, + overriddenType: ResourceType, + provider: Provider, + type: ResourceType + ) { + assumeTrue(overriddenType != type) + val rules = TradingRules(defaultCost) + rules.setCost(overriddenType, provider, overriddenCost) + val transactions = createTransactions(provider, overriddenType, type) + assertEquals(defaultCost + overriddenCost, rules.computeCost(transactions)) + } + + @Theory + fun computeCost_oneDefaultAndOneOverriddenProvider( + defaultCost: Int, + overriddenCost: Int, + overriddenProvider: Provider, + provider: Provider, + type: ResourceType + ) { + assumeTrue(overriddenProvider != provider) + val rules = TradingRules(defaultCost) + rules.setCost(type, overriddenProvider, overriddenCost) + + val boughtResources = createTransactions( + createTransaction(provider, type), + createTransaction(overriddenProvider, type) + ) + + assertEquals(defaultCost + overriddenCost, rules.computeCost(boughtResources)) + } + + companion object { + + @JvmStatic + @DataPoints + fun costs(): IntArray = intArrayOf(0, 1, 2) + + @JvmStatic + @DataPoints + fun providers(): Array<Provider> = Provider.values() + + @JvmStatic + @DataPoints + fun resourceTypes(): Array<ResourceType> = ResourceType.values() + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/test/TestUtils.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/test/TestUtils.kt new file mode 100644 index 00000000..78386b3d --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/test/TestUtils.kt @@ -0,0 +1,137 @@ +package org.luxons.sevenwonders.game.test + +import org.luxons.sevenwonders.game.Player +import org.luxons.sevenwonders.game.PlayerContext +import org.luxons.sevenwonders.game.Settings +import org.luxons.sevenwonders.game.api.CustomizableSettings +import org.luxons.sevenwonders.game.api.PlayerMove +import org.luxons.sevenwonders.game.boards.Board +import org.luxons.sevenwonders.game.boards.RelativeBoardPosition +import org.luxons.sevenwonders.game.boards.Science +import org.luxons.sevenwonders.game.boards.ScienceType +import org.luxons.sevenwonders.game.boards.Table +import org.luxons.sevenwonders.game.cards.Card +import org.luxons.sevenwonders.game.cards.CardBack +import org.luxons.sevenwonders.game.cards.Color +import org.luxons.sevenwonders.game.cards.Requirements +import org.luxons.sevenwonders.game.effects.Effect +import org.luxons.sevenwonders.game.effects.ScienceProgress +import org.luxons.sevenwonders.game.moves.Move +import org.luxons.sevenwonders.game.moves.MoveType +import org.luxons.sevenwonders.game.resources.Production +import org.luxons.sevenwonders.game.resources.Provider +import org.luxons.sevenwonders.game.resources.ResourceTransaction +import org.luxons.sevenwonders.game.resources.ResourceTransactions +import org.luxons.sevenwonders.game.resources.ResourceType +import org.luxons.sevenwonders.game.resources.noTransactions +import org.luxons.sevenwonders.game.resources.resourcesOf +import org.luxons.sevenwonders.game.wonders.Wonder +import org.luxons.sevenwonders.game.wonders.WonderStage + +private const val SEED: Long = 42 + +internal fun testCustomizableSettings(initialGold: Int = 0): CustomizableSettings = + CustomizableSettings(randomSeedForTests = SEED).copy(initialGold = initialGold) + +internal fun testSettings(nbPlayers: Int = 5, initialGold: Int = 0): Settings = + Settings(nbPlayers, testCustomizableSettings(initialGold)) + +internal fun testTable(nbPlayers: Int = 5): Table = testTable(testSettings(nbPlayers)) + +internal fun testTable(settings: Settings): Table = + Table(testBoards(settings.nbPlayers, settings)) + +private fun testBoards(count: Int, settings: Settings): List<Board> = List(count) { testBoard(settings) } + +internal fun testBoard( + initialResource: ResourceType = ResourceType.WOOD, + initialGold: Int = 0, + vararg production: ResourceType +): Board { + val settings = testSettings(initialGold = initialGold) + val board = testBoard(settings, initialResource) + board.production.addAll(fixedProduction(*production)) + return board +} + +private fun testBoard(settings: Settings, initialResource: ResourceType = ResourceType.WOOD): Board = + Board(testWonder(initialResource), 0, settings) + +internal fun testWonder(initialResource: ResourceType = ResourceType.WOOD): Wonder { + val stage1 = WonderStage(Requirements(), emptyList()) + val stage2 = WonderStage(Requirements(), emptyList()) + val stage3 = WonderStage(Requirements(), emptyList()) + return Wonder("Test Wonder ${initialResource.symbol}", initialResource, listOf(stage1, stage2, stage3), "") +} + +internal fun fixedProduction(vararg producedTypes: ResourceType): Production = + Production().apply { addAll(resourcesOf(*producedTypes)) } + +internal fun createTransactions(provider: Provider, vararg resources: ResourceType): ResourceTransactions = + createTransactions(createTransaction(provider, *resources)) + +internal fun createTransactions(vararg transactions: ResourceTransaction): ResourceTransactions = transactions.toSet() + +internal fun createTransaction(provider: Provider, vararg resources: ResourceType): ResourceTransaction = + ResourceTransaction(provider, resourcesOf(*resources)) + +internal fun createRequirements(vararg types: ResourceType): Requirements = Requirements(resources = resourcesOf(*types)) + +internal fun sampleCards(nbCards: Int, fromIndex: Int = 0, color: Color = Color.BLUE): List<Card> = + List(nbCards) { i -> testCard("Test Card ${fromIndex + i}", color) } + +internal fun createGuildCards(count: Int): List<Card> = List(count) { createGuildCard(it) } + +internal fun createGuildCard(num: Int, effect: Effect? = null): Card = + testCard("Test Guild $num", Color.PURPLE, effect = effect) + +internal fun testCard( + name: String = "Test Card", + color: Color = Color.BLUE, + requirements: Requirements = Requirements(), + effect: Effect? = null +): Card { + val effects = if (effect == null) emptyList() else listOf(effect) + return Card(name, color, requirements, effects, null, emptyList(), "path/to/card/image", CardBack("image-III")) +} + +internal fun addCards(board: Board, nbCardsOfColor: Int, nbOtherCards: Int, color: Color) { + addCards(board, nbCardsOfColor, color) + addCards(board, nbOtherCards, getDifferentColorFrom(color)) +} + +internal fun addCards(board: Board, nbCards: Int, color: Color) { + sampleCards(nbCards, color = color).forEach { board.addCard(it) } +} + +internal fun getDifferentColorFrom(vararg colors: Color): Color = + Color.values().firstOrNull { it !in colors } ?: throw IllegalArgumentException("All colors are forbidden!") + +internal fun createScienceProgress(compasses: Int, wheels: Int, tablets: Int, jokers: Int): ScienceProgress = + ScienceProgress(createScience(compasses, wheels, tablets, jokers)) + +internal fun createScience(compasses: Int, wheels: Int, tablets: Int, jokers: Int): Science = Science().apply { + add(ScienceType.COMPASS, compasses) + add(ScienceType.WHEEL, wheels) + add(ScienceType.TABLET, tablets) + addJoker(jokers) +} + +internal fun playCardWithEffect(player: Player, color: Color, effect: Effect) { + val card = testCard(color = color, effect = effect) + player.board.addCard(card) + card.applyTo(player, noTransactions()) +} + +internal fun createMove(context: PlayerContext, card: Card, type: MoveType): Move = + type.resolve(PlayerMove(type, card.name), card, context) + +internal fun singleBoardPlayer(board: Board): Player = object : Player { + override val index = 0 + override val board = board + override fun getBoard(relativePosition: RelativeBoardPosition): Board = when (relativePosition) { + RelativeBoardPosition.LEFT -> throw RuntimeException("No LEFT board") + RelativeBoardPosition.SELF -> this.board + RelativeBoardPosition.RIGHT -> throw RuntimeException("No RIGHT board") + } +} diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/wonders/WonderTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/wonders/WonderTest.kt new file mode 100644 index 00000000..491d13fb --- /dev/null +++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/game/wonders/WonderTest.kt @@ -0,0 +1,34 @@ +package org.luxons.sevenwonders.game.wonders + +import org.junit.Test +import org.luxons.sevenwonders.game.cards.CardBack +import org.luxons.sevenwonders.game.test.testWonder +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class WonderTest { + + @Test + fun buildLevel_increasesNbBuiltStages() { + val wonder = testWonder() + assertEquals(0, wonder.nbBuiltStages) + wonder.placeCard(CardBack("img")) + assertEquals(1, wonder.nbBuiltStages) + wonder.placeCard(CardBack("img")) + assertEquals(2, wonder.nbBuiltStages) + wonder.placeCard(CardBack("img")) + assertEquals(3, wonder.nbBuiltStages) + } + + @Test + fun buildLevel_failsIfFull() { + val wonder = testWonder() + wonder.placeCard(CardBack("img")) + wonder.placeCard(CardBack("img")) + wonder.placeCard(CardBack("img")) + + assertFailsWith(IllegalStateException::class) { + wonder.placeCard(CardBack("img")) + } + } +} |