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. */
|
/** 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");
|
Logger.debug("Connected new user");
|
||||||
synchronized(clients) {
|
synchronized(clients) {
|
||||||
clients.put(conn, new Client(conn));
|
clients.put(conn, new Client(conn));
|
||||||
@ -57,7 +57,7 @@ public class UserManager {
|
|||||||
* @param conn is the {@link WebSocket} that sent the message
|
* @param conn is the {@link WebSocket} that sent the message
|
||||||
* @param message is the {@link String} sent by the connection
|
* @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);
|
Logger.debug("Message received from {}", conn);
|
||||||
Client client = clients.get(conn);
|
Client client = clients.get(conn);
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ public class UserManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Called on closed connection. */
|
/** Called on closed connection. */
|
||||||
void disconnectUser(WebSocket conn, boolean closedByRemote) {
|
public void disconnectUser(WebSocket conn, boolean closedByRemote) {
|
||||||
Logger.info("Disconnecting client '{}'", conn);
|
Logger.info("Disconnecting client '{}'", conn);
|
||||||
Client client = clients.get(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