Repository: ocadotechnology/aimmo

Issue:
Functional Test 1 - Effect expiry 
We want to check that the effect can expire. This would be as follows:

Place an avatar on the map in one of the cells.
Place a pickup somewhere.
Make a move in some direction, towards the pickup.
Call `avatar.pickup.effect.onturn()` 10 times until the effect expires.
Make sure that the avatar holds *no effect* (not pickup!).


Discussion:

Resolving patch:
diff --git a/aimmo-game/simulation/effects.py b/aimmo-game/simulation/effects.py
--- a/aimmo-game/simulation/effects.py
+++ b/aimmo-game/simulation/effects.py
@@ -1,9 +1,10 @@
 from abc import ABCMeta, abstractmethod
 
 import math
-
 import sys
 
+INVULNERABILITY_RESISTANCE = 1000
+
 
 class _Effect(object):
     __metaclass__ = ABCMeta
@@ -40,11 +41,11 @@ def on_turn(self):
 class InvulnerabilityPickupEffect(_TimedEffect):
     def __init__(self, *args):
         super(InvulnerabilityPickupEffect, self).__init__(*args)
-        self._avatar.resistance += 1000
+        self._avatar.resistance += INVULNERABILITY_RESISTANCE
 
     def remove(self):
         super(InvulnerabilityPickupEffect, self).remove()
-        self._avatar.resistance -= 1000
+        self._avatar.resistance -= INVULNERABILITY_RESISTANCE
 
 
 class DamageBoostPickupEffect(_TimedEffect):


