package com.google.collide.shared.ot;
import static com.google.collide.shared.ot.DocOpTestUtils.assertCompose;
import static com.google.collide.shared.ot.DocOpTestUtils.assertComposeFails;
import com.google.collide.dto.DocOp;
import com.google.collide.dto.server.ServerDocOpFactory;
import com.google.collide.shared.document.Document;
import com.google.collide.shared.ot.Composer.ComposeException;
import com.google.collide.shared.util.StringUtils;
import junit.framework.TestCase;
/**
* Tests for the document operation composer.
*
*/
public class ComposerTests extends TestCase {
private static final String[] LINES = {"Hello world\n", "Foo bar\n", "Something else\n"};
private Document doc;
private TerseDocOpBuilder builder;
public void testOneLastRetainLineMatchesOtherLastComponentWithoutNewline() {
{
DocOp a = builder.rl(1).b();
DocOp b = builder.r(14).b();
assertCompose(b, a, b);
}
{
DocOp a = builder.rl(1).b();
DocOp b = builder.d("test").b();
assertCompose(b, a, b);
}
{
DocOp a = builder.r(14).b();
DocOp b = builder.rl(1).b();
assertCompose(a, a, b);
}
{
DocOp a = builder.i("test").b();
DocOp b = builder.rl(1).b();
assertCompose(a, a, b);
}
}
public void testNonRetainLineComposition() {
// R(1) o I(i),R(1)
assertCompose(builder.i("i").r(1).b(),
builder.r(1).b(), builder.i("i").r(1).b());
// R(1) o R(1),I(i)
assertCompose(builder.r(1).i("i").b(),
builder.r(1).b(), builder.r(1).i("i").b());
// D(h) o I(i)
assertCompose(builder.d("h").i("i").b(),
builder.d("h").b(), builder.i("i").b());
// I(h) o R(1),I(i)
assertCompose(builder.i("hi").b(),
builder.i("h").b(), builder.r(1).i("i").b());
// R(1) o D(i)
assertCompose(builder.d("i").b(),
builder.r(1).b(), builder.d("i").b());
// D(h),R(1) o D(i)
assertCompose(builder.d("hi").b(),
builder.d("h").r(1).b(), builder.d("i").b());
// I(h),R(1) o R(1),D(i)
assertCompose(builder.i("h").d("i").b(),
builder.i("h").r(1).b(), builder.r(1).d("i").b());
}
public void testRetainLineIndependent() {
DocOp a = builder.i("a\n").rl(1).b();
DocOp b = builder.rl(2).i("b\n").b();
assertCompose(builder.i("a\n").rl(1).i("b\n").b(), a, b);
a = builder.d("a\n").rl(1).b();
b = builder.i("b").eolR(1).b();
assertCompose(builder.d("a\n").i("b").eolR(1).b(), a, b);
a = builder.d("a\n").rl(2).b();
b = builder.rl(1).i("b").eolR(1).b();
assertCompose(builder.d("a\n").rl(1).i("b").eolR(1).b(), a, b);
}
public void testRetainLineOverlapping() {
DocOp a = builder.rl(1).i("a").eolR(1).rl(1).b();
DocOp b = builder.rl(1).r(1).i("b").eolR(1).rl(1).b();
assertCompose(builder.rl(1).i("ab").eolR(1).rl(1).b(), a, b);
}
public void testAdjacentLineModifications() {
{
DocOp a = builder.rl(1).i("b").eolR(1).b();
DocOp b = builder.i("b").eolR(1).rl(1).b();
assertCompose(builder.i("b").eolR(1).i("b").eolR(1).b(),
a, b);
}
{
DocOp a = builder.rl(15).r(3).i("b").eolR(1).rl(3).b();
DocOp b = builder.rl(14).r(1).i("b").eolR(1).rl(4).b();
assertCompose(builder.rl(14).r(1).i("b").eolR(1).r(3).i("b").eolR(1).rl(3).b(),
a, b);
}
}
public void testRetainLineCanStartMidline() {
{
/*
* Retain line will retain an empty string (this composition was
* discovered in the real world)
*/
DocOp a = builder.r(1).i("a").b();
DocOp b = builder.r(1).d("a").rl(1).b();
assertCompose(builder.r(1).rl(1).b(), a, b);
}
}
public void testRetainLineAndInsertPlayNice() {
{
DocOp a = builder.rl(2).r(5).i("en").eolR(1).rl(1).b();
DocOp b = builder.rl(3).r(1).i("tu").b();
DocOp expected = builder.rl(2).r(5).i("en").eolR(1).r(1).i("tu").b();
assertCompose(expected, a, b);
}
{
DocOp a = builder.rl(2).r(1).i("ip").eolR(1).rl(3).b();
DocOp b = builder.rl(5).r(1).i("is").b();
DocOp expected = builder.rl(2).r(1).i("ip").eolR(1).rl(2).r(1).i("is").b();
assertCompose(expected, a, b);
}
}
public void testThatIncorrectCompositionFails() {
{
DocOp a = builder.i("test").b();
DocOp b = builder.rl(50).rl(1).b();
assertComposeFails(a, b);
}
{
DocOp a = builder.i("test").b();
DocOp b = builder.rl(1).rl(1).b();
assertComposeFails(a, b);
}
}
public void testADeletesAndBInserts() {
{
DocOp a = builder.d("hello").d("world").b();
DocOp b = builder.i("foo").i("bar").b();
DocOp expected = builder.d("helloworld").i("foobar").b();
assertCompose(expected, a, b);
}
}
public void testPositionMigratorCompositionFailure() throws ComposeException {
{
// Simplified test case
// This tickles the path through ProcessingBForAInsert
DocOp a = builder.i("\n").b();
DocOp b = builder.rl(2).b();
DocOp expected = builder.i("\n").b();
assertCompose(expected, a, b);
}
{
// Simplified test case
// This tickles the path through ProcessingBForAInsert
DocOp a = builder.i("\n").b();
DocOp b = builder.rl(1).b();
DocOp expected = builder.i("\n").b();
assertCompose(expected, a, b);
}
{
// Simplified test case
// This tickles the path through ProcessingBForAInsert
DocOp a = builder.i("abc\n").i("def\n").b();
DocOp b = builder.d("a").eolR(3).rl(2).b();
DocOp expected = builder.i("bc\n").i("def\n").b();
assertCompose(expected, a, b);
}
{
// Simplified test case
// This tickles the path through ProcessingAForBRetainLine
DocOp a = builder.i("abc\n").i("def\n").b();
DocOp b = builder.rl(3).b();
DocOp expected = builder.i("abc\n").i("def\n").b();
assertCompose(expected, a, b);
}
{
// This should fail
DocOp a = builder.i("\n").b();
DocOp b = builder.rl(3).b();
assertComposeFails(a, b);
}
{
// This should fail
DocOp a = builder.i("\n").i("\n").b();
DocOp b = builder.rl(4).b();
assertComposeFails(a, b);
}
{
// Related test case
DocOp a = builder.i("abc\n").r(5).b();
DocOp b = builder.rl(2).b();
DocOp expected = builder.i("abc\n").r(5).b();
assertCompose(expected, a, b);
}
{
// Full test case
DocOp a = builder.d("var f = function() {\n").
d(" alert(\"foo!\");\n").
d("}\n").
d("\n").
d("f();\n").
i("d3.svg.diagonal = function() {\n").
i(" var source = d3_svg_chordSource,\n").
i(" target = d3_svg_chordTarget,\n").
i(" projection = d3_svg_diagonalProjection;\n").
i("\n").
i(" function diagonal(d, i) {\n").
i(" var p0 = source.call(this, d, i),\n").
i(" p3 = target.call(this, d, i),\n").
i(" m = (p0.y + p3.y) / 2,\n").
i(" p = [p0, {x: p0.x, y: m}, {x: p3.x, y: m}, p3];\n").
i(" p = p.map(projection);\n").
i(" return \"M\" + p[0] + \"C\" + p[1] + \" \" + p[2] + \" \" + p[3];\n").
i(" }\n").
i("\n").
i(" diagonal.source = function(x) {\n").
i(" if (!arguments.length) return source;\n").
i(" source = d3.functor(x);\n").
i(" return diagonal;\n").
i(" };\n").
i("\n").
i(" diagonal.target = function(x) {\n").
i(" if (!arguments.length) return target;\n").
i(" target = d3.functor(x);\n").
i(" return diagonal;\n").
i(" };\n").
i("\n").
i(" diagonal.projection = function(x) {\n").
i(" if (!arguments.length) return projection;\n").
i(" projection = x;\n").
i(" return diagonal;\n").
i(" };\n").
i("\n").
i(" return diagonal;\n").
i("};\n").b();
DocOp b = builder.d("d3.svg.").eolR(24).rl(34).b();
DocOpTestUtils.compose(a, b);
}
}
public void testUnicodeCompositionFailure() {
{
/*
* Simplified test case so we don't deal with the long unicode escape
* sequences
*/
DocOp a =
builder
.rl(1)
.d("a").i("abcde").eolR(5)
.d("abcde").i("abcde").eolR(5)
.rl(1)
.b();
DocOp b =
builder
.d("abcde\n")
.d("abcdeabcd\n")
.d("abcdeabcd\n")
.b();
DocOp expected = builder
.d("abcde\n")
.d("aabcd\n")
.d("abcdeabcd\n")
.b();
assertCompose(expected, a, b);
}
{
DocOp a = builder.rl(1).b();
DocOp b = builder.b();
DocOp expected = builder.b();
assertCompose(expected, a, b);
}
}
public void testProcessingBForAFinishedMightBeTooLeniant() {
{
// This makes sense
DocOp a = builder.i("a").b();
DocOp b = builder.rl(1).b();
assertCompose(a, a, b);
}
{
// This doesn't
DocOp a = builder.i("a").b();
DocOp b = builder.rl(2).b();
assertComposeFails(a, b);
}
{
// This makes sense
DocOp a = builder.i("\n").b();
DocOp b = builder.rl(2).b();
assertCompose(a, a, b);
}
{
// This doesn't
DocOp a = builder.i("\n").b();
DocOp b = builder.rl(3).b();
assertComposeFails(a, b);
}
{
// This makes sense
DocOp a = builder.r(1).b();
DocOp b = builder.rl(1).b();
assertCompose(a, a, b);
}
{
// This doesn't
DocOp a = builder.r(1).b();
DocOp b = builder.rl(2).b();
assertComposeFails(a, b);
}
{
// This makes sense
DocOp a = builder.eolR(1).b();
DocOp b = builder.rl(2).b();
// It will compact the r("\n") to rl(1)
DocOp expected = builder.rl(1).b();
assertCompose(expected, a, b);
}
{
// This doesn't
DocOp a = builder.eolR(1).b();
DocOp b = builder.rl(3).b();
assertComposeFails(a, b);
}
}
public void testRetainLastLineNotCancelledStillSucceeds() {
{
DocOp a = builder.i("a").d("b").b();
DocOp b = builder.rl(1).b();
DocOp expected = builder.i("a").d("b").b();
assertCompose(expected, a, b);
}
{
DocOp a = builder.rl(1).i("hh\n").i("ii").d("hh\n").d("ii").b();
DocOp b = builder.rl(1).i("hh\n").i("ii\n").eolR(3).rl(1).b();
DocOp expected = builder.rl(1).i("hh\n").i("ii\n").i("hh\n").i("ii").d("hh\n").d("ii").b();
DocOpTestUtils.assertCompose(expected, a, b);
}
}
@Override
protected void setUp() throws Exception {
builder = new TerseDocOpBuilder(ServerDocOpFactory.INSTANCE, false);
doc = Document.createEmpty();
doc.insertText(doc.getFirstLine(), 0, StringUtils.join(LINES, ""));
}
}