Merge "Update mockito and transitive dependencies"
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 528efe3..b5f40ce 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -14,6 +14,8 @@
package com.google.gerrit.extensions.common;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.client.SubmitType;
@@ -110,4 +112,14 @@
public List<PluginDefinedInfo> plugins;
public Collection<TrackingIdInfo> trackingIds;
public Collection<SubmitRequirementInfo> requirements;
+
+ public ChangeInfo() {}
+
+ public ChangeInfo(ChangeMessageInfo... messages) {
+ this.messages = ImmutableList.copyOf(messages);
+ }
+
+ public ChangeInfo(Map<String, RevisionInfo> revisions) {
+ this.revisions = ImmutableMap.copyOf(revisions);
+ }
}
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfoDiffer.java b/java/com/google/gerrit/extensions/common/ChangeInfoDiffer.java
new file mode 100644
index 0000000..647dead
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/ChangeInfoDiffer.java
@@ -0,0 +1,191 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.common;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.Arrays.stream;
+import static java.util.stream.Collectors.groupingBy;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Gets the differences between two {@link ChangeInfo}s.
+ *
+ * <p>This must be in package {@code com.google.gerrit.extensions.common} for access to protected
+ * constructors.
+ *
+ * <p>This assumes that every class reachable from {@link ChangeInfo} has a non-private constructor
+ * with zero parameters and overrides the equals method.
+ */
+public final class ChangeInfoDiffer {
+
+ /**
+ * Returns the difference between two instances of {@link ChangeInfo}.
+ *
+ * <p>The {@link ChangeInfoDifference} returned has the following properties:
+ *
+ * <p>Unrepeated fields are present in the difference returned when they differ between {@code
+ * oldChangeInfo} and {@code newChangeInfo}. When there's an unrepeated field that's not a {@link
+ * String}, primitive, or enum, its fields are only returned when they differ.
+ *
+ * <p>Entries in {@link Map} fields are returned when a key is present in {@code newChangeInfo}
+ * and not {@code oldChangeInfo}. If a key is present in both, the diff of the value is returned.
+ *
+ * <p>{@link Collection} fields in {@link ChangeInfoDifference#added()} contain only items found
+ * in {@code newChangeInfo} and not {@code oldChangeInfo}.
+ *
+ * <p>{@link Collection} fields in {@link ChangeInfoDifference#removed()} contain only items found
+ * in {@code oldChangeInfo} and not {@code newChangeInfo}.
+ *
+ * @param oldChangeInfo the previous {@link ChangeInfo} to diff against {@code newChangeInfo}
+ * @param newChangeInfo the {@link ChangeInfo} to diff against {@code oldChangeInfo}
+ * @return the difference between the given {@link ChangeInfo}s
+ */
+ public static ChangeInfoDifference getDifference(
+ ChangeInfo oldChangeInfo, ChangeInfo newChangeInfo) {
+ return ChangeInfoDifference.create(
+ /* added= */ getAdded(oldChangeInfo, newChangeInfo),
+ /* removed= */ getAdded(newChangeInfo, oldChangeInfo));
+ }
+
+ @SuppressWarnings("unchecked") // reflection is used to construct instances of T
+ private static <T> T getAdded(T oldValue, T newValue) {
+ T toPopulate = (T) construct(newValue.getClass());
+ if (toPopulate == null) {
+ return null;
+ }
+
+ for (Field field : newValue.getClass().getDeclaredFields()) {
+ Object newFieldObj = get(field, newValue);
+ if (oldValue == null || newFieldObj == null) {
+ set(field, toPopulate, newFieldObj);
+ continue;
+ }
+
+ Object oldFieldObj = get(field, oldValue);
+ if (newFieldObj.equals(oldFieldObj)) {
+ continue;
+ }
+
+ if (isSimple(field.getType()) || oldFieldObj == null) {
+ set(field, toPopulate, newFieldObj);
+ } else if (newFieldObj instanceof Collection) {
+ set(
+ field,
+ toPopulate,
+ getAddedForCollection((Collection<?>) oldFieldObj, (Collection<?>) newFieldObj));
+ } else if (newFieldObj instanceof Map) {
+ set(field, toPopulate, getAddedForMap((Map<?, ?>) oldFieldObj, (Map<?, ?>) newFieldObj));
+ } else {
+ // Recurse to set all fields in the non-primitive object.
+ set(field, toPopulate, getAdded(oldFieldObj, newFieldObj));
+ }
+ }
+ return toPopulate;
+ }
+
+ @VisibleForTesting
+ static boolean isSimple(Class<?> c) {
+ return c.isPrimitive()
+ || c.isEnum()
+ || String.class.isAssignableFrom(c)
+ || Number.class.isAssignableFrom(c)
+ || Boolean.class.isAssignableFrom(c)
+ || Timestamp.class.isAssignableFrom(c);
+ }
+
+ @VisibleForTesting
+ static Object construct(Class<?> c) {
+ // Only use constructors without parameters because we can't determine what values to pass.
+ return stream(c.getDeclaredConstructors())
+ .filter(constructor -> constructor.getParameterCount() == 0)
+ .findAny()
+ .map(ChangeInfoDiffer::construct)
+ .orElseThrow(
+ () ->
+ new IllegalStateException("Class " + c + " must have a zero argument constructor"));
+ }
+
+ private static Object construct(Constructor<?> constructor) {
+ try {
+ return constructor.newInstance();
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalStateException("Failed to construct class " + constructor.getName(), e);
+ }
+ }
+
+ /** @return {@code null} if nothing has been added to {@code oldCollection} */
+ private static ImmutableList<?> getAddedForCollection(
+ Collection<?> oldCollection, Collection<?> newCollection) {
+ ImmutableList<?> notInOldCollection = getAdditions(oldCollection, newCollection);
+ return notInOldCollection.isEmpty() ? null : notInOldCollection;
+ }
+
+ private static ImmutableList<Object> getAdditions(
+ Collection<?> oldCollection, Collection<?> newCollection) {
+ Map<Object, List<Object>> duplicatesMap = newCollection.stream().collect(groupingBy(v -> v));
+ oldCollection.forEach(
+ v -> {
+ if (duplicatesMap.containsKey(v)) {
+ duplicatesMap.get(v).remove(v);
+ }
+ });
+ return duplicatesMap.values().stream().flatMap(Collection::stream).collect(toImmutableList());
+ }
+
+ /** @return {@code null} if nothing has been added to {@code oldMap} */
+ private static ImmutableMap<Object, Object> getAddedForMap(Map<?, ?> oldMap, Map<?, ?> newMap) {
+ ImmutableMap.Builder<Object, Object> additionsBuilder = ImmutableMap.builder();
+ for (Map.Entry<?, ?> entry : newMap.entrySet()) {
+ Object added = getAdded(oldMap.get(entry.getKey()), entry.getValue());
+ if (added != null) {
+ additionsBuilder.put(entry.getKey(), added);
+ }
+ }
+ ImmutableMap<Object, Object> additions = additionsBuilder.build();
+ return additions.isEmpty() ? null : additions;
+ }
+
+ private static Object get(Field field, Object obj) {
+ try {
+ return field.get(obj);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(
+ String.format("Access denied getting field %s in %s", field.getName(), obj.getClass()),
+ e);
+ }
+ }
+
+ private static void set(Field field, Object obj, Object value) {
+ try {
+ field.set(obj, value);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(
+ String.format(
+ "Access denied setting field %s in %s", field.getName(), obj.getClass().getName()),
+ e);
+ }
+ }
+
+ private ChangeInfoDiffer() {}
+}
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfoDifference.java b/java/com/google/gerrit/extensions/common/ChangeInfoDifference.java
new file mode 100644
index 0000000..269c673
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/ChangeInfoDifference.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.common;
+
+import com.google.auto.value.AutoValue;
+
+/** The difference between two {@link ChangeInfo}s returned by {@link ChangeInfoDiffer}. */
+@AutoValue
+public abstract class ChangeInfoDifference {
+
+ public abstract ChangeInfo added();
+
+ public abstract ChangeInfo removed();
+
+ public static ChangeInfoDifference create(ChangeInfo added, ChangeInfo removed) {
+ return new AutoValue_ChangeInfoDifference(added, removed);
+ }
+}
diff --git a/java/com/google/gerrit/extensions/common/ChangeMessageInfo.java b/java/com/google/gerrit/extensions/common/ChangeMessageInfo.java
index 07ad71b..10456ff 100644
--- a/java/com/google/gerrit/extensions/common/ChangeMessageInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeMessageInfo.java
@@ -26,6 +26,12 @@
public String message;
public Integer _revisionNumber;
+ public ChangeMessageInfo() {}
+
+ public ChangeMessageInfo(String message) {
+ this.message = message;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof ChangeMessageInfo) {
diff --git a/java/com/google/gerrit/extensions/common/RevisionInfo.java b/java/com/google/gerrit/extensions/common/RevisionInfo.java
index ea61f31..f710ab7 100644
--- a/java/com/google/gerrit/extensions/common/RevisionInfo.java
+++ b/java/com/google/gerrit/extensions/common/RevisionInfo.java
@@ -36,6 +36,21 @@
public PushCertificateInfo pushCertificate;
public String description;
+ public RevisionInfo() {}
+
+ public RevisionInfo(String ref) {
+ this.ref = ref;
+ }
+
+ public RevisionInfo(String ref, int number) {
+ this.ref = ref;
+ _number = number;
+ }
+
+ public RevisionInfo(AccountInfo uploader) {
+ this.uploader = uploader;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof RevisionInfo) {
diff --git a/javatests/com/google/gerrit/extensions/common/ChangeInfoDifferTest.java b/javatests/com/google/gerrit/extensions/common/ChangeInfoDifferTest.java
new file mode 100644
index 0000000..a41d63b
--- /dev/null
+++ b/javatests/com/google/gerrit/extensions/common/ChangeInfoDifferTest.java
@@ -0,0 +1,355 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ChangeInfoDifferTest {
+
+ private static final String REVISION = "abc123";
+
+ @Test
+ public void getDiff_givenEmptyChangeInfos_returnsEmptyDifference() {
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(new ChangeInfo(), new ChangeInfo());
+
+ // Spot check a few fields, including collections and maps.
+ assertThat(diff.added().branch).isNull();
+ assertThat(diff.added().project).isNull();
+ assertThat(diff.added().currentRevision).isNull();
+ assertThat(diff.added().actions).isNull();
+ assertThat(diff.added().messages).isNull();
+ assertThat(diff.added().reviewers).isNull();
+ assertThat(diff.added().hashtags).isNull();
+ assertThat(diff.removed().branch).isNull();
+ assertThat(diff.removed().project).isNull();
+ assertThat(diff.removed().currentRevision).isNull();
+ assertThat(diff.removed().actions).isNull();
+ assertThat(diff.removed().messages).isNull();
+ assertThat(diff.removed().reviewers).isNull();
+ assertThat(diff.removed().hashtags).isNull();
+ }
+
+ @Test
+ public void getDiff_givenUnchangedTopic_returnsNullTopics() {
+ ChangeInfo oldChangeInfo = createChangeInfoWithTopic("topic");
+ ChangeInfo newChangeInfo = createChangeInfoWithTopic(oldChangeInfo.topic);
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().topic).isNull();
+ assertThat(diff.removed().topic).isNull();
+ }
+
+ @Test
+ public void getDiff_givenChangedTopic_returnsTopics() {
+ ChangeInfo oldChangeInfo = createChangeInfoWithTopic("old-topic");
+ ChangeInfo newChangeInfo = createChangeInfoWithTopic("new-topic");
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().topic).isEqualTo(newChangeInfo.topic);
+ assertThat(diff.removed().topic).isEqualTo(oldChangeInfo.topic);
+ }
+
+ @Test
+ public void getDiff_givenEqualAssignees_returnsNullAssignee() {
+ ChangeInfo oldChangeInfo =
+ createChangeInfoWithAccount(new AccountInfo("name", "mail@mail.com"));
+ ChangeInfo newChangeInfo =
+ createChangeInfoWithAccount(
+ new AccountInfo(oldChangeInfo.assignee.name, oldChangeInfo.assignee.email));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().assignee).isNull();
+ assertThat(diff.removed().assignee).isNull();
+ }
+
+ @Test
+ public void getDiff_givenNewAssignee_returnsAssignee() {
+ ChangeInfo oldChangeInfo = new ChangeInfo();
+ ChangeInfo newChangeInfo =
+ createChangeInfoWithAccount(new AccountInfo("name", "mail@mail.com"));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().assignee).isEqualTo(newChangeInfo.assignee);
+ assertThat(diff.removed().assignee).isNull();
+ }
+
+ @Test
+ public void getDiff_withRemovedAssignee_returnsAssignee() {
+ ChangeInfo oldChangeInfo =
+ createChangeInfoWithAccount(new AccountInfo("name", "mail@mail.com"));
+ ChangeInfo newChangeInfo = new ChangeInfo();
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().assignee).isNull();
+ assertThat(diff.removed().assignee).isEqualTo(oldChangeInfo.assignee);
+ }
+
+ @Test
+ public void getDiff_givenAssigneeWithNewName_returnsNameButNotEmail() {
+ ChangeInfo oldChangeInfo =
+ createChangeInfoWithAccount(new AccountInfo("old name", "mail@mail.com"));
+ ChangeInfo newChangeInfo =
+ createChangeInfoWithAccount(new AccountInfo("new name", oldChangeInfo.assignee.email));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().assignee).isNotNull();
+ assertThat(diff.added().assignee.name).isEqualTo(newChangeInfo.assignee.name);
+ assertThat(diff.added().assignee.email).isNull();
+ assertThat(diff.removed().assignee).isNotNull();
+ assertThat(diff.removed().assignee.name).isEqualTo(oldChangeInfo.assignee.name);
+ assertThat(diff.removed().assignee.email).isNull();
+ }
+
+ @Test
+ public void getDiff_whenHashtagsChanged_returnsHashtags() {
+ String removedHashtag = "removed";
+ String addedHashtag = "added";
+ ChangeInfo oldChangeInfo = createChangeInfoWithHashtags(removedHashtag, "existing");
+ ChangeInfo newChangeInfo = createChangeInfoWithHashtags("existing", addedHashtag);
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().hashtags).isNotNull();
+ assertThat(diff.added().hashtags).containsExactly(addedHashtag);
+ assertThat(diff.removed().hashtags).isNotNull();
+ assertThat(diff.removed().hashtags).containsExactly(removedHashtag);
+ }
+
+ @Test
+ public void getDiff_whenDuplicateHashtagAdded_returnsHashtag() {
+ String hashtag = "hashtag";
+ ChangeInfo oldChangeInfo = createChangeInfoWithHashtags(hashtag, hashtag);
+ ChangeInfo newChangeInfo = createChangeInfoWithHashtags(hashtag, hashtag, hashtag);
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().hashtags).isNotNull();
+ assertThat(diff.added().hashtags).containsExactly(hashtag);
+ assertThat(diff.removed().hashtags).isNull();
+ }
+
+ @Test
+ public void getDiff_whenChangeMessageUnchanged_returnsNullMessage() {
+ String message = "message";
+ ChangeInfo oldChangeInfo = new ChangeInfo(new ChangeMessageInfo(message));
+ ChangeInfo newChangeInfo = new ChangeInfo(new ChangeMessageInfo(message));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().messages).isNull();
+ assertThat(diff.removed().messages).isNull();
+ }
+
+ @Test
+ public void getDiff_whenChangeMessageAdded_returnsAdded() {
+ ChangeMessageInfo addedMessage = new ChangeMessageInfo("added");
+ ChangeMessageInfo existingMessage = new ChangeMessageInfo("existing");
+ ChangeInfo oldChangeInfo = new ChangeInfo(existingMessage);
+ ChangeInfo newChangeInfo = new ChangeInfo(existingMessage, addedMessage);
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().messages).isNotNull();
+ assertThat(diff.added().messages).containsExactly(addedMessage);
+ assertThat(diff.removed().messages).isNull();
+ }
+
+ @Test
+ public void getDiff_whenChangeMessageRemoved_returnsRemoved() {
+ ChangeMessageInfo removedMessage = new ChangeMessageInfo("removed");
+ ChangeMessageInfo existingMessage = new ChangeMessageInfo("existing");
+ ChangeInfo oldChangeInfo = new ChangeInfo(existingMessage, removedMessage);
+ ChangeInfo newChangeInfo = new ChangeInfo(existingMessage);
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().messages).isNull();
+ assertThat(diff.removed().messages).isNotNull();
+ assertThat(diff.removed().messages).containsExactly(removedMessage);
+ }
+
+ @Test
+ public void getDiff_whenDuplicateMessagesAdded_returnsDuplicates() {
+ ChangeMessageInfo message = new ChangeMessageInfo("message");
+ ChangeInfo oldChangeInfo = new ChangeInfo(message, message);
+ ChangeInfo newChangeInfo = new ChangeInfo(message, message, message, message);
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().messages).isNotNull();
+ assertThat(diff.added().messages).containsExactly(message, message);
+ assertThat(diff.removed().messages).isNull();
+ }
+
+ @Test
+ public void getDiff_whenNoNewRevisions_returnsNullRevisions() {
+ ChangeInfo oldChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, new RevisionInfo("ref")));
+ ChangeInfo newChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, new RevisionInfo("ref")));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().revisions).isNull();
+ assertThat(diff.removed().revisions).isNull();
+ }
+
+ @Test
+ public void getDiff_whenOneAddedRevision_returnsRevision() {
+ RevisionInfo addedRevision = new RevisionInfo("ref");
+ ChangeInfo oldChangeInfo = new ChangeInfo(ImmutableMap.of());
+ ChangeInfo newChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, addedRevision));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().revisions).isNotNull();
+ assertThat(diff.added().revisions).hasSize(1);
+ assertThat(diff.added().revisions).containsKey(REVISION);
+ assertThat(diff.added().revisions.get(REVISION).ref).isEqualTo(addedRevision.ref);
+ assertThat(diff.removed().revisions).isNull();
+ }
+
+ @Test
+ public void getDiff_whenOneModifiedRevision_returnsModificationsToRevision() {
+ RevisionInfo oldRevision = new RevisionInfo("ref", 1);
+ RevisionInfo newRevision = new RevisionInfo(oldRevision.ref, 2);
+ ChangeInfo oldChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, oldRevision));
+ ChangeInfo newChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, newRevision));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().revisions).isNotNull();
+ assertThat(diff.added().revisions).hasSize(1);
+ assertThat(diff.added().revisions).containsKey(REVISION);
+ assertThat(diff.added().revisions.get(REVISION).ref).isNull();
+ assertThat(diff.added().revisions.get(REVISION)._number).isEqualTo(newRevision._number);
+ assertThat(diff.removed().revisions).isNotNull();
+ assertThat(diff.removed().revisions).hasSize(1);
+ assertThat(diff.removed().revisions).containsKey(REVISION);
+ assertThat(diff.removed().revisions.get(REVISION).ref).isNull();
+ assertThat(diff.removed().revisions.get(REVISION)._number).isEqualTo(oldRevision._number);
+ }
+
+ @Test
+ public void getDiff_whenOneModifiedRevisionUploader_returnsModificationsToRevisionUploader() {
+ RevisionInfo oldRevision = new RevisionInfo(new AccountInfo("name", "email@mail.com"));
+ RevisionInfo newRevision =
+ new RevisionInfo(
+ new AccountInfo(oldRevision.uploader.name, oldRevision.uploader.email + "2"));
+ ChangeInfo oldChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, oldRevision));
+ ChangeInfo newChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, newRevision));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().revisions).isNotNull();
+ assertThat(diff.added().revisions).hasSize(1);
+ assertThat(diff.added().revisions).containsKey(REVISION);
+ assertThat(diff.added().revisions.get(REVISION).uploader).isNotNull();
+ assertThat(diff.added().revisions.get(REVISION).uploader.name).isNull();
+ assertThat(diff.added().revisions.get(REVISION).uploader.email)
+ .isEqualTo(newRevision.uploader.email);
+ assertThat(diff.removed().revisions).isNotNull();
+ assertThat(diff.removed().revisions).hasSize(1);
+ assertThat(diff.removed().revisions).containsKey(REVISION);
+ assertThat(diff.removed().revisions.get(REVISION).uploader).isNotNull();
+ assertThat(diff.removed().revisions.get(REVISION).uploader.name).isNull();
+ assertThat(diff.removed().revisions.get(REVISION).uploader.email)
+ .isEqualTo(oldRevision.uploader.email);
+ }
+
+ @Test
+ public void getDiff_whenOneUnchangedRevisionUploader_returnsNullRevision() {
+ RevisionInfo oldRevision = new RevisionInfo(new AccountInfo("name", "email@mail.com"));
+ RevisionInfo newRevision = new RevisionInfo(oldRevision.uploader);
+ ChangeInfo oldChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, oldRevision));
+ ChangeInfo newChangeInfo = new ChangeInfo(ImmutableMap.of(REVISION, newRevision));
+
+ ChangeInfoDifference diff = ChangeInfoDiffer.getDifference(oldChangeInfo, newChangeInfo);
+
+ assertThat(diff.added().revisions).isNull();
+ assertThat(diff.removed().revisions).isNull();
+ }
+
+ @Test
+ public void getDiff_assertCanConstructAllChangeInfoReferences() throws Exception {
+ buildObjectWithFullFields(ChangeInfo.class);
+ }
+
+ private static Object buildObjectWithFullFields(Class<?> c) throws Exception {
+ if (c == null) {
+ return null;
+ }
+ Object toPopulate = ChangeInfoDiffer.construct(c);
+ for (Field field : toPopulate.getClass().getDeclaredFields()) {
+ Class<?> parameterizedType = getParameterizedType(field);
+ if (!ChangeInfoDiffer.isSimple(field.getType())
+ && !field.getType().isArray()
+ && !Map.class.isAssignableFrom(field.getType())
+ && !Collection.class.isAssignableFrom(field.getType())) {
+ field.set(toPopulate, buildObjectWithFullFields(field.getType()));
+ } else if (Collection.class.isAssignableFrom(field.getType())
+ && parameterizedType != null
+ && !ChangeInfoDiffer.isSimple(parameterizedType)) {
+ field.set(toPopulate, ImmutableList.of(buildObjectWithFullFields(parameterizedType)));
+ }
+ }
+ return toPopulate;
+ }
+
+ private static Class<?> getParameterizedType(Field field) {
+ if (!Collection.class.isAssignableFrom(field.getType())) {
+ return null;
+ }
+ Type genericType = field.getGenericType();
+ if (genericType instanceof ParameterizedType) {
+ return (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
+ }
+ return null;
+ }
+
+ private static ChangeInfo createChangeInfoWithTopic(String topic) {
+ ChangeInfo changeInfo = new ChangeInfo();
+ changeInfo.topic = topic;
+ return changeInfo;
+ }
+
+ private static ChangeInfo createChangeInfoWithAccount(AccountInfo accountInfo) {
+ ChangeInfo changeInfo = new ChangeInfo();
+ changeInfo.assignee = accountInfo;
+ return changeInfo;
+ }
+
+ private static ChangeInfo createChangeInfoWithHashtags(String... hashtags) {
+ ChangeInfo changeInfo = new ChangeInfo();
+ changeInfo.hashtags = ImmutableList.copyOf(hashtags);
+ return changeInfo;
+ }
+}