| /* |
| * Copyright 2000-2011 JetBrains s.r.o. |
| * |
| * 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.intellij.openapi.editor.impl; |
| |
| import org.jetbrains.annotations.NotNull; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import java.util.Arrays; |
| |
| import static java.util.Arrays.asList; |
| import static org.junit.Assert.*; |
| |
| /** |
| * @author Denis Zhdanov |
| * @since 03/02/2011 |
| */ |
| public class TextChangesStorageTest { |
| |
| private TextChangesStorage myStorage; |
| |
| @Before |
| public void setUp() { |
| myStorage = new TextChangesStorage(); |
| } |
| |
| @Test |
| public void clear() { |
| assertTrue(myStorage.isEmpty()); |
| |
| insert("abc", 2); |
| assertFalse(myStorage.isEmpty()); |
| assertEquals(1, myStorage.getChanges().size()); |
| |
| myStorage.clear(); |
| assertTrue(myStorage.isEmpty()); |
| assertTrue(myStorage.getChanges().isEmpty()); |
| } |
| |
| @Test |
| public void singleInsert() { |
| insert("abc", 2); |
| checkChanges(c("abc", 2)); |
| } |
| |
| @Test |
| public void singleLongInsert() { |
| String text = "this is a relatively long text"; |
| insert(text, 2); |
| checkChanges(c(text, 2)); |
| } |
| |
| @Test |
| public void disconnectedInserts() { |
| insert("abc", 2); |
| insert("def", 6); |
| insert("ghi", 11); |
| checkChanges(c("abc", 2), c("def", 3), c("ghi", 5)); |
| } |
| |
| @Test |
| public void disconnectedInsertsFromTailToStart() { |
| insert("abc", 10); |
| insert("def", 1); |
| checkChanges(c("def", 1), c("abc", 10)); |
| } |
| |
| @Test |
| public void adjacentInserts() { |
| insert("abc", 2); |
| insert("def", 5); |
| insert("ghi", 8); |
| checkChanges(c("abcdefghi", 2)); |
| } |
| |
| @Test |
| public void nestedInserts() { |
| insert("abc", 2); |
| insert("XY", 3); |
| insert("1234", 4); |
| checkChanges(c("aX1234Ybc", 2)); |
| } |
| |
| @Test |
| public void singleDelete() { |
| delete(2, 3); |
| checkChanges(c("", 2, 3)); |
| } |
| |
| @Test |
| public void disconnectedDeletes() { |
| delete(2, 3); |
| delete(3, 4); |
| delete(5, 6); |
| checkChanges(c("", 2, 3), c("", 4, 5), c("", 7, 8)); |
| } |
| |
| @Test |
| public void adjacentDeletes() { |
| delete(2, 3); |
| delete(2, 3); |
| delete(2, 3); |
| checkChanges(c("", 2, 5)); |
| } |
| |
| @Test |
| public void adjacentDeletesFromEndToStart() { |
| delete(5, 6); |
| delete(4, 5); |
| checkChanges(c("", 4, 6)); |
| } |
| |
| @Test |
| public void singleReplace() { |
| replace("abc", 3, 4); |
| checkChanges(c("abc", 3, 4)); |
| } |
| |
| @Test |
| public void disconnectedReplaces() { |
| replace("abc", 3, 4); |
| replace("de", 7, 8); |
| replace("fghi", 10, 11); |
| checkChanges(c("abc", 3, 4), c("de", 5, 6), c("fghi", 7, 8)); |
| } |
| |
| @Test |
| public void disconnectedUnorderedReplaces() { |
| replace("abc", 1, 4); |
| replace("def", 10, 13); |
| replace("ghi", 6, 9); |
| checkChanges(c("abc", 1, 4), c("ghi", 6, 9), c("def", 10, 13)); |
| } |
| |
| @Test |
| public void adjacentReplaces() { |
| replace("abc", 3, 4); |
| replace("de", 6, 9); |
| replace("fghi", 8, 9); |
| checkChanges(c("abcdefghi", 3, 8)); |
| } |
| |
| @Test |
| public void intersectedReplaces() { |
| replace("abc", 3, 4); |
| replace("defg", 5, 6); |
| replace("hi", 8, 11); |
| checkChanges(c("abdefhi", 3, 6)); |
| } |
| |
| @Test |
| public void intersectedReplacesFromEndToStart() { |
| replace("abcd", 5, 6); |
| replace("ef", 4, 7); |
| replace("g", 1, 5); |
| checkChanges(c("gfcd", 1, 6)); |
| } |
| |
| @Test |
| public void nestedReplaces() { |
| replace("abcdef", 3, 5); |
| replace("gh", 4, 7); |
| replace("i", 5, 6); |
| checkChanges(c("agief", 3, 5)); |
| } |
| |
| @Test |
| public void exactMultipleReplace() { |
| replace("abc", 3, 4); |
| replace("cde", 3, 6); |
| replace("fg", 3, 6); |
| checkChanges(c("fg", 3, 4)); |
| } |
| |
| @Test |
| public void insertAndExactDelete() { |
| insert("abc", 3); |
| delete(3, 6); |
| checkChanges(); |
| } |
| |
| @Test |
| public void insertAndDeleteInTheMiddle() { |
| insert("abc", 3); |
| delete(4, 6); |
| checkChanges(c("a", 3)); |
| } |
| |
| @Test |
| public void insertAndWiderDelete() { |
| insert("abc", 3); |
| delete(2, 7); |
| checkChanges(c("", 2, 4)); |
| } |
| |
| @Test |
| public void insertAndDeleteFromLeft() { |
| insert("abc", 3); |
| delete(2, 5); |
| checkChanges(c("c", 2, 3)); |
| } |
| |
| @Test |
| public void insertAndDeleteFromRight() { |
| insert("abc", 3); |
| delete(4, 7); |
| checkChanges(c("a", 3, 4)); |
| } |
| |
| @Test |
| public void disconnectedInsertsAndExactLinkingDelete() { |
| insert("a", 1); |
| insert("bcd", 3); |
| insert("efg", 8); |
| delete(3, 11); |
| checkChanges(c("a", 1), c("", 2, 4)); |
| } |
| |
| @Test |
| public void disconnectedInsertsAndWiderLinkingDelete() { |
| insert("abc", 3); |
| insert("def", 8); |
| delete(2, 13); |
| checkChanges(c("", 2, 7)); |
| } |
| |
| @Test |
| public void disconnectedInsertsAndNarrowLinkingDelete() { |
| insert("abc", 3); |
| insert("def", 8); |
| delete(4, 9); |
| checkChanges(c("aef", 3, 5)); |
| } |
| |
| @Test |
| public void replaceAndDeleteWholeTextFromItsStart() { |
| delete(72, 79); |
| insert("a", 72); |
| delete(72, 73); |
| delete(64, 71); |
| delete(51, 62); |
| insert("a", 54); |
| insert("a", 53); |
| insert("a", 51); |
| delete(56, 57); |
| insert("b", 56); |
| checkChanges(c("a", 51, 62), c("a", 64, 71), c("b", 72, 79)); |
| } |
| |
| @Test |
| public void exactRemoveOfPreviousInsert() { |
| insert("a", 1); |
| insert("bcd", 3); |
| insert("efg", 7); |
| delete(3, 6); |
| checkChanges(c("a", 1), c("efg", 3)); |
| } |
| |
| @Test |
| public void removeAdjacentToInsert() { |
| insert("a", 1); |
| insert("bc", 3); |
| delete(2, 3); |
| checkChanges(c("abc", 1, 2)); |
| } |
| |
| private void checkChanges(TextChangeImpl ... changes) { |
| assertEquals(asList(changes), myStorage.getChanges()); |
| assertEquals(changes.length > 0, !myStorage.isEmpty()); |
| if (changes.length <= 0) { |
| return; |
| } |
| int length = changes[changes.length - 1].getEnd() + 2; |
| char[] input = new char[length]; |
| char c = 'A'; |
| for (int i = 0; i < input.length; i++) { |
| input[i] = c++; |
| } |
| char[] output = BulkChangesMerger.INSTANCE.mergeToCharArray(input, input.length, asList(changes)); |
| |
| // charAt(). |
| for (int i = 0; i < output.length; i++) { |
| if (output[i] != myStorage.charAt(input, i)) { |
| fail(String.format( |
| "Detected incorrect charAt() processing. Original text: '%s', changes: %s, index: %d, expected: %c, actual: %c", |
| new String(input), Arrays.asList(changes), i, output[i], myStorage.charAt(input, i) |
| )); |
| } |
| } |
| |
| // substring(). |
| for (int start = 0; start < output.length; start++) { |
| for( int end = start; end < output.length; end++) { |
| String expected = new String(output, start, end - start); |
| String actual = myStorage.substring(input, start, end).toString(); |
| if (!expected.equals(actual)) { |
| fail(String.format( |
| "Detected incorrect substring() processing. Original text: '%s', changes: %s, client text: '%s', range: %d-%d, " |
| + "expected: '%s', actual: '%s'", new String(input), Arrays.asList(changes), new String(output), start, end, expected, actual |
| )); |
| } |
| } |
| } |
| } |
| |
| private static TextChangeImpl c(@NotNull String text, int startOffset) { |
| return c(text, startOffset, startOffset); |
| } |
| |
| private static TextChangeImpl c(@NotNull String text, int startOffset, int endOffset) { |
| return new TextChangeImpl(text, startOffset, endOffset); |
| } |
| |
| private void insert(@NotNull String text, int offset) { |
| myStorage.store(c(text, offset)); |
| } |
| |
| private void delete(int start, int end) { |
| myStorage.store(c("", start, end)); |
| } |
| |
| private void replace(@NotNull String text, int start, int end) { |
| myStorage.store(c(text, start, end)); |
| } |
| } |