Skip to content

Commit

Permalink
Created JUnit tests for DroidCombatTask and TNTCombatTask
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohamad11Dab committed Sep 10, 2023
1 parent 1b26c61 commit 373f153
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ public class DroidCombatTask extends DefaultTask implements PriorityTask {
private static final int INTERVAL = 1; // time interval to scan for enemies in seconds
private static final short TARGET = PhysicsLayer.NPC; // The type of targets that the tower will detect
// the following four constants are the event names that will be triggered in the state machine
private static final String GO_UP = "goUpStart";
private static final String GO_DOWN = "goDownStart";
private static final String ATTACK_UP = "attackUpStart";
private static final String ATTACK_DOWN = "attackDownStart";
private static final String WALK = "walkStart";
private static final String DEATH = "deathStart";
private static final String IDLE = "idleStart";
public static final String GO_UP = "goUpStart";
public static final String GO_DOWN = "goDownStart";
public static final String ATTACK_UP = "attackUpStart";
public static final String ATTACK_DOWN = "attackDownStart";
public static final String WALK = "walkStart";
public static final String DEATH = "deathStart";
public static final String IDLE = "idleStart";
public static final String SHOOT_UP = "ShootUp";
public static final String SHOOT_DOWN = "ShootDown";


// class attributes
Expand All @@ -43,10 +45,10 @@ public class DroidCombatTask extends DefaultTask implements PriorityTask {
private long endTime;
private final RaycastHit hit = new RaycastHit();

private enum STATE {
public enum STATE {
IDLE, UP, DOWN, SHOOT_UP, SHOOT_DOWN, WALK, DIE
}
private STATE towerState = STATE.WALK;
public STATE towerState = STATE.WALK;

/**
* @param priority Task priority when targets are detected (0 when nothing detected). Must be a positive integer.
Expand Down Expand Up @@ -105,6 +107,7 @@ public void updateTowerState() {
case IDLE -> {
if (isTargetVisible()) {
owner.getEntity().getEvents().trigger(ATTACK_UP);
owner.getEntity().getEvents().trigger(SHOOT_UP);
towerState = STATE.DOWN;
} else {
owner.getEntity().getEvents().trigger(IDLE);
Expand All @@ -113,12 +116,7 @@ public void updateTowerState() {
case SHOOT_DOWN -> {
if (isTargetVisible()) {
owner.getEntity().getEvents().trigger(ATTACK_DOWN);
Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100,
owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false);
Projectile.setScale(new Vector2(0.5f,0.5f));
Projectile.setPosition((float) (owner.getEntity().getPosition().x + 0.2),
(float) (owner.getEntity().getPosition().y - 0.2));
ServiceLocator.getEntityService().register(Projectile);
owner.getEntity().getEvents().trigger(SHOOT_DOWN);
towerState = STATE.UP;
} else {
owner.getEntity().getEvents().trigger(GO_UP);
Expand All @@ -129,13 +127,8 @@ public void updateTowerState() {
if (isTargetVisible()) {

owner.getEntity().getEvents().trigger(ATTACK_UP);
owner.getEntity().getEvents().trigger(SHOOT_UP);
towerState = STATE.DOWN;
Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100,
owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false);
Projectile.setScale(new Vector2(0.5f,0.5f));
Projectile.setPosition((float) (owner.getEntity().getPosition().x + 0.2),
(float) (owner.getEntity().getPosition().y + 0.5));
ServiceLocator.getEntityService().register(Projectile);
} else {
owner.getEntity().getEvents().trigger(IDLE);
towerState = STATE.IDLE;
Expand Down Expand Up @@ -176,6 +169,15 @@ public void stop() {
// owner.getEntity().getEvents().trigger(STOW);
}

/**
* Returns the current state of the tower.
*
* @return the current state of the tower.
*/
public STATE getState() {
return this.towerState;
}

/**
* Returns the current priority of the task.
* @return active priority value if targets detected, inactive priority otherwise
Expand All @@ -189,8 +191,10 @@ public int getPriority() {
* Uses a raycast to determine whether there are any targets in detection range
* @return true if a target is visible, false otherwise
*/
private boolean isTargetVisible() {
public boolean isTargetVisible() {
// If there is an obstacle in the path to the max range point, mobs visible.
return physics.raycast(towerPosition, maxRangePosition, TARGET, hit);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,12 @@ public boolean isTargetVisible() {
return physics.raycast(towerPosition, maxRangePosition, TARGET, hit);
}

private boolean isReadyToDelete() {
public boolean isReadyToDelete() {

return readToDelete;
}


}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.csse3200.game.components.tasks;

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

import com.csse3200.game.ai.tasks.AITaskComponent;
import com.csse3200.game.components.CombatStatsComponent;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.entities.EntityService;
import com.csse3200.game.events.listeners.EventListener0;
import com.csse3200.game.physics.PhysicsLayer;
import com.csse3200.game.physics.PhysicsService;
import com.csse3200.game.physics.components.ColliderComponent;
import com.csse3200.game.physics.components.HitboxComponent;
import com.csse3200.game.physics.components.PhysicsComponent;
import com.csse3200.game.services.GameTime;
import com.csse3200.game.services.ServiceLocator;
import com.csse3200.game.entities.factories.ProjectileFactory;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class DroidCombatTaskTest {
DroidCombatTask droidCombatTask;

@BeforeEach
void setUp() {
GameTime gameTime = mock(GameTime.class);
ServiceLocator.registerTimeSource(gameTime);
ServiceLocator.registerPhysicsService(new PhysicsService());
ServiceLocator.registerEntityService(new EntityService());
droidCombatTask = new DroidCombatTask(1, 4);
}

@Test
public void testStartTriggersWalkEvent() {
Entity entity = createDroid();
EventListener0 walkListener = mock(EventListener0.class);
// Deploy Droid in the walking state
entity.getEvents().addListener(DroidCombatTask.WALK, walkListener);
droidCombatTask.start();
verify(walkListener).handle();
}

@Test
public void testUpdateTowerStateWithTargetInRange() {
Entity entity = createDroid();
entity.setPosition(10,10);

Entity Target = createNPC();
Target.setPosition(12,10);

EventListener0 attackUp = mock(EventListener0.class);
EventListener0 attackDown = mock(EventListener0.class);
EventListener0 switchDown = mock(EventListener0.class);
EventListener0 shootUp = mock(EventListener0.class);
EventListener0 shootDown = mock(EventListener0.class);
entity.getEvents().addListener(DroidCombatTask.ATTACK_UP, attackUp);
entity.getEvents().addListener(DroidCombatTask.SHOOT_UP,shootUp);
entity.getEvents().addListener(DroidCombatTask.ATTACK_DOWN, attackDown);
entity.getEvents().addListener(DroidCombatTask.GO_DOWN,switchDown);
entity.getEvents().addListener(DroidCombatTask.SHOOT_DOWN,shootDown);
//Jump to IDLE state
droidCombatTask.start();
droidCombatTask.towerState = DroidCombatTask.STATE.IDLE;

ServiceLocator.getPhysicsService().getPhysics().update();
entity.update();

assertTrue(droidCombatTask.isTargetVisible());

droidCombatTask.updateTowerState();
// By default, Droid aims from top, so shoot from top
verify(attackUp).handle();
// shoot projectiles from top
verify(shootUp).handle();
assertEquals(DroidCombatTask.STATE.DOWN, droidCombatTask.getState());

droidCombatTask.updateTowerState();
// switch to aim downwards
verify(switchDown).handle();
assertEquals(DroidCombatTask.STATE.SHOOT_DOWN, droidCombatTask.getState());

ServiceLocator.getPhysicsService().getPhysics().update();
entity.update();
// check if the target is still there to shoot from below
assertTrue(droidCombatTask.isTargetVisible());

droidCombatTask.updateTowerState();
// Shoot from below
verify(attackDown).handle();
//shoot projectiles from below
verify(shootUp).handle();
// switch back to aim from top
assertEquals(DroidCombatTask.STATE.UP, droidCombatTask.getState());
}

@Test
public void testUpdateTowerStateWithTargetNotInRange() {
Entity entity = createDroid();
entity.setPosition(10, 10);

Entity Target = createNPC();
Target.setPosition(15, 10);

EventListener0 idle = mock(EventListener0.class);
EventListener0 attackUp = mock(EventListener0.class);
entity.getEvents().addListener(DroidCombatTask.IDLE, idle);
entity.getEvents().addListener(DroidCombatTask.ATTACK_UP,attackUp);
//Jump to IDLE state
droidCombatTask.towerState = DroidCombatTask.STATE.IDLE;

ServiceLocator.getPhysicsService().getPhysics().update();
entity.update();
// Target out of range
assertFalse(droidCombatTask.isTargetVisible());

droidCombatTask.updateTowerState();
// Droid will remain in Idle and will not shoot
verify(idle).handle();
verifyNoInteractions(attackUp);
assertEquals(DroidCombatTask.STATE.IDLE, droidCombatTask.getState());

}


Entity createDroid() {
AITaskComponent aiTaskComponent = new AITaskComponent().addTask(droidCombatTask);
Entity entity = new Entity().addComponent(aiTaskComponent)
.addComponent(new PhysicsComponent())
.addComponent(new HitboxComponent())
.addComponent(new ColliderComponent())
.addComponent(new CombatStatsComponent(100,10));
entity.create();
return entity;
}

Entity createNPC() {
Entity Target = new Entity().addComponent(new HitboxComponent().setLayer(PhysicsLayer.NPC))
.addComponent(new ColliderComponent())
.addComponent(new PhysicsComponent());
Target.create();
return Target;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,26 @@

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import com.badlogic.gdx.math.Vector2;
import com.csse3200.game.ai.tasks.AITaskComponent;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.entities.factories.TowerFactory;
import com.csse3200.game.events.EventHandler;
import com.csse3200.game.entities.EntityService;
import com.csse3200.game.events.listeners.EventListener0;
import com.csse3200.game.physics.PhysicsEngine;
import com.csse3200.game.physics.PhysicsLayer;
import com.csse3200.game.physics.PhysicsService;
import com.csse3200.game.physics.components.ColliderComponent;
import com.csse3200.game.physics.components.HitboxComponent;
import com.csse3200.game.physics.components.PhysicsComponent;
import com.csse3200.game.physics.raycast.RaycastHit;
import com.csse3200.game.services.GameTime;
import com.csse3200.game.services.ServiceLocator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class TNTTowerCombatTaskTest {



Entity entity;

TNTTowerCombatTask tntTowerCombatTask;

@BeforeEach
Expand All @@ -39,6 +30,7 @@ void setUp() {

ServiceLocator.registerTimeSource(gameTime);
ServiceLocator.registerPhysicsService(new PhysicsService());
ServiceLocator.registerEntityService(new EntityService());

tntTowerCombatTask = new TNTTowerCombatTask(2,4);
}
Expand All @@ -57,41 +49,75 @@ public void testStartTriggersDefaultEvent() {
}

@Test
public void testUpdateTowerStateIdleMode() {
public void testUpdateTowerStateWithTargetInRange() {

Entity entity = createTNT();
entity.setPosition(10,10);

Entity Target = createNPC();
Target.setPosition(12,10);

EventListener0 defaultStartListener = mock(EventListener0.class);
EventListener0 dig = mock(EventListener0.class);
EventListener0 explode = mock(EventListener0.class);
EventListener0 damage = mock(EventListener0.class);
// still in idle
assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState());
entity.getEvents().addListener(TNTTowerCombatTask.DIG, defaultStartListener);
entity.getEvents().addListener(TNTTowerCombatTask.DIG, dig);
entity.getEvents().addListener(TNTTowerCombatTask.EXPLOSION,explode);
entity.getEvents().addListener(TNTTowerCombatTask.DAMAGE,damage);

ServiceLocator.getPhysicsService().getPhysics().update();
entity.update();
// TNT saw the target
assertTrue(tntTowerCombatTask.isTargetVisible());

tntTowerCombatTask.updateTowerState();

verify(defaultStartListener).handle();
// ready to explode
// TNT just Dug into the ground
verify(dig).handle();
// READY TO EXPLODE !!!
assertEquals(TNTTowerCombatTask.STATE.EXPLODE, tntTowerCombatTask.getState());

tntTowerCombatTask.updateTowerState();

// BOOOOOOOOM !!
verify(explode).handle();
// Apply Damage and Knock-back to Target
verify(damage).handle();

// Ready to dispose TNT
assertEquals(TNTTowerCombatTask.STATE.REMOVE, tntTowerCombatTask.getState());

tntTowerCombatTask.updateTowerState();
// Set flag to dispose
assertTrue(tntTowerCombatTask.isReadyToDelete());

}

@Test
public void testGetPriority() {
// Arrange
tntTowerCombatTask.readToDelete = false;
public void testStayAtIdleWhenNoTargetInRange() {

// Act
int priority = tntTowerCombatTask.getPriority();
Entity entity = createTNT();
entity.setPosition(10,10);

// Assert
Entity Target = createNPC();
Target.setPosition(15,10);

EventListener0 defaultStartListener = mock(EventListener0.class);
// still in idle
assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState());
entity.getEvents().addListener(TNTTowerCombatTask.DIG, defaultStartListener);

ServiceLocator.getPhysicsService().getPhysics().update();
entity.update();
// Target not in range
assertFalse(tntTowerCombatTask.isTargetVisible());

tntTowerCombatTask.updateTowerState();

verifyNoInteractions(defaultStartListener);
// still in idle
assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState());

assertEquals(2, priority);
}

Entity createTNT() {
Expand Down

0 comments on commit 373f153

Please sign in to comment.