| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.server.patch; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.gerrit.jgit.diff.ReplaceEdit; |
| import java.util.List; |
| import org.eclipse.jgit.diff.Edit; |
| import org.eclipse.jgit.diff.EditList; |
| import org.junit.Test; |
| |
| public class IntraLineLoaderTest { |
| |
| @Test |
| public void rewriteAtStartOfLineIsRecognized() throws Exception { |
| String a = "abc1\n"; |
| String b = "def1\n"; |
| assertThat(intraline(a, b)).isEqualTo(ref().replace("abc", "def").common("1\n").edits); |
| } |
| |
| @Test |
| public void rewriteAtEndOfLineIsRecognized() throws Exception { |
| String a = "abc1\n"; |
| String b = "abc2\n"; |
| assertThat(intraline(a, b)).isEqualTo(ref().common("abc").replace("1", "2").common("\n").edits); |
| } |
| |
| @Test |
| public void completeRewriteIncludesNewline() throws Exception { |
| String a = "abc1\n"; |
| String b = "def2\n"; |
| assertThat(intraline(a, b)).isEqualTo(ref().replace("abc1\n", "def2\n").edits); |
| } |
| |
| @Test |
| public void closeEditsAreCombined() throws Exception { |
| String a = "ab1cdef2gh\n"; |
| String b = "ab2cdef3gh\n"; |
| assertThat(intraline(a, b)) |
| .isEqualTo(ref().common("ab").replace("1cdef2", "2cdef3").common("gh\n").edits); |
| } |
| |
| @Test |
| public void preferInsertAfterCommonPart1() throws Exception { |
| String a = "start middle end\n"; |
| String b = "start middlemiddle end\n"; |
| assertThat(intraline(a, b)) |
| .isEqualTo(ref().common("start middle").insert("middle").common(" end\n").edits); |
| } |
| |
| @Test |
| public void preferInsertAfterCommonPart2() throws Exception { |
| String a = "abc def\n"; |
| String b = "abc def\n"; |
| assertThat(intraline(a, b)).isEqualTo(ref().common("abc ").insert(" ").common("def\n").edits); |
| } |
| |
| @Test |
| public void preferInsertAtLineBreak1() throws Exception { |
| String a = "multi\nline\n"; |
| String b = "multi\nlinemulti\nline\n"; |
| assertThat(intraline(a, b)).isEqualTo(wordEdit(10, 10, 6, 16)); |
| // better would be: |
| // assertThat(intraline(a, b)).isEqualTo(wordEdit(6, 6, 6, 16)); |
| // or the equivalent: |
| // assertThat(intraline(a, b)).isEqualTo(ref() |
| // .common("multi\n").insert("linemulti\n").common("line\n").edits |
| // ); |
| } |
| |
| // TODO: expected failure |
| // the current code does not work on the first line |
| // and the insert marker is in the wrong location |
| @Test |
| public void preferInsertAtLineBreak2() throws Exception { |
| assertThrows( |
| AssertionError.class, |
| () -> { |
| String a = " abc\n def\n"; |
| String b = " abc\n def\n"; |
| assertThat(intraline(a, b)) |
| .isEqualTo(ref().insert(" ").common(" abc\n").insert(" ").common(" def\n").edits); |
| }); |
| } |
| |
| // TODO: expected failure |
| // the current code does not work on the first line |
| @Test |
| public void preferDeleteAtLineBreak() throws Exception { |
| assertThrows( |
| AssertionError.class, |
| () -> { |
| String a = " abc\n def\n"; |
| String b = " abc\n def\n"; |
| assertThat(intraline(a, b)) |
| .isEqualTo(ref().remove(" ").common(" abc\n").remove(" ").common(" def\n").edits); |
| }); |
| } |
| |
| @Test |
| public void insertedWhitespaceIsRecognized() throws Exception { |
| String a = " int *foobar\n"; |
| String b = " int * foobar\n"; |
| assertThat(intraline(a, b)) |
| .isEqualTo(ref().common(" int *").insert(" ").common("foobar\n").edits); |
| } |
| |
| @Test |
| public void insertedWhitespaceIsRecognizedInMultipleLines() throws Exception { |
| // |0 5 10 | 5 20 5 30 |
| String a = " int *foobar\n int *foobar\n"; |
| String b = " int * foobar\n int * foobar\n"; |
| assertThat(intraline(a, b)) |
| .isEqualTo( |
| ref() |
| .common(" int *") |
| .insert(" ") |
| .common("foobar\n") |
| .common(" int *") |
| .insert(" ") |
| .common("foobar\n") |
| .edits); |
| } |
| |
| // helper functions to call IntraLineLoader.compute |
| |
| private static int countLines(String s) { |
| int count = 0; |
| for (int i = 0; i < s.length(); i++) { |
| if (s.charAt(i) == '\n') { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| private static List<Edit> intraline(String a, String b) throws Exception { |
| return intraline(a, b, new Edit(0, countLines(a), 0, countLines(b))); |
| } |
| |
| private static List<Edit> intraline(String a, String b, Edit lines) throws Exception { |
| Text aText = new Text(a.getBytes(UTF_8)); |
| Text bText = new Text(b.getBytes(UTF_8)); |
| |
| IntraLineDiff diff = |
| IntraLineLoader.compute(aText, bText, ImmutableList.of(lines), ImmutableSet.of()); |
| |
| assertThat(diff.getStatus()).isEqualTo(IntraLineDiff.Status.EDIT_LIST); |
| List<Edit> actualEdits = diff.getEdits(); |
| assertThat(actualEdits).hasSize(1); |
| Edit actualEdit = actualEdits.get(0); |
| assertThat(actualEdit.getBeginA()).isEqualTo(lines.getBeginA()); |
| assertThat(actualEdit.getEndA()).isEqualTo(lines.getEndA()); |
| assertThat(actualEdit.getBeginB()).isEqualTo(lines.getBeginB()); |
| assertThat(actualEdit.getEndB()).isEqualTo(lines.getEndB()); |
| assertThat(actualEdit).isInstanceOf(ReplaceEdit.class); |
| |
| return ((ReplaceEdit) actualEdit).getInternalEdits(); |
| } |
| |
| // helpers to compute reference values |
| |
| private static List<Edit> wordEdit(int as, int ae, int bs, int be) { |
| return EditList.singleton(new Edit(as, ae, bs, be)); |
| } |
| |
| private static Reference ref() { |
| return new Reference(); |
| } |
| |
| private static class Reference { |
| List<Edit> edits; |
| private int posA; |
| private int posB; |
| |
| Reference() { |
| edits = new EditList(); |
| posA = posB = 0; |
| } |
| |
| Reference common(String s) { |
| int len = s.length(); |
| posA += len; |
| posB += len; |
| return this; |
| } |
| |
| Reference insert(String s) { |
| return replace("", s); |
| } |
| |
| Reference remove(String s) { |
| return replace(s, ""); |
| } |
| |
| Reference replace(String a, String b) { |
| int lenA = a.length(); |
| int lenB = b.length(); |
| edits.add(new Edit(posA, posA + lenA, posB, posB + lenB)); |
| posA += lenA; |
| posB += lenB; |
| return this; |
| } |
| } |
| } |