wip: add overengineered full socket test
This commit is contained in:
parent
8daa7afac3
commit
afc413507a
@ -45,7 +45,7 @@ public class UserManager {
|
||||
}
|
||||
|
||||
/** Called on a new WebSocket connection. Places the WebSocket and its ResourceDescriptor in a HashMap. */
|
||||
void connectUser(WebSocket conn) {
|
||||
public void connectUser(WebSocket conn) {
|
||||
Logger.debug("Connected new user");
|
||||
synchronized(clients) {
|
||||
clients.put(conn, new Client(conn));
|
||||
@ -57,7 +57,7 @@ public class UserManager {
|
||||
* @param conn is the {@link WebSocket} that sent the message
|
||||
* @param message is the {@link String} sent by the connection
|
||||
*/
|
||||
void messageReceived(WebSocket conn, String message) {
|
||||
public void messageReceived(WebSocket conn, String message) {
|
||||
Logger.debug("Message received from {}", conn);
|
||||
Client client = clients.get(conn);
|
||||
|
||||
@ -90,7 +90,7 @@ public class UserManager {
|
||||
}
|
||||
|
||||
/** Called on closed connection. */
|
||||
void disconnectUser(WebSocket conn, boolean closedByRemote) {
|
||||
public void disconnectUser(WebSocket conn, boolean closedByRemote) {
|
||||
Logger.info("Disconnecting client '{}'", conn);
|
||||
Client client = clients.get(conn);
|
||||
|
||||
|
@ -0,0 +1,81 @@
|
||||
package uulm.teamname.marvelous.server;
|
||||
|
||||
import uulm.teamname.marvelous.gamelibrary.config.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class BaseGameLogicTest {
|
||||
protected static final Iterator<Integer> randomIntegers = ThreadLocalRandom.current().ints().iterator();
|
||||
|
||||
protected static final PartyConfig partyConfig = new PartyConfig();
|
||||
protected static final CharacterConfig characterConfig = new CharacterConfig();
|
||||
protected static final ScenarioConfig scenarioConfig = new ScenarioConfig();
|
||||
|
||||
protected static final ArrayList<Integer> player1Selection = new ArrayList<>();
|
||||
protected static final ArrayList<Integer> player2Selection = new ArrayList<>();
|
||||
|
||||
protected static void generate() {
|
||||
partyConfig.maxRounds = 50;
|
||||
partyConfig.mindStoneCD = 2;
|
||||
partyConfig.powerStoneCD = 3;
|
||||
partyConfig.realityStoneCD = 4;
|
||||
partyConfig.soulStoneCD = 5;
|
||||
partyConfig.spaceStoneCD = 6;
|
||||
partyConfig.timeStoneCD = 7;
|
||||
partyConfig.mindStoneDMG = 3;
|
||||
|
||||
characterConfig.characters = new CharacterProperties[24];
|
||||
for(int i = 0; i < characterConfig.characters.length; i++) {
|
||||
characterConfig.characters[i] = generateCharacter(i);
|
||||
}
|
||||
|
||||
scenarioConfig.name = generateName(20);
|
||||
scenarioConfig.author = generateName(20);
|
||||
scenarioConfig.scenario = new FieldType[20][20];
|
||||
for(int x = 0; x < scenarioConfig.scenario[0].length; x++) {
|
||||
for(int y = 0; y < scenarioConfig.scenario.length; y++) {
|
||||
if(Math.abs(randomIntegers.next() % 100) < 10) {
|
||||
scenarioConfig.scenario[y][x] = FieldType.ROCK;
|
||||
}else {
|
||||
scenarioConfig.scenario[y][x] = FieldType.GRASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 6; i++) {
|
||||
player1Selection.add(i);
|
||||
}
|
||||
|
||||
for(int i = 6; i < 12; i++) {
|
||||
player2Selection.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateName(int length) {
|
||||
StringBuilder name = new StringBuilder();
|
||||
for (int j = 0; j < length; j++) {
|
||||
name.append((char) (
|
||||
65 + Math.abs(randomIntegers.next() % 26) + 32 * Math.abs(randomIntegers.next() % 2)
|
||||
));
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
private static CharacterProperties generateCharacter(int id) {
|
||||
CharacterProperties props = new CharacterProperties();
|
||||
|
||||
props.characterID = id;
|
||||
props.name = generateName(10);
|
||||
|
||||
props.HP = Math.abs(randomIntegers.next() % 15) + 5;
|
||||
props.MP = Math.abs(randomIntegers.next() % 5) + 2;
|
||||
props.AP = Math.abs(randomIntegers.next() % 5) + 2;
|
||||
props.meleeDamage = Math.abs(randomIntegers.next() % 5) + 2;
|
||||
props.rangedDamage = Math.abs(randomIntegers.next() % 5) + 2;
|
||||
props.attackRange = Math.abs(randomIntegers.next() % 5) + 2;
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
package uulm.teamname.marvelous.server;
|
||||
|
||||
import org.java_websocket.WebSocket;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.tinylog.configuration.Configuration;
|
||||
import uulm.teamname.marvelous.gamelibrary.json.JSON;
|
||||
import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
|
||||
import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
|
||||
import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
|
||||
import uulm.teamname.marvelous.gamelibrary.messages.client.HelloServerMessage;
|
||||
import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
|
||||
import uulm.teamname.marvelous.gamelibrary.messages.server.*;
|
||||
import uulm.teamname.marvelous.server.lobbymanager.LobbyManager;
|
||||
import uulm.teamname.marvelous.server.netconnector.UserManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class MarvelousServerTest extends BaseGameLogicTest {
|
||||
private static MockedStatic<Server> serverMock;
|
||||
private static JSON json;
|
||||
|
||||
@BeforeAll
|
||||
static void start() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
Constructor<UserManager> um = UserManager.class.getDeclaredConstructor();
|
||||
um.setAccessible(true);
|
||||
um.newInstance();
|
||||
Constructor<LobbyManager> lm = LobbyManager.class.getDeclaredConstructor();
|
||||
lm.setAccessible(true);
|
||||
lm.newInstance();
|
||||
|
||||
serverMock = Mockito.mockStatic(Server.class);
|
||||
|
||||
generate();
|
||||
|
||||
serverMock.when(Server::getMaxLobbies).thenReturn(10);
|
||||
serverMock.when(Server::getPartyConfig).thenReturn(partyConfig);
|
||||
serverMock.when(Server::getScenarioConfig).thenReturn(scenarioConfig);
|
||||
serverMock.when(Server::getCharacterConfig).thenReturn(characterConfig);
|
||||
|
||||
json = new JSON(characterConfig);
|
||||
|
||||
Map<String, String> map = new HashMap<>();
|
||||
Configuration.replace(map);
|
||||
map.put("writer1", "console");
|
||||
map.put("writer1.level", "trace");
|
||||
map.put("writer1.format", "[{thread}] {level}: {message}");
|
||||
Configuration.replace(map);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void main() {
|
||||
UserManager m = UserManager.getInstance();
|
||||
|
||||
WebSocket p1 = mock(WebSocket.class);
|
||||
WebSocket p2 = mock(WebSocket.class);
|
||||
WebSocket s1 = mock(WebSocket.class);
|
||||
WebSocket s2 = mock(WebSocket.class);
|
||||
|
||||
m.connectUser(p1);
|
||||
ensureHandshake(m, p1, "Player 1", "1234", false);
|
||||
|
||||
m.connectUser(p2);
|
||||
ensureHandshake(m, p2, "Player 2", "4321", false);
|
||||
|
||||
m.connectUser(s1);
|
||||
ensureHandshake(m, s1, "Spectator 1", "3333", false);
|
||||
|
||||
m.connectUser(s2);
|
||||
ensureHandshake(m, s2, "Spectator 2", "4444", false);
|
||||
|
||||
ensurePlayerReady(m, p1, true, RoleEnum.PLAYER);
|
||||
ensurePlayerReady(m, p2, true, RoleEnum.PLAYER);
|
||||
ensureSpectatorReady(m, s1, true, RoleEnum.SPECTATOR);
|
||||
|
||||
//these are broken right now because Server doesn't get mocked in lobby threads
|
||||
/*
|
||||
ensureCharacterSelection(m, p1, false);
|
||||
ensureCharacterSelection(m, p2, true);
|
||||
|
||||
ensureReceived(p1, new GameStructureMessage());
|
||||
ensureReceived(p2, new GameStructureMessage());
|
||||
ensureReceived(s1, new GameStructureMessage());
|
||||
*/
|
||||
}
|
||||
|
||||
private void ensureHandshake(UserManager m, WebSocket c, String name, String deviceID, boolean runningGame) {
|
||||
HelloServerMessage message = new HelloServerMessage();
|
||||
message.name = name;
|
||||
message.deviceID = deviceID;
|
||||
|
||||
HelloClientMessage response = new HelloClientMessage();
|
||||
response.runningGame = runningGame;
|
||||
|
||||
ensureResponse(m, c, message, response);
|
||||
}
|
||||
|
||||
private void ensurePlayerReady(UserManager m, WebSocket c, boolean startGame, RoleEnum role) {
|
||||
PlayerReadyMessage message = new PlayerReadyMessage();
|
||||
message.startGame = startGame;
|
||||
message.role = role;
|
||||
|
||||
GameAssignmentMessage response = new GameAssignmentMessage();
|
||||
//properties are left null because we can't test their content (they are random)
|
||||
|
||||
ensureResponse(m, c, message, response);
|
||||
}
|
||||
|
||||
private void ensureSpectatorReady(UserManager m, WebSocket c, boolean startGame, RoleEnum role) {
|
||||
PlayerReadyMessage message = new PlayerReadyMessage();
|
||||
message.startGame = startGame;
|
||||
message.role = role;
|
||||
|
||||
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
|
||||
//properties are left null because we can't test their content (they are random)
|
||||
|
||||
ensureResponse(m, c, message, response);
|
||||
}
|
||||
|
||||
private void ensureCharacterSelection(UserManager m, WebSocket c, boolean selectionComplete) {
|
||||
CharacterSelectionMessage message = new CharacterSelectionMessage();
|
||||
message.characters = new Boolean[12];
|
||||
for(int i = 0; i < 6; i++) {
|
||||
message.characters[i] = true;
|
||||
}
|
||||
for(int i = 6; i < 12; i++) {
|
||||
message.characters[i] = false;
|
||||
}
|
||||
|
||||
ConfirmSelectionMessage response = new ConfirmSelectionMessage();
|
||||
response.selectionComplete = selectionComplete;
|
||||
|
||||
ensureResponse(m, c, message, response);
|
||||
}
|
||||
|
||||
|
||||
/** Ensures that the given socket received the given response in response to the given message. */
|
||||
private void ensureResponse(UserManager m, WebSocket c, BasicMessage message, BasicMessage response) {
|
||||
Optional<String> in = json.stringify(message);
|
||||
|
||||
if(in.isPresent()) {
|
||||
m.messageReceived(c, in.get());
|
||||
verify(c).send((String)argThat(
|
||||
(out)->verifyResponse((String)out, response)
|
||||
));
|
||||
clearInvocations(c);
|
||||
}else {
|
||||
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensures that the given socket received the given response. */
|
||||
private void ensureReceived(WebSocket c, BasicMessage response) {
|
||||
verify(c).send((String)argThat(
|
||||
(out)->verifyResponse((String)out, response)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies a response message and requires all non-null fields of the expected message
|
||||
* to equal the respective values in the actual received message.
|
||||
*/
|
||||
private <T extends BasicMessage> boolean verifyResponse(String actual, T expected) {
|
||||
Optional<BasicMessage> in = json.parse(actual);
|
||||
|
||||
if(in.isPresent()) {
|
||||
T message = (T) in.get();
|
||||
|
||||
for(Field field: expected.getClass().getDeclaredFields()) {
|
||||
try {
|
||||
Object value = field.get(expected);
|
||||
if(value != null && !value.equals(field.get(message))) {
|
||||
throw new IllegalArgumentException("[TEST] Field " + field.getName() + " expected to be '" + value + "' but was '" + field.get(message) + "'");
|
||||
}
|
||||
}catch(IllegalAccessException ignore) { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}else {
|
||||
throw new IllegalArgumentException("[TEST] Response in test call could not be deserialized!");
|
||||
}
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void stop() {
|
||||
serverMock.close();
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package uulm.teamname.marvelous.server.netconnector;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class MarvelousServerTest {
|
||||
|
||||
MarvelousServer server;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
server = spy(MarvelousServer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void runServerForTesting() throws InterruptedException {
|
||||
// server = new MarvelousServer(new InetSocketAddress(1234));
|
||||
// Thread serverThread = new Thread(() -> server.start());
|
||||
// serverThread.join();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user