Skip to content

Commit

Permalink
Merge f57df24 into d9a76c9
Browse files Browse the repository at this point in the history
  • Loading branch information
fslev committed Oct 4, 2022
2 parents d9a76c9 + f57df24 commit 4dce87b
Show file tree
Hide file tree
Showing 16 changed files with 619 additions and 159 deletions.
33 changes: 17 additions & 16 deletions src/main/java/io/json/compare/JSONCompare.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.fasterxml.jackson.databind.JsonNode;
import io.json.compare.matcher.JsonMatcher;
import io.json.compare.matcher.MatcherException;
import io.json.compare.util.JsonUtils;
import org.junit.jupiter.api.AssertionFailureBuilder;

import java.io.IOException;
import java.util.List;
import java.util.Set;


Expand All @@ -16,7 +16,7 @@

public class JSONCompare {

private static final String ASSERTION_ERROR_HINT_MESSAGE = "Json matching by default uses regular expressions.\n" +
private static final String ASSERTION_ERROR_HINT_MESSAGE = "Json matching by default uses regular expressions." + System.lineSeparator() +
"In case expected json contains any unintentional regexes, then quote them between \\Q and \\E delimiters or use a custom comparator.";

public static void assertMatches(Object expected, Object actual) {
Expand Down Expand Up @@ -62,30 +62,31 @@ public static void assertNotMatches(Object expected, Object actual, Set<CompareM
public static void assertMatches(Object expected, Object actual, JsonComparator comparator, Set<CompareMode> compareModes, String message) {
JsonNode expectedJson = toJson(expected);
JsonNode actualJson = toJson(actual);
try {
new JsonMatcher(expectedJson, actualJson,
comparator == null ? new DefaultJsonComparator() : comparator, compareModes).match();
} catch (MatcherException e) {
String defaultMessage = String.format("%s\n", e.getMessage());
List<String> diffs = new JsonMatcher(expectedJson, actualJson,
comparator == null ? new DefaultJsonComparator() : comparator, compareModes).match();
if (!diffs.isEmpty()) {
String defaultMessage = String.format("FOUND %s DIFFERENCE(S):" + System.lineSeparator() + "%s" + System.lineSeparator(),
diffs.size(), diffs.stream().map(diff ->
System.lineSeparator() + System.lineSeparator() + "_________________________DIFF__________________________" +
System.lineSeparator() + diff).reduce(String::concat).get());
if (comparator == null || comparator.getClass().equals(DefaultJsonComparator.class)) {
defaultMessage += "\n\n" + ASSERTION_ERROR_HINT_MESSAGE + "\n";
defaultMessage += System.lineSeparator() + System.lineSeparator() + ASSERTION_ERROR_HINT_MESSAGE + System.lineSeparator();
}
AssertionFailureBuilder.assertionFailure().message(message == null ? defaultMessage : defaultMessage + "\n" + message)
AssertionFailureBuilder.assertionFailure().message(message == null ? defaultMessage : defaultMessage + System.lineSeparator() + message)
.expected(prettyPrint(expectedJson)).actual(prettyPrint(actualJson)).buildAndThrow();
}
}

public static void assertNotMatches(Object expected, Object actual, JsonComparator comparator, Set<CompareMode> compareModes, String message) {
JsonNode expectedJson = toJson(expected);
JsonNode actualJson = toJson(actual);
try {
new JsonMatcher(expectedJson, actualJson,
comparator == null ? new DefaultJsonComparator() : comparator, compareModes).match();
} catch (MatcherException e) {
List<String> diffs = new JsonMatcher(expectedJson, actualJson,
comparator == null ? new DefaultJsonComparator() : comparator, compareModes).match();
if (!diffs.isEmpty()) {
return;
}
String defaultMessage = "JSONs are equal";
AssertionFailureBuilder.assertionFailure().message(message == null ? defaultMessage : defaultMessage + "\n" + message)
String defaultMessage = System.lineSeparator() + "JSONs are equal";
AssertionFailureBuilder.assertionFailure().message(message == null ? defaultMessage : defaultMessage + System.lineSeparator() + message)
.expected(prettyPrint(expectedJson)).actual(prettyPrint(actualJson))
.includeValuesInMessage(false).buildAndThrow();
}
Expand All @@ -102,7 +103,7 @@ private static JsonNode toJson(Object obj) {
try {
return JsonUtils.toJson(obj);
} catch (IOException e) {
throw new RuntimeException(String.format("Invalid JSON\n%s\n", e));
throw new RuntimeException(String.format("Invalid JSON" + System.lineSeparator() + "%s" + System.lineSeparator(), e));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import io.json.compare.DefaultJsonComparator;
import io.json.compare.JsonComparator;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.*;

abstract class AbstractJsonMatcher {

Expand All @@ -27,7 +24,7 @@ abstract class AbstractJsonMatcher {
this.compareModes = compareModes == null ? new HashSet<>() : compareModes;
}

protected abstract void match() throws MatcherException;
protected abstract List<String> match();

protected static UseCase getUseCase(JsonNode node) {
if (node.isValueNode()) {
Expand Down
89 changes: 45 additions & 44 deletions src/main/java/io/json/compare/matcher/JsonArrayMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import io.json.compare.JsonComparator;
import io.json.compare.util.MessageUtil;

import java.util.HashSet;
import java.util.Set;
import java.util.*;

class JsonArrayMatcher extends AbstractJsonMatcher {

Expand All @@ -18,79 +17,81 @@ class JsonArrayMatcher extends AbstractJsonMatcher {
}

@Override
public void match() throws MatcherException {
public List<String> match() {
List<String> diffs = new ArrayList<>();

for (int i = 0; i < expected.size(); i++) {
JsonNode expElement = expected.get(i);
if (isJsonPathNode(expElement)) {
new JsonMatcher(expElement, actual, comparator, compareModes).match();
diffs.addAll(new JsonMatcher(expElement, actual, comparator, compareModes).match());
} else {
matchWithActualJsonArray(i, expElement, actual);
diffs.addAll(matchWithJsonArray(i, expElement, actual));
}
}
if (compareModes.contains(CompareMode.JSON_ARRAY_NON_EXTENSIBLE) && expected.size() < actual.size()) {
throw new MatcherException("Actual JSON ARRAY has extra elements");
diffs.add("Actual JSON ARRAY has extra elements");
}
return diffs;
}

private void matchWithActualJsonArray(int expPosition, JsonNode expElement, JsonNode actual) throws MatcherException {
private List<String> matchWithJsonArray(int expPosition, JsonNode expElement, JsonNode actualArray) {
List<String> diffs = new ArrayList<>();
UseCase useCase = getUseCase(expElement);
boolean found = false;
actualElementsLoop:
for (int j = 0; j < actual.size(); j++) {

for (int j = 0; j < actualArray.size(); j++) {
if (matchedPositions.contains(j)) {
continue;
}
if (compareModes.contains(CompareMode.JSON_ARRAY_STRICT_ORDER) && j != expPosition) {
continue;
}
List<String> elementDiffs;
switch (useCase) {
case MATCH:
JsonNode actElement = actual.get(j);
try {
new JsonMatcher(expElement, actElement, comparator, compareModes).match();
} catch (MatcherException e) {
JsonNode actElement = actualArray.get(j);
elementDiffs = new JsonMatcher(expElement, actElement, comparator, compareModes).match();
if (elementDiffs.isEmpty()) {
matchedPositions.add(j);
return Collections.emptyList();
} else {
if (compareModes.contains(CompareMode.JSON_ARRAY_STRICT_ORDER)) {
throw new MatcherException(String
.format("JSON ARRAY elements differ at position %s:\n%s", expPosition + 1,
MessageUtil.cropL(JSONCompare.prettyPrint(expElement))));
diffs.add(String.format("JSON ARRAY elements differ at position %s:" +
System.lineSeparator() + "%s" + System.lineSeparator() +
"________diffs________" + System.lineSeparator() + "%s", expPosition + 1,
MessageUtil.cropL(JSONCompare.prettyPrint(expElement)), String.join(
System.lineSeparator() + "_____________________" + System.lineSeparator(), elementDiffs)));
return diffs;
}
continue actualElementsLoop;
}
found = true;
matchedPositions.add(j);
break actualElementsLoop;
break;
case MATCH_ANY:
matchedPositions.add(j);
return;
return Collections.emptyList();
case DO_NOT_MATCH:
actElement = actual.get(j);
if (!areOfSameType(expElement, actElement)) {
continue actualElementsLoop;
}
try {
new JsonMatcher(expElement, actElement, comparator, compareModes).match();
} catch (MatcherException e) {
found = true;
break actualElementsLoop;
actElement = actualArray.get(j);
if (areOfSameType(expElement, actElement)) {
elementDiffs = new JsonMatcher(expElement, actElement, comparator, compareModes).match();
if (!elementDiffs.isEmpty()) {
diffs.add("Expected element from position " + (expPosition + 1)
+ " was FOUND:" + System.lineSeparator() + MessageUtil.cropL(JSONCompare.prettyPrint(expElement)));
return diffs;
}
}
break;
case DO_NOT_MATCH_ANY:
throw new MatcherException("Expected element from position " + (expPosition + 1)
+ " was FOUND:\n" + MessageUtil.cropL(JSONCompare.prettyPrint(expElement)));
diffs.add(String.format("Expected condition %s from position %s was not met." +
" Actual JSON array has extra elements.",
expElement, expPosition + 1));
return diffs;
}
}
if (!found && useCase == UseCase.MATCH) {
throw new MatcherException("Expected element from position " + (expPosition + 1) + " was NOT FOUND:\n"
+ MessageUtil.cropL(JSONCompare.prettyPrint(expElement)));
}
if (found && useCase == UseCase.DO_NOT_MATCH) {
throw new MatcherException("Expected element from position " + (expPosition + 1)
+ " was FOUND:\n" + MessageUtil.cropL(JSONCompare.prettyPrint(expElement)));
}
if (useCase == UseCase.MATCH_ANY) {
throw new MatcherException("Expected condition of type MATCH_ANY from position " + (expPosition + 1)
+ " was NOT MET. Actual Json Array has no extra elements:\n"
if (useCase == UseCase.MATCH) {
diffs.add(System.lineSeparator() + "Expected element from position " + (expPosition + 1) + " was NOT FOUND:" + System.lineSeparator()
+ MessageUtil.cropL(JSONCompare.prettyPrint(expElement)));
} else if (useCase == UseCase.MATCH_ANY) {
diffs.add(String.format("Expected condition %s from position %s was not met." +
" Actual Json Array has no extra elements.", expElement, expPosition + 1));
}
return diffs;
}
}
20 changes: 12 additions & 8 deletions src/main/java/io/json/compare/matcher/JsonMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import io.json.compare.CompareMode;
import io.json.compare.JsonComparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class JsonMatcher extends AbstractJsonMatcher {
Expand All @@ -13,20 +16,21 @@ public JsonMatcher(JsonNode expected, JsonNode actual, JsonComparator comparator
}

@Override
public void match() throws MatcherException {
public List<String> match() {
if (isJsonObject(expected) && isJsonObject(actual)) {
new JsonObjectMatcher(expected, actual, comparator, compareModes).match();
return new JsonObjectMatcher(expected, actual, comparator, compareModes).match();
} else if (isJsonArray(expected) && isJsonArray(actual)) {
new JsonArrayMatcher(expected, actual, comparator, compareModes).match();
return new JsonArrayMatcher(expected, actual, comparator, compareModes).match();
} else if (isValueNode(expected) && isValueNode(actual)) {
new JsonValueMatcher(expected, actual, comparator, compareModes).match();
return new JsonValueMatcher(expected, actual, comparator, compareModes).match();
} else if (isJsonPathNode(expected)) {
new JsonObjectMatcher(expected, actual, comparator, compareModes).match();
return new JsonObjectMatcher(expected, actual, comparator, compareModes).match();
} else if (isMissingNode(expected) && isMissingNode(actual)) {
//do nothing
return Collections.emptyList();
} else {
throw new MatcherException("Different JSON types: "
+ expected.getClass().getSimpleName() + " vs " + actual.getClass().getSimpleName());
List<String> diffs = new ArrayList<>();
diffs.add("Different JSON types: expected " + expected.getClass().getSimpleName() + " but got " + actual.getClass().getSimpleName());
return diffs;
}
}
}
Loading

0 comments on commit 4dce87b

Please sign in to comment.