Merge changes I0da14436,I1e15767f
* changes:
Remove workaround regarding newlines on GWT UI
Fix diff regarding newline at end of file
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index fb0a0df..1c66c44 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6282,10 +6282,14 @@
Only set if the file was renamed or copied.
|`lines_inserted`|optional|
Number of inserted lines. +
-Not set for binary files or if no lines were inserted.
+Not set for binary files or if no lines were inserted. +
+An empty last line is not included in the count and hence this number can
+differ by one from details provided in <<#diff-info,DiffInfo>>.
|`lines_deleted` |optional|
Number of deleted lines. +
-Not set for binary files or if no lines were deleted.
+Not set for binary files or if no lines were deleted. +
+An empty last line is not included in the count and hence this number can
+differ by one from details provided in <<#diff-info,DiffInfo>>.
|`size_delta` ||
Number of bytes by which the file size increased/decreased.
|`size` ||
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
index cf40762..d942c2e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
@@ -137,7 +137,10 @@
private static void append(StringBuilder s, JsArrayString lines) {
for (int i = 0; i < lines.length(); i++) {
- s.append(lines.get(i)).append('\n');
+ if (s.length() > 0) {
+ s.append('\n');
+ }
+ s.append(lines.get(i));
}
}
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 53686a7..960b558 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -1345,8 +1345,7 @@
protected void assertDiffForNewFile(
DiffInfo diff, RevCommit commit, String path, String expectedContentSideB) throws Exception {
- List<String> expectedLines = new ArrayList<>();
- Collections.addAll(expectedLines, expectedContentSideB.split("\n"));
+ List<String> expectedLines = ImmutableList.copyOf(expectedContentSideB.split("\n", -1));
assertThat(diff.binary).isNull();
assertThat(diff.changeType).isEqualTo(ChangeType.ADDED);
diff --git a/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
index 6918325..057a1a2 100644
--- a/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
@@ -47,4 +47,16 @@
DiffInfo diffInfo = actual();
return Truth.assertThat(diffInfo.changeType).named("changeType");
}
+
+ public FileMetaSubject metaA() {
+ isNotNull();
+ DiffInfo diffInfo = actual();
+ return FileMetaSubject.assertThat(diffInfo.metaA).named("metaA");
+ }
+
+ public FileMetaSubject metaB() {
+ isNotNull();
+ DiffInfo diffInfo = actual();
+ return FileMetaSubject.assertThat(diffInfo.metaB).named("metaB");
+ }
}
diff --git a/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java b/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
new file mode 100644
index 0000000..e77eef1
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.common.testing;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.IntegerSubject;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+import com.google.gerrit.extensions.common.DiffInfo.FileMeta;
+
+public class FileMetaSubject extends Subject<FileMetaSubject, FileMeta> {
+
+ public static FileMetaSubject assertThat(FileMeta fileMeta) {
+ return assertAbout(FileMetaSubject::new).that(fileMeta);
+ }
+
+ private FileMetaSubject(FailureMetadata failureMetadata, FileMeta fileMeta) {
+ super(failureMetadata, fileMeta);
+ }
+
+ public IntegerSubject totalLineCount() {
+ isNotNull();
+ FileMeta fileMeta = actual();
+ return Truth.assertThat(fileMeta.lines).named("total line count");
+ }
+}
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index 6f3e055..b4f7251 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -37,6 +37,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Optional;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -172,6 +173,8 @@
}
}
+ correctForDifferencesInNewlineAtEnd();
+
if (comments != null) {
ensureCommentsVisible(comments);
}
@@ -277,6 +280,43 @@
}
}
+ private void correctForDifferencesInNewlineAtEnd() {
+ // a.src.size() is the size ignoring a newline at the end whereas a.size() considers it.
+ int aSize = a.src.size();
+ int bSize = b.src.size();
+
+ Optional<Edit> lastEdit = getLast(edits);
+ if (isNewlineAtEndDeleted()) {
+ Optional<Edit> lastLineEdit = lastEdit.filter(edit -> edit.getEndA() == aSize);
+ if (lastLineEdit.isPresent()) {
+ lastLineEdit.get().extendA();
+ } else {
+ Edit newlineEdit = new Edit(aSize, aSize + 1, bSize, bSize);
+ edits.add(newlineEdit);
+ }
+ } else if (isNewlineAtEndAdded()) {
+ Optional<Edit> lastLineEdit = lastEdit.filter(edit -> edit.getEndB() == bSize);
+ if (lastLineEdit.isPresent()) {
+ lastLineEdit.get().extendB();
+ } else {
+ Edit newlineEdit = new Edit(aSize, aSize, bSize, bSize + 1);
+ edits.add(newlineEdit);
+ }
+ }
+ }
+
+ private static <T> Optional<T> getLast(List<T> list) {
+ return list.isEmpty() ? Optional.empty() : Optional.ofNullable(list.get(list.size() - 1));
+ }
+
+ private boolean isNewlineAtEndDeleted() {
+ return !a.src.isMissingNewlineAtEnd() && b.src.isMissingNewlineAtEnd();
+ }
+
+ private boolean isNewlineAtEndAdded() {
+ return a.src.isMissingNewlineAtEnd() && !b.src.isMissingNewlineAtEnd();
+ }
+
private void ensureCommentsVisible(CommentDetail comments) {
if (comments.getCommentsA().isEmpty() && comments.getCommentsB().isEmpty()) {
// No comments, no additional dummy edits are required.
@@ -396,14 +436,14 @@
for (EditList.Hunk hunk : list.getHunks()) {
while (hunk.next()) {
if (hunk.isContextLine()) {
- final String lineA = a.src.getString(hunk.getCurA());
+ String lineA = a.getSourceLine(hunk.getCurA());
a.dst.addLine(hunk.getCurA(), lineA);
if (ignoredWhitespace) {
// If we ignored whitespace in some form, also get the line
// from b when it does not exactly match the line from a.
//
- final String lineB = b.src.getString(hunk.getCurB());
+ String lineB = b.getSourceLine(hunk.getCurB());
if (!lineA.equals(lineB)) {
b.dst.addLine(hunk.getCurB(), lineB);
}
@@ -437,11 +477,22 @@
final SparseFileContent dst = new SparseFileContent();
int size() {
- return src != null ? src.size() : 0;
+ if (src == null) {
+ return 0;
+ }
+ if (src.isMissingNewlineAtEnd()) {
+ return src.size();
+ }
+ return src.size() + 1;
}
- void addLine(int line) {
- dst.addLine(line, src.getString(line));
+ void addLine(int lineNumber) {
+ String lineContent = getSourceLine(lineNumber);
+ dst.addLine(lineNumber, lineContent);
+ }
+
+ String getSourceLine(int lineNumber) {
+ return lineNumber >= src.size() ? "" : src.getString(lineNumber);
}
void resolve(Side other, ObjectId within) throws IOException {
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index 51946e9..53cc5ad 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -32,6 +32,7 @@
import com.google.gerrit.common.RawInputUtil;
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;
@@ -112,16 +113,115 @@
}
@Test
- public void diffDeletedFile() throws Exception {
+ 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);
+ }
- DiffInfo diff = getDiffRequest(changeId, CURRENT, FILE_NAME).get();
- assertThat(diff.metaA.lines).isEqualTo(100);
- assertThat(diff.metaB).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
@@ -136,6 +236,91 @@
}
@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);
@@ -193,11 +378,11 @@
// automerge
diff = getDiffRequest(r.getChangeId(), r.getCommit().name(), "foo").get();
- assertThat(diff.metaA.lines).isEqualTo(5);
+ 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(5);
+ assertThat(diff.metaA.lines).isEqualTo(6);
assertThat(diff.metaB.lines).isEqualTo(1);
// parent 1
@@ -212,6 +397,596 @@
}
@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("An unchanged patchset");
+ gApi.changes().id(changeId).edit().publish();
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, filePath).withBase(previousPatchSetId).get();
+ assertThat(diffInfo)
+ .content()
+ .onlyElement()
+ .commonLines()
+ .containsAllOf("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("An unchanged patchset");
+ 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("An unchanged patchset");
+ 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 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 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");
@@ -382,7 +1157,7 @@
.content()
.element(1)
.commonLines()
- .containsExactly("Line 2", "Line 3", "Line 4", "Line 5")
+ .containsExactly("Line 2", "Line 3", "Line 4", "Line 5", "")
.inOrder();
}
@@ -430,7 +1205,7 @@
.content()
.element(2)
.commonLines()
- .containsExactly("Line 4", "Line 5", "Line 6")
+ .containsExactly("Line 4", "Line 5", "Line 6", "")
.inOrder();
}
@@ -1027,7 +1802,7 @@
DiffInfo diffInfo =
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
assertThat(diffInfo).changeType().isEqualTo(ChangeType.DELETED);
- assertThat(diffInfo).content().element(0).linesOfA().hasSize(100);
+ assertThat(diffInfo).content().element(0).linesOfA().hasSize(101);
assertThat(diffInfo).content().element(0).linesOfB().isNull();
assertThat(diffInfo).content().element(0).isNotDueToRebase();
@@ -1050,7 +1825,7 @@
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(3);
+ assertThat(diffInfo).content().element(0).linesOfB().hasSize(4);
assertThat(diffInfo).content().element(0).isNotDueToRebase();
Map<String, FileInfo> changedFiles =