Test: validate that view data is meant to be sent.

This commit is contained in:
Susucre 2023-08-30 23:57:12 +02:00
parent fa8e93a29d
commit cf7045452d

View file

@ -0,0 +1,184 @@
package mage.view;
import org.checkerframework.checker.units.qual.C;
import org.junit.Assert;
import org.junit.Test;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
public class ValidateViews {
// All those class are sent to client as Views or inside Views.
// The test in this file is attempting to find any class that should
// not be sent.
private static List<Class<?>> toValidate = Arrays.asList(
// Views
AbilityPickerView.class, AbilityView.class, CardsView.class,
CardView.class, ChatMessage.class, CombatGroupView.class,
CommanderView.class, CommandObjectView.class, CounterView.class,
DeckView.class, DraftClientMessage.class, DraftPickView.class,
DraftView.class, DungeonView.class, EmblemView.class,
ExileView.class, GameClientMessage.class, GameEndView.class,
GameTypeView.class, GameView.class, LookedAtView.class,
ManaPoolView.class, MatchView.class, PermanentView.class,
PermanentView.class, PlaneView.class, PlayerView.class,
RevealedView.class, RoomUsersView.class, RoundView.class,
SeatView.class, SelectableObjectView.class, SimpleCardsView.class,
SimpleCardView.class, StackAbilityView.class, TableClientMessage.class,
TableView.class, TournamentGameView.class, TournamentPlayerView.class,
TournamentTypeView.class, TournamentView.class, UserDataView.class,
UserRequestMessage.class, UsersView.class, UserView.class,
ChatMessage.MessageColor.class,
ChatMessage.SoundToPlay.class,
ChatMessage.MessageType.class,
// From mage
mage.constants.MageObjectType.class,
mage.constants.AbilityType.class,
mage.players.PlayerType.class,
mage.MageInt.class,
mage.ObjectColor.class,
mage.constants.CardType.class,
mage.constants.CardType.CardTypePredicate.class,
mage.constants.SuperType.class,
mage.constants.SuperType.SuperTypePredicate.class,
mage.constants.SubType.class,
mage.constants.SubType.SubTypePredicate.class,
mage.constants.SubTypeSet.class,
mage.util.SubTypes.class,
mage.constants.Rarity.class,
mage.constants.Zone.class,
mage.constants.PhaseStep.class,
mage.constants.TurnPhase.class,
mage.players.PlayableObjectStats.class,
mage.players.PlayableObjectsList.class,
mage.counters.Counters.class,
mage.counters.Counter.class,
mage.filter.FilterMana.class,
mage.abilities.icon.CardIcon.class,
mage.cards.ArtRect.class,
java.awt.geom.Rectangle2D.class,
mage.cards.FrameStyle.class,
mage.cards.FrameStyle.BorderType.class,
mage.choices.Choice.class,
mage.util.MultiAmountMessage.class,
mage.constants.PlayerAction.class,
mage.constants.SkillLevel.class,
mage.constants.TableState.class,
mage.players.net.UserData.class,
mage.players.net.UserSkipPrioritySteps.class,
mage.players.net.SkipPrioritySteps.class
);
// Those are safe java class we allow in Views.
private static List<Class<?>> safeClass = Arrays.asList(
boolean.class, int.class, long.class, float.class,
String.class, Date.class, UUID.class
);
// Those are non-public class
private static List<String> nonPublicNames = Arrays.asList(
"mage.players.PlayableObjectRecord"
);
// Those are excluded as Reflection on type of Map/Set/Collection is really hard.
private static List<String> tooHardToRecurseInto = Arrays.asList(
"mage.view.CardsView<java.util.LinkedHashMap>", // <UUID, CardView>
"mage.view.CardsView<java.util.HashMap>", // <UUID, CardView>
"mage.view.CardsView<java.util.AbstractMap>", // <UUID, CardView>
"mage.view.ExileView<java.util.LinkedHashMap>", // <UUID, CardView>
"mage.view.ExileView<java.util.HashMap>", // <UUID, CardView>
"mage.view.ExileView<java.util.AbstractMap>", // <UUID, CardView>
"mage.view.SimpleCardsView<java.util.LinkedHashMap>", // <UUID, CardView>
"mage.view.SimpleCardsView<java.util.HashMap>", // <UUID, CardView>
"mage.view.SimpleCardsView<java.util.AbstractMap>", // <UUID, CardView>
"mage.counters.Counters<java.util.AbstractMap>", // <String, Counter>
"mage.counters.Counters<java.util.HashMap>", // <String, Counter>
"mage.util.SubTypes<java.util.ArrayList>" // <SubType>
);
// Same, but for fields.
private static List<String> tooHardToRecurseIntoField = Arrays.asList(
"mage.view.GameClientMessage: <mage.view.GameClientMessage>::targets<java.util.Set>" // <UUID>
);
@Test
public void validateViewsContainSafeClass() {
for (Class<?> clazz : toValidate) {
validateView(clazz);
}
}
private void validateView(Class clazz) {
Class subClass = clazz;
while (subClass != Object.class && subClass != null) {
for (Field field : subClass.getDeclaredFields()) {
if (field.getName().startsWith("$VALUES")) {
// enum values.
continue;
}
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (tooHardToRecurseInto.contains(clazz.getName() + "<" + subClass.getName() + ">")) {
continue;
}
expectedClass(field.getGenericType(), field.getType(), clazz.getName() + ": <" + subClass.getName() + ">::" + field.getName());
}
subClass = subClass.getSuperclass();
}
}
private void expectedClass(Type type, Class clazz, String msg) {
if (toValidate.contains(clazz)) {
return;
}
if (safeClass.contains(clazz)) {
return;
}
if (nonPublicNames.contains(clazz.getName())) {
return;
}
if (clazz.equals(List.class) || clazz.equals(ArrayList.class) || clazz.equals(Map.class)) {
ParameterizedType paramType = (ParameterizedType) type;
if (type != null) {
Class paramClass = (Class) paramType.getActualTypeArguments()[0];
if (paramClass != null) {
expectedClass(null, paramClass, msg + "::List");
return;
}
}
}
msg = msg + "<" + clazz.getName() + ">";
if(tooHardToRecurseIntoField.contains(msg)) {
return;
}
// How to fix: If you added a new type meant to be sent to the client, add it
// in one of the class List above.
Assert.fail("Unexpected type in View " + msg);
}
}