| // Copyright (C) 2017 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.common.truth.TruthJUnit.assume; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow; |
| import static com.google.gerrit.entities.Patch.COMMIT_MSG; |
| import static com.google.gerrit.entities.Patch.MERGE_LIST; |
| import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL; |
| import static com.google.gerrit.extensions.common.testing.DiffInfoSubject.assertThat; |
| import static com.google.gerrit.extensions.common.testing.FileInfoSubject.assertThat; |
| import static com.google.gerrit.git.ObjectIds.abbreviateName; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| import static java.util.stream.Collectors.joining; |
| import static java.util.stream.Collectors.toMap; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.ExtensionRegistry; |
| import com.google.gerrit.acceptance.ExtensionRegistry.Registration; |
| import com.google.gerrit.acceptance.GitUtil; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.acceptance.PushOneCommit.Result; |
| import com.google.gerrit.acceptance.testsuite.change.ChangeOperations; |
| import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; |
| import com.google.gerrit.common.RawInputUtil; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.Patch; |
| import com.google.gerrit.entities.Permission; |
| import com.google.gerrit.extensions.api.changes.FileApi; |
| import com.google.gerrit.extensions.api.changes.RebaseInput; |
| import com.google.gerrit.extensions.client.DiffPreferencesInfo; |
| import com.google.gerrit.extensions.common.ChangeType; |
| import com.google.gerrit.extensions.common.DiffInfo; |
| import com.google.gerrit.extensions.common.FileInfo; |
| import com.google.gerrit.extensions.common.WebLinkInfo; |
| import com.google.gerrit.extensions.common.testing.ContentEntrySubject; |
| import com.google.gerrit.extensions.restapi.BadRequestException; |
| import com.google.gerrit.extensions.restapi.BinaryResult; |
| import com.google.gerrit.extensions.webui.EditWebLink; |
| import com.google.gerrit.extensions.webui.FileWebLink; |
| import com.google.gerrit.server.patch.DiffOperations; |
| import com.google.gerrit.server.patch.DiffOptions; |
| import com.google.gerrit.server.patch.filediff.FileDiffOutput; |
| import com.google.inject.Inject; |
| import java.awt.image.BufferedImage; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.time.format.DateTimeFormatter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.function.Function; |
| import java.util.stream.Collectors; |
| import java.util.stream.IntStream; |
| import javax.imageio.ImageIO; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| public class RevisionDiffIT extends AbstractDaemonTest { |
| // @RunWith(Parameterized.class) can't be used as AbstractDaemonTest is annotated with another |
| // runner. Using different configs is a workaround to achieve the same. |
| protected static final String TEST_PARAMETER_MARKER = "test_only_parameter"; |
| |
| private static final String CURRENT = "current"; |
| private static final String FILE_NAME = "some_file.txt"; |
| private static final String FILE_NAME2 = "another_file.txt"; |
| private static final String FILE_CONTENT = |
| IntStream.rangeClosed(1, 100) |
| .mapToObj(number -> String.format("Line %d\n", number)) |
| .collect(joining()); |
| private static final String FILE_CONTENT2 = "1st line\n2nd line\n3rd line\n"; |
| |
| @Inject private ExtensionRegistry extensionRegistry; |
| @Inject private DiffOperations diffOperations; |
| @Inject private ChangeOperations changeOperations; |
| @Inject private ProjectOperations projectOperations; |
| |
| private boolean intraline; |
| |
| private ObjectId initialCommit; |
| private ObjectId commit1; |
| private String changeId; |
| private String initialPatchSetId; |
| |
| @Before |
| public void setUp() throws Exception { |
| // Reduce flakiness of tests. (If tests aren't fast enough, we would use a fall-back |
| // computation, which might yield different results.) |
| baseConfig.setString("cache", "git_file_diff", "timeout", "1 minute"); |
| baseConfig.setString("cache", "diff_intraline", "timeout", "1 minute"); |
| |
| intraline = baseConfig.getBoolean(TEST_PARAMETER_MARKER, "intraline", false); |
| |
| ObjectId headCommit = testRepo.getRepository().resolve("HEAD"); |
| initialCommit = headCommit; |
| |
| commit1 = |
| addCommit(headCommit, ImmutableMap.of(FILE_NAME, FILE_CONTENT, FILE_NAME2, FILE_CONTENT2)); |
| |
| Result result = createEmptyChange(); |
| changeId = result.getChangeId(); |
| initialPatchSetId = result.getPatchSetId().getId(); |
| } |
| |
| @Test |
| public void diff() throws Exception { |
| // The assertions assume that intraline is false. |
| assume().that(intraline).isFalse(); |
| |
| String fileName = "a_new_file.txt"; |
| String fileContent = "First line\nSecond line\n"; |
| PushOneCommit.Result result = createChange("Add a file", fileName, fileContent); |
| assertDiffForNewFile(result, fileName, fileContent); |
| assertDiffForNewFile(result, COMMIT_MSG, result.getCommit().getFullMessage()); |
| } |
| |
| @Test |
| public void diffWithRootCommit() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.PUSH).ref("refs/*").group(adminGroupUuid()).force(true)) |
| .update(); |
| |
| testRepo.reset(initialCommit); |
| PushOneCommit push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, "subject", ImmutableMap.of("f.txt", "content")) |
| .noParent(); |
| push.setForce(true); |
| PushOneCommit.Result result = push.to("refs/heads/master"); |
| |
| Map<String, FileDiffOutput> modifiedFiles = |
| diffOperations.listModifiedFilesAgainstParent( |
| project, result.getCommit(), /* parentNum= */ 0, DiffOptions.DEFAULTS); |
| |
| assertThat(modifiedFiles.keySet()).containsExactly("/COMMIT_MSG", "f.txt"); |
| assertThat( |
| modifiedFiles.values().stream() |
| .map(FileDiffOutput::oldCommitId) |
| .collect(Collectors.toSet())) |
| .containsExactly(ObjectId.zeroId()); |
| assertThat(modifiedFiles.get("/COMMIT_MSG").changeType()).isEqualTo(Patch.ChangeType.ADDED); |
| assertThat(modifiedFiles.get("f.txt").changeType()).isEqualTo(Patch.ChangeType.ADDED); |
| } |
| |
| @Test |
| public void patchsetLevelFileDiffIsEmpty() throws Exception { |
| PushOneCommit.Result result = createChange(); |
| DiffInfo diffForPatchsetLevelFile = |
| gApi.changes() |
| .id(result.getChangeId()) |
| .revision(result.getCommit().name()) |
| .file(PATCHSET_LEVEL) |
| .diff(); |
| // This behavior is the same as the behavior for non-existent files. |
| assertThat(diffForPatchsetLevelFile).binary().isNull(); |
| assertThat(diffForPatchsetLevelFile).content().isEmpty(); |
| assertThat(diffForPatchsetLevelFile).diffHeader().isNull(); |
| assertThat(diffForPatchsetLevelFile).metaA().isNull(); |
| assertThat(diffForPatchsetLevelFile).metaB().isNull(); |
| assertThat(diffForPatchsetLevelFile).webLinks().isNull(); |
| } |
| |
| @Test |
| public void editWebLinkIncludedInDiff() throws Exception { |
| try (Registration registration = newEditWebLink()) { |
| String fileName = "a_new_file.txt"; |
| String fileContent = "First line\nSecond line\n"; |
| PushOneCommit.Result result = createChange("Add a file", fileName, fileContent); |
| DiffInfo info = |
| gApi.changes() |
| .id(result.getChangeId()) |
| .revision(result.getCommit().name()) |
| .file(fileName) |
| .diff(); |
| assertThat(info.editWebLinks).hasSize(1); |
| assertThat(info.editWebLinks.get(0).url).isEqualTo("http://edit/" + project + "/" + fileName); |
| } |
| } |
| |
| @Test |
| public void gitwebFileWebLinkIncludedInDiff() throws Exception { |
| try (Registration registration = newGitwebFileWebLink()) { |
| String fileName = "foo.txt"; |
| String fileContent = "bar\n"; |
| PushOneCommit.Result result = createChange("Add a file", fileName, fileContent); |
| DiffInfo info = |
| gApi.changes() |
| .id(result.getChangeId()) |
| .revision(result.getCommit().name()) |
| .file(fileName) |
| .diff(); |
| assertThat(info.metaB.webLinks).hasSize(1); |
| assertThat(info.metaB.webLinks.get(0).url) |
| .isEqualTo( |
| String.format( |
| "http://gitweb/?p=%s;hb=%s;f=%s", project, result.getCommit().name(), fileName)); |
| } |
| } |
| |
| @Test |
| public void deletedFileIsIncludedInDiff() throws Exception { |
| gApi.changes().id(changeId).edit().deleteFile(FILE_NAME); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, FILE_NAME); |
| } |
| |
| @Test |
| public void fileModeChangeIsIncludedInListFilesDiff() throws Exception { |
| String fileName = "file.txt"; |
| PushOneCommit push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, "Commit Subject", /* files= */ ImmutableMap.of()) |
| .addFile(fileName, "content", /* fileMode= */ 0100644); |
| PushOneCommit.Result result = push.to("refs/for/master"); |
| String commitRev1 = gApi.changes().id(result.getChangeId()).get().currentRevision; |
| push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, result.getChangeId()) |
| .addFile(fileName, "content", /* fileMode= */ 0100755); |
| result = push.to("refs/for/master"); |
| String commitRev2 = gApi.changes().id(result.getChangeId()).get().currentRevision; |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(result.getChangeId()).revision(commitRev2).files(commitRev1); |
| |
| assertThat(changedFiles.get(fileName)).oldMode().isEqualTo(0100644); |
| assertThat(changedFiles.get(fileName)).newMode().isEqualTo(0100755); |
| } |
| |
| @Test |
| public void fileMode_oldMode_isMissingInListFilesDiff_forAddedFile() throws Exception { |
| String fileName = "file.txt"; |
| PushOneCommit push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, "Commit Subject", /* files= */ ImmutableMap.of()) |
| .addFile(fileName, "content", /* fileMode= */ 0100644); |
| PushOneCommit.Result result = push.to("refs/for/master"); |
| String commitRev = gApi.changes().id(result.getChangeId()).get().currentRevision; |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(result.getChangeId()).revision(commitRev).files(); |
| |
| assertThat(changedFiles.get(fileName)).oldMode().isNull(); |
| assertThat(changedFiles.get(fileName)).newMode().isEqualTo(0100644); |
| } |
| |
| @Test |
| public void fileMode_newMode_isMissingInListFilesDiff_forDeletedFile() throws Exception { |
| String fileName = "file.txt"; |
| PushOneCommit push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, "Commit Subject", /* files= */ ImmutableMap.of()) |
| .addFile(fileName, "content", /* fileMode= */ 0100644); |
| PushOneCommit.Result result = push.to("refs/for/master"); |
| String commitRev1 = gApi.changes().id(result.getChangeId()).get().currentRevision; |
| push = pushFactory.create(admin.newIdent(), testRepo, result.getChangeId()).rmFile(fileName); |
| result = push.to("refs/for/master"); |
| String commitRev2 = gApi.changes().id(result.getChangeId()).get().currentRevision; |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(result.getChangeId()).revision(commitRev2).files(commitRev1); |
| |
| assertThat(changedFiles.get(fileName)).oldMode().isEqualTo(0100644); |
| assertThat(changedFiles.get(fileName)).newMode().isNull(); |
| } |
| |
| @Test |
| public void numberOfLinesInDiffOfDeletedFileWithoutNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().deleteFile(filePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(3); |
| assertThat(diffInfo).metaB().isNull(); |
| } |
| |
| @Test |
| public void numberOfLinesInFileInfoOfDeletedFileWithoutNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().deleteFile(filePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(filePath)).linesInserted().isNull(); |
| assertThat(changedFiles.get(filePath)).linesDeleted().isEqualTo(3); |
| } |
| |
| @Test |
| public void numberOfLinesInDiffOfDeletedFileWithNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().deleteFile(filePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(4); |
| assertThat(diffInfo).metaB().isNull(); |
| } |
| |
| @Test |
| public void numberOfLinesInFileInfoOfDeletedFileWithNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().deleteFile(filePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(filePath)).linesInserted().isNull(); |
| // Inherited from Git: An empty last line is ignored in the count. |
| assertThat(changedFiles.get(filePath)).linesDeleted().isEqualTo(3); |
| } |
| |
| @Test |
| public void deletedFileWithoutNewlineAtEndResultsInOneDiffEntry() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().deleteFile(filePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .onlyElement() |
| .linesOfA() |
| .containsExactly("Line 1", "Line 2", "Line 3"); |
| } |
| |
| @Test |
| public void deletedFileWithNewlineAtEndResultsInOneDiffEntry() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().deleteFile(filePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .onlyElement() |
| .linesOfA() |
| .containsExactly("Line 1", "Line 2", "Line 3", ""); |
| } |
| |
| @Test |
| public void addedFileIsIncludedInDiff() throws Exception { |
| String newFilePath = "a_new_file.txt"; |
| String newFileContent = "arbitrary content"; |
| gApi.changes().id(changeId).edit().modifyFile(newFilePath, RawInputUtil.create(newFileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, newFilePath); |
| } |
| |
| @Test |
| public void numberOfLinesInDiffOfAddedFileWithoutNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).metaA().isNull(); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(3); |
| } |
| |
| @Test |
| public void numberOfLinesInFileInfoOfAddedFileWithoutNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(filePath)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(filePath)).linesDeleted().isNull(); |
| } |
| |
| @Test |
| public void numberOfLinesInDiffOfAddedFileWithNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).metaA().isNull(); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(4); |
| } |
| |
| @Test |
| public void numberOfLinesInFileInfoOfAddedFileWithNewlineAtEndIsCorrect() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| // Inherited from Git: An empty last line is ignored in the count. |
| assertThat(changedFiles.get(filePath)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(filePath)).linesDeleted().isNull(); |
| } |
| |
| @Test |
| public void addedFileWithoutNewlineAtEndResultsInOneDiffEntry() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .onlyElement() |
| .linesOfB() |
| .containsExactly("Line 1", "Line 2", "Line 3"); |
| } |
| |
| @Test |
| public void addedFileWithNewlineAtEndResultsInOneDiffEntry() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .onlyElement() |
| .linesOfB() |
| .containsExactly("Line 1", "Line 2", "Line 3", ""); |
| } |
| |
| @Test |
| public void renamedFileIsIncludedInDiff() throws Exception { |
| String newFilePath = "a_new_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, newFilePath); |
| } |
| |
| @Test |
| public void copiedFileTreatedAsAddedFileInDiff() throws Exception { |
| String copyFilePath = "copy_of_some_file.txt"; |
| gApi.changes().id(changeId).edit().modifyFile(copyFilePath, RawInputUtil.create(FILE_CONTENT)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, copyFilePath); |
| // If this ever changes, please add tests which cover copied files. |
| assertThat(changedFiles.get(copyFilePath)).status().isEqualTo('A'); |
| assertThat(changedFiles.get(copyFilePath)).linesInserted().isEqualTo(100); |
| assertThat(changedFiles.get(copyFilePath)).linesDeleted().isNull(); |
| } |
| |
| @Test |
| public void copiedFileDetectedIfOriginalFileIsRenamedInDiff() throws Exception { |
| /* |
| * Copies are detected when a file is deleted and more than 1 file with the same content are |
| * added. In this case, the added file with the closest name to the original file is tagged as a |
| * rename and the remaining files are considered copies. This implementation is done by JGit in |
| * the RenameDetector component. |
| */ |
| String renamedFileName = "renamed_some_file.txt"; |
| String copyFileName1 = "copy1_with_different_name.txt"; |
| String copyFileName2 = "copy2_with_different_name.txt"; |
| gApi.changes().id(changeId).edit().modifyFile(copyFileName1, RawInputUtil.create(FILE_CONTENT)); |
| gApi.changes().id(changeId).edit().modifyFile(copyFileName2, RawInputUtil.create(FILE_CONTENT)); |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFileName); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| |
| assertThat(changedFiles.keySet()) |
| .containsExactly("/COMMIT_MSG", renamedFileName, copyFileName1, copyFileName2); |
| assertThat(changedFiles.get(renamedFileName).status).isEqualTo('R'); |
| assertThat(changedFiles.get(copyFileName1).status).isEqualTo('C'); |
| assertThat(changedFiles.get(copyFileName2).status).isEqualTo('C'); |
| } |
| |
| @Test |
| public void addedBinaryFileIsIncludedInDiff() throws Exception { |
| String imageFileName = "an_image.png"; |
| byte[] imageBytes = createRgbImage(255, 0, 0); |
| gApi.changes().id(changeId).edit().modifyFile(imageFileName, RawInputUtil.create(imageBytes)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, imageFileName); |
| } |
| |
| @Test |
| public void modifiedBinaryFileIsIncludedInDiff() throws Exception { |
| String imageFileName = "an_image.png"; |
| byte[] imageBytes1 = createRgbImage(255, 100, 0); |
| ObjectId commit2 = addCommit(commit1, imageFileName, imageBytes1); |
| |
| rebaseChangeOn(changeId, commit2); |
| byte[] imageBytes2 = createRgbImage(0, 100, 255); |
| gApi.changes().id(changeId).edit().modifyFile(imageFileName, RawInputUtil.create(imageBytes2)); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, imageFileName); |
| } |
| |
| @Test |
| public void diffOnMergeCommitChange() throws Exception { |
| PushOneCommit.Result r = createMergeCommitChange("refs/for/master"); |
| |
| DiffInfo diff; |
| |
| // automerge |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "foo").get(); |
| assertThat(diff.metaA.lines).isEqualTo(6); |
| assertThat(diff.metaB.lines).isEqualTo(1); |
| |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "bar").get(); |
| assertThat(diff.metaA.lines).isEqualTo(6); |
| assertThat(diff.metaB.lines).isEqualTo(1); |
| |
| // parent 1 |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "bar").withParent(1).get(); |
| assertThat(diff.metaA.lines).isEqualTo(1); |
| assertThat(diff.metaB.lines).isEqualTo(1); |
| |
| // parent 2 |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "foo").withParent(2).get(); |
| assertThat(diff.metaA.lines).isEqualTo(1); |
| assertThat(diff.metaB.lines).isEqualTo(1); |
| } |
| |
| @Test |
| public void diffWithThreeParentsMergeCommitChange() throws Exception { |
| // Create a merge commit of 3 files: foo, bar, baz. The merge commit is pointing to 3 different |
| // parents: the merge commit contains foo of parent1, bar of parent2 and baz of parent3. |
| PushOneCommit.Result r = |
| createNParentsMergeCommitChange("refs/for/master", ImmutableList.of("foo", "bar", "baz")); |
| |
| DiffInfo diff; |
| |
| // parent 1 |
| Map<String, FileInfo> changedFiles = gApi.changes().id(r.getChangeId()).current().files(1); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, MERGE_LIST, "bar", "baz"); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "foo").withParent(1).get(); |
| assertThat(diff.diffHeader).isNull(); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "bar").withParent(1).get(); |
| assertThat(diff.diffHeader).hasSize(4); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "baz").withParent(1).get(); |
| assertThat(diff.diffHeader).hasSize(4); |
| |
| // parent 2 |
| changedFiles = gApi.changes().id(r.getChangeId()).current().files(2); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "baz"); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "foo").withParent(2).get(); |
| assertThat(diff.diffHeader).hasSize(4); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "bar").withParent(2).get(); |
| assertThat(diff.diffHeader).isNull(); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "baz").withParent(2).get(); |
| assertThat(diff.diffHeader).hasSize(4); |
| |
| // parent 3 |
| changedFiles = gApi.changes().id(r.getChangeId()).current().files(3); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar"); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "foo").withParent(3).get(); |
| assertThat(diff.diffHeader).hasSize(4); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "bar").withParent(3).get(); |
| assertThat(diff.diffHeader).hasSize(4); |
| diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "baz").withParent(3).get(); |
| assertThat(diff.diffHeader).isNull(); |
| } |
| |
| @Test |
| public void diffWithThreeParentsMergeCommitAgainstAutoMergeReturnsCommitMsgAndMergeListOnly() |
| throws Exception { |
| PushOneCommit.Result r = |
| createNParentsMergeCommitChange("refs/for/master", ImmutableList.of("foo", "bar", "baz")); |
| |
| // Diff against auto-merge returns COMMIT_MSG and MERGE_LIST only |
| Map<String, FileInfo> changedFiles = gApi.changes().id(r.getChangeId()).current().files(); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, MERGE_LIST); |
| } |
| |
| @Test |
| public void diffBetweenPatchSetsOfMergeCommitCanBeRetrievedForCommitMessageAndMergeList() |
| throws Exception { |
| PushOneCommit.Result result = createMergeCommitChange("refs/for/master", "my_file.txt"); |
| String changeId = result.getChangeId(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, "my_file.txt", content -> content.concat("Line I\nLine II\n")); |
| |
| // Call both of them in succession to ensure that they don't share the same cache keys. |
| DiffInfo commitMessageDiffInfo = |
| getDiffRequest(changeId, CURRENT, COMMIT_MSG).withBase(previousPatchSetId).get(); |
| DiffInfo mergeListDiffInfo = |
| getDiffRequest(changeId, CURRENT, MERGE_LIST).withBase(previousPatchSetId).get(); |
| |
| assertThat(commitMessageDiffInfo).content().hasSize(3); |
| assertThat(mergeListDiffInfo).content().hasSize(1); |
| } |
| |
| @Test |
| public void diffAgainstAutoMergeCanBeRetrievedForCommitMessageAndMergeList() throws Exception { |
| PushOneCommit.Result result = createMergeCommitChange("refs/for/master", "my_file.txt"); |
| String changeId = result.getChangeId(); |
| addModifiedPatchSet(changeId, "my_file.txt", content -> content.concat("Line I\nLine II\n")); |
| |
| DiffInfo commitMessageDiffInfo = |
| getDiffRequest(changeId, CURRENT, COMMIT_MSG) |
| .get(); // diff latest PS against base (auto-merge) |
| DiffInfo mergeListDiffInfo = |
| getDiffRequest(changeId, CURRENT, MERGE_LIST) |
| .get(); // diff latest PS against base (auto-merge) |
| |
| assertThat(commitMessageDiffInfo).changeType().isEqualTo(ChangeType.ADDED); |
| assertThat(commitMessageDiffInfo).content().hasSize(1); |
| |
| assertThat(mergeListDiffInfo).changeType().isEqualTo(ChangeType.ADDED); |
| assertThat(mergeListDiffInfo).content().hasSize(1); |
| assertThat(mergeListDiffInfo) |
| .content() |
| .element(0) |
| .linesOfB() |
| .element(0) |
| .isEqualTo("Merge List:"); |
| } |
| |
| @Test |
| public void diffOfUnmodifiedFileMarksAllLinesAsCommon() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(updatedCommitMessage()); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .onlyElement() |
| .commonLines() |
| .containsExactly("Line 1", "Line 2", "Line 3", "") |
| .inOrder(); |
| assertThat(diffInfo).content().onlyElement().linesOfA().isNull(); |
| assertThat(diffInfo).content().onlyElement().linesOfB().isNull(); |
| } |
| |
| @Test |
| public void diffOfUnmodifiedFileWithNewlineAtEndHasEmptyLineAtEnd() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(updatedCommitMessage()); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().onlyElement().commonLines().lastElement().isEqualTo(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(4); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(4); |
| } |
| |
| @Test |
| public void diffOfUnmodifiedFileWithoutNewlineAtEndEndsWithLastLineContent() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| gApi.changes().id(changeId).edit().modifyCommitMessage(updatedCommitMessage()); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().onlyElement().commonLines().lastElement().isEqualTo("Line 3"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(3); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(3); |
| } |
| |
| @Test |
| public void diffOfModifiedFileWithNewlineAtEndHasEmptyLineAtEnd() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 1\n", "Line one\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().lastElement().commonLines().lastElement().isEqualTo(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(4); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(4); |
| } |
| |
| @Test |
| public void diffOfModifiedFileWithoutNewlineAtEndEndsWithLastLineContent() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 1\n", "Line one\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().lastElement().commonLines().lastElement().isEqualTo("Line 3"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(3); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(3); |
| } |
| |
| @Test |
| public void diffOfModifiedLastLineWithNewlineAtEndHasEmptyLineAtEnd() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 3\n", "Line three\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().lastElement().commonLines().lastElement().isEqualTo(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(4); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(4); |
| } |
| |
| @Test |
| public void diffOfModifiedLastLineWithoutNewlineAtEndEndsWithLastLineContent() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 3", "Line three")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().lastElement().linesOfA().containsExactly("Line 3"); |
| assertThat(diffInfo).content().lastElement().linesOfB().containsExactly("Line three"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(3); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(3); |
| } |
| |
| @Test |
| public void addedNewlineAtEndOfFileIsMarkedInDiffWhenWhitespaceIsConsidered() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 101", ""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(102); |
| } |
| |
| @Test |
| public void addedNewlineAtEndOfFileIsMarkedInDiffWhenWhitespaceIsIgnored() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(102); |
| } |
| |
| @Test |
| public void addedNewlineAtEndOfFileMeansOneModifiedLine() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void addedNewlineAtEndOfFileIsMarkedInDiffWhenOtherwiseOnlyEditsDueToRebaseExist() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).isNotNull(); // Line 70 modification |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 101", ""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(102); |
| } |
| |
| @Test |
| // TODO: Fix this issue. This test documents the current behavior and ensures that we at least |
| // don't run into an internal server error. |
| public void addedNewlineAtEndOfFileIsNotIdentifiedAsDueToRebaseEvenThoughItShould() |
| throws Exception { |
| String baseFileContent = FILE_CONTENT.concat("Line 101"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = baseFileContent.concat("\n"); |
| ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit3); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly(""); |
| // This should actually be isDueToRebase(). |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| } |
| |
| @Test |
| public void |
| addedNewlineAtEndOfFileIsMarkedWhenEditDueToRebaseIncreasedLineCountAndWhitespaceConsidered() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line 70\nLine 70.5\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).isNotNull(); // Line 70.5 insertion |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 101", ""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(103); |
| } |
| |
| @Test |
| // TODO: Fix this issue. This test documents the current behavior and ensures that we at least |
| // don't run into an internal server error. |
| public void |
| addedNewlineAtEndOfFileIsNotMarkedWhenEditDueToRebaseIncreasedLineCountAndWhitespaceIgnoredEvenThoughItShould() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line 70\nLine 70.5\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0); |
| } |
| |
| @Test |
| public void addedLastLineWithoutNewlineBeforeAndAfterwardsIsMarkedInDiff() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\nLine 102")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 101", "Line 102"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(102); |
| } |
| |
| @Test |
| public void addedLastLineWithoutNewlineBeforeAndAfterwardsMeansTwoModifiedLines() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\nLine 102")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void addedLastLineWithoutNewlineBeforeButWithOneAfterwardsIsMarkedInDiff() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\nLine 102\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .withBase(previousPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line 101", "Line 102", ""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(103); |
| } |
| |
| @Test |
| public void addedLastLineWithoutNewlineBeforeButWithOneAfterwardsMeansTwoModifiedLines() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\nLine 102\n")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| // Inherited from Git: An empty last line is ignored in the count. |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void addedLastLineWithNewlineBeforeAndAfterwardsIsMarkedInDiff() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 101"); |
| assertThat(diffInfo).content().element(2).commonLines().containsExactly(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(102); |
| } |
| |
| @Test |
| public void addedLastLineWithNewlineBeforeAndAfterwardsMeansOneInsertedLine() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101\n")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isNull(); |
| } |
| |
| @Test |
| public void addedLastLineWithNewlineBeforeButWithoutOneAfterwardsIsMarkedInDiff() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly(""); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 101"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(101); |
| } |
| |
| @Test |
| public void addedLastLineWithNewlineBeforeButWithoutOneAfterwardsMeansOneInsertedLine() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| // Inherited from Git: An empty last line is ignored in the count. |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isNull(); |
| } |
| |
| @Test |
| public void hunkForModifiedLastLineIsCombinedWithHunkForAddedNewlineAtEnd() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 101", "Line one oh one\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line one oh one", ""); |
| } |
| |
| @Test |
| public void intralineEditsAreIdentified() throws Exception { |
| // In some corner cases, intra-line diffs produce wrong results. In this case, the algorithm |
| // falls back to a single edit covering the whole range. |
| // See: https://issues.gerritcodereview.com/issues/40013030 |
| |
| assume().that(intraline).isTrue(); |
| |
| String orig = "[-9999,9999]"; |
| String replace = "[-999,999]"; |
| |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat(orig)); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace(orig, replace)); |
| |
| // Intra-line logic wrongly produces |
| // replace [-9999{,99}99] with [-999{,}999]. |
| // which if done, results in an incorrect [-9999,99]. |
| // the intra-line algorithm detects this case and falls back to a single region edit. |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| |
| List<List<Integer>> editsA = diffInfo.content.get(1).editA; |
| List<List<Integer>> editsB = diffInfo.content.get(1).editB; |
| String reconstructed = transformStringUsingEditList(orig, replace, editsA, editsB); |
| |
| assertThat(reconstructed).isEqualTo(replace); |
| } |
| |
| @Test |
| public void intralineEditsForModifiedLastLineArePreservedWhenNewlineIsAlsoAddedAtEnd() |
| throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 101", "Line one oh one\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .intralineEditsOfA() |
| .containsExactly(ImmutableList.of(5, 3)); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .intralineEditsOfB() |
| .containsExactly(ImmutableList.of(5, 11)); |
| } |
| |
| @Test |
| public void hunkForModifiedSecondToLastLineIsNotCombinedWithHunkForAddedNewlineAtEnd() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| fileContent -> fileContent.replace("Line 100\n", "Line one hundred\n").concat("\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line one hundred"); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly(""); |
| } |
| |
| @Test |
| public void deletedNewlineAtEndOfFileIsMarkedInDiffWhenWhitespaceIsConsidered() throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(initialPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100", ""); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 100"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(100); |
| } |
| |
| @Test |
| public void deletedNewlineAtEndOfFileIsMarkedInDiffWhenWhitespaceIsIgnored() throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(initialPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly(""); |
| assertThat(diffInfo).content().element(1).linesOfB().isNull(); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(100); |
| } |
| |
| @Test |
| public void deletedNewlineAtEndOfFileMeansOneModifiedLine() throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void deletedLastLineWithoutNewlineBeforeAndAfterwardsIsMarkedInDiff() throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace("\nLine 100", "")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(previousPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 99", "Line 100"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 99"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(100); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(99); |
| } |
| |
| @Test |
| public void deletedLastLineWithoutNewlineBeforeAndAfterwardsMeansTwoModifiedLines() |
| throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace("\nLine 100", "")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void deletedLastLineWithoutNewlineBeforeButWithOneAfterwardsIsMarkedInDiff() |
| throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100", "")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(previousPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(100); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(100); |
| } |
| |
| @Test |
| public void deletedLastLineWithoutNewlineBeforeButWithOneAfterwardsMeansOneDeletedLine() |
| throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line 100")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100", "")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| // Inherited from Git: An empty last line is ignored in the count. |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isNull(); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void deletedLastLineWithNewlineBeforeAndAfterwardsIsMarkedInDiff() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(initialPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(1).linesOfB().isNull(); |
| assertThat(diffInfo).content().element(2).commonLines().containsExactly(""); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(100); |
| } |
| |
| @Test |
| public void deletedLastLineWithNewlineBeforeAndAfterwardsMeansOneDeletedLine() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isNull(); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void deletedLastLineWithNewlineBeforeButWithoutOneAfterwardsIsMarkedInDiff() |
| throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("\nLine 100\n", "")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(initialPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 99", "Line 100", ""); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 99"); |
| |
| assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101); |
| assertThat(diffInfo).metaB().totalLineCount().isEqualTo(99); |
| } |
| |
| @Test |
| public void deletedLastLineWithNewlineBeforeButWithoutOneAfterwardsMeansTwoModifiedLines() |
| throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("\nLine 100\n", "")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void hunkForModifiedLastLineIsCombinedWithHunkForDeletedNewlineAtEnd() throws Exception { |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line one hundred")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100", ""); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line one hundred"); |
| } |
| |
| @Test |
| public void intralineEditsForModifiedLastLineArePreservedWhenNewlineIsAlsoDeletedAtEnd() |
| throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 100\n", "Line one hundred")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .intralineEditsOfA() |
| .containsExactly(ImmutableList.of(5, 4)); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .intralineEditsOfB() |
| .containsExactly(ImmutableList.of(5, 11)); |
| } |
| |
| @Test |
| public void hunkForModifiedSecondToLastLineIsNotCombinedWithHunkForDeletedNewlineAtEnd() |
| throws Exception { |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| fileContent -> |
| fileContent |
| .replace("Line 99\n", "Line ninety-nine\n") |
| .replace("Line 100\n", "Line 100")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 99"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line ninety-nine"); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly(""); |
| assertThat(diffInfo).content().element(3).linesOfB().isNull(); |
| } |
| |
| @Test |
| public void addedUnrelatedFileIsIgnored_forPatchSetDiffWithRebase() throws Exception { |
| ObjectId commit2 = addCommit(commit1, "file_added_in_another_commit.txt", "Some file content"); |
| |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, "Another line\n"::concat); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, FILE_NAME); |
| } |
| |
| @Test |
| public void removedUnrelatedFileIsIgnored_forPatchSetDiffWithRebase() throws Exception { |
| ObjectId commit2 = addCommitRemovingFiles(commit1, FILE_NAME2); |
| |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, "Another line\n"::concat); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, FILE_NAME); |
| } |
| |
| @Test |
| public void renamedUnrelatedFileIsIgnored_forPatchSetDiffWithRebase() throws Exception { |
| ObjectId commit2 = addCommitRenamingFile(commit1, FILE_NAME2, "a_new_file_name.txt"); |
| |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, "Another line\n"::concat); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, FILE_NAME); |
| } |
| |
| @Test |
| @Ignore |
| public void renamedUnrelatedFileIsIgnored_forPatchSetDiffWithRebase_whenEquallyModifiedInBoth() |
| throws Exception { |
| // TODO(ghareeb): fix this test for the new diff cache implementation |
| |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("1st line\n", "First line\n"); |
| addModifiedPatchSet(changeId, FILE_NAME2, contentModification); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| // Revert the modification to be able to rebase. |
| addModifiedPatchSet( |
| changeId, FILE_NAME2, fileContent -> fileContent.replace("First line\n", "1st line\n")); |
| |
| String renamedFileName = "renamed_file.txt"; |
| ObjectId commit2 = addCommitRenamingFile(commit1, FILE_NAME2, renamedFileName); |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, renamedFileName, contentModification); |
| addModifiedPatchSet(changeId, FILE_NAME, "Another line\n"::concat); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, FILE_NAME); |
| } |
| |
| @Test |
| public void renamedUnrelatedFileIsIgnored_forPatchSetDiffWithRebase_whenModifiedDuringRebase() |
| throws Exception { |
| String renamedFilePath = "renamed_some_file.txt"; |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 5\n", "Line five\n")); |
| ObjectId commit3 = addCommitRenamingFile(commit2, FILE_NAME, renamedFilePath); |
| |
| rebaseChangeOn(changeId, commit3); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG); |
| } |
| |
| @Test |
| public void fileRenamedDuringRebaseSameAsInPatchSetIsIgnored() throws Exception { |
| String renamedFileName = "renamed_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFileName); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| // Revert the renaming to be able to rebase. |
| gApi.changes().id(changeId).edit().renameFile(renamedFileName, FILE_NAME); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| ObjectId commit2 = addCommitRenamingFile(commit1, FILE_NAME, renamedFileName); |
| rebaseChangeOn(changeId, commit2); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG); |
| } |
| |
| @Test |
| public void fileWithRebaseHunksRenamedDuringRebaseSameAsInPatchSetIsIgnored() throws Exception { |
| String renamedFileName = "renamed_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFileName); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| // Revert the renaming to be able to rebase. |
| gApi.changes().id(changeId).edit().renameFile(renamedFileName, FILE_NAME); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 10\n", "Line ten\n")); |
| ObjectId commit3 = addCommitRenamingFile(commit2, FILE_NAME, renamedFileName); |
| rebaseChangeOn(changeId, commit3); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG); |
| } |
| |
| @Test |
| public void filesNotTouchedByPatchSetsAndContainingOnlyRebaseHunksAreIgnored() throws Exception { |
| String newFileContent = FILE_CONTENT.replace("Line 10\n", "Line ten\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| ObjectId commit3 = addCommitRenamingFile(commit2, FILE_NAME2, "a_new_file_name.txt"); |
| |
| rebaseChangeOn(changeId, commit3); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG); |
| } |
| |
| @Test |
| @Ignore |
| public void filesTouchedByPatchSetsAndContainingOnlyRebaseHunksAreIgnored() throws Exception { |
| // TODO(ghareeb): fix this test for the new diff cache implementation |
| |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 50\n", "Line fifty\n")); |
| addModifiedPatchSet( |
| changeId, FILE_NAME2, fileContent -> fileContent.replace("1st line\n", "First line\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| // Revert the modification to allow rebasing. |
| addModifiedPatchSet( |
| changeId, FILE_NAME2, fileContent -> fileContent.replace("First line\n", "1st line\n")); |
| |
| String newFileContent = FILE_CONTENT.replace("Line 10\n", "Line ten\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| String newFilePath = "a_new_file_name.txt"; |
| ObjectId commit3 = addCommitRenamingFile(commit2, FILE_NAME2, newFilePath); |
| |
| rebaseChangeOn(changeId, commit3); |
| // Apply the modification again to bring the file into the same state as for the previous |
| // patch set. |
| addModifiedPatchSet( |
| changeId, newFilePath, fileContent -> fileContent.replace("1st line\n", "First line\n")); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG); |
| } |
| |
| @Test |
| public void singleHunkAtBeginningIsFollowedByCorrectCommonLines() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 1\n", "Line one\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().isNotEmpty(); |
| assertThat(diffInfo).content().element(0).linesOfB().isNotEmpty(); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .commonLines() |
| .containsExactly("Line 2", "Line 3", "Line 4", "Line 5", "") |
| .inOrder(); |
| } |
| |
| @Test |
| public void singleHunkAtEndIsPrecededByCorrectCommonLines() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 5\n", "Line five\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsExactly("Line 1", "Line 2", "Line 3", "Line 4") |
| .inOrder(); |
| assertThat(diffInfo).content().element(1).linesOfA().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfB().isNotEmpty(); |
| } |
| |
| @Test |
| public void singleHunkInTheMiddleIsSurroundedByCorrectCommonLines() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet(changeId, filePath, content -> content.replace("Line 3\n", "Line three\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsExactly("Line 1", "Line 2") |
| .inOrder(); |
| assertThat(diffInfo).content().element(1).linesOfA().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfB().isNotEmpty(); |
| assertThat(diffInfo) |
| .content() |
| .element(2) |
| .commonLines() |
| .containsExactly("Line 4", "Line 5", "Line 6", "") |
| .inOrder(); |
| } |
| |
| @Test |
| public void twoHunksAreSeparatedByCorrectCommonLines() throws Exception { |
| String filePath = "a_new_file.txt"; |
| String fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n"; |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet( |
| changeId, |
| filePath, |
| content -> content.replace("Line 2\n", "Line two\n").replace("Line 5\n", "Line five\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfB().isNotEmpty(); |
| assertThat(diffInfo) |
| .content() |
| .element(2) |
| .commonLines() |
| .containsExactly("Line 3", "Line 4") |
| .inOrder(); |
| assertThat(diffInfo).content().element(3).linesOfA().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfB().isNotEmpty(); |
| } |
| |
| @Test |
| public void rebaseHunksAtStartOfFileAreIdentified() throws Exception { |
| String newFileContent = |
| FILE_CONTENT.replace("Line 1\n", "Line one\n").replace("Line 5\n", "Line five\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one"); |
| assertThat(diffInfo).content().element(0).isDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(2).isDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 50"); |
| assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line fifty"); |
| assertThat(diffInfo).content().element(4).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(5).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunksAtEndOfFileAreIdentified() throws Exception { |
| String newFileContent = |
| FILE_CONTENT |
| .replace("Line 60\n", "Line sixty\n") |
| .replace("Line 100\n", "Line one hundred\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 50"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line fifty"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 60"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line sixty"); |
| assertThat(diffInfo).content().element(3).isDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line one hundred"); |
| assertThat(diffInfo).content().element(5).isDueToRebase(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunksInBetweenRegularHunksAreIdentified() throws Exception { |
| String newFileContent = |
| FILE_CONTENT.replace("Line 40\n", "Line forty\n").replace("Line 45\n", "Line forty five\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> |
| fileContent |
| .replace("Line 1\n", "Line one\n") |
| .replace("Line 100\n", "Line one hundred\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one"); |
| assertThat(diffInfo).content().element(0).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 40"); |
| assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line forty"); |
| assertThat(diffInfo).content().element(2).isDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 45"); |
| assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line forty five"); |
| assertThat(diffInfo).content().element(4).isDueToRebase(); |
| assertThat(diffInfo).content().element(5).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(6).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(6).linesOfB().containsExactly("Line one hundred"); |
| assertThat(diffInfo).content().element(6).isNotDueToRebase(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void rebaseHunkIsIdentifiedWhenMovedDownInPreviousPatchSet() throws Exception { |
| // Move the code down by introducing additional lines (pure insert + enlarging replacement) in |
| // the previous patch set. |
| Function<String, String> contentModification1 = |
| fileContent -> |
| "Line zero\n" + fileContent.replace("Line 10\n", "Line ten\nLine ten and a half\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification1); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| String newFileContent = FILE_CONTENT.replace("Line 40\n", "Line forty\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification2 = |
| fileContent -> fileContent.replace("Line 100\n", "Line one hundred\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification2); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 40"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line forty"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line one hundred"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunkIsIdentifiedWhenMovedDownInLatestPatchSet() throws Exception { |
| String newFileContent = FILE_CONTENT.replace("Line 40\n", "Line forty\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| // Move the code down by introducing additional lines (pure insert + enlarging replacement) in |
| // the latest patch set. |
| Function<String, String> contentModification = |
| fileContent -> |
| "Line zero\n" + fileContent.replace("Line 10\n", "Line ten\nLine ten and a half\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line zero"); |
| assertThat(diffInfo).content().element(0).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 10"); |
| assertThat(diffInfo) |
| .content() |
| .element(2) |
| .linesOfB() |
| .containsExactly("Line ten", "Line ten and a half"); |
| assertThat(diffInfo).content().element(2).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 40"); |
| assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line forty"); |
| assertThat(diffInfo).content().element(4).isDueToRebase(); |
| assertThat(diffInfo).content().element(5).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunkIsIdentifiedWhenMovedUpInPreviousPatchSet() throws Exception { |
| // Move the code up by removing lines (pure deletion + shrinking replacement) in the previous |
| // patch set. |
| Function<String, String> contentModification1 = |
| fileContent -> |
| fileContent.replace("Line 1\n", "").replace("Line 10\nLine 11\n", "Line ten\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification1); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| String newFileContent = FILE_CONTENT.replace("Line 40\n", "Line forty\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification2 = |
| fileContent -> fileContent.replace("Line 100\n", "Line one hundred\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification2); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 40"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line forty"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 100"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line one hundred"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunkIsIdentifiedWhenMovedUpInLatestPatchSet() throws Exception { |
| String newFileContent = FILE_CONTENT.replace("Line 40\n", "Line forty\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| // Move the code up by removing lines (pure deletion + shrinking replacement) in the latest |
| // patch set. |
| Function<String, String> contentModification = |
| fileContent -> |
| fileContent.replace("Line 1\n", "").replace("Line 10\nLine 11\n", "Line ten\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().isNull(); |
| assertThat(diffInfo).content().element(0).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 10", "Line 11"); |
| assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line ten"); |
| assertThat(diffInfo).content().element(2).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 40"); |
| assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line forty"); |
| assertThat(diffInfo).content().element(4).isDueToRebase(); |
| assertThat(diffInfo).content().element(5).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(3); |
| } |
| |
| @Test |
| public void modifiedRebaseHunkWithSameRegionConsideredAsRegularHunk() throws Exception { |
| String newFileContent = FILE_CONTENT.replace("Line 40\n", "Line forty\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line forty\n", "Line modified after rebase\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 40"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line modified after rebase"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunkOverlappingAtBeginningConsideredAsRegularHunk() throws Exception { |
| String newFileContent = |
| FILE_CONTENT.replace("Line 40\nLine 41\n", "Line forty\nLine forty one\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> |
| fileContent |
| .replace("Line 39\n", "Line thirty nine\n") |
| .replace("Line forty one\n", "Line 41\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 39", "Line 40"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line thirty nine", "Line forty"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void rebaseHunkOverlappingAtEndConsideredAsRegularHunk() throws Exception { |
| String newFileContent = |
| FILE_CONTENT.replace("Line 40\nLine 41\n", "Line forty\nLine forty one\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> |
| fileContent |
| .replace("Line forty\n", "Line 40\n") |
| .replace("Line 42\n", "Line forty two\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 41", "Line 42"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line forty one", "Line forty two"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void rebaseHunkModifiedInsideConsideredAsRegularHunk() throws Exception { |
| String newFileContent = |
| FILE_CONTENT.replace( |
| "Line 39\nLine 40\nLine 41\n", "Line thirty nine\nLine forty\nLine forty one\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line forty\n", "A different line forty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfA() |
| .containsExactly("Line 39", "Line 40", "Line 41"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line thirty nine", "A different line forty", "Line forty one"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(3); |
| } |
| |
| @Test |
| public void rebaseHunkAfterLineNumberChangingOverlappingHunksIsIdentified() throws Exception { |
| String newFileContent = |
| FILE_CONTENT |
| .replace("Line 40\nLine 41\n", "Line forty\nLine forty one\n") |
| .replace("Line 60\n", "Line sixty\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> |
| fileContent |
| .replace("Line forty\n", "Line 40\n") |
| .replace("Line 42\n", "Line forty two\nLine forty two and a half\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 41", "Line 42"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line forty one", "Line forty two", "Line forty two and a half"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 60"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line sixty"); |
| assertThat(diffInfo).content().element(3).isDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void rebaseHunksOneLineApartFromRegularHunkAreIdentified() throws Exception { |
| String newFileContent = |
| FILE_CONTENT.replace("Line 1\n", "Line one\n").replace("Line 5\n", "Line five\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 3\n", "Line three\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one"); |
| assertThat(diffInfo).content().element(0).isDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 3"); |
| assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line three"); |
| assertThat(diffInfo).content().element(2).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(4).linesOfA().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(4).linesOfB().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(4).isDueToRebase(); |
| assertThat(diffInfo).content().element(5).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunksDirectlyTouchingHunksOfPatchSetsNotModifiedBetweenThemAreIdentified() |
| throws Exception { |
| // Add to hunks in a patch set and remove them in a further patch set to allow rebasing. |
| Function<String, String> contentModification = |
| fileContent -> |
| fileContent.replace("Line 1\n", "Line one\n").replace("Line 3\n", "Line three\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| Function<String, String> reverseContentModification = |
| fileContent -> |
| fileContent.replace("Line one\n", "Line 1\n").replace("Line three\n", "Line 3\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, reverseContentModification); |
| |
| String newFileContent = FILE_CONTENT.replace("Line 2\n", "Line two\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| rebaseChangeOn(changeId, commit2); |
| |
| // Add the hunks again and modify another line so that we get a diff for the file. |
| // (Files with only edits due to rebase are filtered out.) |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| contentModification.andThen(fileContent -> fileContent.replace("Line 10\n", "Line ten\n"))); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 2"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line two"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 10"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line ten"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void multipleRebaseEditsMixedWithRegularEditsCanBeIdentified() throws Exception { |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| fileContent -> fileContent.replace("Line 7\n", "Line seven\n").replace("Line 24\n", "")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| ObjectId commit2 = |
| addCommit( |
| commit1, |
| FILE_NAME, |
| FILE_CONTENT |
| .replace("Line 2\n", "Line two\n") |
| .replace("Line 18\nLine 19\n", "Line eighteen\nLine nineteen\n") |
| .replace("Line 50\n", "Line fifty\n")); |
| |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| fileContent -> |
| fileContent |
| .replace("Line seven\n", "Line 7\n") |
| .replace("Line 9\n", "Line nine\n") |
| .replace("Line 60\n", "Line sixty\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 2"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line two"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line seven"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 7"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 9"); |
| assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line nine"); |
| assertThat(diffInfo).content().element(5).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(6).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(7).linesOfA().containsExactly("Line 18", "Line 19"); |
| assertThat(diffInfo) |
| .content() |
| .element(7) |
| .linesOfB() |
| .containsExactly("Line eighteen", "Line nineteen"); |
| assertThat(diffInfo).content().element(7).isDueToRebase(); |
| assertThat(diffInfo).content().element(8).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(9).linesOfA().containsExactly("Line 50"); |
| assertThat(diffInfo).content().element(9).linesOfB().containsExactly("Line fifty"); |
| assertThat(diffInfo).content().element(9).isDueToRebase(); |
| assertThat(diffInfo).content().element(10).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(11).linesOfA().containsExactly("Line 60"); |
| assertThat(diffInfo).content().element(11).linesOfB().containsExactly("Line sixty"); |
| assertThat(diffInfo).content().element(11).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(12).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(3); |
| } |
| |
| @Test |
| public void deletedFileDuringRebaseConsideredAsRegularHunkWhenModifiedInDiff() throws Exception { |
| // Modify the file and revert the modifications to allow rebasing. |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line 50\n", "Line fifty\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet( |
| changeId, FILE_NAME, fileContent -> fileContent.replace("Line fifty\n", "Line 50\n")); |
| |
| ObjectId commit2 = addCommitRemovingFiles(commit1, FILE_NAME); |
| |
| rebaseChangeOn(changeId, commit2); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).changeType().isEqualTo(ChangeType.DELETED); |
| assertThat(diffInfo).content().element(0).linesOfA().hasSize(101); |
| assertThat(diffInfo).content().element(0).linesOfB().isNull(); |
| assertThat(diffInfo).content().element(0).isNotDueToRebase(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isNull(); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(100); |
| } |
| |
| @Test |
| public void addedFileDuringRebaseConsideredAsRegularHunkWhenModifiedInDiff() throws Exception { |
| String newFilePath = "a_new_file.txt"; |
| ObjectId commit2 = addCommit(commit1, newFilePath, "1st line\n2nd line\n3rd line\n"); |
| |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet( |
| changeId, newFilePath, fileContent -> fileContent.replace("1st line\n", "First line\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, newFilePath).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).changeType().isEqualTo(ChangeType.ADDED); |
| assertThat(diffInfo).content().element(0).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(0).linesOfB().hasSize(4); |
| assertThat(diffInfo).content().element(0).isNotDueToRebase(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(newFilePath)).linesInserted().isEqualTo(3); |
| assertThat(changedFiles.get(newFilePath)).linesDeleted().isNull(); |
| } |
| |
| @Test |
| public void rebaseHunkInRenamedFileIsIdentified_whenFileIsRenamedDuringRebase() throws Exception { |
| String renamedFilePath = "renamed_some_file.txt"; |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 1\n", "Line one\n")); |
| ObjectId commit3 = addCommitRenamingFile(commit2, FILE_NAME, renamedFilePath); |
| |
| rebaseChangeOn(changeId, commit3); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"); |
| addModifiedPatchSet(changeId, renamedFilePath, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, renamedFilePath).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one"); |
| assertThat(diffInfo).content().element(0).isDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 50"); |
| assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line fifty"); |
| assertThat(diffInfo).content().element(2).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.get(renamedFilePath)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(renamedFilePath)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void rebaseHunkInRenamedFileIsIdentified_whenFileIsRenamedInPatchSets() throws Exception { |
| String renamedFilePath = "renamed_some_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFilePath); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| // Revert the renaming to be able to rebase. |
| gApi.changes().id(changeId).edit().renameFile(renamedFilePath, FILE_NAME); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 5\n", "Line five\n")); |
| |
| rebaseChangeOn(changeId, commit2); |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFilePath); |
| gApi.changes().id(changeId).edit().publish(); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"); |
| addModifiedPatchSet(changeId, renamedFilePath, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, renamedFilePath).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 50"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line fifty"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(renamedFilePath)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(renamedFilePath)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void renamedFileWithOnlyRebaseHunksIsIdentified_whenRenamedBetweenPatchSets() |
| throws Exception { |
| String newFilePath1 = "renamed_some_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath1); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| // Revert the renaming to be able to rebase. |
| gApi.changes().id(changeId).edit().renameFile(newFilePath1, FILE_NAME); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 5\n", "Line five\n")); |
| |
| rebaseChangeOn(changeId, commit2); |
| String newFilePath2 = "renamed_some_file_to_something_else.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath2); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, newFilePath2); |
| assertThat(changedFiles.get(newFilePath2)).linesInserted().isNull(); |
| assertThat(changedFiles.get(newFilePath2)).linesDeleted().isNull(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, newFilePath2).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| } |
| |
| @Test |
| public void renamedFileWithOnlyRebaseHunksIsIdentified_whenRenamedForRebaseAndForPatchSets() |
| throws Exception { |
| String newFilePath1 = "renamed_some_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath1); |
| gApi.changes().id(changeId).edit().publish(); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| // Revert the renaming to be able to rebase. |
| gApi.changes().id(changeId).edit().renameFile(newFilePath1, FILE_NAME); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 5\n", "Line five\n")); |
| String newFilePath2 = "renamed_some_file_during_rebase.txt"; |
| ObjectId commit3 = addCommitRenamingFile(commit2, FILE_NAME, newFilePath2); |
| |
| rebaseChangeOn(changeId, commit3); |
| String newFilePath3 = "renamed_some_file_to_something_else.txt"; |
| gApi.changes().id(changeId).edit().renameFile(newFilePath2, newFilePath3); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, newFilePath3); |
| assertThat(changedFiles.get(newFilePath3)).linesInserted().isNull(); |
| assertThat(changedFiles.get(newFilePath3)).linesDeleted().isNull(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, newFilePath3).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| } |
| |
| @Test |
| public void copiedAndRenamedFilesWithOnlyRebaseHunksAreIdentified() throws Exception { |
| String newFileContent = FILE_CONTENT.replace("Line 5\n", "Line five\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| // Copies are only identified by JGit when paired with renaming. |
| String copyFileName = "copy_of_some_file.txt"; |
| String renamedFileName = "renamed_some_file.txt"; |
| gApi.changes() |
| .id(changeId) |
| .edit() |
| .modifyFile(copyFileName, RawInputUtil.create(newFileContent)); |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFileName); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(initialPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, copyFileName, renamedFileName); |
| |
| DiffInfo renamedFileDiffInfo = |
| getDiffRequest(changeId, CURRENT, renamedFileName).withBase(initialPatchSetId).get(); |
| assertThat(renamedFileDiffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(renamedFileDiffInfo).content().element(1).linesOfA().containsExactly("Line 5"); |
| assertThat(renamedFileDiffInfo).content().element(1).linesOfB().containsExactly("Line five"); |
| assertThat(renamedFileDiffInfo).content().element(1).isDueToRebase(); |
| assertThat(renamedFileDiffInfo).content().element(2).commonLines().isNotEmpty(); |
| |
| DiffInfo copiedFileDiffInfo = |
| getDiffRequest(changeId, CURRENT, copyFileName).withBase(initialPatchSetId).get(); |
| assertThat(copiedFileDiffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(copiedFileDiffInfo).content().element(1).linesOfA().containsExactly("Line 5"); |
| assertThat(copiedFileDiffInfo).content().element(1).linesOfB().containsExactly("Line five"); |
| assertThat(copiedFileDiffInfo).content().element(1).isDueToRebase(); |
| assertThat(copiedFileDiffInfo).content().element(2).commonLines().isNotEmpty(); |
| } |
| |
| /* |
| * change PS B |
| * | |
| * change PS A commit4 |
| * | | |
| * commit2 commit3 |
| * | / |
| * commit1 -------- |
| */ |
| @Test |
| public void rebaseHunksWhenRebasingOnAnotherChangeOrPatchSetAreIdentified() throws Exception { |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 5\n", "Line five\n")); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| String commit3FileContent = FILE_CONTENT.replace("Line 35\n", "Line thirty five\n"); |
| ObjectId commit3 = addCommit(commit1, FILE_NAME, commit3FileContent); |
| ObjectId commit4 = |
| addCommit(commit3, FILE_NAME, commit3FileContent.replace("Line 60\n", "Line sixty\n")); |
| |
| rebaseChangeOn(changeId, commit4); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 20\n", "Line twenty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 20"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line twenty"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 35"); |
| assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line thirty five"); |
| assertThat(diffInfo).content().element(5).isDueToRebase(); |
| assertThat(diffInfo).content().element(6).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(7).linesOfA().containsExactly("Line 60"); |
| assertThat(diffInfo).content().element(7).linesOfB().containsExactly("Line sixty"); |
| assertThat(diffInfo).content().element(7).isDueToRebase(); |
| assertThat(diffInfo).content().element(8).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| /* |
| * change PS B |
| * | |
| * change PS A commit4 |
| * | | |
| * commit2 commit3 |
| * | / |
| * commit1 -------- |
| */ |
| @Test |
| public void unrelatedFileWhenRebasingOnAnotherChangeOrPatchSetIsIgnored() throws Exception { |
| ObjectId commit2 = |
| addCommit(commit1, FILE_NAME, FILE_CONTENT.replace("Line 5\n", "Line five\n")); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| ObjectId commit3 = |
| addCommit(commit1, FILE_NAME2, FILE_CONTENT2.replace("2nd line\n", "Second line\n")); |
| ObjectId commit4 = |
| addCommit(commit3, FILE_NAME, FILE_CONTENT.replace("Line 60\n", "Line sixty\n")); |
| |
| rebaseChangeOn(changeId, commit4); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 20\n", "Line twenty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, FILE_NAME); |
| } |
| |
| @Test |
| public void rebaseHunksWhenReversingPatchSetOrderAreIdentified() throws Exception { |
| ObjectId commit2 = |
| addCommit( |
| commit1, |
| FILE_NAME, |
| FILE_CONTENT.replace("Line 5\n", "Line five\n").replace("Line 35\n", "")); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 20\n", "Line twenty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| String currentPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, initialPatchSetId, FILE_NAME).withBase(currentPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line five"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line 5"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line twenty"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 20"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(5).linesOfA().isNull(); |
| assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line 35"); |
| assertThat(diffInfo).content().element(5).isDueToRebase(); |
| assertThat(diffInfo).content().element(6).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).revision(initialPatchSetId).files(currentPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void intralineEditsInNonRebaseHunksAreIdentified() throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 1\n", "Line one\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one"); |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .intralineEditsOfA() |
| .containsExactly(ImmutableList.of(5, 1)); |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .intralineEditsOfB() |
| .containsExactly(ImmutableList.of(5, 3)); |
| assertThat(diffInfo).content().element(0).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| } |
| |
| @Test |
| public void intralineEditsInRebaseHunksAreIdentified() throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| String newFileContent = FILE_CONTENT.replace("Line 1\n", "Line one\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent); |
| |
| rebaseChangeOn(changeId, commit2); |
| Function<String, String> contentModification = |
| fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"); |
| addModifiedPatchSet(changeId, FILE_NAME, contentModification); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(initialPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).linesOfA().containsExactly("Line 1"); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("Line one"); |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .intralineEditsOfA() |
| .containsExactly(ImmutableList.of(5, 1)); |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .intralineEditsOfB() |
| .containsExactly(ImmutableList.of(5, 3)); |
| assertThat(diffInfo).content().element(0).isDueToRebase(); |
| assertThat(diffInfo).content().element(1).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(2).linesOfA().containsExactly("Line 50"); |
| assertThat(diffInfo).content().element(2).linesOfB().containsExactly("Line fifty"); |
| assertThat(diffInfo).content().element(2).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(3).commonLines().isNotEmpty(); |
| } |
| |
| @Test |
| public void closeNonRebaseHunksAreCombinedForIntralineOptimizations() throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| String fileContent = FILE_CONTENT.replace("Line 5\n", "{\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, fileContent); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| content -> content.replace("Line 4\n", "Line four\n").replace("Line 6\n", "Line six\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4", "{", "Line 6"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line four", "{", "Line six"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| // Lines which weren't modified but are included in a hunk due to optimization don't count for |
| // the number of inserted/deleted lines. |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void closeRebaseHunksAreNotCombinedForIntralineOptimizations() throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| String fileContent = FILE_CONTENT.replace("Line 5\n", "{\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, fileContent); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| String newFileContent = |
| fileContent.replace("Line 4\n", "Line four\n").replace("Line 6\n", "Line six\n"); |
| ObjectId commit3 = addCommit(commit1, FILE_NAME, newFileContent); |
| rebaseChangeOn(changeId, commit3); |
| |
| addModifiedPatchSet( |
| changeId, FILE_NAME, content -> content.replace("Line 20\n", "Line twenty\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line four"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 6"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line six"); |
| assertThat(diffInfo).content().element(3).isDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 20"); |
| assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line twenty"); |
| assertThat(diffInfo).content().element(5).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(6).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void closeRebaseAndNonRebaseHunksAreNotCombinedForIntralineOptimizations() |
| throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| String fileContent = FILE_CONTENT.replace("Line 5\n", "{\n").replace("Line 7\n", "{\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, fileContent); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| String newFileContent = |
| fileContent.replace("Line 4\n", "Line four\n").replace("Line 8\n", "Line eight\n"); |
| ObjectId commit3 = addCommit(commit1, FILE_NAME, newFileContent); |
| rebaseChangeOn(changeId, commit3); |
| |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 6\n", "Line six\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line four"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 6"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line six"); |
| assertThat(diffInfo).content().element(3).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(5).linesOfA().containsExactly("Line 8"); |
| assertThat(diffInfo).content().element(5).linesOfB().containsExactly("Line eight"); |
| assertThat(diffInfo).content().element(5).isDueToRebase(); |
| assertThat(diffInfo).content().element(6).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(1); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(1); |
| } |
| |
| @Test |
| public void closeNonRebaseHunksNextToRebaseHunksAreCombinedForIntralineOptimizations() |
| throws Exception { |
| assume().that(intraline).isTrue(); |
| |
| String fileContent = FILE_CONTENT.replace("Line 5\n", "{\n").replace("Line 7\n", "{\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, fileContent); |
| rebaseChangeOn(changeId, commit2); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| |
| String newFileContent = fileContent.replace("Line 8\n", "Line eight!\n"); |
| ObjectId commit3 = addCommit(commit1, FILE_NAME, newFileContent); |
| rebaseChangeOn(changeId, commit3); |
| |
| addModifiedPatchSet( |
| changeId, |
| FILE_NAME, |
| content -> content.replace("Line 4\n", "Line four\n").replace("Line 6\n", "Line six\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| assertThat(diffInfo).content().element(0).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 4", "{", "Line 6"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line four", "{", "Line six"); |
| assertThat(diffInfo).content().element(1).isNotDueToRebase(); |
| assertThat(diffInfo).content().element(2).commonLines().isNotEmpty(); |
| assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 8"); |
| assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line eight!"); |
| assertThat(diffInfo).content().element(3).isDueToRebase(); |
| assertThat(diffInfo).content().element(4).commonLines().isNotEmpty(); |
| |
| Map<String, FileInfo> changedFiles = |
| gApi.changes().id(changeId).current().files(previousPatchSetId); |
| assertThat(changedFiles.get(FILE_NAME)).linesInserted().isEqualTo(2); |
| assertThat(changedFiles.get(FILE_NAME)).linesDeleted().isEqualTo(2); |
| } |
| |
| @Test |
| public void diffOfUnmodifiedFileReturnsAllFileContents() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| addModifiedPatchSet( |
| changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| // We don't list the full file contents here as that is not the focus of this test. |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5") |
| .inOrder(); |
| } |
| |
| @Test |
| // TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only |
| // filter such files in the GetFiles REST endpoint. |
| @Ignore |
| public void diffOfFileWithOnlyRebaseHunksConsideringWhitespaceReturnsFileContents() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(previousPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| // We don't list the full file contents here as that is not the focus of this test. |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5") |
| .inOrder(); |
| // It's crucial that the line changed in the rebase is reported correctly. |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 70"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line seventy"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| } |
| |
| @Test |
| // TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only |
| // filter such files in the GetFiles REST endpoint. |
| @Ignore |
| public void diffOfFileWithOnlyRebaseHunksAndIgnoringWhitespaceReturnsFileContents() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(previousPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL) |
| .get(); |
| // We don't list the full file contents here as that is not the focus of this test. |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5") |
| .inOrder(); |
| // It's crucial that the line changed in the rebase is reported correctly. |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 70"); |
| assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line seventy"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| } |
| |
| @Test |
| // TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only |
| // filter such files in the GetFiles REST endpoint. |
| @Ignore |
| public void diffOfFileWithMultilineRebaseHunkAddingNewlineAtEndOfFileReturnsFileContents() |
| throws Exception { |
| String baseFileContent = FILE_CONTENT.concat("Line 101"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = baseFileContent.concat("\nLine 102\nLine 103\n"); |
| ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit3); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(previousPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| // We don't list the full file contents here as that is not the focus of this test. |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5") |
| .inOrder(); |
| // It's crucial that the lines changed in the rebase are reported correctly. |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 101"); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line 101", "Line 102", "Line 103", ""); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| } |
| |
| @Test |
| // TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only |
| // filter such files in the GetFiles REST endpoint. |
| @Ignore |
| public void diffOfFileWithMultilineRebaseHunkRemovingNewlineAtEndOfFileReturnsFileContents() |
| throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newBaseFileContent = FILE_CONTENT.concat("Line 101\nLine 102\nLine 103"); |
| ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent); |
| rebaseChangeOn(changeId, commit2); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME) |
| .withBase(previousPatchSetId) |
| .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE) |
| .get(); |
| // We don't list the full file contents here as that is not the focus of this test. |
| assertThat(diffInfo) |
| .content() |
| .element(0) |
| .commonLines() |
| .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5") |
| .inOrder(); |
| // It's crucial that the lines changed in the rebase are reported correctly. |
| assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100", ""); |
| assertThat(diffInfo) |
| .content() |
| .element(1) |
| .linesOfB() |
| .containsExactly("Line 100", "Line 101", "Line 102", "Line 103"); |
| assertThat(diffInfo).content().element(1).isDueToRebase(); |
| } |
| |
| @Test |
| public void addDeleteByJgit_isIdentifiedAsRewritten() throws Exception { |
| String target = "file.txt"; |
| String symlink = "link.lnk"; |
| |
| // Create a change adding file "FileName" and a symlink "symLink" pointing to the file |
| PushOneCommit push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, "Commit Subject", target, "content") |
| .addSymlink(symlink, target); |
| PushOneCommit.Result result = push.to("refs/for/master"); |
| String initialRev = gApi.changes().id(result.getChangeId()).get().currentRevision; |
| String cId = result.getChangeId(); |
| |
| // Delete the symlink with PS2 |
| gApi.changes().id(cId).edit().deleteFile(symlink); |
| gApi.changes().id(cId).edit().publish(); |
| |
| // Re-add the symlink as a regular file with PS3 |
| gApi.changes().id(cId).edit().modifyFile(symlink, RawInputUtil.create("new content")); |
| gApi.changes().id(cId).edit().publish(); |
| |
| // Changed files: JGit returns two {DELETED/ADDED} entries for the file. |
| // The diff logic combines both into a single REWRITTEN entry. |
| Map<String, FileInfo> changedFiles = gApi.changes().id(cId).current().files(initialRev); |
| assertThat(changedFiles.keySet()).containsExactly("/COMMIT_MSG", symlink); |
| assertThat(changedFiles.get(symlink).status).isEqualTo('W'); // Rewritten |
| |
| // Detailed diff: Old diff cache returns ADDED entry. New Diff Cache returns REWRITE. |
| DiffInfo diffInfo = gApi.changes().id(cId).current().file(symlink).diff(initialRev); |
| assertThat(diffInfo.content).hasSize(1); |
| assertThat(diffInfo).content().element(0).linesOfB().containsExactly("new content"); |
| assertThat(diffInfo.changeType).isEqualTo(ChangeType.REWRITE); |
| } |
| |
| @Test |
| public void renameDeleteByJgit_isIdentifiedAsRewritten() throws Exception { |
| String target = "file.txt"; |
| String symlink = "link.lnk"; |
| PushOneCommit push = |
| pushFactory |
| .create(admin.newIdent(), testRepo, "Commit Subject", target, "content") |
| .addSymlink(symlink, target); |
| PushOneCommit.Result result = push.to("refs/for/master"); |
| String cId = result.getChangeId(); |
| String initialRev = gApi.changes().id(cId).get().currentRevision; |
| |
| // Delete both symlink and target with PS2 |
| gApi.changes().id(cId).edit().deleteFile(symlink); |
| gApi.changes().id(cId).edit().deleteFile(target); |
| gApi.changes().id(cId).edit().publish(); |
| |
| // Re-create the symlink as a regular file with PS3 |
| gApi.changes().id(cId).edit().modifyFile(symlink, RawInputUtil.create("content")); |
| gApi.changes().id(cId).edit().publish(); |
| |
| // Changed files: JGit returns two {DELETED/RENAMED} entries for the file. |
| // The diff logic combines both into a single REWRITTEN entry. |
| Map<String, FileInfo> changedFiles = gApi.changes().id(cId).current().files(initialRev); |
| assertThat(changedFiles.keySet()).containsExactly("/COMMIT_MSG", symlink); |
| assertThat(changedFiles.get(symlink).status).isEqualTo('W'); // Rewritten |
| |
| // Detailed diff: Old diff cache returns RENAMED entry. New Diff Cache returns REWRITE. |
| DiffInfo diffInfo = gApi.changes().id(cId).current().file(symlink).diff(initialRev); |
| assertThat(diffInfo) |
| .diffHeader() |
| .containsExactly( |
| "diff --git a/file.txt b/link.lnk", |
| "similarity index 100%", |
| "rename from file.txt", |
| "rename to link.lnk"); |
| assertThat(diffInfo.content).hasSize(1); |
| assertThat(diffInfo).content().element(0).commonLines().containsExactly("content"); |
| assertThat(diffInfo.changeType).isEqualTo(ChangeType.REWRITE); |
| } |
| |
| @Test |
| public void diffOfNonExistentFileIsAnEmptyDiffResult() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, "a_non-existent_file.txt") |
| .withBase(initialPatchSetId) |
| .get(); |
| assertThat(diffInfo).content().isEmpty(); |
| } |
| |
| @Test |
| public void requestingDiffForOldFileNameOfRenamedFileYieldsReasonableResult() throws Exception { |
| addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n")); |
| String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision; |
| String newFilePath = "a_new_file.txt"; |
| gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath); |
| gApi.changes().id(changeId).edit().publish(); |
| |
| DiffInfo diffInfo = |
| getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get(); |
| // This behavior has been present in Gerrit for quite some time. It differs from the results |
| // returned for other cases (e.g. requesting the diff for an unmodified file; requesting the |
| // diff for a non-existent file). After a rename, the original file doesn't exist anymore. |
| // Hence, the most reasonable thing would be to match the behavior of requesting the diff for a |
| // non-existent file, which returns an empty diff. |
| // This test at least guarantees that we don't run into an internal error. |
| assertThat(diffInfo).content().element(0).commonLines().isNull(); |
| assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0); |
| } |
| |
| @Test |
| public void editNotAllowedAsBase() throws Exception { |
| gApi.changes().id(changeId).edit().create(); |
| |
| BadRequestException e = |
| assertThrows( |
| BadRequestException.class, |
| () -> getDiffRequest(changeId, CURRENT, FILE_NAME).withBase("edit").get()); |
| assertThat(e).hasMessageThat().isEqualTo("edit not allowed as base"); |
| |
| e = |
| assertThrows( |
| BadRequestException.class, |
| () -> getDiffRequest(changeId, CURRENT, FILE_NAME).withBase("0").get()); |
| assertThat(e).hasMessageThat().isEqualTo("edit not allowed as base"); |
| } |
| |
| @Test |
| public void diffForAddedBinaryFile() throws Exception { |
| String imageFileName = "an_image.png"; |
| byte[] imageBytes = createRgbImage(255, 0, 0); |
| Change.Id changeId = |
| changeOperations |
| .newChange() |
| .file(imageFileName) |
| .content(new String(imageBytes, UTF_8)) |
| .create(); |
| |
| DiffInfo diffInfo = gApi.changes().id(changeId.get()).current().file(imageFileName).diff(); |
| |
| assertThat(diffInfo).binary().isTrue(); |
| assertThat(diffInfo).content().isEmpty(); |
| assertThat(diffInfo).diffHeader().contains("Binary files differ"); |
| assertThat(diffInfo).metaA().isNull(); |
| assertThat(diffInfo).metaB().isNotNull(); |
| assertThat(diffInfo).webLinks().isNull(); |
| } |
| |
| @Test |
| public void diffForModifiedBinaryFile() throws Exception { |
| String imageFileName = "an_image.png"; |
| byte[] imageBytes = createRgbImage(255, 0, 0); |
| Change.Id changeId1 = |
| changeOperations |
| .newChange() |
| .file(imageFileName) |
| .content(new String(imageBytes, UTF_8)) |
| .create(); |
| |
| byte[] newImageBytes = createRgbImage(0, 255, 0); |
| Change.Id changeId2 = |
| changeOperations |
| .newChange() |
| .childOf() |
| .change(changeId1) |
| .file(imageFileName) |
| .content(new String(newImageBytes, UTF_8)) |
| .create(); |
| |
| DiffInfo diffInfo = gApi.changes().id(changeId2.get()).current().file(imageFileName).diff(); |
| |
| assertThat(diffInfo).binary().isTrue(); |
| |
| // All fields in the contentEntry are null, except the 'skip' field. It's probably a bug that |
| // this is set for binary files. |
| ContentEntrySubject contentEntry = assertThat(diffInfo).content().onlyElement(); |
| contentEntry.linesOfA().isNull(); |
| contentEntry.linesOfB().isNull(); |
| contentEntry.commonLines().isNull(); |
| |
| assertThat(diffInfo).diffHeader().contains("Binary files differ"); |
| assertThat(diffInfo).metaA().isNotNull(); |
| assertThat(diffInfo).metaB().isNotNull(); |
| assertThat(diffInfo).webLinks().isNull(); |
| } |
| |
| @Test |
| public void diffForDeletedBinaryFile() throws Exception { |
| String imageFileName = "an_image.png"; |
| byte[] imageBytes = createRgbImage(255, 0, 0); |
| Change.Id changeId1 = |
| changeOperations |
| .newChange() |
| .file(imageFileName) |
| .content(new String(imageBytes, UTF_8)) |
| .create(); |
| |
| Change.Id changeId2 = |
| changeOperations |
| .newChange() |
| .childOf() |
| .change(changeId1) |
| .file(imageFileName) |
| .delete() |
| .create(); |
| |
| DiffInfo diffInfo = gApi.changes().id(changeId2.get()).current().file(imageFileName).diff(); |
| |
| assertThat(diffInfo).binary().isTrue(); |
| |
| // All fields in the contentEntry are null, except the 'skip' field. It's probably a bug that |
| // this is set for binary files. |
| ContentEntrySubject contentEntry = assertThat(diffInfo).content().onlyElement(); |
| contentEntry.linesOfA().isNull(); |
| contentEntry.linesOfB().isNull(); |
| contentEntry.commonLines().isNull(); |
| |
| assertThat(diffInfo).diffHeader().contains("Binary files differ"); |
| assertThat(diffInfo).metaA().isNotNull(); |
| assertThat(diffInfo).metaB().isNull(); |
| assertThat(diffInfo).webLinks().isNull(); |
| } |
| |
| @Test |
| public void diffForBinaryFileThatIsNotTouchedInTheChange() throws Exception { |
| String imageFileName1 = "an_image.png"; |
| byte[] imageBytes1 = createRgbImage(255, 0, 0); |
| String imageContent1 = new String(imageBytes1, UTF_8); |
| Change.Id changeId1 = |
| changeOperations.newChange().file(imageFileName1).content(imageContent1).create(); |
| |
| String imageFileName2 = "another_image.png"; |
| byte[] imageBytes2 = createRgbImage(0, 255, 0); |
| Change.Id changeId2 = |
| changeOperations |
| .newChange() |
| .childOf() |
| .change(changeId1) |
| .file(imageFileName2) |
| .content(new String(imageBytes2, UTF_8)) |
| .create(); |
| |
| // Since file imageFileName1 was not touched in the second change, trying to get the diff for it |
| // should probably fail with '404 Not Found'. |
| DiffInfo diffInfo = gApi.changes().id(changeId2.get()).current().file(imageFileName1).diff(); |
| |
| // This should be detected as a binary file, but it isn't. |
| assertThat(diffInfo).binary().isNull(); |
| |
| // For binary files linesOfA, linesOfB and commonLines are expected to be null, but the content |
| // of the binary file is returned as common lines. |
| ContentEntrySubject contentEntry = assertThat(diffInfo).content().onlyElement(); |
| contentEntry.linesOfA().isNull(); |
| contentEntry.linesOfB().isNull(); |
| contentEntry |
| .commonLines() |
| .containsExactlyElementsIn(Splitter.on("\n").splitToList(imageContent1)); |
| |
| // For binary file the header list should contain "Binary files differ", but it doesn't. |
| assertThat(diffInfo).diffHeader().isNull(); |
| |
| assertThat(diffInfo).metaA().isNotNull(); |
| assertThat(diffInfo).metaB().isNotNull(); |
| assertThat(diffInfo).webLinks().isNull(); |
| } |
| |
| private Registration newEditWebLink() { |
| EditWebLink webLink = |
| new EditWebLink() { |
| @Override |
| public WebLinkInfo getEditWebLink(String projectName, String revision, String fileName) { |
| return new WebLinkInfo( |
| "name", "imageURL", "http://edit/" + projectName + "/" + fileName); |
| } |
| }; |
| return extensionRegistry.newRegistration().add(webLink); |
| } |
| |
| private Registration newGitwebFileWebLink() { |
| FileWebLink fileWebLink = |
| new FileWebLink() { |
| @Override |
| public WebLinkInfo getFileWebLink( |
| String projectName, String revision, String hash, String fileName) { |
| return new WebLinkInfo( |
| "name", |
| "imageURL", |
| String.format("http://gitweb/?p=%s;hb=%s;f=%s", projectName, hash, fileName)); |
| } |
| }; |
| return extensionRegistry.newRegistration().add(fileWebLink); |
| } |
| |
| private String updatedCommitMessage() { |
| return "An unchanged patchset\n\nChange-Id: " + changeId; |
| } |
| |
| private void assertDiffForNewFile( |
| PushOneCommit.Result pushResult, String path, String expectedContentSideB) throws Exception { |
| DiffInfo diff = |
| gApi.changes() |
| .id(pushResult.getChangeId()) |
| .revision(pushResult.getCommit().name()) |
| .file(path) |
| .diff(); |
| |
| List<String> headers = new ArrayList<>(); |
| if (path.equals(COMMIT_MSG)) { |
| RevCommit c = pushResult.getCommit(); |
| |
| RevCommit parentCommit = c.getParents()[0]; |
| String parentCommitId = |
| abbreviateName(parentCommit, 8, testRepo.getRevWalk().getObjectReader()); |
| headers.add("Parent: " + parentCommitId + " (" + parentCommit.getShortMessage() + ")"); |
| |
| PersonIdent author = c.getAuthorIdent(); |
| DateTimeFormatter fmt = |
| DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z") |
| .withLocale(Locale.US) |
| .withZone(author.getZoneId()); |
| headers.add("Author: " + author.getName() + " <" + author.getEmailAddress() + ">"); |
| headers.add("AuthorDate: " + fmt.format(author.getWhenAsInstant())); |
| |
| PersonIdent committer = c.getCommitterIdent(); |
| fmt = fmt.withZone(committer.getZoneId()); |
| headers.add("Commit: " + committer.getName() + " <" + committer.getEmailAddress() + ">"); |
| headers.add("CommitDate: " + fmt.format(committer.getWhenAsInstant())); |
| headers.add(""); |
| } |
| |
| if (!headers.isEmpty()) { |
| String header = Joiner.on("\n").join(headers); |
| expectedContentSideB = header + "\n" + expectedContentSideB; |
| } |
| |
| assertDiffForNewFile(diff, pushResult.getCommit(), path, expectedContentSideB); |
| } |
| |
| private void rebaseChangeOn(String changeId, ObjectId newParent) throws Exception { |
| RebaseInput rebaseInput = new RebaseInput(); |
| rebaseInput.base = newParent.getName(); |
| gApi.changes().id(changeId).current().rebase(rebaseInput); |
| } |
| |
| private ObjectId addCommit(ObjectId parentCommit, String filePath, String fileContent) |
| throws Exception { |
| ImmutableMap<String, String> files = ImmutableMap.of(filePath, fileContent); |
| return addCommit(parentCommit, files); |
| } |
| |
| private ObjectId addCommit(ObjectId parentCommit, ImmutableMap<String, String> files) |
| throws Exception { |
| testRepo.reset(parentCommit); |
| PushOneCommit push = |
| pushFactory.create(admin.newIdent(), testRepo, "Adjust files of repo", files); |
| PushOneCommit.Result result = push.to("refs/for/master"); |
| return result.getCommit(); |
| } |
| |
| private ObjectId addCommit(ObjectId parentCommit, String filePath, byte[] fileContent) |
| throws Exception { |
| testRepo.reset(parentCommit); |
| PushOneCommit.Result result = createEmptyChange(); |
| String changeId = result.getChangeId(); |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(fileContent)); |
| gApi.changes().id(changeId).edit().publish(); |
| String currentRevision = gApi.changes().id(changeId).get().currentRevision; |
| GitUtil.fetch(testRepo, "refs/*:refs/*"); |
| return ObjectId.fromString(currentRevision); |
| } |
| |
| private ObjectId addCommitRemovingFiles(ObjectId parentCommit, String... removedFilePaths) |
| throws Exception { |
| testRepo.reset(parentCommit); |
| Map<String, String> files = |
| Arrays.stream(removedFilePaths) |
| .collect(toMap(Function.identity(), path -> "Irrelevant content")); |
| PushOneCommit push = |
| pushFactory.create(admin.newIdent(), testRepo, "Remove files from repo", files); |
| PushOneCommit.Result result = push.rm("refs/for/master"); |
| return result.getCommit(); |
| } |
| |
| private ObjectId addCommitRenamingFile( |
| ObjectId parentCommit, String oldFilePath, String newFilePath) throws Exception { |
| testRepo.reset(parentCommit); |
| PushOneCommit.Result result = createEmptyChange(); |
| String changeId = result.getChangeId(); |
| gApi.changes().id(changeId).edit().renameFile(oldFilePath, newFilePath); |
| gApi.changes().id(changeId).edit().publish(); |
| String currentRevision = gApi.changes().id(changeId).get().currentRevision; |
| GitUtil.fetch(testRepo, "refs/*:refs/*"); |
| return ObjectId.fromString(currentRevision); |
| } |
| |
| private Result createEmptyChange() throws Exception { |
| PushOneCommit push = |
| pushFactory.create(admin.newIdent(), testRepo, "Test change", ImmutableMap.of()); |
| return push.to("refs/for/master"); |
| } |
| |
| private void addModifiedPatchSet( |
| String changeId, String filePath, Function<String, String> contentModification) |
| throws Exception { |
| try (BinaryResult content = gApi.changes().id(changeId).current().file(filePath).content()) { |
| String newContent = contentModification.apply(content.asString()); |
| gApi.changes().id(changeId).edit().modifyFile(filePath, RawInputUtil.create(newContent)); |
| } |
| gApi.changes().id(changeId).edit().publish(); |
| } |
| |
| private static byte[] createRgbImage(int red, int green, int blue) throws IOException { |
| BufferedImage bufferedImage = new BufferedImage(10, 20, BufferedImage.TYPE_INT_RGB); |
| for (int x = 0; x < bufferedImage.getWidth(); x++) { |
| for (int y = 0; y < bufferedImage.getHeight(); y++) { |
| int rgb = (red << 16) + (green << 8) + blue; |
| bufferedImage.setRGB(x, y, rgb); |
| } |
| } |
| |
| ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); |
| ImageIO.write(bufferedImage, "png", byteArrayOutputStream); |
| return byteArrayOutputStream.toByteArray(); |
| } |
| |
| private FileApi.DiffRequest getDiffRequest(String changeId, String revisionId, String fileName) |
| throws Exception { |
| return gApi.changes() |
| .id(changeId) |
| .revision(revisionId) |
| .file(fileName) |
| .diffRequest() |
| .withIntraline(intraline); |
| } |
| |
| /** |
| * This method transforms the {@code orig} input String using the list of replace edits {@code |
| * editsA}, {@code editsB} and the resulting {@code replace} String. This method currently assumes |
| * that all input edits are replace edits, and that the edits are sorted according to their |
| * indices. |
| * |
| * @return The transformed String after applying the list of replace edits to the original String. |
| */ |
| private String transformStringUsingEditList( |
| String orig, String replace, List<List<Integer>> editsA, List<List<Integer>> editsB) { |
| assertThat(editsA).hasSize(editsB.size()); |
| StringBuilder process = new StringBuilder(orig); |
| // The edits are processed right to left to avoid recomputation of indices when characters |
| // are removed. |
| for (int i = editsA.size() - 1; i >= 0; i--) { |
| List<Integer> leftEdit = editsA.get(i); |
| List<Integer> rightEdit = editsB.get(i); |
| process.replace( |
| leftEdit.get(0), |
| leftEdit.get(0) + leftEdit.get(1), |
| replace.substring(rightEdit.get(0), rightEdit.get(0) + rightEdit.get(1))); |
| } |
| return process.toString(); |
| } |
| } |