// Copyright 2012 Google Inc. All Rights Reserved. // // 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.collide.shared.ot; import static com.google.collide.shared.ot.DocOpTestUtils.*; import com.google.collide.dto.DocOp; import com.google.collide.dto.server.ServerDocOpFactory; import com.google.collide.shared.document.Document; import junit.framework.TestCase; /** * Tests various functionality related to OT ensuring that in cases where the * last Line of a Document is empty (has Line.getText() == ""), there is a * RetainLine that covers that empty Line. * * For legacy reasons, the composer and transformer currently support inputs * that don't follow this requirement. However, if the inputs follow the * requirement, their output must also follow the requirement. That is what will * be tested by the test methods in this class. * */ public class EmptyLastLineRetainLineTests extends TestCase { private final TerseDocOpBuilder builder = new TerseDocOpBuilder(ServerDocOpFactory.INSTANCE, false); private Document doc; public void testDocumentMutationsProduceEmptyLastLineRL() { // Empty mutations { // This is an strange edge case, but might as well cover it doc = Document.createFromString(""); DocOp actual = delete(0, 0, 0); DocOp expected = builder.rl(1).b(); assertDocOpEquals(expected, actual); } { // This is an strange edge case, but might as well cover it doc = Document.createFromString(""); DocOp actual = insert(0, 0, ""); DocOp expected = builder.rl(1).b(); assertDocOpEquals(expected, actual); } // Mutations without newlines { // Ensure no RL doc = Document.createFromString(""); DocOp actual = insert(0, 0, "a"); DocOp expected = builder.i("a").b(); assertDocOpEquals(expected, actual); } { doc = Document.createFromString("a"); DocOp actual = delete(0, 0, 1); DocOp expected = builder.d("a").rl(1).b(); assertDocOpEquals(expected, actual); } // One/two-line documents and mutations with newlines { doc = Document.createFromString(""); DocOp actual = insert(0, 0, "\n"); DocOp expected = builder.i("\n").rl(1).b(); assertDocOpEquals(expected, actual); } { doc = Document.createFromString("\n"); DocOp actual = delete(0, 0, 1); DocOp expected = builder.d("\n").rl(1).b(); assertDocOpEquals(expected, actual); } // Multiple line documents { doc = Document.createFromString("\n\n"); DocOp actual = insert(0, 0, "\n"); DocOp expected = builder.i("\n").eolR(1).rl(2).b(); assertDocOpEquals(expected, actual); } { doc = Document.createFromString("\n\n"); DocOp actual = insert(0, 0, "\n\n"); DocOp expected = builder.i("\n").i("\n").eolR(1).rl(2).b(); assertDocOpEquals(expected, actual); } { doc = Document.createFromString("\n\n"); DocOp actual = delete(0, 0, 1); DocOp expected = builder.d("\n").eolR(1).rl(1).b(); assertDocOpEquals(expected, actual); } { doc = Document.createFromString("\n\n"); DocOp actual = delete(0, 0, 2); DocOp expected = builder.d("\n").d("\n").rl(1).b(); assertDocOpEquals(expected, actual); } // Misc { doc = Document.createFromString("a\n"); DocOp actual = insert(0, 0, "a"); DocOp expected = builder.i("a").eolR(2).rl(1).b(); assertDocOpEquals(expected, actual); } } public void testComposer() { DocOp a, b; // Insert vs Delete a = builder.i("a").b(); b = builder.d("a").rl(1).b(); assertCompose(builder.rl(1).b(), a, b); a = builder.i("a\n").rl(1).b(); b = builder.d("a\n").rl(1).b(); assertCompose(builder.rl(1).b(), a, b); a = builder.i("a\n").rl(1).b(); b = builder.d("a").rl(2).b(); assertCompose(builder.i("\n").rl(1).b(), a, b); // Delete vs Insert a = builder.d("a").rl(1).b(); b = builder.i("a").b(); assertCompose(builder.d("a").i("a").b(), a, b); a = builder.d("\n").rl(1).b(); b = builder.i("a").b(); assertCompose(builder.d("\n").i("a").b(), a, b); a = builder.d("\n").rl(1).b(); b = builder.i("\n").rl(1).b(); assertCompose(builder.d("\n").i("\n").rl(1).b(), a, b); // Insert vs Insert a = builder.i("\n").rl(1).b(); b = builder.i("\n").rl(2).b(); assertCompose(builder.i("\n").i("\n").rl(1).b(), a, b); a = builder.i("a").b(); b = builder.r(1).i("\n").rl(1).b(); assertCompose(builder.i("a\n").rl(1).b(), a, b); a = builder.i("\n").rl(1).b(); b = builder.i("a").rl(2).b(); assertCompose(builder.i("a\n").rl(1).b(), a, b); // Insert vs (Retain or RetainLine) a = builder.i("\n").rl(1).b(); b = builder.eolR(1).rl(1).b(); assertCompose(a, a, b); a = builder.i("\n").i("\n").rl(1).b(); b = builder.eolR(1).eolR(1).rl(1).b(); assertCompose(a, a, b); a = builder.i("\n").rl(1).b(); b = builder.rl(2).b(); assertCompose(a, a, b); a = builder.i("abc\n").rl(1).b(); b = builder.eolR(4).rl(1).b(); assertCompose(a, a, b); // Delete vs (Retain or RetainLine) a = builder.d("a").rl(1).b(); b = builder.rl(1).b(); assertCompose(a, a, b); a = builder.d("\n").rl(1).b(); b = builder.rl(1).b(); assertCompose(a, a, b); a = builder.d("abc\n").rl(1).b(); b = builder.rl(1).b(); assertCompose(a, a, b); a = builder.d("\n").d("\n").rl(1).b(); b = builder.rl(1).b(); assertCompose(a, a, b); // Retain vs (RetainLine or Retain) a = builder.eolR(1).rl(1).b(); b = builder.rl(2).b(); assertCompose(builder.rl(2).b(), a, b); a = builder.eolR(1).rl(1).b(); assertCompose(builder.rl(2).b(), a, a); a = builder.eolR(1).rl(1).b(); b = builder.rl(2).b(); assertCompose(builder.rl(2).b(), a, b); // (Retain or RetainLine) vs Delete a = builder.eolR(2).rl(1).b(); b = builder.d("a").eolR(1).rl(1).b(); assertCompose(builder.d("a").eolR(1).rl(1).b(), a, b); a = builder.eolR(2).rl(1).b(); b = builder.d("a").rl(2).b(); assertCompose(builder.d("a").eolR(1).rl(1).b(), a, b); a = builder.rl(2).b(); b = builder.d("a").eolR(1).rl(1).b(); assertCompose(builder.d("a").eolR(1).rl(1).b(), a, b); a = builder.rl(2).b(); b = builder.d("a").rl(2).b(); assertCompose(builder.d("a").rl(2).b(), a, b); } private DocOp delete(int lineNumber, int column, int deleteCount) { return asDocOp(doc.deleteText(doc.getLineFinder().findLine(0).line(), column, deleteCount)); } private DocOp insert(int lineNumber, int column, String text) { return asDocOp(doc.insertText(doc.getLineFinder().findLine(0).line(), column, text)); } }