package mage.verify;
import mage.ObjectColor;
import mage.cards.*;
import mage.cards.basiclands.BasicLand;
import mage.constants.CardType;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class VerifyCardDataTest {
// right now this is very noisy, and not useful enough to make any assertions on
private static final boolean CHECK_SOURCE_TOKENS = false;
public static List<Card> allCards() {
Collection<ExpansionSet> sets = Sets.getInstance().values();
List<Card> cards = new ArrayList<>();
for (ExpansionSet set : sets) {
if (set.isCustomSet()) {
continue;
}
for (ExpansionSet.SetCardInfo setInfo : set.getSetCardInfo()) {
cards.add(CardImpl.createCard(setInfo.getCardClass(), new CardSetInfo(setInfo.getName(), set.getCode(),
setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo())));
}
}
return cards;
}
private void warn(Card card, String message) {
System.out.println("Warning: " + message + " for " + card.getName());
}
private void fail(Card card, String category, String message) {
failed++;
System.out.println("Error: (" + category + ") " + message + " for " + card.getName());
}
private int failed = 0;
@Test
public void verifyCards() throws IOException {
for (Card card : allCards()) {
Set<String> tokens = findSourceTokens(card.getClass());
if (card.isSplitCard()) {
check(((SplitCard) card).getLeftHalfCard(), null);
check(((SplitCard) card).getRightHalfCard(), null);
} else {
check(card, tokens);
}
}
if (failed > 0) {
Assert.fail(failed + " Errors");
}
}
private static final Pattern SHORT_JAVA_STRING = Pattern.compile("(?<=\")[A-Z][a-z]+(?=\")");
private Set<String> findSourceTokens(Class c) throws IOException {
if (!CHECK_SOURCE_TOKENS || BasicLand.class.isAssignableFrom(c)) {
return null;
}
String path = "../Mage.Sets/src/" + c.getName().replace(".", "/") + ".java";
try {
String source = new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
Matcher matcher = SHORT_JAVA_STRING.matcher(source);
Set<String> tokens = new HashSet<>();
while (matcher.find()) {
tokens.add(matcher.group());
}
return tokens;
} catch (NoSuchFileException e) {
System.out.println("failed to read " + path);
return null;
}
}
private void check(Card card, Set<String> tokens) {
JsonCard ref = MtgJson.card(card.getName());
if (ref == null) {
warn(card, "Missing card reference");
return;
}
checkAll(card, ref);
if (tokens != null) {
JsonCard ref2 = null;
if (card.isFlipCard()) {
ref2 = MtgJson.card(card.getFlipCardName());
}
for (String token : tokens) {
if (!(token.equals(card.getName())
|| containsInTypesOrText(ref, token)
|| containsInTypesOrText(ref, token.toLowerCase())
|| (ref2 != null && (containsInTypesOrText(ref2, token) || containsInTypesOrText(ref2, token.toLowerCase()))))) {
System.out.println("unexpected token " + token + " in " + card);
}
}
}
}
private boolean containsInTypesOrText(JsonCard ref, String token) {
return contains(ref.types, token)
|| contains(ref.subtypes, token)
|| contains(ref.supertypes, token)
|| ref.text.contains(token);
}
private boolean contains(Collection<String> options, String value) {
return options != null && options.contains(value);
}
private void checkAll(Card card, JsonCard ref) {
checkCost(card, ref);
checkPT(card, ref);
checkSubtypes(card, ref);
checkSupertypes(card, ref);
checkTypes(card, ref);
checkColors(card, ref);
}
private void checkColors(Card card, JsonCard ref) {
// gatherer is missing the color indicator on one card:
if ("Ulrich, Uncontested Alpha".equals(ref.name)) {
return;
}
Collection<String> expected = ref.colors;
ObjectColor color = card.getColor(null);
if (expected == null) {
expected = Collections.emptyList();
}
if (expected.size() != color.getColorCount()
|| (color.isBlack() && !expected.contains("Black"))
|| (color.isBlue() && !expected.contains("Blue"))
|| (color.isGreen() && !expected.contains("Green"))
|| (color.isRed() && !expected.contains("Red"))
|| (color.isWhite() && !expected.contains("White"))) {
fail(card, "colors", color + " != " + expected);
}
}
private void checkSubtypes(Card card, JsonCard ref) {
Collection<String> expected = ref.subtypes;
if (expected != null && expected.contains("Urza’s")) {
expected = new ArrayList<>(expected);
for (ListIterator<String> it = ((List<String>) expected).listIterator(); it.hasNext();) {
if (it.next().equals("Urza’s")) {
it.set("Urza's");
}
}
}
if (!eqSet(card.getSubtype(null), expected)) {
fail(card, "subtypes", card.getSubtype(null) + " != " + expected);
}
}
private void checkSupertypes(Card card, JsonCard ref) {
Collection<String> expected = ref.supertypes;
if (!eqSet(card.getSuperType().stream().map(s->s.toString()).collect(Collectors.toList()), expected)) {
fail(card, "supertypes", card.getSuperType() + " != " + expected);
}
}
private void checkTypes(Card card, JsonCard ref) {
Collection<String> expected = ref.types;
List<String> type = new ArrayList<>();
for (CardType cardType : card.getCardType()) {
type.add(cardType.toString());
}
if (!eqSet(type, expected)) {
fail(card, "types", type + " != " + expected);
}
}
private static <T> boolean eqSet(Collection<T> a, Collection<T> b) {
if (a == null || a.isEmpty()) {
return b == null || b.isEmpty();
}
return b != null && a.size() == b.size() && a.containsAll(b);
}
private void checkPT(Card card, JsonCard ref) {
if (!eqPT(card.getPower().toString(), ref.power) || !eqPT(card.getToughness().toString(), ref.toughness)) {
String pt = card.getPower() + "/" + card.getToughness();
String expected = ref.power + '/' + ref.toughness;
fail(card, "pt", pt + " != " + expected);
}
}
private boolean eqPT(String found, String expected) {
if (expected == null) {
return "0".equals(found);
} else {
return found.equals(expected) || expected.contains("*");
}
}
private void checkCost(Card card, JsonCard ref) {
String expected = ref.manaCost;
String cost = join(card.getManaCost().getSymbols());
if (cost != null && cost.isEmpty()) {
cost = null;
}
if (cost != null) {
cost = cost.replaceAll("P\\}", "P}");
}
if (!Objects.equals(cost, expected)) {
fail(card, "cost", cost + " != " + expected);
}
}
private String join(Iterable<?> items) {
StringBuilder result = new StringBuilder();
for (Object item : items) {
result.append(item);
}
return result.toString();
}
}