From eb1933c0a980b15bd1fdb861abad0ad8499bd324 Mon Sep 17 00:00:00 2001 From: Relentless Date: Mon, 5 Dec 2022 21:55:59 +0100 Subject: [PATCH] port 1.19 changes - improve duplicate checks for implicit counts of 1 (#28) - reorganize json compare tests - fixes #27 --- CHANGELOG.md | 5 + .../unified/recipe/RecipeLink.java | 4 +- .../unified/utils/JsonCompare.java | 136 +++++++++++- .../unified/utils/JsonQuery.java | 22 ++ .../com/almostreliable/unified/TestUtils.java | 192 +++++++++++++++-- .../unified/util/JsonCompareTest.java | 201 ++++++------------ 6 files changed, 409 insertions(+), 151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f20ec4..4521a4c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning]. +## Unreleased + +### Changed +- improved duplicate checks for recipes with implicit counts of 1 + ## [0.3.1] - 2022-12-01 ### Fixed diff --git a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java index 7ed878e9..4252f0be 100644 --- a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java +++ b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java @@ -46,8 +46,8 @@ public static RecipeLink compare(RecipeLink first, RecipeLink second, JsonCompar JsonObject compare = null; if (first.getType().toString().equals("minecraft:crafting_shaped")) { - compare = JsonCompare.compareShaped(selfActual, toCompareActual, compareSettings.getIgnoredFields()); - } else if (JsonCompare.matches(selfActual, toCompareActual, compareSettings.getIgnoredFields())) { + compare = JsonCompare.compareShaped(selfActual, toCompareActual, compareSettings); + } else if (JsonCompare.matches(selfActual, toCompareActual, compareSettings)) { compare = JsonCompare.compare(compareSettings.getRules(), selfActual, toCompareActual); } diff --git a/Common/src/main/java/com/almostreliable/unified/utils/JsonCompare.java b/Common/src/main/java/com/almostreliable/unified/utils/JsonCompare.java index 0b658f46..8a459fd7 100644 --- a/Common/src/main/java/com/almostreliable/unified/utils/JsonCompare.java +++ b/Common/src/main/java/com/almostreliable/unified/utils/JsonCompare.java @@ -10,6 +10,8 @@ public final class JsonCompare { + private static final Set SANITIZE_KEYS = Set.of("item", "tag", "id"); + private JsonCompare() {} public static int compare(JsonObject first, JsonObject second, Map rules) { @@ -36,8 +38,8 @@ public static JsonObject compare(Map rules, JsonObject... jsonObje } @Nullable - public static JsonObject compareShaped(JsonObject first, JsonObject second, Collection ignoredFields) { - if (!matches(first, second, ignoredFields)) return null; + public static JsonObject compareShaped(JsonObject first, JsonObject second, CompareSettings compareSettings) { + if (!matches(first, second, compareSettings)) return null; JsonArray firstPattern = JsonUtils.arrayOrSelf(first.get("pattern")); JsonArray secondPattern = JsonUtils.arrayOrSelf(second.get("pattern")); @@ -86,22 +88,35 @@ private static Map createShapedKeyMap(JsonObject json) { return keyMap; } - public static boolean matches(JsonObject first, JsonObject second, Collection ignoredProperties) { + public static boolean matches(JsonObject first, JsonObject second, CompareSettings compareSettings) { + Collection ignoredFields = compareSettings.getIgnoredFields(); List firstValidKeys = first .keySet() .stream() - .filter(key -> !ignoredProperties.contains(key)) + .filter(key -> !ignoredFields.contains(key)) .toList(); List secondValidKeys = second .keySet() .stream() - .filter(key -> !ignoredProperties.contains(key)) + .filter(key -> !ignoredFields.contains(key)) .toList(); if (firstValidKeys.size() != secondValidKeys.size()) return false; for (String firstKey : firstValidKeys) { - if (!first.get(firstKey).equals(second.get(firstKey))) { + JsonElement firstElem = first.get(firstKey); + JsonElement secondElem = second.get(firstKey); + + // the second element can still be null although the valid keys have the same size + if (secondElem == null) return false; + + // sanitize elements for implicit counts of 1 + if (compareSettings.shouldSanitize && needsSanitizing(firstElem, secondElem)) { + firstElem = sanitize(firstElem); + secondElem = sanitize(secondElem); + } + + if (!firstElem.equals(secondElem)) { return false; } } @@ -109,6 +124,105 @@ public static boolean matches(JsonObject first, JsonObject second, Collection + * Conditions are both elements being a JSON array with the same size, both elements being + * a JSON object, one element being a JSON object and the other being a JSON primitive. + * @param firstElem the first element + * @param secondElem the second element + * @return true if the elements need to be sanitized, false otherwise + */ + private static boolean needsSanitizing(JsonElement firstElem, JsonElement secondElem) { + return (firstElem instanceof JsonArray firstArray && secondElem instanceof JsonArray secondArray && + firstArray.size() == secondArray.size()) || + (firstElem instanceof JsonObject && secondElem instanceof JsonObject) || + (firstElem instanceof JsonPrimitive && secondElem instanceof JsonObject) || + (firstElem instanceof JsonObject && secondElem instanceof JsonPrimitive); + } + + /** + * Creates a sanitized object from the given element with a count of 1 and the + * value from the original object under a dummy key called "au_sanitized". + *

+ * If the element is not a string primitive, the default object is returned. + * @param value The value to sanitize + * @param defaultValue The default value to return if the element is not a string primitive + * @return The sanitized object or the default value + */ + private static JsonElement createSanitizedObjectOrDefault(JsonElement value, JsonElement defaultValue) { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + var newObject = new JsonObject(); + newObject.addProperty("au_sanitized", primitive.getAsString()); + newObject.addProperty("count", 1); + return newObject; + } + return defaultValue; + } + + /** + * Used to sanitize root level JSON elements to make them comparable when the count of 1 is implicit. + *

+ * This transforms string primitives, JSON objects and JSON arrays to JSON objects where the + * count of 1 is explicitly set. The transformation is only applied to a dummy object and not to + * the original recipe, so it can be safely used for comparison. + *

+ * If the object doesn't support this transformation, the original object is returned. + * @param element The element to sanitize + * @return The sanitized element or the original element if it can't be sanitized + */ + @SuppressWarnings("ChainOfInstanceofChecks") + private static JsonElement sanitize(JsonElement element) { + if (element instanceof JsonArray jsonArray) { + JsonArray newArray = new JsonArray(); + for (JsonElement arrayElement : jsonArray) { + newArray.add(sanitize(arrayElement)); + } + return newArray; + } + + if (element instanceof JsonObject jsonObject) { + var keySet = jsonObject.keySet(); + + if (keySet.stream().filter(SANITIZE_KEYS::contains).count() != 1) { + return element; + } + + // if it has a count property, it needs to be 1, otherwise it's implicit 1 as well and needs sanitizing + if (keySet.contains("count") && JsonQuery.of(jsonObject, "count").asInt().filter(i -> i == 1).isEmpty()) { + return element; + } + + var key = keySet.stream().filter(SANITIZE_KEYS::contains).findFirst().orElseThrow(); + var sanitized = createSanitizedObjectOrDefault(jsonObject.get(key), jsonObject); + + // ensure the object changed (was sanitized) and that we got a JsonObject + //noinspection ObjectEquality + if (sanitized == jsonObject || !(sanitized instanceof JsonObject sanitizedObject)) { + return jsonObject; + } + + mergeRemainingProperties(jsonObject, sanitizedObject); + return sanitizedObject; + } + + return createSanitizedObjectOrDefault(element, element); + } + + /** + * Merges remaining properties from the original object to the sanitized object. + * @param jsonObject The original object + * @param sanitizedObject The sanitized object + */ + private static void mergeRemainingProperties(JsonObject jsonObject, JsonObject sanitizedObject) { + for (var entry : jsonObject.entrySet()) { + if (!SANITIZE_KEYS.contains(entry.getKey()) && !entry.getKey().equals("count")) { + sanitizedObject.add(entry.getKey(), entry.getValue()); + } + } + } + public interface Rule { /** * Compare two JsonElements. The caller must ensure that at least one element is not null. @@ -154,9 +268,11 @@ public String getName() { public static class CompareSettings { public static final String IGNORED_FIELDS = "ignoredFields"; public static final String RULES = "rules"; + public static final String SHOULD_SANITIZE = "shouldSanitize"; private final LinkedHashMap rules = new LinkedHashMap<>(); private final Set ignoredFields = new HashSet<>(); + private boolean shouldSanitize; public void ignoreField(String property) { ignoredFields.add(property); @@ -170,6 +286,10 @@ public void addRule(String key, Rule rule) { } } + public void setShouldSanitize(boolean shouldSanitize) { + this.shouldSanitize = shouldSanitize; + } + public Set getIgnoredFields() { return Collections.unmodifiableSet(ignoredFields); } @@ -187,6 +307,8 @@ public JsonObject serialize() { }); result.add(RULES, rulesJson); + result.addProperty(SHOULD_SANITIZE, shouldSanitize); + return result; } @@ -203,6 +325,8 @@ public void deserialize(JsonObject json) { }; addRule(e.getKey(), r); }); + + shouldSanitize = json.getAsJsonPrimitive(SHOULD_SANITIZE).getAsBoolean(); } public Map getRules() { diff --git a/Common/src/main/java/com/almostreliable/unified/utils/JsonQuery.java b/Common/src/main/java/com/almostreliable/unified/utils/JsonQuery.java index f943f52f..6560df9e 100644 --- a/Common/src/main/java/com/almostreliable/unified/utils/JsonQuery.java +++ b/Common/src/main/java/com/almostreliable/unified/utils/JsonQuery.java @@ -3,6 +3,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; @@ -79,4 +80,25 @@ public Optional asArray() { public Optional asString() { return asElement().filter(JsonElement::isJsonPrimitive).map(JsonElement::getAsString); } + + public Optional asInt() { + return asElement().filter(JsonElement::isJsonPrimitive).map(JsonElement::getAsJsonPrimitive) + .filter(JsonPrimitive::isNumber).map(JsonElement::getAsInt); + } + + public JsonQuery shallowCopy() { + if (element == null) { + return new JsonQuery(); + } + + if (element instanceof JsonObject jsonObject) { + var copyObject = new JsonObject(); + for (var entry : jsonObject.entrySet()) { + copyObject.add(entry.getKey(), entry.getValue()); + } + return new JsonQuery(copyObject); + } + + throw new UnsupportedOperationException(); + } } diff --git a/Common/src/test/java/com/almostreliable/unified/TestUtils.java b/Common/src/test/java/com/almostreliable/unified/TestUtils.java index ba348e44..827b7d28 100644 --- a/Common/src/test/java/com/almostreliable/unified/TestUtils.java +++ b/Common/src/test/java/com/almostreliable/unified/TestUtils.java @@ -5,10 +5,7 @@ import com.almostreliable.unified.config.UnifyConfig; import com.almostreliable.unified.recipe.RecipeTransformer; import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory; -import com.almostreliable.unified.utils.ReplacementMap; -import com.almostreliable.unified.utils.TagMap; -import com.almostreliable.unified.utils.TagMapTests; -import com.almostreliable.unified.utils.UnifyTag; +import com.almostreliable.unified.utils.*; import com.google.gson.Gson; import com.google.gson.JsonObject; import net.minecraft.core.Registry; @@ -22,36 +19,59 @@ import java.util.Set; import java.util.function.Consumer; -public class TestUtils { +public final class TestUtils { + public static final String TEST_MOD_1 = "test_mod_1"; public static final String TEST_MOD_2 = "test_mod_2"; public static final String TEST_MOD_3 = "test_mod_3"; public static final String TEST_MOD_4 = "test_mod_4"; public static final String TEST_MOD_5 = "test_mod_5"; - public static final List TEST_MOD_PRIORITIES = List.of(TEST_MOD_1, + + public static final List TEST_MOD_PRIORITIES = List.of( + TEST_MOD_1, TEST_MOD_2, TEST_MOD_3, TEST_MOD_4, - TEST_MOD_5); - public static UnifyConfig DEFAULT_UNIFY_CONFIG = new UnifyConfig(Defaults.STONE_STRATA, + TEST_MOD_5 + ); + + public static final UnifyConfig DEFAULT_UNIFY_CONFIG = new UnifyConfig( + Defaults.STONE_STRATA, Defaults.MATERIALS, Defaults.getTags(Platform.FORGE), - TestUtils.TEST_MOD_PRIORITIES, + TEST_MOD_PRIORITIES, new HashMap<>(), new HashSet<>(), new HashSet<>(), new HashSet<>(), new HashSet<>(), - true); + true + ); + + public static final JsonCompare.CompareSettings DEFAULT_COMPARE_SETTINGS = getDefaultCompareSettings(); + public static final JsonCompare.CompareSettings DEFAULT_SHAPED_COMPARE_SETTINGS = getDefaultShapedCompareSettings(); + + private TestUtils() {} + + public static JsonCompare.CompareSettings getDefaultCompareSettings() { + return Defaults.getDefaultDuplicateRules(Platform.FORGE); + } + + public static JsonCompare.CompareSettings getDefaultShapedCompareSettings() { + return Defaults.getDefaultDuplicateOverrides(Platform.FORGE).get(new ResourceLocation("crafting_shaped")); + } + public static final ResourceKey> FAKE_ITEM_REGISTRY = FakeResourceKeyRegistry.create("item"); public static final UnifyTag BRONZE_ORES_TAG = tag("forge:ores/bronze"); public static final UnifyTag INVAR_ORES_TAG = tag("forge:ores/invar"); public static final UnifyTag TIN_ORES_TAG = tag("forge:ores/tin"); public static final UnifyTag SILVER_ORES_TAG = tag("forge:ores/silver"); - public static final List> TEST_ALLOWED_TAGS = List.of(BRONZE_ORES_TAG, + public static final List> TEST_ALLOWED_TAGS = List.of( + BRONZE_ORES_TAG, INVAR_ORES_TAG, TIN_ORES_TAG, - SILVER_ORES_TAG); + SILVER_ORES_TAG + ); /** * ResourceKey is null because otherwise tests can't run because Minecraft is not bootstrapped ... @@ -106,4 +126,152 @@ public static JsonObject json(String json, Consumer consumer) { consumer.accept(obj); return obj; } + + public static final class Recipes { + + public static final String SMELTING = """ + { + "type": "minecraft:smelting", + "group": "coal", + "ingredient": { + "item": "minecraft:coal_ore" + }, + "result": "minecraft:coal", + "experience": 0.1, + "cookingtime": 200 + } + """; + public static final String SHAPED_NO_MATCH_1 = """ + { + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "ici", + "iii" + ], + "key": { + "i": { + "tag": "forge:raw_materials/iron" + }, + "k": { + "item": "minecraft:carrot" + } + }, + "result": "minecraft:iron_ingot" + } + """; + public static final String SHAPED_NO_MATCH_2 = """ + { + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "ici", + "iii" + ], + "key": { + "i": { + "tag": "forge:raw_materials/iron" + }, + "k": { + "item": "minecraft:pumpkin" + } + }, + "result": "minecraft:iron_ingot" + } + """; + public static final String SHAPED_SPECIAL_MATCH_1 = """ + { + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "iii", + "iii" + ], + "key": { + "i": { + "tag": "forge:raw_materials/iron" + } + }, + "result": "minecraft:iron_ingot" + } + """; + public static final String SHAPED_SPECIAL_MATCH_2 = """ + { + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "iki", + "iii" + ], + "key": { + "i": { + "tag": "forge:raw_materials/iron" + }, + "k": { + "tag": "forge:raw_materials/iron" + } + }, + "result": "minecraft:iron_ingot" + } + """; + public static final String SHAPED_SANITIZE_1 = """ + { + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "iii", + "iii" + ], + "key": { + "i": { + "tag": "forge:raw_materials/iron" + } + }, + "result": "minecraft:iron_ingot" + } + """; + public static final String SHAPED_SANITIZE_2 = """ + { + "type": "minecraft:crafting_shaped", + "pattern": [ + "iii", + "iii", + "iii" + ], + "key": { + "i": { + "tag": "forge:raw_materials/iron" + } + }, + "result": { + "item": "minecraft:iron_ingot", + "count": 1 + } + } + """; + public static final String CRUSHING_NESTED_SANITIZE_1 = """ + { + "type": "create:crushing", + "ingredients": [{ "tag": "forge:raw_materials/lead" }], + "processingTime": 400, + "results": [ + { "item": "emendatusenigmatica:crushed_lead_ore" }, + { "chance": 0.75, "item": "create:experience_nugget" } + ] + } + """; + public static final String CRUSHING_NESTED_SANITIZE_2 = """ + { + "type": "create:crushing", + "ingredients": [{ "tag": "forge:raw_materials/lead" }], + "processingTime": 400, + "results": [ + { "count": 1, "item": "emendatusenigmatica:crushed_lead_ore" }, + { "chance": 0.75, "count": 1, "item": "create:experience_nugget" } + ] + } + """; + + private Recipes() {} + } } diff --git a/Common/src/test/java/com/almostreliable/unified/util/JsonCompareTest.java b/Common/src/test/java/com/almostreliable/unified/util/JsonCompareTest.java index 4689d0e3..95c242ab 100644 --- a/Common/src/test/java/com/almostreliable/unified/util/JsonCompareTest.java +++ b/Common/src/test/java/com/almostreliable/unified/util/JsonCompareTest.java @@ -8,78 +8,70 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.*; public class JsonCompareTest { - public static String recipe = """ - { - "type": "minecraft:smelting", - "group": "coal", - "ingredient": { - "item": "minecraft:coal_ore" - }, - "result": "minecraft:coal", - "experience": 0.1, - "cookingtime": 200 - } - """; + + private static final String EXPERIENCE = "experience"; + private static final String COOKING_TIME = "cookingtime"; @Test public void simpleCompareFirst() { - JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 0.2)); - JsonObject second = TestUtils.json(recipe); // 0.1 experience + JsonObject first = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.2)); + JsonObject second = TestUtils.json(TestUtils.Recipes.SMELTING); // 0.1 experience - LinkedHashMap rules = new LinkedHashMap<>(); - rules.put("experience", new JsonCompare.LowerRule()); + Map rules = new LinkedHashMap<>(); + rules.put(EXPERIENCE, new JsonCompare.LowerRule()); JsonObject result = JsonCompare.compare(rules, first, second); assertEquals(second, result); } @Test public void simpleCompareSecond() { - JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 0.05)); - JsonObject second = TestUtils.json(recipe); // 0.1 experience + JsonObject first = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.05)); + JsonObject second = TestUtils.json(TestUtils.Recipes.SMELTING); // 0.1 experience - LinkedHashMap rules = new LinkedHashMap<>(); - rules.put("experience", new JsonCompare.LowerRule()); + Map rules = new LinkedHashMap<>(); + rules.put(EXPERIENCE, new JsonCompare.LowerRule()); JsonObject result = JsonCompare.compare(rules, first, second); assertEquals(first, result); } @Test public void compareHigherWins() { - JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 0.05)); - JsonObject second = TestUtils.json(recipe); // 0.1 experience // 0.1 experience + JsonObject first = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.05)); + JsonObject second = TestUtils.json(TestUtils.Recipes.SMELTING); // 0.1 experience - LinkedHashMap rules = new LinkedHashMap<>(); - rules.put("experience", new JsonCompare.HigherRule()); + Map rules = new LinkedHashMap<>(); + rules.put(EXPERIENCE, new JsonCompare.HigherRule()); JsonObject result = JsonCompare.compare(rules, first, second); assertEquals(second, result); } @Test public void compareMulti() { - JsonObject a = TestUtils.json(recipe, j -> { - j.addProperty("experience", 0.1); - j.addProperty("cookingtime", 100); + JsonObject a = TestUtils.json(TestUtils.Recipes.SMELTING, j -> { + j.addProperty(EXPERIENCE, 0.1); + j.addProperty(COOKING_TIME, 100); }); - JsonObject b = TestUtils.json(recipe, j -> j.addProperty("experience", 0.1)); - JsonObject c = TestUtils.json(recipe, j -> { - j.addProperty("experience", 0.1); - j.addProperty("cookingtime", 50); + JsonObject b = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.1)); + JsonObject c = TestUtils.json(TestUtils.Recipes.SMELTING, j -> { + j.addProperty(EXPERIENCE, 0.1); + j.addProperty(COOKING_TIME, 50); }); - JsonObject d = TestUtils.json(recipe, j -> j.addProperty("experience", 0.2)); - JsonObject e = TestUtils.json(recipe, j -> j.addProperty("experience", 0.2)); - JsonObject f = TestUtils.json(recipe, j -> j.addProperty("experience", 0.1)); - JsonObject g = TestUtils.json(recipe, j -> { - j.addProperty("experience", 0.2); - j.addProperty("cookingtime", 100); + JsonObject d = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.2)); + JsonObject e = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.2)); + JsonObject f = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 0.1)); + JsonObject g = TestUtils.json(TestUtils.Recipes.SMELTING, j -> { + j.addProperty(EXPERIENCE, 0.2); + j.addProperty(COOKING_TIME, 100); }); - LinkedHashMap rules = new LinkedHashMap<>(); - rules.put("experience", new JsonCompare.HigherRule()); - rules.put("cookingtime", new JsonCompare.LowerRule()); + Map rules = new LinkedHashMap<>(); + rules.put(EXPERIENCE, new JsonCompare.HigherRule()); + rules.put(COOKING_TIME, new JsonCompare.LowerRule()); List list = Arrays.asList(a, b, c, d, e, f, g); list.sort((first, second) -> JsonCompare.compare(first, second, rules)); @@ -91,116 +83,63 @@ public void compareMulti() { @Test public void simpleMatch() { - JsonObject first = TestUtils.json(recipe); - JsonObject second = TestUtils.json(recipe); - boolean matches = JsonCompare.matches(first, second, List.of()); + JsonObject first = TestUtils.json(TestUtils.Recipes.SMELTING); + JsonObject second = TestUtils.json(TestUtils.Recipes.SMELTING); + boolean matches = JsonCompare.matches(first, second, TestUtils.DEFAULT_COMPARE_SETTINGS); assertTrue(matches); } @Test public void noMatch() { - JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 100)); - JsonObject second = TestUtils.json(recipe); - boolean matches = JsonCompare.matches(first, second, List.of()); + JsonObject first = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 100)); + JsonObject second = TestUtils.json(TestUtils.Recipes.SMELTING); + boolean matches = JsonCompare.matches(first, second, new JsonCompare.CompareSettings()); assertFalse(matches); } @Test public void matchBecauseIgnore() { - JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 100)); - JsonObject second = TestUtils.json(recipe); - boolean matches = JsonCompare.matches(first, second, List.of("experience")); + JsonObject first = TestUtils.json(TestUtils.Recipes.SMELTING, j -> j.addProperty(EXPERIENCE, 100)); + JsonObject second = TestUtils.json(TestUtils.Recipes.SMELTING); + var compareSettings = TestUtils.getDefaultCompareSettings(); + compareSettings.ignoreField(EXPERIENCE); + boolean matches = JsonCompare.matches(first, second, compareSettings); assertTrue(matches); } @Test public void shapedNoMatch() { - String recipe1 = """ - { - "type": "minecraft:crafting_shaped", - "pattern": [ - "iii", - "ici", - "iii" - ], - "key": { - "i": { - "tag": "forge:raw_materials/iron" - }, - "k": { - "item": "minecraft:carrot" - } - }, - "result": "minecraft:iron_ingot" - } - """; - String recipe2 = """ - { - "type": "minecraft:crafting_shaped", - "pattern": [ - "iii", - "ici", - "iii" - ], - "key": { - "i": { - "tag": "forge:raw_materials/iron" - }, - "k": { - "item": "minecraft:pumpkin" - } - }, - "result": "minecraft:iron_ingot" - } - """; - - JsonObject first = TestUtils.json(recipe1); - JsonObject second = TestUtils.json(recipe2); - JsonObject result = JsonCompare.compareShaped(first, second, List.of("pattern", "key")); + JsonObject first = TestUtils.json(TestUtils.Recipes.SHAPED_NO_MATCH_1); + JsonObject second = TestUtils.json(TestUtils.Recipes.SHAPED_NO_MATCH_2); + JsonObject result = JsonCompare.compareShaped(first, second, TestUtils.DEFAULT_SHAPED_COMPARE_SETTINGS); assertNull(result); } @Test - public void shapedMatch() { - String recipe1 = """ - { - "type": "minecraft:crafting_shaped", - "pattern": [ - "iii", - "iii", - "iii" - ], - "key": { - "i": { - "tag": "forge:raw_materials/iron" - } - }, - "result": "minecraft:iron_ingot" - } - """; - String recipe2 = """ - { - "type": "minecraft:crafting_shaped", - "pattern": [ - "iii", - "iki", - "iii" - ], - "key": { - "i": { - "tag": "forge:raw_materials/iron" - }, - "k": { - "tag": "forge:raw_materials/iron" - } - }, - "result": "minecraft:iron_ingot" - } - """; - - JsonObject first = TestUtils.json(recipe1); - JsonObject second = TestUtils.json(recipe2); - JsonObject result = JsonCompare.compareShaped(first, second, List.of("pattern", "key")); + public void shapedSpecialMatch() { + JsonObject first = TestUtils.json(TestUtils.Recipes.SHAPED_SPECIAL_MATCH_1); + JsonObject second = TestUtils.json(TestUtils.Recipes.SHAPED_SPECIAL_MATCH_2); + JsonObject result = JsonCompare.compareShaped(first, second, TestUtils.DEFAULT_SHAPED_COMPARE_SETTINGS); + assertEquals(first, result); + } + + @Test + public void sanitizeImplicitCount() { + JsonObject first = TestUtils.json(TestUtils.Recipes.SHAPED_SANITIZE_1); + JsonObject second = TestUtils.json(TestUtils.Recipes.SHAPED_SANITIZE_2); + var compareSettings = TestUtils.getDefaultShapedCompareSettings(); + compareSettings.setShouldSanitize(true); + JsonObject result = JsonCompare.compareShaped(first, second, compareSettings); assertEquals(first, result); } + + @Test + public void sanitizeImplicitCountNested() { + JsonObject first = TestUtils.json(TestUtils.Recipes.CRUSHING_NESTED_SANITIZE_1); + JsonObject second = TestUtils.json(TestUtils.Recipes.CRUSHING_NESTED_SANITIZE_2); + var compareSettings = TestUtils.getDefaultCompareSettings(); + compareSettings.setShouldSanitize(true); + boolean result = JsonCompare.matches(first, second, compareSettings); + assertTrue(result); + } }