diff options
Diffstat (limited to 'gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java')
-rw-r--r-- | gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java | 1972 |
1 files changed, 0 insertions, 1972 deletions
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java deleted file mode 100644 index 80a8ab94c4..0000000000 --- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java +++ /dev/null @@ -1,1972 +0,0 @@ -// Copyright (C) 2016 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.server.notedb; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.common.TimeUtil.roundToSecond; -import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB; -import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB; -import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC; -import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; - -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Table; -import com.google.gerrit.common.TimeUtil; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.ChangeMessage; -import com.google.gerrit.reviewdb.client.LabelId; -import com.google.gerrit.reviewdb.client.Patch; -import com.google.gerrit.reviewdb.client.PatchLineComment; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.reviewdb.client.PatchSetApproval; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RevId; -import com.google.gerrit.server.ReviewerSet; -import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl; -import com.google.gerrit.testutil.GerritBaseTests; -import com.google.gerrit.testutil.TestChanges; -import com.google.gerrit.testutil.TestTimeUtil; -import com.google.gwtorm.protobuf.CodecFactory; -import com.google.gwtorm.protobuf.ProtobufCodec; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.TimeZone; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class ChangeBundleTest extends GerritBaseTests { - private static final ProtobufCodec<Change> CHANGE_CODEC = CodecFactory.encoder(Change.class); - private static final ProtobufCodec<ChangeMessage> CHANGE_MESSAGE_CODEC = - CodecFactory.encoder(ChangeMessage.class); - private static final ProtobufCodec<PatchSet> PATCH_SET_CODEC = - CodecFactory.encoder(PatchSet.class); - private static final ProtobufCodec<PatchSetApproval> PATCH_SET_APPROVAL_CODEC = - CodecFactory.encoder(PatchSetApproval.class); - private static final ProtobufCodec<PatchLineComment> PATCH_LINE_COMMENT_CODEC = - CodecFactory.encoder(PatchLineComment.class); - - private String systemTimeZoneProperty; - private TimeZone systemTimeZone; - - private Project.NameKey project; - private Account.Id accountId; - - @Before - public void setUp() { - String tz = "US/Eastern"; - systemTimeZoneProperty = System.setProperty("user.timezone", tz); - systemTimeZone = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone(tz)); - long maxMs = ChangeRebuilderImpl.MAX_WINDOW_MS; - assertThat(maxMs).isGreaterThan(1000L); - TestTimeUtil.resetWithClockStep(maxMs * 2, MILLISECONDS); - project = new Project.NameKey("project"); - accountId = new Account.Id(100); - } - - @After - public void tearDown() { - TestTimeUtil.useSystemTime(); - System.setProperty("user.timezone", systemTimeZoneProperty); - TimeZone.setDefault(systemTimeZone); - } - - private void superWindowResolution() { - TestTimeUtil.setClockStep(ChangeRebuilderImpl.MAX_WINDOW_MS * 2, MILLISECONDS); - TimeUtil.nowTs(); - } - - private void subWindowResolution() { - TestTimeUtil.setClockStep(1, SECONDS); - TimeUtil.nowTs(); - } - - @Test - public void diffChangesDifferentIds() throws Exception { - Change c1 = TestChanges.newChange(project, accountId); - int id1 = c1.getId().get(); - Change c2 = TestChanges.newChange(project, accountId); - int id2 = c2.getId().get(); - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - - assertDiffs( - b1, - b2, - "changeId differs for Changes: {" + id1 + "} != {" + id2 + "}", - "createdOn differs for Changes: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:06.0}", - "effective last updated time differs for Changes:" - + " {2009-09-30 17:00:00.0} != {2009-09-30 17:00:06.0}"); - } - - @Test - public void diffChangesSameId() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - Change c2 = clone(c1); - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - - assertNoDiffs(b1, b2); - - c2.setTopic("topic"); - assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {null} != {topic}"); - } - - @Test - public void diffChangesMixedSourcesAllowsSlop() throws Exception { - subWindowResolution(); - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - Change c2 = clone(c1); - c2.setCreatedOn(TimeUtil.nowTs()); - c2.setLastUpdatedOn(TimeUtil.nowTs()); - - // Both are ReviewDb, exact timestamp match is required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "createdOn differs for Change.Id " - + c1.getId() - + ":" - + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:02.0}", - "effective last updated time differs for Change.Id " - + c1.getId() - + ":" - + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:03.0}"); - - // One NoteDb, slop is allowed. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - // But not too much slop. - superWindowResolution(); - Change c3 = clone(c1); - c3.setLastUpdatedOn(TimeUtil.nowTs()); - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - ChangeBundle b3 = - new ChangeBundle( - c3, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - String msg = - "effective last updated time differs for Change.Id " - + c1.getId() - + " in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:10.0}"; - assertDiffs(b1, b3, msg); - assertDiffs(b3, b1, msg); - } - - @Test - public void diffChangesIgnoresOriginalSubjectInReviewDb() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - c1.setCurrentPatchSet(c1.currentPatchSetId(), "Subject", "Original A"); - Change c2 = clone(c1); - c2.setCurrentPatchSet(c2.currentPatchSetId(), c1.getSubject(), "Original B"); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "originalSubject differs for Change.Id " - + c1.getId() - + ":" - + " {Original A} != {Original B}"); - - // Both NoteDb, exact match required. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs( - b1, - b2, - "originalSubject differs for Change.Id " - + c1.getId() - + ":" - + " {Original A} != {Original B}"); - - // One ReviewDb, one NoteDb, original subject is ignored. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangesSanitizesSubjectsBeforeComparison() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - c1.setCurrentPatchSet(c1.currentPatchSetId(), "Subject\r\rbody", "Original"); - Change c2 = clone(c1); - c2.setCurrentPatchSet(c2.currentPatchSetId(), "Subject body", "Original"); - - // Both ReviewDb, exact match required - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Subject\r\rbody} != {Subject body}"); - - // Both NoteDb, exact match required (although it should be impossible to - // create a NoteDb change with '\r' in the subject). - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Subject\r\rbody} != {Subject body}"); - - // One ReviewDb, one NoteDb, '\r' is normalized to ' '. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangesConsidersEmptyReviewDbTopicEquivalentToNullInNoteDb() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - c1.setTopic(""); - Change c2 = clone(c1); - c2.setTopic(null); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {} != {null}"); - - // Topic ignored if ReviewDb is empty and NoteDb is null. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - - // Exact match still required if NoteDb has empty value (not realistic). - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {} != {null}"); - - // Null is not equal to a non-empty string. - Change c3 = clone(c1); - c3.setTopic("topic"); - b1 = - new ChangeBundle( - c3, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {topic} != {null}"); - - // Null is equal to a string that is all whitespace. - Change c4 = clone(c1); - c4.setTopic(" "); - b1 = - new ChangeBundle( - c4, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangesIgnoresLeadingAndTrailingWhitespaceInReviewDbTopics() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - c1.setTopic(" abc "); - Change c2 = clone(c1); - c2.setTopic("abc"); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": { abc } != {abc}"); - - // Leading whitespace in ReviewDb topic is ignored. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - // Must match except for the leading/trailing whitespace. - Change c3 = clone(c1); - c3.setTopic("cba"); - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c3, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": { abc } != {cba}"); - } - - @Test - public void diffChangesTakesMaxEntityTimestampFromReviewDb() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - PatchSet ps = new PatchSet(c1.currentPatchSetId()); - ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps.setUploader(accountId); - ps.setCreatedOn(TimeUtil.nowTs()); - PatchSetApproval a = - new PatchSetApproval( - new PatchSetApproval.Key(c1.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 1, - TimeUtil.nowTs()); - - Change c2 = clone(c1); - c2.setLastUpdatedOn(a.getGranted()); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "effective last updated time differs for Change.Id " - + c1.getId() - + ":" - + " {2009-09-30 17:00:00.0} != {2009-09-30 17:00:12.0}"); - - // NoteDb allows latest timestamp from all entities in bundle. - b2 = - new ChangeBundle( - c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - } - - @Test - public void diffChangesIgnoresChangeTimestampIfAnyOtherEntitiesExist() { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - PatchSet ps = new PatchSet(c1.currentPatchSetId()); - ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps.setUploader(accountId); - ps.setCreatedOn(TimeUtil.nowTs()); - PatchSetApproval a = - new PatchSetApproval( - new PatchSetApproval.Key(c1.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 1, - TimeUtil.nowTs()); - c1.setLastUpdatedOn(a.getGranted()); - - Change c2 = clone(c1); - c2.setLastUpdatedOn(TimeUtil.nowTs()); - - // ReviewDb has later lastUpdatedOn timestamp than NoteDb, allowed since - // NoteDb matches the latest timestamp of a non-Change entity. - ChangeBundle b1 = - new ChangeBundle( - c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c1, messages(), patchSets(ps), approvals(a), comments(), reviewers(), NOTE_DB); - assertThat(b1.getChange().getLastUpdatedOn()).isGreaterThan(b2.getChange().getLastUpdatedOn()); - assertNoDiffs(b1, b2); - - // Timestamps must actually match if Change is the only entity. - b1 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs( - b1, - b2, - "effective last updated time differs for Change.Id " - + c1.getId() - + " in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:12.0} != {2009-09-30 17:00:18.0}"); - } - - @Test - public void diffChangesAllowsReviewDbSubjectToBePrefixOfNoteDbSubject() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - Change c2 = clone(c1); - c2.setCurrentPatchSet( - c1.currentPatchSetId(), c1.getSubject().substring(0, 10), c1.getOriginalSubject()); - assertThat(c2.getSubject()).isNotEqualTo(c1.getSubject()); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " + c1.getId() + ": {Change subject} != {Change sub}"); - - // ReviewDb has shorter subject, allowed. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - - // NoteDb has shorter subject, not allowed. - b1 = - new ChangeBundle( - c1, messages(), latest(c1), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle(c2, messages(), latest(c2), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " + c1.getId() + ": {Change subject} != {Change sub}"); - } - - @Test - public void diffChangesTrimsLeadingSpacesFromReviewDbComparingToNoteDb() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - Change c2 = clone(c1); - c2.setCurrentPatchSet(c1.currentPatchSetId(), " " + c1.getSubject(), c1.getOriginalSubject()); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Change subject} != { Change subject}"); - - // ReviewDb is missing leading spaces, allowed. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangesDoesntTrimLeadingNonSpaceWhitespaceFromSubject() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - Change c2 = clone(c1); - c2.setCurrentPatchSet(c1.currentPatchSetId(), "\t" + c1.getSubject(), c1.getOriginalSubject()); - - // Both ReviewDb. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Change subject} != {\tChange subject}"); - - // One NoteDb. - b1 = - new ChangeBundle(c1, messages(), latest(c1), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c2, messages(), latest(c2), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Change subject} != {\tChange subject}"); - assertDiffs( - b2, - b1, - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {\tChange subject} != {Change subject}"); - } - - @Test - public void diffChangesHandlesBuggyJGitSubjectExtraction() throws Exception { - Change c1 = TestChanges.newChange(project, accountId); - String buggySubject = "Subject\r \r Rest of message."; - c1.setCurrentPatchSet(c1.currentPatchSetId(), buggySubject, buggySubject); - Change c2 = clone(c1); - c2.setCurrentPatchSet(c2.currentPatchSetId(), "Subject", "Subject"); - - // Both ReviewDb. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "originalSubject differs for Change.Id " - + c1.getId() - + ":" - + " {Subject\r \r Rest of message.} != {Subject}", - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Subject\r \r Rest of message.} != {Subject}"); - - // NoteDb has correct subject without "\r ". - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangesIgnoresInvalidCurrentPatchSetIdInReviewDb() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - Change c2 = clone(c1); - c2.setCurrentPatchSet( - new PatchSet.Id(c2.getId(), 0), "Unrelated subject", c2.getOriginalSubject()); - - // Both ReviewDb. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "currentPatchSetId differs for Change.Id " + c1.getId() + ": {1} != {0}", - "subject differs for Change.Id " - + c1.getId() - + ":" - + " {Change subject} != {Unrelated subject}"); - - // One NoteDb. - // - // This is based on a real corrupt change where all patch sets were deleted - // but the Change entity stuck around, resulting in a currentPatchSetId of 0 - // after converting to NoteDb. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangesAllowsCreatedToMatchLastUpdated() throws Exception { - Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100)); - c1.setCreatedOn(TimeUtil.nowTs()); - assertThat(c1.getCreatedOn()).isGreaterThan(c1.getLastUpdatedOn()); - Change c2 = clone(c1); - c2.setCreatedOn(c2.getLastUpdatedOn()); - - // Both ReviewDb. - ChangeBundle b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "createdOn differs for Change.Id " - + c1.getId() - + ": {2009-09-30 17:00:06.0} != {2009-09-30 17:00:00.0}"); - - // One NoteDb. - b1 = - new ChangeBundle( - c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffChangeMessageKeySets() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid1"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - ChangeMessage cm2 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid2"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - - assertDiffs( - b1, - b2, - "ChangeMessage.Key sets differ:" - + " [" - + id - + ",uuid1] only in A; [" - + id - + ",uuid2] only in B"); - } - - @Test - public void diffChangeMessages() throws Exception { - Change c = TestChanges.newChange(project, accountId); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - cm1.setMessage("message 1"); - ChangeMessage cm2 = clone(cm1); - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - - assertNoDiffs(b1, b2); - - cm2.setMessage("message 2"); - assertDiffs( - b1, - b2, - "message differs for ChangeMessage.Key " - + c.getId() - + ",uuid:" - + " {message 1} != {message 2}"); - } - - @Test - public void diffChangeMessagesIgnoresUuids() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid1"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - cm1.setMessage("message 1"); - ChangeMessage cm2 = clone(cm1); - cm2.getKey().set("uuid2"); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - // Both are ReviewDb, exact UUID match is required. - assertDiffs( - b1, - b2, - "ChangeMessage.Key sets differ:" - + " [" - + id - + ",uuid1] only in A; [" - + id - + ",uuid2] only in B"); - - // One NoteDb, UUIDs are ignored. - b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - } - - @Test - public void diffChangeMessagesWithDifferentCounts() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid1"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - cm1.setMessage("message 1"); - ChangeMessage cm2 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid2"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - cm1.setMessage("message 2"); - - // Both ReviewDb: Uses same keySet diff as other types. - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1, cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, b2, "ChangeMessage.Key sets differ: [" + id + ",uuid2] only in A; [] only in B"); - - // One NoteDb: UUIDs in keys can't be used for comparison, just diff counts. - b1 = - new ChangeBundle( - c, messages(cm1, cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs(b1, b2, "ChangeMessages differ for Change.Id " + id + "\nOnly in A:\n " + cm2); - assertDiffs(b2, b1, "ChangeMessages differ for Change.Id " + id + "\nOnly in B:\n " + cm2); - } - - @Test - public void diffChangeMessagesMixedSourcesWithDifferences() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid1"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - cm1.setMessage("message 1"); - ChangeMessage cm2 = clone(cm1); - cm2.setMessage("message 2"); - ChangeMessage cm3 = clone(cm1); - cm3.getKey().set("uuid2"); // Differs only in UUID. - - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1, cm3), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm2, cm3), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - // Implementation happens to pair up cm1 in b1 with cm3 in b2 because it - // depends on iteration order and doesn't care about UUIDs. The important - // thing is that there's some diff. - assertDiffs( - b1, - b2, - "ChangeMessages differ for Change.Id " - + id - + "\n" - + "Only in A:\n " - + cm3 - + "\n" - + "Only in B:\n " - + cm2); - assertDiffs( - b2, - b1, - "ChangeMessages differ for Change.Id " - + id - + "\n" - + "Only in A:\n " - + cm2 - + "\n" - + "Only in B:\n " - + cm3); - } - - @Test - public void diffChangeMessagesMixedSourcesAllowsSlop() throws Exception { - subWindowResolution(); - Change c = TestChanges.newChange(project, accountId); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid1"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - ChangeMessage cm2 = clone(cm1); - cm2.setWrittenOn(TimeUtil.nowTs()); - - // Both are ReviewDb, exact timestamp match is required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "writtenOn differs for ChangeMessage.Key " - + c.getId() - + ",uuid1:" - + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}"); - - // One NoteDb, slop is allowed. - b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - // But not too much slop. - superWindowResolution(); - ChangeMessage cm3 = clone(cm1); - cm3.setWrittenOn(TimeUtil.nowTs()); - b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - ChangeBundle b3 = - new ChangeBundle( - c, messages(cm3), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - int id = c.getId().get(); - assertDiffs( - b1, - b3, - "ChangeMessages differ for Change.Id " - + id - + "\n" - + "Only in A:\n " - + cm1 - + "\n" - + "Only in B:\n " - + cm3); - assertDiffs( - b3, - b1, - "ChangeMessages differ for Change.Id " - + id - + "\n" - + "Only in A:\n " - + cm3 - + "\n" - + "Only in B:\n " - + cm1); - } - - @Test - public void diffChangeMessagesAllowsNullPatchSetIdFromReviewDb() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - cm1.setMessage("message 1"); - ChangeMessage cm2 = clone(cm1); - cm2.setPatchSetId(null); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - - // Both are ReviewDb, exact patch set ID match is required. - assertDiffs( - b1, - b2, - "patchset differs for ChangeMessage.Key " - + c.getId() - + ",uuid:" - + " {" - + id - + ",1} != {null}"); - - // Null patch set ID on ReviewDb is ignored. - b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - - // Null patch set ID on NoteDb is not ignored (but is not realistic). - b1 = - new ChangeBundle( - c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c, messages(cm2), latest(c), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs( - b1, - b2, - "ChangeMessages differ for Change.Id " - + id - + "\n" - + "Only in A:\n " - + cm1 - + "\n" - + "Only in B:\n " - + cm2); - assertDiffs( - b2, - b1, - "ChangeMessages differ for Change.Id " - + id - + "\n" - + "Only in A:\n " - + cm2 - + "\n" - + "Only in B:\n " - + cm1); - } - - @Test - public void diffPatchSetIdSets() throws Exception { - Change c = TestChanges.newChange(project, accountId); - TestChanges.incrementPatchSet(c); - - PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1)); - ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps1.setUploader(accountId); - ps1.setCreatedOn(TimeUtil.nowTs()); - PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2)); - ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee")); - ps2.setUploader(accountId); - ps2.setCreatedOn(TimeUtil.nowTs()); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), patchSets(ps1, ps2), approvals(), comments(), reviewers(), REVIEW_DB); - - assertDiffs(b1, b2, "PatchSet.Id sets differ: [] only in A; [" + c.getId() + ",1] only in B"); - } - - @Test - public void diffPatchSets() throws Exception { - Change c = TestChanges.newChange(project, accountId); - PatchSet ps1 = new PatchSet(c.currentPatchSetId()); - ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps1.setUploader(accountId); - ps1.setCreatedOn(TimeUtil.nowTs()); - PatchSet ps2 = clone(ps1); - ChangeBundle b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB); - - assertNoDiffs(b1, b2); - - ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee")); - assertDiffs( - b1, - b2, - "revision differs for PatchSet.Id " - + c.getId() - + ",1:" - + " {RevId{deadbeefdeadbeefdeadbeefdeadbeefdeadbeef}}" - + " != {RevId{badc0feebadc0feebadc0feebadc0feebadc0fee}}"); - } - - @Test - public void diffPatchSetsMixedSourcesAllowsSlop() throws Exception { - subWindowResolution(); - Change c = TestChanges.newChange(project, accountId); - PatchSet ps1 = new PatchSet(c.currentPatchSetId()); - ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps1.setUploader(accountId); - ps1.setCreatedOn(roundToSecond(TimeUtil.nowTs())); - PatchSet ps2 = clone(ps1); - ps2.setCreatedOn(TimeUtil.nowTs()); - - // Both are ReviewDb, exact timestamp match is required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "createdOn differs for PatchSet.Id " - + c.getId() - + ",1:" - + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}"); - - // One NoteDb, slop is allowed. - b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - - // But not too much slop. - superWindowResolution(); - PatchSet ps3 = clone(ps1); - ps3.setCreatedOn(TimeUtil.nowTs()); - b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB); - ChangeBundle b3 = - new ChangeBundle( - c, messages(), patchSets(ps3), approvals(), comments(), reviewers(), REVIEW_DB); - String msg = - "createdOn differs for PatchSet.Id " - + c.getId() - + ",1 in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:10.0}"; - assertDiffs(b1, b3, msg); - assertDiffs(b3, b1, msg); - } - - @Test - public void diffPatchSetsIgnoresTrailingNewlinesInPushCertificate() throws Exception { - subWindowResolution(); - Change c = TestChanges.newChange(project, accountId); - PatchSet ps1 = new PatchSet(c.currentPatchSetId()); - ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps1.setUploader(accountId); - ps1.setCreatedOn(roundToSecond(TimeUtil.nowTs())); - ps1.setPushCertificate("some cert"); - PatchSet ps2 = clone(ps1); - ps2.setPushCertificate(ps2.getPushCertificate() + "\n\n"); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffPatchSetsGreaterThanCurrent() throws Exception { - Change c = TestChanges.newChange(project, accountId); - - PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1)); - ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps1.setUploader(accountId); - ps1.setCreatedOn(TimeUtil.nowTs()); - PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2)); - ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee")); - ps2.setUploader(accountId); - ps2.setCreatedOn(TimeUtil.nowTs()); - assertThat(ps2.getId().get()).isGreaterThan(c.currentPatchSetId().get()); - - ChangeMessage cm1 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid1"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - ChangeMessage cm2 = - new ChangeMessage( - new ChangeMessage.Key(c.getId(), "uuid2"), - accountId, - TimeUtil.nowTs(), - c.currentPatchSetId()); - - PatchSetApproval a1 = - new PatchSetApproval( - new PatchSetApproval.Key(ps1.getId(), accountId, new LabelId("Code-Review")), - (short) 1, - TimeUtil.nowTs()); - PatchSetApproval a2 = - new PatchSetApproval( - new PatchSetApproval.Key(ps2.getId(), accountId, new LabelId("Code-Review")), - (short) 1, - TimeUtil.nowTs()); - - // Both ReviewDb. - ChangeBundle b1 = - new ChangeBundle( - c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, - messages(cm1, cm2), - patchSets(ps1, ps2), - approvals(a1, a2), - comments(), - reviewers(), - REVIEW_DB); - assertDiffs( - b1, - b2, - "ChangeMessage.Key sets differ: [] only in A; [" + cm2.getKey() + "] only in B", - "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B", - "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B"); - - // One NoteDb. - b1 = - new ChangeBundle( - c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, - messages(cm1, cm2), - patchSets(ps1, ps2), - approvals(a1, a2), - comments(), - reviewers(), - REVIEW_DB); - assertDiffs( - b1, - b2, - "ChangeMessages differ for Change.Id " + c.getId() + "\nOnly in B:\n " + cm2, - "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B", - "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B"); - - // Both NoteDb. - b1 = - new ChangeBundle( - c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, - messages(cm1, cm2), - patchSets(ps1, ps2), - approvals(a1, a2), - comments(), - reviewers(), - NOTE_DB); - assertDiffs( - b1, - b2, - "ChangeMessages differ for Change.Id " + c.getId() + "\nOnly in B:\n " + cm2, - "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B", - "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B"); - } - - @Test - public void diffPatchSetsIgnoresLeadingAndTrailingWhitespaceInReviewDbDescriptions() - throws Exception { - Change c = TestChanges.newChange(project, accountId); - - PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1)); - ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - ps1.setUploader(accountId); - ps1.setCreatedOn(TimeUtil.nowTs()); - ps1.setDescription(" abc "); - PatchSet ps2 = clone(ps1); - ps2.setDescription("abc"); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ": { abc } != {abc}"); - - // Whitespace in ReviewDb description is ignored. - b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - // Must match except for the leading/trailing whitespace. - PatchSet ps3 = clone(ps1); - ps3.setDescription("cba"); - b1 = - new ChangeBundle( - c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle( - c, messages(), patchSets(ps3), approvals(), comments(), reviewers(), NOTE_DB); - assertDiffs( - b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ": { abc } != {cba}"); - } - - @Test - public void diffPatchSetsIgnoresCreatedOnWhenReviewDbIsNonMonotonic() throws Exception { - Change c = TestChanges.newChange(project, accountId); - - Timestamp beforePs1 = TimeUtil.nowTs(); - - PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1)); - goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - goodPs1.setUploader(accountId); - goodPs1.setCreatedOn(TimeUtil.nowTs()); - PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2)); - goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - goodPs2.setUploader(accountId); - goodPs2.setCreatedOn(TimeUtil.nowTs()); - assertThat(goodPs2.getCreatedOn()).isGreaterThan(goodPs1.getCreatedOn()); - - PatchSet badPs2 = clone(goodPs2); - badPs2.setCreatedOn(beforePs1); - assertThat(badPs2.getCreatedOn()).isLessThan(goodPs1.getCreatedOn()); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c, - messages(), - patchSets(goodPs1, goodPs2), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, - messages(), - patchSets(goodPs1, badPs2), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - assertDiffs( - b1, - b2, - "createdOn differs for PatchSet.Id " - + badPs2.getId() - + ":" - + " {2009-09-30 17:00:18.0} != {2009-09-30 17:00:06.0}"); - - // Non-monotonic in ReviewDb but monotonic in NoteDb, timestamps are - // ignored, including for ps1. - PatchSet badPs1 = clone(goodPs1); - badPs1.setCreatedOn(TimeUtil.nowTs()); - b1 = - new ChangeBundle( - c, - messages(), - patchSets(badPs1, badPs2), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - b2 = - new ChangeBundle( - c, - messages(), - patchSets(goodPs1, goodPs2), - approvals(), - comments(), - reviewers(), - NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - // Non-monotonic in NoteDb but monotonic in ReviewDb, timestamps are not - // ignored. - b1 = - new ChangeBundle( - c, - messages(), - patchSets(goodPs1, goodPs2), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - b2 = - new ChangeBundle( - c, - messages(), - patchSets(badPs1, badPs2), - approvals(), - comments(), - reviewers(), - NOTE_DB); - assertDiffs( - b1, - b2, - "createdOn differs for PatchSet.Id " - + badPs1.getId() - + " in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:24.0} != {2009-09-30 17:00:12.0}", - "createdOn differs for PatchSet.Id " - + badPs2.getId() - + " in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:06.0} != {2009-09-30 17:00:18.0}"); - } - - @Test - public void diffPatchSetsAllowsFirstPatchSetCreatedOnToMatchChangeCreatedOn() { - Change c = TestChanges.newChange(project, accountId); - c.setLastUpdatedOn(TimeUtil.nowTs()); - - PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1)); - goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - goodPs1.setUploader(accountId); - goodPs1.setCreatedOn(TimeUtil.nowTs()); - assertThat(goodPs1.getCreatedOn()).isGreaterThan(c.getCreatedOn()); - - PatchSet ps1AtCreatedOn = clone(goodPs1); - ps1AtCreatedOn.setCreatedOn(c.getCreatedOn()); - - PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2)); - goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - goodPs2.setUploader(accountId); - goodPs2.setCreatedOn(TimeUtil.nowTs()); - - PatchSet ps2AtCreatedOn = clone(goodPs2); - ps2AtCreatedOn.setCreatedOn(c.getCreatedOn()); - - // Both ReviewDb, exact match required. - ChangeBundle b1 = - new ChangeBundle( - c, - messages(), - patchSets(goodPs1, goodPs2), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, - messages(), - patchSets(ps1AtCreatedOn, ps2AtCreatedOn), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - assertDiffs( - b1, - b2, - "createdOn differs for PatchSet.Id " - + c.getId() - + ",1: {2009-09-30 17:00:12.0} != {2009-09-30 17:00:00.0}", - "createdOn differs for PatchSet.Id " - + c.getId() - + ",2: {2009-09-30 17:00:18.0} != {2009-09-30 17:00:00.0}"); - - // One ReviewDb, PS1 is allowed to match change createdOn, but PS2 isn't. - b1 = - new ChangeBundle( - c, - messages(), - patchSets(goodPs1, goodPs2), - approvals(), - comments(), - reviewers(), - REVIEW_DB); - b2 = - new ChangeBundle( - c, - messages(), - patchSets(ps1AtCreatedOn, ps2AtCreatedOn), - approvals(), - comments(), - reviewers(), - NOTE_DB); - assertDiffs( - b1, - b2, - "createdOn differs for PatchSet.Id " - + c.getId() - + ",2 in NoteDb vs. ReviewDb: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:18.0}"); - assertDiffs( - b2, - b1, - "createdOn differs for PatchSet.Id " - + c.getId() - + ",2 in NoteDb vs. ReviewDb: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:18.0}"); - } - - @Test - public void diffPatchSetApprovalKeySets() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - PatchSetApproval a1 = - new PatchSetApproval( - new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 1, - TimeUtil.nowTs()); - PatchSetApproval a2 = - new PatchSetApproval( - new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Verified")), - (short) 1, - TimeUtil.nowTs()); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - - assertDiffs( - b1, - b2, - "PatchSetApproval.Key sets differ:" - + " [" - + id - + "%2C1,100,Code-Review] only in A;" - + " [" - + id - + "%2C1,100,Verified] only in B"); - } - - @Test - public void diffPatchSetApprovals() throws Exception { - Change c = TestChanges.newChange(project, accountId); - PatchSetApproval a1 = - new PatchSetApproval( - new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 1, - TimeUtil.nowTs()); - PatchSetApproval a2 = clone(a1); - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - - assertNoDiffs(b1, b2); - - a2.setValue((short) -1); - assertDiffs( - b1, - b2, - "value differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review: {1} != {-1}"); - } - - @Test - public void diffPatchSetApprovalsMixedSourcesAllowsSlop() throws Exception { - Change c = TestChanges.newChange(project, accountId); - subWindowResolution(); - PatchSetApproval a1 = - new PatchSetApproval( - new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 1, - roundToSecond(TimeUtil.nowTs())); - PatchSetApproval a2 = clone(a1); - a2.setGranted(TimeUtil.nowTs()); - - // Both are ReviewDb, exact timestamp match is required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "granted differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review:" - + " {2009-09-30 17:00:07.0} != {2009-09-30 17:00:08.0}"); - - // One NoteDb, slop is allowed. - b1 = - new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - - // But not too much slop. - superWindowResolution(); - PatchSetApproval a3 = clone(a1); - a3.setGranted(TimeUtil.nowTs()); - b1 = - new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB); - ChangeBundle b3 = - new ChangeBundle( - c, messages(), latest(c), approvals(a3), comments(), reviewers(), REVIEW_DB); - String msg = - "granted differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:07.0} != {2009-09-30 17:00:15.0}"; - assertDiffs(b1, b3, msg); - assertDiffs(b3, b1, msg); - } - - @Test - public void diffPatchSetApprovalsAllowsTruncatedTimestampInNoteDb() throws Exception { - Change c = TestChanges.newChange(project, accountId); - PatchSetApproval a1 = - new PatchSetApproval( - new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 1, - c.getCreatedOn()); - PatchSetApproval a2 = clone(a1); - a2.setGranted( - new Timestamp( - new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forTimeZone(TimeZone.getDefault())) - .getMillis())); - - // Both are ReviewDb, exact match is required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "granted differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review:" - + " {2009-09-30 17:00:00.0} != {1900-01-01 00:00:00.0}"); - - // Truncating NoteDb timestamp is allowed. - b1 = - new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - } - - @Test - public void diffPatchSetApprovalsIgnoresPostSubmitBitOnZeroVote() throws Exception { - Change c = TestChanges.newChange(project, accountId); - c.setStatus(Change.Status.MERGED); - PatchSetApproval a1 = - new PatchSetApproval( - new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")), - (short) 0, - TimeUtil.nowTs()); - a1.setPostSubmit(false); - PatchSetApproval a2 = clone(a1); - a2.setPostSubmit(true); - - // Both are ReviewDb, exact match is required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "postSubmit differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review:" - + " {false} != {true}"); - - // One NoteDb, postSubmit is ignored. - b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB); - b2 = - new ChangeBundle(c, messages(), latest(c), approvals(a2), comments(), reviewers(), NOTE_DB); - assertNoDiffs(b1, b2); - assertNoDiffs(b2, b1); - - // postSubmit is not ignored if vote isn't 0. - a1.setValue((short) 1); - a2.setValue((short) 1); - assertDiffs( - b1, - b2, - "postSubmit differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review:" - + " {false} != {true}"); - assertDiffs( - b2, - b1, - "postSubmit differs for PatchSetApproval.Key " - + c.getId() - + "%2C1,100,Code-Review:" - + " {true} != {false}"); - } - - @Test - public void diffReviewers() throws Exception { - Change c = TestChanges.newChange(project, accountId); - Timestamp now = TimeUtil.nowTs(); - ReviewerSet r1 = reviewers(REVIEWER, new Account.Id(1), now); - ReviewerSet r2 = reviewers(REVIEWER, new Account.Id(2), now); - - ChangeBundle b1 = - new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r1, REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r2, REVIEW_DB); - assertNoDiffs(b1, b1); - assertNoDiffs(b2, b2); - assertDiffs(b1, b2, "reviewer sets differ: [1] only in A; [2] only in B"); - } - - @Test - public void diffReviewersIgnoresStateAndTimestamp() throws Exception { - Change c = TestChanges.newChange(project, accountId); - ReviewerSet r1 = reviewers(REVIEWER, new Account.Id(1), TimeUtil.nowTs()); - ReviewerSet r2 = reviewers(CC, new Account.Id(1), TimeUtil.nowTs()); - - ChangeBundle b1 = - new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r1, REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r2, REVIEW_DB); - assertNoDiffs(b1, b1); - assertNoDiffs(b2, b2); - } - - @Test - public void diffPatchLineCommentKeySets() throws Exception { - Change c = TestChanges.newChange(project, accountId); - int id = c.getId().get(); - PatchLineComment c1 = - new PatchLineComment( - new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename1"), "uuid1"), - 5, - accountId, - null, - TimeUtil.nowTs()); - PatchLineComment c2 = - new PatchLineComment( - new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename2"), "uuid2"), - 5, - accountId, - null, - TimeUtil.nowTs()); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB); - - assertDiffs( - b1, - b2, - "PatchLineComment.Key sets differ:" - + " [" - + id - + ",1,filename1,uuid1] only in A;" - + " [" - + id - + ",1,filename2,uuid2] only in B"); - } - - @Test - public void diffPatchLineComments() throws Exception { - Change c = TestChanges.newChange(project, accountId); - PatchLineComment c1 = - new PatchLineComment( - new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename"), "uuid"), - 5, - accountId, - null, - TimeUtil.nowTs()); - PatchLineComment c2 = clone(c1); - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB); - - assertNoDiffs(b1, b2); - - c2.setStatus(PatchLineComment.Status.PUBLISHED); - assertDiffs( - b1, - b2, - "status differs for PatchLineComment.Key " + c.getId() + ",1,filename,uuid: {d} != {P}"); - } - - @Test - public void diffPatchLineCommentsMixedSourcesAllowsSlop() throws Exception { - subWindowResolution(); - Change c = TestChanges.newChange(project, accountId); - PatchLineComment c1 = - new PatchLineComment( - new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename"), "uuid"), - 5, - accountId, - null, - roundToSecond(TimeUtil.nowTs())); - PatchLineComment c2 = clone(c1); - c2.setWrittenOn(TimeUtil.nowTs()); - - // Both are ReviewDb, exact timestamp match is required. - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB); - assertDiffs( - b1, - b2, - "writtenOn differs for PatchLineComment.Key " - + c.getId() - + ",1,filename,uuid:" - + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}"); - - // One NoteDb, slop is allowed. - b1 = - new ChangeBundle(c, messages(), latest(c), approvals(), comments(c1), reviewers(), NOTE_DB); - b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - - // But not too much slop. - superWindowResolution(); - PatchLineComment c3 = clone(c1); - c3.setWrittenOn(TimeUtil.nowTs()); - b1 = - new ChangeBundle(c, messages(), latest(c), approvals(), comments(c1), reviewers(), NOTE_DB); - ChangeBundle b3 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c3), reviewers(), REVIEW_DB); - String msg = - "writtenOn differs for PatchLineComment.Key " - + c.getId() - + ",1,filename,uuid in NoteDb vs. ReviewDb:" - + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:10.0}"; - assertDiffs(b1, b3, msg); - assertDiffs(b3, b1, msg); - } - - @Test - public void diffPatchLineCommentsIgnoresCommentsOnInvalidPatchSet() throws Exception { - Change c = TestChanges.newChange(project, accountId); - PatchLineComment c1 = - new PatchLineComment( - new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename1"), "uuid1"), - 5, - accountId, - null, - TimeUtil.nowTs()); - PatchLineComment c2 = - new PatchLineComment( - new PatchLineComment.Key( - new Patch.Key(new PatchSet.Id(c.getId(), 0), "filename2"), "uuid2"), - 5, - accountId, - null, - TimeUtil.nowTs()); - - ChangeBundle b1 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c1, c2), reviewers(), REVIEW_DB); - ChangeBundle b2 = - new ChangeBundle( - c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB); - assertNoDiffs(b1, b2); - } - - private static void assertNoDiffs(ChangeBundle a, ChangeBundle b) { - assertThat(a.differencesFrom(b)).isEmpty(); - assertThat(b.differencesFrom(a)).isEmpty(); - } - - private static void assertDiffs(ChangeBundle a, ChangeBundle b, String first, String... rest) { - List<String> actual = a.differencesFrom(b); - if (actual.size() == 1 && rest.length == 0) { - // This error message is much easier to read. - assertThat(actual.get(0)).isEqualTo(first); - } else { - List<String> expected = new ArrayList<>(1 + rest.length); - expected.add(first); - Collections.addAll(expected, rest); - assertThat(actual).containsExactlyElementsIn(expected).inOrder(); - } - assertThat(a).isNotEqualTo(b); - } - - private static List<ChangeMessage> messages(ChangeMessage... ents) { - return Arrays.asList(ents); - } - - private static List<PatchSet> patchSets(PatchSet... ents) { - return Arrays.asList(ents); - } - - private static List<PatchSet> latest(Change c) { - PatchSet ps = new PatchSet(c.currentPatchSetId()); - ps.setCreatedOn(c.getLastUpdatedOn()); - return ImmutableList.of(ps); - } - - private static List<PatchSetApproval> approvals(PatchSetApproval... ents) { - return Arrays.asList(ents); - } - - private static ReviewerSet reviewers(Object... ents) { - checkArgument(ents.length % 3 == 0); - Table<ReviewerStateInternal, Account.Id, Timestamp> t = HashBasedTable.create(); - for (int i = 0; i < ents.length; i += 3) { - t.put((ReviewerStateInternal) ents[i], (Account.Id) ents[i + 1], (Timestamp) ents[i + 2]); - } - return ReviewerSet.fromTable(t); - } - - private static List<PatchLineComment> comments(PatchLineComment... ents) { - return Arrays.asList(ents); - } - - private static Change clone(Change ent) { - return clone(CHANGE_CODEC, ent); - } - - private static ChangeMessage clone(ChangeMessage ent) { - return clone(CHANGE_MESSAGE_CODEC, ent); - } - - private static PatchSet clone(PatchSet ent) { - return clone(PATCH_SET_CODEC, ent); - } - - private static PatchSetApproval clone(PatchSetApproval ent) { - return clone(PATCH_SET_APPROVAL_CODEC, ent); - } - - private static PatchLineComment clone(PatchLineComment ent) { - return clone(PATCH_LINE_COMMENT_CODEC, ent); - } - - private static <T> T clone(ProtobufCodec<T> codec, T obj) { - return codec.decode(codec.encodeToByteArray(obj)); - } -} |