Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support comparison of out-of-order basic collection types #47

Merged
merged 3 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public class GenericCompare {
public static void jsonCompare(Object obj1, Object obj2, CompareContext compareContext)
throws Exception {

compareContext.currentBaseObj = obj1;
compareContext.currentTestObj = obj2;

List<NodeEntity> currentNode =
compareContext.currentNodeLeft.size() >= compareContext.currentNodeRight.size()
? compareContext.currentNodeLeft : compareContext.currentNodeRight;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,79 @@

import com.arextest.diff.model.compare.CompareContext;
import com.arextest.diff.model.log.NodeEntity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;

public class IndexSelectorFactory {

private static Logger LOGGER = Logger.getLogger(IndexSelectorFactory.class.getName());

public static IndexSelector getIndexSelector(List<NodeEntity> currentNodeLeft,
List<NodeEntity> currentNodeRight, CompareContext compareContext) {
Map<Integer, String> indexKeysLeft = compareContext.listIndexKeysLeft.get(currentNodeLeft);
Map<Integer, String> indexKeysRight = compareContext.listIndexKeysRight.get(currentNodeRight);

if (indexKeysLeft == null && indexKeysRight == null) {
return new GeneralIndexSelector();
} else {
if (indexKeysLeft != null || indexKeysRight != null) {
return new ListKeyIndexSelector(indexKeysLeft, indexKeysRight, compareContext);
} else if (isPrimitiveArrayAndEqual(compareContext)) {
LOGGER.info("use PrimitiveArrayIndexSelector");
return new PrimitiveArrayIndexSelector(compareContext);
} else {
return new GeneralIndexSelector();
}
}

private static boolean isPrimitiveArrayAndEqual(CompareContext compareContext) {
Object currentBaseObj = compareContext.currentBaseObj;
Object currentTestObj = compareContext.currentTestObj;

if (!(currentBaseObj instanceof ArrayNode) || !(currentTestObj instanceof ArrayNode)) {
return false;
}

ArrayNode baseArray = (ArrayNode) currentBaseObj;
ArrayNode testArray = (ArrayNode) currentTestObj;

if (baseArray.size() != testArray.size()) {
return false;
}

if ((!baseArray.isEmpty() && !(baseArray.get(0).isValueNode())) ||
(!testArray.isEmpty() && !(testArray.get(0).isValueNode()))) {
return false;
}

List<String> baseList = new ArrayList<>();
List<String> testList = new ArrayList<>();

for (JsonNode jsonNode : baseArray) {
if (!jsonNode.isValueNode()) {
return false;
}
baseList.add(jsonNode.asText());
}

for (JsonNode jsonNode : testArray) {
if (!jsonNode.isValueNode()) {
return false;
}
testList.add(jsonNode.asText());
}

// If it can be directly compared before sorting,
// do not use PrimitiveArrayIndexSelector.
if (Objects.equals(baseList, testList)) {
return false;
coryhh marked this conversation as resolved.
Show resolved Hide resolved
}

Collections.sort(baseList);
Collections.sort(testList);
return Objects.equals(baseList, testList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.arextest.diff.compare.feature;

import com.arextest.diff.model.compare.CompareContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PrimitiveArrayIndexSelector implements IndexSelector {

private Map<JsonNode, List<Integer>> leftIndexKeys;
private Map<JsonNode, List<Integer>> rightIndexKeys;

public PrimitiveArrayIndexSelector() {
}

public PrimitiveArrayIndexSelector(CompareContext compareContext) {
this.leftIndexKeys = buildIndexKeys((ArrayNode) compareContext.currentBaseObj);
this.rightIndexKeys = buildIndexKeys((ArrayNode) compareContext.currentTestObj);
}

@Override
public int findCorrespondLeftIndex(int curRightIndex, List<Integer> leftComparedIndex,
ArrayNode obj1Array, ArrayNode obj2Array) {
JsonNode jsonNode = obj2Array.get(curRightIndex);
if (leftIndexKeys.containsKey(jsonNode)) {
List<Integer> indexCollection = leftIndexKeys.get(jsonNode);
for (Integer index : indexCollection) {
if (!leftComparedIndex.contains(index)) {
return index;
}
}
}
return -1;
}

@Override
public int findCorrespondRightIndex(int curLeftIndex, List<Integer> rightComparedIndex,
ArrayNode obj1Array, ArrayNode obj2Array) {
JsonNode jsonNode = obj1Array.get(curLeftIndex);
if (rightIndexKeys.containsKey(jsonNode)) {
List<Integer> indexCollection = rightIndexKeys.get(jsonNode);
for (Integer index : indexCollection) {
if (!rightComparedIndex.contains(index)) {
return index;
}
}
}
return -1;
}

@Override
public String judgeLeftIndexStandard(int leftIndex) {
return indexKey(leftIndex);
}

@Override
public String judgeRightIndexStandard(int rightIndex) {
return indexKey(rightIndex);
}

private Map<JsonNode, List<Integer>> buildIndexKeys(ArrayNode arrayNode) {
Map<JsonNode, List<Integer>> result = new HashMap<>();
for (int i = 0; i < arrayNode.size(); i++) {
JsonNode jsonNode = arrayNode.get(i);
if (result.containsKey(jsonNode)) {
result.get(jsonNode).add(i);
} else {
List<Integer> indexCollection = new ArrayList<>();
indexCollection.add(i);
result.put(jsonNode, indexCollection);
}
}
return result;
}

private String indexKey(int index) {
return "Index:[" + index + "]";
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@

public class CompareContext {

// baseObj and testObj are the original objects to be compared
public Object baseObj;
public Object testObj;

// currentBaseObj and currentTestObj are the objects being compared
public Object currentBaseObj;
public Object currentTestObj;

public List<List<ExpressionNodeEntity>> exclusions;
public List<List<ExpressionNodeEntity>> expressionExclusions;
public Set<String> ignoreNodeSet;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.arextest.diff.compare.feature;

import com.arextest.diff.model.compare.CompareContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.util.Collections;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class PrimitiveArrayIndexSelectorUnitTest {

static PrimitiveArrayIndexSelector primitiveArrayIndexSelector;

@BeforeAll
static void setUpClass() throws JsonProcessingException {
CompareContext compareContext = new CompareContext();
ObjectMapper objectMapper = new ObjectMapper();
compareContext.currentBaseObj = objectMapper.readValue("[1,2,3]", ArrayNode.class);
compareContext.currentTestObj = objectMapper.readValue("[3,1,2]", ArrayNode.class);
primitiveArrayIndexSelector = new PrimitiveArrayIndexSelector(compareContext);
}


@Test
public void testFindCorrespondLeftIndex() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode obj1 = objectMapper.readValue("[1,2,3]", ArrayNode.class);
ArrayNode obj2 = objectMapper.readValue("[3,1,2]", ArrayNode.class);

int correspondLeftIndex = primitiveArrayIndexSelector.findCorrespondLeftIndex(0,
Collections.emptyList(), obj1, obj2);
Assertions.assertEquals(2, correspondLeftIndex);
}

@Test
public void testFindCorrespondRightIndex() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode obj1 = objectMapper.readValue("[1,2,3]", ArrayNode.class);
ArrayNode obj2 = objectMapper.readValue("[3,1,2]", ArrayNode.class);

int correspondRightIndex = primitiveArrayIndexSelector.findCorrespondRightIndex(0,
Collections.emptyList(), obj1, obj2);
Assertions.assertEquals(1, correspondRightIndex);
}

@Test
public void testJudgeLeftIndexStandard() {
String indexKey = primitiveArrayIndexSelector.judgeLeftIndexStandard(0);
Assertions.assertEquals("Index:[0]", indexKey);
}

@Test
public void testJudgeRightIndexStandard() {
String indexKey = primitiveArrayIndexSelector.judgeRightIndexStandard(0);
Assertions.assertEquals("Index:[0]", indexKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -482,4 +482,33 @@ public void testExpressionNodeIgnore() {
System.out.println();
}

@Test
public void testPrimitiveArrayNodeAutoSort() {

CompareSDK sdk = new CompareSDK();
sdk.getGlobalOptions()
.putNameToLower(true)
.putNullEqualsEmpty(true);

String str1 = "{\n"
+ " \"classroom\": [\n"
+ " \"classroomA\",\n"
+ " \"classroomB\",\n"
+ " \"classroomA\",\n"
+ " \"classroomC\"\n"
+ " ]\n"
+ "}";

String str2 = "{\n"
+ " \"classroom\": [\n"
+ " \"classroomB\",\n"
+ " \"classroomA\",\n"
+ " \"classroomA\",\n"
+ " \"classroomC\"\n"
+ " ]\n"
+ "}";

CompareResult result = sdk.compare(str1, str2);
Assertions.assertEquals(0, result.getCode());
}
}