| // Copyright (C) 2024 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.acceptance.api.revision; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.server.change.CommentsUtil.createFixReplacementInfo; |
| import static com.google.gerrit.acceptance.server.change.CommentsUtil.createFixSuggestionInfo; |
| import static com.google.gerrit.acceptance.server.change.CommentsUtil.createRange; |
| import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL; |
| import static com.google.gerrit.extensions.common.testing.CommentInfoSubject.assertThatList; |
| import static com.google.gerrit.extensions.common.testing.DiffInfoSubject.assertThat; |
| import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| import static java.util.stream.Collectors.toList; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.acceptance.config.GerritConfig; |
| import com.google.gerrit.acceptance.testsuite.account.AccountOperations; |
| import com.google.gerrit.acceptance.testsuite.change.ChangeOperations; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.Patch; |
| import com.google.gerrit.extensions.api.changes.PublishChangeEditInput; |
| import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput; |
| import com.google.gerrit.extensions.common.ChangeType; |
| import com.google.gerrit.extensions.common.CommentInfo; |
| import com.google.gerrit.extensions.common.DiffInfo; |
| import com.google.gerrit.extensions.common.DiffInfo.IntraLineStatus; |
| import com.google.gerrit.extensions.common.EditInfo; |
| import com.google.gerrit.extensions.common.FixReplacementInfo; |
| import com.google.gerrit.extensions.common.FixSuggestionInfo; |
| import com.google.gerrit.extensions.restapi.BadRequestException; |
| import com.google.gerrit.extensions.restapi.BinaryResult; |
| import com.google.gerrit.extensions.restapi.ResourceConflictException; |
| import com.google.gerrit.extensions.restapi.ResourceNotFoundException; |
| import com.google.gerrit.extensions.restapi.RestApiException; |
| import com.google.gerrit.extensions.restapi.testing.BinaryResultSubject; |
| import com.google.gerrit.server.experiments.ExperimentFeatures; |
| import com.google.gerrit.server.experiments.ExperimentFeaturesConstants; |
| import com.google.gerrit.testing.ConfigSuite; |
| import com.google.gerrit.testing.TestCommentHelper; |
| import com.google.inject.Inject; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Optional; |
| import org.eclipse.jgit.lib.Config; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class CommentWithFixIT extends AbstractDaemonTest { |
| @Inject private TestCommentHelper testCommentHelper; |
| @Inject private ChangeOperations changeOperations; |
| @Inject private AccountOperations accountOperations; |
| @Inject private RequestScopeOperations requestScopeOperations; |
| @Inject private ExperimentFeatures experimentFeatures; |
| |
| private static final String PLAIN_TEXT_CONTENT_TYPE = "text/plain"; |
| private static final String GERRIT_COMMIT_MESSAGE_TYPE = "text/x-gerrit-commit-message"; |
| |
| private static final String FILE_NAME = "file_to_fix.txt"; |
| private static final String FILE_NAME2 = "another_file_to_fix.txt"; |
| private static final String FILE_NAME3 = "file_without_newline_at_end.txt"; |
| private static final String FILE_CONTENT = |
| "First line\nSecond line\nThird line\nFourth line\nFifth line\nSixth line" |
| + "\nSeventh line\nEighth line\nNinth line\nTenth line\n"; |
| private static final String FILE_CONTENT2 = "1st line\n2nd line\n3rd line\n"; |
| private static final String FILE_CONTENT3 = "1st line\n2nd line"; |
| private String changeId; |
| private String commitId; |
| private FixReplacementInfo fixReplacementInfo; |
| private FixSuggestionInfo fixSuggestionInfo; |
| private CommentInput withFixCommentInput; |
| |
| @Before |
| public void setUp() throws Exception { |
| PushOneCommit push = |
| pushFactory.create( |
| admin.newIdent(), |
| testRepo, |
| "Provide files which can be used for fixes", |
| ImmutableMap.of( |
| FILE_NAME, FILE_CONTENT, FILE_NAME2, FILE_CONTENT2, FILE_NAME3, FILE_CONTENT3)); |
| PushOneCommit.Result changeResult = push.to("refs/for/master"); |
| changeId = changeResult.getChangeId(); |
| commitId = changeResult.getCommit().getName(); |
| |
| fixReplacementInfo = createFixReplacementInfo(); |
| fixSuggestionInfo = createFixSuggestionInfo(fixReplacementInfo); |
| withFixCommentInput = TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo); |
| } |
| |
| @ConfigSuite.Default |
| public static Config setExperimentFlag() { |
| Config cfg = new Config(); |
| cfg.setString( |
| "experiments", |
| null, |
| "enabled", |
| ExperimentFeaturesConstants.ALLOW_FIX_SUGGESTIONS_IN_COMMENTS); |
| return cfg; |
| } |
| |
| @Test |
| public void fixSuggestionCannotPointToPatchsetLevel() throws Exception { |
| CommentInput input = TestCommentHelper.createCommentInput(FILE_NAME); |
| FixReplacementInfo brokenFixReplacement = createFixReplacementInfo(); |
| brokenFixReplacement.path = PATCHSET_LEVEL; |
| input.fixSuggestions = ImmutableList.of(createFixSuggestionInfo(brokenFixReplacement)); |
| BadRequestException ex = |
| assertThrows( |
| BadRequestException.class, () -> testCommentHelper.addComment(changeId, input)); |
| assertThat(ex.getMessage()).contains("file path must not be " + PATCHSET_LEVEL); |
| } |
| |
| @Test |
| public void hugeCommentIsRejected() { |
| int defaultSizeLimit = 1 << 20; |
| fixReplacementInfo.replacement = getStringFor(defaultSizeLimit + 1); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown).hasMessageThat().contains("limit"); |
| } |
| |
| @Test |
| public void reasonablyLargeCommentIsAccepted() throws Exception { |
| int defaultSizeLimit = 1 << 10; |
| // Allow for a few hundred bytes in other fields. |
| fixReplacementInfo.replacement = getStringFor(defaultSizeLimit - 666); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| assertThat(commentInfos).hasSize(1); |
| } |
| |
| @Test |
| @GerritConfig(name = "change.commentSizeLimit", value = "0") |
| public void zeroForMaximumAllowedSizeOfCommentRemovesRestriction() throws Exception { |
| int defaultSizeLimit = 1 << 10; |
| fixReplacementInfo.replacement = getStringFor(2 * defaultSizeLimit); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| assertThat(commentInfos).hasSize(1); |
| } |
| |
| @Test |
| @GerritConfig(name = "change.commentSizeLimit", value = "-1") |
| public void negativeValueForMaximumAllowedSizeOfCommentRemovesRestriction() throws Exception { |
| int defaultSizeLimit = 1 << 20; |
| fixReplacementInfo.replacement = getStringFor(2 * defaultSizeLimit); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| assertThat(commentInfos).hasSize(1); |
| } |
| |
| @Test |
| public void addedFixSuggestionCanBeRetrieved() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos).onlyElement().onlyFixSuggestion().isNotNull(); |
| } |
| |
| @Test |
| public void fixIdIsGeneratedForFixSuggestion() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos).onlyElement().onlyFixSuggestion().fixId().isNotEmpty(); |
| assertThatList(commentInfos) |
| .onlyElement() |
| .onlyFixSuggestion() |
| .fixId() |
| .isNotEqualTo(fixSuggestionInfo.fixId); |
| } |
| |
| @Test |
| public void descriptionOfFixSuggestionIsAcceptedAsIs() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos) |
| .onlyElement() |
| .onlyFixSuggestion() |
| .description() |
| .isEqualTo(fixSuggestionInfo.description); |
| } |
| |
| @Test |
| public void descriptionOfFixSuggestionIsMandatory() { |
| fixSuggestionInfo.description = null; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "A description is required for the suggested fix of the comment on %s", |
| withFixCommentInput.path)); |
| } |
| |
| @Test |
| public void addedFixReplacementCanBeRetrieved() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos).onlyElement().onlyFixSuggestion().onlyReplacement().isNotNull(); |
| } |
| |
| @Test |
| public void fixReplacementsAreMandatory() { |
| fixSuggestionInfo.replacements = Collections.emptyList(); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "At least one replacement is required" |
| + " for the suggested fix of the comment on %s", |
| withFixCommentInput.path)); |
| } |
| |
| @Test |
| public void pathOfFixReplacementIsAcceptedAsIs() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos) |
| .onlyElement() |
| .onlyFixSuggestion() |
| .onlyReplacement() |
| .path() |
| .isEqualTo(fixReplacementInfo.path); |
| } |
| |
| @Test |
| public void pathOfFixReplacementIsMandatory() { |
| fixReplacementInfo.path = null; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "A file path must be given for the replacement of the comment on %s", |
| withFixCommentInput.path)); |
| } |
| |
| @Test |
| public void rangeOfFixReplacementIsAcceptedAsIs() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos) |
| .onlyElement() |
| .onlyFixSuggestion() |
| .onlyReplacement() |
| .range() |
| .isEqualTo(fixReplacementInfo.range); |
| } |
| |
| @Test |
| public void rangeOfFixReplacementIsMandatory() { |
| fixReplacementInfo.range = null; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "A range must be given for the replacement of the comment on %s", |
| withFixCommentInput.path)); |
| } |
| |
| @Test |
| public void rangeOfFixReplacementNeedsToBeValid() { |
| fixReplacementInfo.range = createRange(13, 9, 5, 10); |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown).hasMessageThat().contains("Range (13:9 - 5:10)"); |
| } |
| |
| @Test |
| public void commentWithRangeAndLine_lineIsIgnored() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 1); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| |
| withFixCommentInput.line = 1; |
| withFixCommentInput.range = createRange(2, 0, 3, 1); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> comments = getComments(); |
| assertThat(comments.get(0).line).isEqualTo(3); |
| } |
| |
| @Test |
| public void rangesOfFixReplacementsOfSameFixSuggestionForSameFileMayNotOverlap() { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 1); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(3, 0, 4, 0); |
| fixReplacementInfo2.replacement = "Second modification\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown).hasMessageThat().contains("overlap"); |
| } |
| |
| @Test |
| public void rangesOfFixReplacementsOfSameFixSuggestionForDifferentFileMayOverlap() |
| throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 1); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME2; |
| fixReplacementInfo2.range = createRange(3, 0, 4, 0); |
| fixReplacementInfo2.replacement = "Second modification\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| assertThatList(commentInfos).onlyElement().fixSuggestions().hasSize(1); |
| } |
| |
| @Test |
| public void rangesOfFixReplacementsOfDifferentFixSuggestionsForSameFileMayOverlap() |
| throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 1); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| FixSuggestionInfo fixSuggestionInfo1 = createFixSuggestionInfo(fixReplacementInfo1); |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(3, 0, 4, 0); |
| fixReplacementInfo2.replacement = "Second modification\n"; |
| FixSuggestionInfo fixSuggestionInfo2 = createFixSuggestionInfo(fixReplacementInfo2); |
| |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo1, fixSuggestionInfo2); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| assertThatList(commentInfos).onlyElement().fixSuggestions().hasSize(2); |
| } |
| |
| @Test |
| public void fixReplacementsDoNotNeedToBeOrderedAccordingToRange() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 0); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(3, 0, 4, 0); |
| fixReplacementInfo2.replacement = "Second modification\n"; |
| |
| FixReplacementInfo fixReplacementInfo3 = new FixReplacementInfo(); |
| fixReplacementInfo3.path = FILE_NAME; |
| fixReplacementInfo3.range = createRange(4, 0, 5, 0); |
| fixReplacementInfo3.replacement = "Third modification\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo2, fixReplacementInfo1, fixReplacementInfo3); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| assertThatList(commentInfos).onlyElement().onlyFixSuggestion().replacements().hasSize(3); |
| } |
| |
| @Test |
| public void replacementStringOfFixReplacementIsAcceptedAsIs() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| |
| assertThatList(commentInfos) |
| .onlyElement() |
| .onlyFixSuggestion() |
| .onlyReplacement() |
| .replacement() |
| .isEqualTo(fixReplacementInfo.replacement); |
| } |
| |
| @Test |
| public void replacementStringOfFixReplacementIsMandatory() { |
| fixReplacementInfo.replacement = null; |
| |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "A content for replacement must be " |
| + "indicated for the replacement of the comment on %s", |
| withFixCommentInput.path)); |
| } |
| |
| @Test |
| public void storedFixWithinALineCanBeApplied() throws Exception { |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nSecond line\nTModified contentrd line\nFourth line\nFifth line\n" |
| + "Sixth line\nSeventh line\nEighth line\nNinth line\nTenth line\n"); |
| } |
| |
| @Test |
| public void applyStoredFixAfterUpdatingPreferredEmail() throws Exception { |
| String emailOne = "email1@example.com"; |
| Account.Id testUser = accountOperations.newAccount().preferredEmail(emailOne).create(); |
| |
| // Create change |
| Change.Id change = |
| changeOperations |
| .newChange() |
| .project(project) |
| .file(FILE_NAME) |
| .content(FILE_CONTENT) |
| .owner(testUser) |
| .create(); |
| |
| // Add Robot Comment to the change |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| testCommentHelper.addComment(project + "~" + change.get(), withFixCommentInput); |
| |
| // Change preferred email for the user |
| String emailTwo = "email2@example.com"; |
| accountOperations.account(testUser).forUpdate().preferredEmail(emailTwo).update(); |
| requestScopeOperations.setApiUser(testUser); |
| |
| // Fetch Fix ID |
| List<CommentInfo> commentInfoList = gApi.changes().id(change.get()).current().commentsAsList(); |
| |
| List<String> fixIds = getFixIds(commentInfoList); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| // Apply fix |
| gApi.changes().id(change.get()).current().applyFix(fixId); |
| |
| EditInfo editInfo = gApi.changes().id(change.get()).edit().get().orElseThrow(); |
| assertThat(editInfo.commit.committer.email).isEqualTo(emailOne); |
| } |
| |
| @Test |
| public void storedFixSpanningMultipleLinesCanBeApplied() throws Exception { |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content\n5"; |
| fixReplacementInfo.range = createRange(3, 2, 5, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nSecond line\nThModified content\n5th line\nSixth line\nSeventh line\n" |
| + "Eighth line\nNinth line\nTenth line\n"); |
| } |
| |
| @Test |
| public void storedFixWithTwoCloseReplacementsOnSameFileCanBeApplied() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 0); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(3, 0, 4, 0); |
| fixReplacementInfo2.replacement = "Some other modified content\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nFirst modification\nSome other modified content\nFourth line\nFifth line\n" |
| + "Sixth line\nSeventh line\nEighth line\nNinth line\nTenth line\n"); |
| } |
| |
| @Test |
| public void twoStoredFixesOnSameFileCanBeApplied() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 0); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| FixSuggestionInfo fixSuggestionInfo1 = createFixSuggestionInfo(fixReplacementInfo1); |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(8, 0, 9, 0); |
| fixReplacementInfo2.replacement = "Some other modified content\n"; |
| FixSuggestionInfo fixSuggestionInfo2 = createFixSuggestionInfo(fixReplacementInfo2); |
| |
| CommentInput commentInput1 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo1); |
| CommentInput commentInput2 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo2); |
| testCommentHelper.addComment(changeId, commentInput1); |
| testCommentHelper.addComment(changeId, commentInput2); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(0)); |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(1)); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nFirst modification\nThird line\nFourth line\nFifth line\nSixth line\n" |
| + "Seventh line\nSome other modified content\nNinth line\nTenth line\n"); |
| } |
| |
| @Test |
| public void twoConflictingStoredFixesOnSameFileCannotBeApplied() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 1); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| FixSuggestionInfo fixSuggestionInfo1 = createFixSuggestionInfo(fixReplacementInfo1); |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(3, 0, 4, 0); |
| fixReplacementInfo2.replacement = "Some other modified content\n"; |
| FixSuggestionInfo fixSuggestionInfo2 = createFixSuggestionInfo(fixReplacementInfo2); |
| |
| CommentInput commentInput1 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo1); |
| CommentInput commentInput2 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo2); |
| testCommentHelper.addComment(changeId, commentInput1); |
| testCommentHelper.addComment(changeId, commentInput2); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(0)); |
| ResourceConflictException thrown = |
| assertThrows( |
| ResourceConflictException.class, |
| () -> gApi.changes().id(changeId).current().applyFix(fixIds.get(1))); |
| assertThat(thrown).hasMessageThat().contains("merge"); |
| } |
| |
| @Test |
| public void twoStoredFixesOfSameCommentCanBeApplied() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 0); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| FixSuggestionInfo fixSuggestionInfo1 = createFixSuggestionInfo(fixReplacementInfo1); |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME; |
| fixReplacementInfo2.range = createRange(8, 0, 9, 0); |
| fixReplacementInfo2.replacement = "Some other modified content\n"; |
| FixSuggestionInfo fixSuggestionInfo2 = createFixSuggestionInfo(fixReplacementInfo2); |
| |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo1, fixSuggestionInfo2); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(0)); |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(1)); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nFirst modification\nThird line\nFourth line\nFifth line\nSixth line\n" |
| + "Seventh line\nSome other modified content\nNinth line\nTenth line\n"); |
| } |
| |
| @Test |
| public void storedFixReferringToDifferentFileThanCommentCanBeApplied() throws Exception { |
| fixReplacementInfo.path = FILE_NAME2; |
| fixReplacementInfo.range = createRange(2, 0, 3, 0); |
| fixReplacementInfo.replacement = "Modified content\n"; |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME2); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo("1st line\nModified content\n3rd line\n"); |
| } |
| |
| @Test |
| public void storedFixInvolvingTwoFilesCanBeApplied() throws Exception { |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = FILE_NAME; |
| fixReplacementInfo1.range = createRange(2, 0, 3, 0); |
| fixReplacementInfo1.replacement = "First modification\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME2; |
| fixReplacementInfo2.range = createRange(1, 0, 2, 0); |
| fixReplacementInfo2.replacement = "Different file modification\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nFirst modification\nThird line\nFourth line\nFifth line\nSixth line\n" |
| + "Seventh line\nEighth line\nNinth line\nTenth line\n"); |
| Optional<BinaryResult> file2 = gApi.changes().id(changeId).edit().getFile(FILE_NAME2); |
| BinaryResultSubject.assertThat(file2) |
| .value() |
| .asString() |
| .isEqualTo("Different file modification\n2nd line\n3rd line\n"); |
| } |
| |
| @Test |
| public void storedFixReferringToNonExistentFileCannotBeApplied() throws Exception { |
| fixReplacementInfo.path = "a_non_existent_file.txt"; |
| fixReplacementInfo.range = createRange(1, 0, 2, 0); |
| fixReplacementInfo.replacement = "Modified content\n"; |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| assertThrows( |
| ResourceNotFoundException.class, |
| () -> gApi.changes().id(changeId).current().applyFix(fixId)); |
| } |
| |
| @Test |
| public void storedFixOnPreviousPatchSetWithoutChangeEditCannotBeApplied() throws Exception { |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| // Remember patch set and add another one. |
| String previousRevision = gApi.changes().id(changeId).get().currentRevision; |
| amendChange(changeId); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| ResourceConflictException thrown = |
| assertThrows( |
| ResourceConflictException.class, |
| () -> gApi.changes().id(changeId).revision(previousRevision).applyFix(fixId)); |
| assertThat(thrown).hasMessageThat().contains("current"); |
| } |
| |
| @Test |
| public void storedFixOnPreviousPatchSetWithExistingChangeEditCanBeApplied() throws Exception { |
| // Create an empty change edit. |
| gApi.changes().id(changeId).edit().create(); |
| |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| // Remember patch set and add another one. |
| String previousRevision = gApi.changes().id(changeId).get().currentRevision; |
| amendChange(changeId); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| EditInfo editInfo = gApi.changes().id(changeId).revision(previousRevision).applyFix(fixId); |
| |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo( |
| "First line\nSecond line\nTModified contentrd line\nFourth line\nFifth line\n" |
| + "Sixth line\nSeventh line\nEighth line\nNinth line\nTenth line\n"); |
| assertThat(editInfo).baseRevision().isEqualTo(previousRevision); |
| } |
| |
| @Test |
| public void storedFixOnCurrentPatchSetWithChangeEditOnPreviousPatchSetCannotBeApplied() |
| throws Exception { |
| // Create an empty change edit. |
| gApi.changes().id(changeId).edit().create(); |
| |
| // Add another patch set. |
| amendChange(changeId); |
| |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| ResourceConflictException thrown = |
| assertThrows( |
| ResourceConflictException.class, |
| () -> gApi.changes().id(changeId).current().applyFix(fixId)); |
| assertThat(thrown).hasMessageThat().contains("based"); |
| } |
| |
| @Test |
| public void storedFixDoesNotModifyCommitMessageOfChangeEdit() throws Exception { |
| String changeEditCommitMessage = |
| "This is the commit message of the change edit.\n\nChange-Id: " + changeId + "\n"; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(changeEditCommitMessage); |
| |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| String fixId = Iterables.getOnlyElement(getFixIds(getComments())); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage(); |
| assertThat(commitMessage).isEqualTo(changeEditCommitMessage); |
| } |
| |
| @Test |
| public void storedFixOnCommitMessageCanBeApplied() throws Exception { |
| // Set a dedicated commit message. |
| String footer = "\nChange-Id: " + changeId + "\n"; |
| String originalCommitMessage = "Line 1 of commit message\nLine 2 of commit message\n" + footer; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(originalCommitMessage); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| withFixCommentInput.path = Patch.COMMIT_MSG; |
| fixReplacementInfo.path = Patch.COMMIT_MSG; |
| fixReplacementInfo.replacement = "Modified line\n"; |
| fixReplacementInfo.range = createRange(7, 0, 8, 0); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| String fixId = Iterables.getOnlyElement(getFixIds(getComments())); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage(); |
| assertThat(commitMessage).isEqualTo("Modified line\nLine 2 of commit message\n" + footer); |
| } |
| |
| @Test |
| public void storedFixOnHeaderPartOfCommitMessageCannotBeApplied() throws Exception { |
| // Set a dedicated commit message. |
| String footer = "Change-Id: " + changeId; |
| String originalCommitMessage = |
| "Line 1 of commit message\nLine 2 of commit message\n" + "\n" + footer + "\n"; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(originalCommitMessage); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| withFixCommentInput.path = Patch.COMMIT_MSG; |
| fixReplacementInfo.path = Patch.COMMIT_MSG; |
| fixReplacementInfo.replacement = "Modified line\n"; |
| fixReplacementInfo.range = createRange(1, 0, 2, 0); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| String fixId = Iterables.getOnlyElement(getFixIds(getComments())); |
| |
| ResourceConflictException exception = |
| assertThrows( |
| ResourceConflictException.class, |
| () -> gApi.changes().id(changeId).current().applyFix(fixId)); |
| assertThat(exception).hasMessageThat().contains("header"); |
| } |
| |
| @Test |
| public void storedFixContainingSeveralModificationsOfCommitMessageCanBeApplied() |
| throws Exception { |
| // Set a dedicated commit message. |
| String footer = "\nChange-Id: " + changeId + "\n"; |
| String originalCommitMessage = |
| "Line 1 of commit message\nLine 2 of commit message\nLine 3 of commit message\n" + footer; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(originalCommitMessage); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = Patch.COMMIT_MSG; |
| fixReplacementInfo1.range = createRange(7, 0, 8, 0); |
| fixReplacementInfo1.replacement = "Modified line 1\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = Patch.COMMIT_MSG; |
| fixReplacementInfo2.range = createRange(9, 0, 10, 0); |
| fixReplacementInfo2.replacement = "Modified line 3\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| withFixCommentInput.path = Patch.COMMIT_MSG; |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| String fixId = Iterables.getOnlyElement(getFixIds(getComments())); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage(); |
| assertThat(commitMessage) |
| .isEqualTo("Modified line 1\nLine 2 of commit message\nModified line 3\n" + footer); |
| } |
| |
| @Test |
| public void storedFixModifyingTheCommitMessageAndAFileCanBeApplied() throws Exception { |
| // Set a dedicated commit message. |
| String footer = "\nChange-Id: " + changeId + "\n"; |
| String originalCommitMessage = "Line 1 of commit message\nLine 2 of commit message\n" + footer; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(originalCommitMessage); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = Patch.COMMIT_MSG; |
| fixReplacementInfo1.range = createRange(7, 0, 8, 0); |
| fixReplacementInfo1.replacement = "Modified line 1\n"; |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = FILE_NAME2; |
| fixReplacementInfo2.range = createRange(1, 0, 2, 0); |
| fixReplacementInfo2.replacement = "File modification\n"; |
| |
| FixSuggestionInfo fixSuggestionInfo = |
| createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2); |
| withFixCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| String fixId = Iterables.getOnlyElement(getFixIds(getComments())); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage(); |
| assertThat(commitMessage).isEqualTo("Modified line 1\nLine 2 of commit message\n" + footer); |
| Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME2); |
| BinaryResultSubject.assertThat(file) |
| .value() |
| .asString() |
| .isEqualTo("File modification\n2nd line\n3rd line\n"); |
| } |
| |
| @Test |
| public void twoStoredFixesOnCommitMessageCanBeAppliedOneAfterTheOther() throws Exception { |
| // Set a dedicated commit message. |
| String footer = "\nChange-Id: " + changeId + "\n"; |
| String originalCommitMessage = |
| "Line 1 of commit message\nLine 2 of commit message\nLine 3 of commit message\n" + footer; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(originalCommitMessage); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = Patch.COMMIT_MSG; |
| fixReplacementInfo1.range = createRange(7, 0, 8, 0); |
| fixReplacementInfo1.replacement = "Modified line 1\n"; |
| FixSuggestionInfo fixSuggestionInfo1 = createFixSuggestionInfo(fixReplacementInfo1); |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = Patch.COMMIT_MSG; |
| fixReplacementInfo2.range = createRange(9, 0, 10, 0); |
| fixReplacementInfo2.replacement = "Modified line 3\n"; |
| FixSuggestionInfo fixSuggestionInfo2 = createFixSuggestionInfo(fixReplacementInfo2); |
| |
| CommentInput commentInput1 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo1); |
| CommentInput commentInput2 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo2); |
| testCommentHelper.addComment(changeId, commentInput1); |
| testCommentHelper.addComment(changeId, commentInput2); |
| List<String> fixIds = getFixIds(getComments()); |
| |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(0)); |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(1)); |
| |
| String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage(); |
| assertThat(commitMessage) |
| .isEqualTo("Modified line 1\nLine 2 of commit message\nModified line 3\n" + footer); |
| } |
| |
| @Test |
| public void twoConflictingStoredFixesOnCommitMessageCanNotBeAppliedOneAfterTheOther() |
| throws Exception { |
| // Set a dedicated commit message. |
| String footer = "Change-Id: " + changeId; |
| String originalCommitMessage = |
| "Line 1 of commit message\nLine 2 of commit message\nLine 3 of commit message\n\n" |
| + footer |
| + "\n"; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(originalCommitMessage); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo(); |
| fixReplacementInfo1.path = Patch.COMMIT_MSG; |
| fixReplacementInfo1.range = createRange(7, 0, 8, 0); |
| fixReplacementInfo1.replacement = "Modified line 1\n"; |
| FixSuggestionInfo fixSuggestionInfo1 = createFixSuggestionInfo(fixReplacementInfo1); |
| |
| FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo(); |
| fixReplacementInfo2.path = Patch.COMMIT_MSG; |
| fixReplacementInfo2.range = createRange(7, 0, 10, 0); |
| fixReplacementInfo2.replacement = "Differently modified line 1\n"; |
| FixSuggestionInfo fixSuggestionInfo2 = createFixSuggestionInfo(fixReplacementInfo2); |
| |
| CommentInput commentInput1 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo1); |
| CommentInput commentInput2 = |
| TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo2); |
| testCommentHelper.addComment(changeId, commentInput1); |
| testCommentHelper.addComment(changeId, commentInput2); |
| List<String> fixIds = getFixIds(getComments()); |
| |
| gApi.changes().id(changeId).current().applyFix(fixIds.get(0)); |
| assertThrows( |
| ResourceConflictException.class, |
| () -> gApi.changes().id(changeId).current().applyFix(fixIds.get(1))); |
| } |
| |
| @Test |
| public void applyingStoredFixTwiceIsIdempotent() throws Exception { |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| String expectedEditCommit = |
| gApi.changes().id(changeId).edit().get().map(edit -> edit.commit.commit).orElse(""); |
| |
| // Apply the fix again. |
| gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<EditInfo> editInfo = gApi.changes().id(changeId).edit().get(); |
| assertThat(editInfo).value().commit().commit().isEqualTo(expectedEditCommit); |
| } |
| |
| @Test |
| public void nonExistentStoredFixCannotBeApplied() throws Exception { |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| String nonExistentFixId = fixId + "_non-existent"; |
| |
| assertThrows( |
| ResourceNotFoundException.class, |
| () -> gApi.changes().id(changeId).current().applyFix(nonExistentFixId)); |
| } |
| |
| @Test |
| public void applyingStoredFixReturnsEditInfoForCreatedChangeEdit() throws Exception { |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| EditInfo editInfo = gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<EditInfo> expectedEditInfo = gApi.changes().id(changeId).edit().get(); |
| String expectedEditCommit = expectedEditInfo.map(edit -> edit.commit.commit).orElse(""); |
| assertThat(editInfo).commit().commit().isEqualTo(expectedEditCommit); |
| String expectedBaseRevision = expectedEditInfo.map(edit -> edit.baseRevision).orElse(""); |
| assertThat(editInfo).baseRevision().isEqualTo(expectedBaseRevision); |
| } |
| |
| @Test |
| public void applyingStoredFixOnTopOfChangeEditReturnsEditInfoForUpdatedChangeEdit() |
| throws Exception { |
| gApi.changes().id(changeId).edit().create(); |
| |
| fixReplacementInfo.path = FILE_NAME; |
| fixReplacementInfo.replacement = "Modified content"; |
| fixReplacementInfo.range = createRange(3, 1, 3, 3); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| EditInfo editInfo = gApi.changes().id(changeId).current().applyFix(fixId); |
| |
| Optional<EditInfo> expectedEditInfo = gApi.changes().id(changeId).edit().get(); |
| String expectedEditCommit = expectedEditInfo.map(edit -> edit.commit.commit).orElse(""); |
| assertThat(editInfo).commit().commit().isEqualTo(expectedEditCommit); |
| String expectedBaseRevision = expectedEditInfo.map(edit -> edit.baseRevision).orElse(""); |
| assertThat(editInfo).baseRevision().isEqualTo(expectedBaseRevision); |
| } |
| |
| @Test |
| public void previewStoredFixWithNonexistentFixId() throws Exception { |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| |
| assertThrows( |
| ResourceNotFoundException.class, |
| () -> gApi.changes().id(changeId).current().getFixPreview("Non existing fixId")); |
| } |
| |
| @Test |
| public void previewStoredFixForCommitMsg() throws Exception { |
| String footer = "Change-Id: " + changeId; |
| updateCommitMessage( |
| changeId, |
| "Commit title\n\nCommit message line 1\nLine 2\nLine 3\nLast line\n\n" + footer + "\n"); |
| FixReplacementInfo commitMsgReplacement = new FixReplacementInfo(); |
| commitMsgReplacement.path = Patch.COMMIT_MSG; |
| // The test assumes that the first 5 lines is a header. |
| // Line 10 has content "Line 2" |
| commitMsgReplacement.range = createRange(10, 0, 11, 0); |
| commitMsgReplacement.replacement = "New content\n"; |
| |
| FixSuggestionInfo commitMsgSuggestionInfo = createFixSuggestionInfo(commitMsgReplacement); |
| CommentInput commitMsgCommentInput = |
| TestCommentHelper.createCommentInput(Patch.COMMIT_MSG, commitMsgSuggestionInfo); |
| testCommentHelper.addComment(changeId, commitMsgCommentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| Map<String, DiffInfo> fixPreview = gApi.changes().id(changeId).current().getFixPreview(fixId); |
| assertThat(fixPreview).hasSize(1); |
| assertThat(fixPreview).containsKey(Patch.COMMIT_MSG); |
| |
| DiffInfo diff = fixPreview.get(Patch.COMMIT_MSG); |
| assertThat(diff).metaA().name().isEqualTo(Patch.COMMIT_MSG); |
| assertThat(diff).metaA().contentType().isEqualTo(GERRIT_COMMIT_MESSAGE_TYPE); |
| assertThat(diff).metaB().name().isEqualTo(Patch.COMMIT_MSG); |
| assertThat(diff).metaB().contentType().isEqualTo(GERRIT_COMMIT_MESSAGE_TYPE); |
| |
| assertThat(diff).content().element(0).commonLines().hasSize(9); |
| // Header has a dynamic content, do not check it |
| assertThat(diff).content().element(0).commonLines().element(6).isEqualTo("Commit title"); |
| assertThat(diff).content().element(0).commonLines().element(7).isEqualTo(""); |
| assertThat(diff) |
| .content() |
| .element(0) |
| .commonLines() |
| .element(8) |
| .isEqualTo("Commit message line 1"); |
| assertThat(diff).content().element(1).linesOfA().containsExactly("Line 2"); |
| assertThat(diff).content().element(1).linesOfB().containsExactly("New content"); |
| assertThat(diff) |
| .content() |
| .element(2) |
| .commonLines() |
| .containsExactly("Line 3", "Last line", "", footer, ""); |
| } |
| |
| @Test |
| @GerritConfig( |
| name = "experiments.disabled", |
| values = {ExperimentFeaturesConstants.ALLOW_FIX_SUGGESTIONS_IN_COMMENTS}) |
| public void commentWithFixFailsToPersistWithoutFeatureFlag() { |
| IllegalStateException thrown = |
| assertThrows( |
| IllegalStateException.class, |
| () -> testCommentHelper.addComment(changeId, withFixCommentInput)); |
| assertThat(thrown).hasMessageThat().contains("feature flag prohibits setting fixSuggestions"); |
| } |
| |
| private void updateCommitMessage(String changeId, String newCommitMessage) throws Exception { |
| gApi.changes().id(changeId).edit().create(); |
| gApi.changes().id(changeId).edit().modifyCommitMessage(newCommitMessage); |
| PublishChangeEditInput publishInput = new PublishChangeEditInput(); |
| gApi.changes().id(changeId).edit().publish(publishInput); |
| } |
| |
| @Test |
| public void previewStoredFixForNonExistingFile() throws Exception { |
| FixReplacementInfo replacement = new FixReplacementInfo(); |
| replacement.path = "a_non_existent_file.txt"; |
| replacement.range = createRange(1, 0, 2, 0); |
| replacement.replacement = "Modified content\n"; |
| |
| FixSuggestionInfo fixSuggestion = createFixSuggestionInfo(replacement); |
| CommentInput commentInput = TestCommentHelper.createCommentInput(FILE_NAME2, fixSuggestion); |
| testCommentHelper.addComment(changeId, commentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| assertThrows( |
| ResourceNotFoundException.class, |
| () -> gApi.changes().id(changeId).current().getFixPreview(fixId)); |
| } |
| |
| @Test |
| public void previewStoredFix() throws Exception { |
| FixReplacementInfo fixReplacementInfoFile1 = new FixReplacementInfo(); |
| fixReplacementInfoFile1.path = FILE_NAME; |
| fixReplacementInfoFile1.replacement = "some replacement code"; |
| fixReplacementInfoFile1.range = createRange(3, 9, 8, 4); |
| |
| FixReplacementInfo fixReplacementInfoFile2 = new FixReplacementInfo(); |
| fixReplacementInfoFile2.path = FILE_NAME2; |
| fixReplacementInfoFile2.replacement = "New line\n"; |
| fixReplacementInfoFile2.range = createRange(2, 0, 2, 0); |
| |
| fixSuggestionInfo = createFixSuggestionInfo(fixReplacementInfoFile1, fixReplacementInfoFile2); |
| |
| withFixCommentInput = TestCommentHelper.createCommentInput(FILE_NAME, fixSuggestionInfo); |
| |
| testCommentHelper.addComment(changeId, withFixCommentInput); |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| Map<String, DiffInfo> fixPreview = gApi.changes().id(changeId).current().getFixPreview(fixId); |
| assertThat(fixPreview).hasSize(2); |
| assertThat(fixPreview).containsKey(FILE_NAME); |
| assertThat(fixPreview).containsKey(FILE_NAME2); |
| |
| DiffInfo diff = fixPreview.get(FILE_NAME); |
| assertThat(diff).intralineStatus().isEqualTo(IntraLineStatus.OK); |
| assertThat(diff).webLinks().isNull(); |
| assertThat(diff).binary().isNull(); |
| assertThat(diff).diffHeader().isNull(); |
| assertThat(diff).changeType().isEqualTo(ChangeType.MODIFIED); |
| assertThat(diff).metaA().totalLineCount().isEqualTo(11); |
| assertThat(diff).metaA().name().isEqualTo(FILE_NAME); |
| assertThat(diff).metaA().commitId().isEqualTo(commitId); |
| assertThat(diff).metaA().contentType().isEqualTo(PLAIN_TEXT_CONTENT_TYPE); |
| assertThat(diff).metaA().webLinks().isNull(); |
| assertThat(diff).metaB().totalLineCount().isEqualTo(6); |
| assertThat(diff).metaB().name().isEqualTo(FILE_NAME); |
| assertThat(diff).metaB().commitId().isNull(); |
| assertThat(diff).metaB().contentType().isEqualTo(PLAIN_TEXT_CONTENT_TYPE); |
| assertThat(diff).metaB().webLinks().isNull(); |
| |
| assertThat(diff).content().hasSize(3); |
| assertThat(diff) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsExactly("First line", "Second line"); |
| assertThat(diff).content().element(0).linesOfA().isNull(); |
| assertThat(diff).content().element(0).linesOfB().isNull(); |
| |
| assertThat(diff).content().element(1).commonLines().isNull(); |
| assertThat(diff) |
| .content() |
| .element(1) |
| .linesOfA() |
| .containsExactly( |
| "Third line", "Fourth line", "Fifth line", "Sixth line", "Seventh line", "Eighth line"); |
| assertThat(diff) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Third linsome replacement codeth line"); |
| |
| assertThat(diff) |
| .content() |
| .element(2) |
| .commonLines() |
| .containsExactly("Ninth line", "Tenth line", ""); |
| assertThat(diff).content().element(2).linesOfA().isNull(); |
| assertThat(diff).content().element(2).linesOfB().isNull(); |
| |
| DiffInfo diff2 = fixPreview.get(FILE_NAME2); |
| assertThat(diff2).intralineStatus().isEqualTo(IntraLineStatus.OK); |
| assertThat(diff2).webLinks().isNull(); |
| assertThat(diff2).binary().isNull(); |
| assertThat(diff2).diffHeader().isNull(); |
| assertThat(diff2).changeType().isEqualTo(ChangeType.MODIFIED); |
| assertThat(diff2).metaA().totalLineCount().isEqualTo(4); |
| assertThat(diff2).metaA().name().isEqualTo(FILE_NAME2); |
| assertThat(diff2).metaA().commitId().isEqualTo(commitId); |
| assertThat(diff2).metaA().contentType().isEqualTo(PLAIN_TEXT_CONTENT_TYPE); |
| assertThat(diff2).metaA().webLinks().isNull(); |
| assertThat(diff2).metaB().totalLineCount().isEqualTo(5); |
| assertThat(diff2).metaB().name().isEqualTo(FILE_NAME2); |
| assertThat(diff2).metaB().commitId().isNull(); |
| assertThat(diff2).metaA().contentType().isEqualTo(PLAIN_TEXT_CONTENT_TYPE); |
| assertThat(diff2).metaB().webLinks().isNull(); |
| |
| assertThat(diff2).content().hasSize(3); |
| assertThat(diff2).content().element(0).commonLines().containsExactly("1st line"); |
| assertThat(diff2).content().element(0).linesOfA().isNull(); |
| assertThat(diff2).content().element(0).linesOfB().isNull(); |
| |
| assertThat(diff2).content().element(1).commonLines().isNull(); |
| assertThat(diff2).content().element(1).linesOfA().isNull(); |
| assertThat(diff2).content().element(1).linesOfB().containsExactly("New line"); |
| |
| assertThat(diff2) |
| .content() |
| .element(2) |
| .commonLines() |
| .containsExactly("2nd line", "3rd line", ""); |
| assertThat(diff2).content().element(2).linesOfA().isNull(); |
| assertThat(diff2).content().element(2).linesOfB().isNull(); |
| } |
| |
| @Test |
| public void previewStoredFixAddNewLineAtEnd() throws Exception { |
| FixReplacementInfo replacement = new FixReplacementInfo(); |
| replacement.path = FILE_NAME3; |
| replacement.range = createRange(2, 8, 2, 8); |
| replacement.replacement = "\n"; |
| |
| FixSuggestionInfo fixSuggestion = createFixSuggestionInfo(replacement); |
| CommentInput commentInput = TestCommentHelper.createCommentInput(FILE_NAME3, fixSuggestion); |
| testCommentHelper.addComment(changeId, commentInput); |
| |
| List<CommentInfo> commentInfos = getComments(); |
| |
| List<String> fixIds = getFixIds(commentInfos); |
| String fixId = Iterables.getOnlyElement(fixIds); |
| |
| Map<String, DiffInfo> fixPreview = gApi.changes().id(changeId).current().getFixPreview(fixId); |
| |
| assertThat(fixPreview).hasSize(1); |
| assertThat(fixPreview).containsKey(FILE_NAME3); |
| |
| DiffInfo diff = fixPreview.get(FILE_NAME3); |
| assertThat(diff).metaA().totalLineCount().isEqualTo(2); |
| // Original file doesn't have EOL marker at the end of file. |
| // Due to the additional EOL mark diff has one additional line |
| // This behavior is in line with ordinary get diff API. |
| assertThat(diff).metaB().totalLineCount().isEqualTo(3); |
| |
| assertThat(diff).content().hasSize(2); |
| assertThat(diff).content().element(0).commonLines().containsExactly("1st line"); |
| assertThat(diff).content().element(1).linesOfA().containsExactly("2nd line"); |
| assertThat(diff).content().element(1).linesOfB().containsExactly("2nd line", ""); |
| } |
| |
| private List<CommentInfo> getComments() throws RestApiException { |
| return gApi.changes().id(changeId).current().commentsAsList(); |
| } |
| |
| private static String getStringFor(int numberOfBytes) { |
| char[] chars = new char[numberOfBytes]; |
| // 'a' will require one byte even when mapped to a JSON string |
| Arrays.fill(chars, 'a'); |
| return new String(chars); |
| } |
| |
| private static List<String> getFixIds(List<CommentInfo> comments) { |
| assertThatList(comments).isNotNull(); |
| return comments.stream() |
| .map(commentInfo -> commentInfo.fixSuggestions) |
| .filter(Objects::nonNull) |
| .flatMap(List::stream) |
| .map(fixSuggestionInfo -> fixSuggestionInfo.fixId) |
| .collect(toList()); |
| } |
| } |