281 lines
10 KiB
Java
281 lines
10 KiB
Java
package uulm.teamname.marvelous.server;
|
|
|
|
import org.junit.jupiter.api.*;
|
|
import org.mockito.MockedStatic;
|
|
import org.mockito.Mockito;
|
|
import org.mockito.invocation.Invocation;
|
|
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.ParticipantType;
|
|
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.client.ReconnectMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.*;
|
|
import uulm.teamname.marvelous.server.net.Client;
|
|
import uulm.teamname.marvelous.server.net.ServerSession;
|
|
import uulm.teamname.marvelous.server.net.SocketEvent;
|
|
import uulm.teamname.marvelous.server.net.SocketEventType;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.util.Optional;
|
|
|
|
import static org.mockito.Mockito.*;
|
|
|
|
@SuppressWarnings({"ResultOfMethodCallIgnored", "SameParameterValue", "unchecked"})
|
|
class MarvelousServerApplicationTest extends BaseGameLogicTest {
|
|
private static ServerSession session;
|
|
private static MockedStatic<ServerApplication> serverMock;
|
|
private static JSON json;
|
|
|
|
@BeforeAll
|
|
static void start() {
|
|
session = new ServerSession();
|
|
|
|
serverMock = Mockito.mockStatic(ServerApplication.class);
|
|
|
|
generate();
|
|
|
|
serverMock.when(ServerApplication::getSession).thenReturn(session);
|
|
serverMock.when(ServerApplication::getPartyConfig).thenReturn(partyConfig);
|
|
serverMock.when(ServerApplication::getScenarioConfig).thenReturn(scenarioConfig);
|
|
serverMock.when(ServerApplication::getCharacterConfig).thenReturn(characterConfig);
|
|
|
|
json = new JSON(characterConfig);
|
|
|
|
if(!Configuration.isFrozen()) {
|
|
Configuration.set("writer1", "console");
|
|
Configuration.set("writer1.level", "trace");
|
|
Configuration.set("writer1.format", "[{thread}] {level}: <TEST> {message}");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@Disabled
|
|
void main() {
|
|
session.run();
|
|
|
|
Client p1 = mock(Client.class);
|
|
Client p2 = mock(Client.class);
|
|
Client s1 = mock(Client.class);
|
|
Client s2 = mock(Client.class);
|
|
|
|
session.addEvent(new SocketEvent(SocketEventType.Connect, p1));
|
|
ensureHandshake(p1, "Player 1", "1234", false);
|
|
|
|
session.addEvent(new SocketEvent(SocketEventType.Connect, p2));
|
|
ensureHandshake(p2, "Player 2", "4321", false);
|
|
|
|
session.addEvent(new SocketEvent(SocketEventType.Connect, s1));
|
|
ensureHandshake(s1, "Spectator 1", "3333", false);
|
|
|
|
session.addEvent(new SocketEvent(SocketEventType.Connect, s2));
|
|
ensureHandshake(s2, "Spectator 2", "4444", false);
|
|
|
|
ensurePlayerReady(p1, true, RoleEnum.PLAYER);
|
|
ensurePlayerReady(p2, true, RoleEnum.PLAYER);
|
|
ensureSpectatorReady(s1, true, RoleEnum.SPECTATOR);
|
|
|
|
ensureCharacterSelection(p1, true);
|
|
ensureCharacterSelection(p2, false);
|
|
|
|
GameStructureMessage game = new GameStructureMessage();
|
|
game.playerOneName = "Player 1";
|
|
game.playerTwoName = "Player 2";
|
|
game.matchconfig = partyConfig;
|
|
game.scenarioconfig = scenarioConfig;
|
|
|
|
game.assignment = ParticipantType.PlayerOne;
|
|
ensureReceived(p1, game);
|
|
|
|
game.assignment = ParticipantType.PlayerTwo;
|
|
ensureReceived(p2, game);
|
|
|
|
game.assignment = ParticipantType.Spectator;
|
|
ensureReceived(s1, game);
|
|
|
|
EventMessage events = new EventMessage();
|
|
ensureReceived(p1, events);
|
|
ensureReceived(p2, events);
|
|
ensureReceived(s1, events);
|
|
|
|
clearInvocations(p1, p2, s1);
|
|
|
|
session.addEvent(new SocketEvent(SocketEventType.Disconnect, p1));
|
|
|
|
session.addEvent(new SocketEvent(SocketEventType.Connect, p1));
|
|
ensureHandshake(p1, "Player 1", "1234", true);
|
|
|
|
ReconnectMessage message = new ReconnectMessage();
|
|
message.reconnect = true;
|
|
sendMessage(p1, message);
|
|
|
|
ensureReceived(p1, new GeneralAssignmentMessage());
|
|
ensureReceived(p1, new GameStructureMessage());
|
|
|
|
System.out.println("Test Completed");
|
|
}
|
|
|
|
private void ensureHandshake(Client 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(c, message, response);
|
|
}
|
|
|
|
private void ensurePlayerReady(Client 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(c, message, response);
|
|
}
|
|
|
|
private void ensureSpectatorReady(Client 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(c, message, response);
|
|
}
|
|
|
|
private void ensureCharacterSelection(Client c, boolean confirmSelection) {
|
|
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;
|
|
}
|
|
|
|
if(confirmSelection) {
|
|
ConfirmSelectionMessage response = new ConfirmSelectionMessage();
|
|
response.selectionComplete = false;
|
|
ensureResponse(c, message, response);
|
|
} else {
|
|
sendMessage(c, message);
|
|
}
|
|
}
|
|
|
|
|
|
/** Sends a message from the socket. */
|
|
private void sendMessage(Client c, BasicMessage message) {
|
|
Optional<String> in = json.stringify(message);
|
|
|
|
if(in.isPresent()) {
|
|
session.addEvent(new SocketEvent(SocketEventType.Message, c, in.get()));
|
|
}else {
|
|
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
|
|
}
|
|
}
|
|
|
|
/** Ensures that the given socket received the given response in response to the given message. */
|
|
private void ensureResponse(Client c, BasicMessage message, BasicMessage response) {
|
|
Optional<String> in = json.stringify(message);
|
|
|
|
if(in.isPresent()) {
|
|
session.addEvent(new SocketEvent(SocketEventType.Message, c, in.get()));
|
|
verify(c.getSocket()).send((String)argThat(
|
|
(out)->verifyResponse((String)out, response)
|
|
));
|
|
clearInvocations(c.getSocket());
|
|
}else {
|
|
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
|
|
}
|
|
}
|
|
|
|
/** Ensures that the given socket received the given response. */
|
|
private <T extends BasicMessage> void ensureReceived(Client c, T response) {
|
|
boolean found = false;
|
|
|
|
for(Invocation i: mockingDetails(c.getSocket()).getInvocations()) {
|
|
Optional<BasicMessage> received = json.parse(i.getArgument(0, String.class));
|
|
|
|
if(received.isPresent()) {
|
|
BasicMessage parsed = received.get();
|
|
|
|
if(parsed.getClass() != response.getClass()) {
|
|
continue; //message is not of the right type
|
|
}
|
|
|
|
T message = (T)parsed;
|
|
|
|
for(Field field: response.getClass().getDeclaredFields()) {
|
|
try {
|
|
Object value = field.get(response);
|
|
Object actualValue;
|
|
try {
|
|
actualValue = field.get(message);
|
|
}catch(IllegalArgumentException ignored) {
|
|
continue;
|
|
}
|
|
if(value != null && !value.equals(actualValue)) {
|
|
throw new IllegalArgumentException("[TEST] Field " + field.getName() + " expected to be '" + value + "' but was '" + field.get(message) + "'");
|
|
}
|
|
}catch(IllegalAccessException ignored) { }
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}else {
|
|
throw new IllegalArgumentException("[TEST] Response in test call could not be deserialized!");
|
|
}
|
|
}
|
|
|
|
if(!found) {
|
|
throw new IllegalArgumentException("[TEST] Expected response " + response + " was not received!");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|
|
Object actualValue;
|
|
try {
|
|
actualValue = field.get(message);
|
|
}catch(IllegalArgumentException e) {
|
|
throw new IllegalArgumentException("[TEST] Response message expected to be '" + expected.getClass().getName() + "' but was '" + message.getClass().getName() + "'");
|
|
}
|
|
if(value != null && !value.equals(actualValue)) {
|
|
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() {
|
|
session.stop();
|
|
serverMock.close();
|
|
}
|
|
}
|