Test patch:
diff --git a/aimmo-game/tests/functional/test_effect_expiry.py b/aimmo-game/tests/functional/test_effect_expiry.py
new file mode 100644
--- /dev/null
+++ b/aimmo-game/tests/functional/test_effect_expiry.py
@@ -0,0 +1,170 @@
+from unittest import TestCase
+
+from mock_world import MockWorld
+
+from simulation.location import Location
+from simulation.pickups import DamageBoostPickup, InvulnerabilityPickup
+from simulation.effects import INVULNERABILITY_RESISTANCE
+
+
+class TestEffectExpiry(TestCase):
+    """
+    Tests if effects of pickups can expire correctly in multiple scenarios. Note: applies only to pickups that inherit
+    TimedEffect!
+    """
+
+    def setUp(self):
+        """
+        Mock a game for each test individually. MockWorld() will set up a:
+        avatar manager, game state, turn manager and a map generator.
+        """
+        SETTINGS = {
+            'START_HEIGHT': 5,
+            'START_WIDTH': 15,
+            'OBSTACLE_RATIO': 0,
+        }
+
+        self.game = MockWorld(SETTINGS)
+        self.game.game_state.add_avatar(1, None, Location(0, 0))
+        self.avatar = self.game.avatar_manager.get_avatar(1)
+        self.cell = self.game.game_state.world_map.get_cell(Location(1, 0))
+
+    def test_single_damage_boost_pickup_expiry(self):
+        """
+        Avatar spawns at ORIGIN. DamageBoostPickup is at 1,0. Avatar moves to the pickup and picks it up next turn and
+        then we wait for the effect to expire EFFECT_TIME turns later (value defined in the effects class).
+        """
+        pickup_created = DamageBoostPickup(self.cell, 10)
+        self.cell.pickup = pickup_created
+        self.assertEqual(self.avatar.attack_strength, 1)
+
+        # Avatar moves EAST to (1,0) where pickup is located, then repeats it 5 times.
+        for i in range(6):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created.EFFECT))
+        self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 5)
+        self.assertEqual(self.avatar.attack_strength, 11)
+
+        # Run 5 more turns and expect the effect to expire.
+        for i in range(5):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertEqual(len(self.avatar.effects), 0)
+        self.assertEqual(self.avatar.attack_strength, 1)
+
+    def test_single_invulnerability_pickup_pickup_expiry(self):
+        """
+        Avatar spawns at ORIGIN. InvulnerabilityPickup is at 1,0. Avatar moves to the pickup and picks it up next turn
+        and then we wait for the effect to expire EFFECT_TIME turns later (value defined in the effects class).
+        """
+        pickup_created = InvulnerabilityPickup(self.cell)
+        self.cell.pickup = pickup_created
+        self.assertEqual(self.avatar.resistance, 0)
+
+        # Avatar moves EAST to (1,0) where pickup is located, then repeats it 5 times.
+        for i in range(6):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created.EFFECT))
+        self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 5)
+        self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE)
+
+        # Run 5 more turns and expect the effect to expire.
+        for i in range(5):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertEqual(len(self.avatar.effects), 0)
+        self.assertEqual(self.avatar.resistance, 0)
+
+    def test_multiple_damage_boost_pickup_expiry(self):
+        """
+        Ensure that each damage boost effect expires at the appropriate time, even if the user stacks up on them. One
+        pickup will be at (1,0) and another at (3,0).
+        """
+        cell_one = self.game.game_state.world_map.get_cell(Location(1, 0))
+        cell_two = self.game.game_state.world_map.get_cell(Location(3, 0))
+        pickup_created_one = DamageBoostPickup(cell_one, 10)
+        pickup_created_two = DamageBoostPickup(cell_two, 15)
+
+        cell_one.pickup = pickup_created_one
+        cell_two.pickup = pickup_created_two
+
+        self.assertEqual(self.avatar.attack_strength, 1)
+
+        # Avatar moves EAST to (1,0) where pickup one is located.
+        self.game.turn_manager._run_single_turn()
+
+        self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created_one.EFFECT))
+        self.assertEqual(len(self.avatar.effects), 1)
+        self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 10)
+        self.assertEqual(self.avatar.attack_strength, 11)
+
+        # Move twice to the second pickup.
+        for i in range(2):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertTrue(isinstance(list(self.avatar.effects)[1], pickup_created_two.EFFECT))
+        self.assertEqual(len(self.avatar.effects), 2)
+        self.assertEqual(self.avatar.attack_strength, 26)
+
+        # Eight turns later, we expect the first effect to expire.
+        for i in range(8):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertEqual(len(self.avatar.effects), 1)
+        self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 2)
+        self.assertEqual(self.avatar.attack_strength, 16)
+
+        # Two turns later, the second pickup expires too.
+        for i in range(2):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertEqual(len(self.avatar.effects), 0)
+        self.assertEqual(self.avatar.attack_strength, 1)
+
+    def test_multiple_invulnerability_boost_pickup_expiry(self):
+        """
+        Ensure that each invulnerability boost effect expires at the appropriate time, even if the user stacks up on
+        them. One pickup will be at (1,0) and another at (3,0).
+        """
+        cell_one = self.game.game_state.world_map.get_cell(Location(1, 0))
+        cell_two = self.game.game_state.world_map.get_cell(Location(3, 0))
+        pickup_created_one = InvulnerabilityPickup(cell_one)
+        pickup_created_two = InvulnerabilityPickup(cell_two)
+
+        cell_one.pickup = pickup_created_one
+        cell_two.pickup = pickup_created_two
+
+        self.assertEqual(self.avatar.resistance, 0)
+
+        # Avatar moves EAST to (1,0) where pickup one is located.
+        self.game.turn_manager._run_single_turn()
+
+        self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created_one.EFFECT))
+        self.assertEqual(len(self.avatar.effects), 1)
+        self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 10)
+        self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE)
+
+        # Move twice to the second pickup.
+        for i in range(2):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertTrue(isinstance(list(self.avatar.effects)[1], pickup_created_two.EFFECT))
+        self.assertEqual(len(self.avatar.effects), 2)
+        self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE * 2)
+
+        # Eight turns later, we expect the first effect to expire.
+        for i in range(8):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertEqual(len(self.avatar.effects), 1)
+        self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 2)
+        self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE)
+
+        # Two turns later, the second pickup expires too.
+        for i in range(2):
+            self.game.turn_manager._run_single_turn()
+
+        self.assertEqual(len(self.avatar.effects), 0)
+        self.assertEqual(self.avatar.resistance, 0)