Package org.waveprotocol.wave.model.document

Source Code of org.waveprotocol.wave.model.document.MutableDocumentImplTest

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.waveprotocol.wave.model.document;


import junit.framework.TestCase;

import org.waveprotocol.wave.model.document.MutableAnnotationSet.CompareRangedValueByStartThenEnd;
import org.waveprotocol.wave.model.document.MutableAnnotationSet.RangedValue;
import org.waveprotocol.wave.model.document.indexed.IndexedDocument;
import org.waveprotocol.wave.model.document.operation.DocOp;
import org.waveprotocol.wave.model.document.operation.Nindo;
import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema;
import org.waveprotocol.wave.model.document.operation.impl.AttributesImpl;
import org.waveprotocol.wave.model.document.operation.impl.DocOpUtil;
import org.waveprotocol.wave.model.document.raw.impl.Element;
import org.waveprotocol.wave.model.document.raw.impl.Node;
import org.waveprotocol.wave.model.document.raw.impl.Text;
import org.waveprotocol.wave.model.document.util.DocProviders;
import org.waveprotocol.wave.model.document.util.Point;
import org.waveprotocol.wave.model.document.util.PointRange;
import org.waveprotocol.wave.model.document.util.Range;
import org.waveprotocol.wave.model.operation.OperationException;
import org.waveprotocol.wave.model.operation.OperationRuntimeException;
import org.waveprotocol.wave.model.operation.OperationSequencer;

import java.util.Collections;
import java.util.LinkedList;

/**
* Test cases for MutableDocument implementations
*
* @author danilatos@google.com (Daniel Danilatos)
*/

public class MutableDocumentImplTest extends TestCase {

  /**
   * A parser for documents.
   */
  public static final DocumentTestCases.DocumentParser<
      IndexedDocument<Node, Element, Text>> documentParser =
      new DocumentTestCases.DocumentParser<IndexedDocument<
          Node, Element, Text>>() {

    public IndexedDocument<Node, Element, Text>
        parseDocument(String innerXml) {
      return DocProviders.POJO.parse(innerXml);
    }

    @Override
    public IndexedDocument<Node, Element, Text> copyDocument(
        IndexedDocument<Node, Element, Text> other) {
      return DocProviders.POJO.build(other.asOperation(), DocumentSchema.NO_SCHEMA_CONSTRAINTS);
    }

    public String asString(IndexedDocument<Node, Element, Text> document) {
      return document.toXmlString();
    }

  };

  /** Indexed document to feed into a MutableDocument and apply ops to */
  IndexedDocument<Node, Element, Text> indexed;
  /** MutableDocument that gets tested */
  MutableDocumentImpl<Node, Element, Text> doc;
  /** Latest document mutation to have been applied */
  DocOp latestOp;

  /**
   * Creates and returns a sequencer which applies incoming ops to the given document
   */
  OperationSequencer<Nindo> createSequencer(
      final IndexedDocument<Node, Element, Text> document) {
    return new OperationSequencer<Nindo>() {
      @Override
      public void begin() {
      }

      @Override
      public void end() {
      }

      @Override
      public void consume(Nindo op) {
        try {
          latestOp = document.consumeAndReturnInvertible(op);
        } catch (OperationException oe) {
          throw new OperationRuntimeException("sequencer consume failed.", oe);
        }
      }
    };
  }

  /**
   * Tests for the delete range method.
   * Tests correct deletion behaviour, and returned point range value
   */
  public void testDeleteRange() {
    // deletes nothing
    String str = "123<b>asdf</b>34<x/>5";
    init(str);
    for (int i = 0; i <= 14; i++) {
      assertCollapsedAt(i, doc.deleteRange(l(i), l(i)));
      assertResult(str);
    }
    // delete start or end tag does nothing
    assertRangeAt(3, 4, doc.deleteRange(l(3), l(4)));
    assertResult(str);
    assertRangeAt(8, 9, doc.deleteRange(l(8), l(9)));
    assertResult(str);

    // text only
    init("12345678");
    assertCollapsedAt(1, doc.deleteRange(l(1), l(3))); // middle
    assertResult("145678");
    assertCollapsedAt(0, doc.deleteRange(l(0), l(1))); // start
    assertResult("45678");
    assertCollapsedAt(3, doc.deleteRange(l(3), l(5))); // end
    assertResult("456");
    assertCollapsedAt(0, doc.deleteRange(l(0), l(3))); // all
    assertResult("");

    // within single element, but multiple nodes being deleted
    init("123<b>5</b><i>6</i>78");
    assertCollapsedAt(3, doc.deleteRange(l(3), l(9))); // middle
    assertResult("12378");

    // into element
    init("123<b>456</b>");
    assertRangeAt(2, 3, doc.deleteRange(l(2), l(5)));
    assertResult("12<b>56</b>");

    // into elements, depth 2
    init("123<b>4<i>56</i></b>");
    assertRangeAt(2, 4, doc.deleteRange(l(2), l(7)));
    assertResult("12<b><i>6</i></b>");

    // out of element
    init("<b>123</b>456");
    assertRangeAt(3, 4, doc.deleteRange(l(3), l(6)));
    assertResult("<b>12</b>56");

    // out of element, depth 2
    init("<b><i>12</i>3</b>456");
    assertRangeAt(3, 5, doc.deleteRange(l(3), l(8)));
    assertResult("<b><i>1</i></b>56");

    // across elements
    init("<b>123</b>456<i>789</i>");
    assertRangeAt(3, 5, doc.deleteRange(l(3), l(10)));
    assertResult("<b>12</b><i>89</i>");

    // across elements with extra element in the middle
    init("<b>123</b>4<x></x>6<i>789</i>");
    assertRangeAt(3, 5, doc.deleteRange(l(3), l(11)));
    assertResult("<b>12</b><i>89</i>");

    // across elements with elements as the bounding content
    init("<b>123<x></x></b>456<i><y></y>789</i>");
    assertRangeAt(4, 6, doc.deleteRange(l(4), l(13)));
    assertResult("<b>123</b><i>789</i>");
  }

  public void testDeleteRangeIndices() {
    String str = "123<b>asdf</b>34<x/>5";
    init(str);
    for (int i = 0; i <= 14; i++) {
      assertCollapsedAt(i, doc.deleteRange(i, i));
      assertResult(str);
    }

    // delete start or end tag does nothing
    assertRangeAt(3, 4, doc.deleteRange(3, 4));
    assertResult(str);
    assertRangeAt(8, 9, doc.deleteRange(8, 9));
    assertResult(str);

    // text only
    init("12345678");
    assertCollapsedAt(1, doc.deleteRange(1, 3)); // middle
    assertResult("145678");
    assertCollapsedAt(0, doc.deleteRange(0, 1)); // start
    assertResult("45678");
    assertCollapsedAt(3, doc.deleteRange(3, 5)); // end
    assertResult("456");
    assertCollapsedAt(0, doc.deleteRange(0, 3)); // all
    assertResult("");
  }

  /**
   * Test basic get attribute.
   */
  public void testGetAttributes() {
    init("<p t=\"0\" s=\"hi\">hello</p>");
    Element e = (Element) doc.getFirstChild(doc.getDocumentElement());
    assertEquals("0", doc.getAttribute(e, "t"));
    assertEquals("hi", doc.getAttribute(e, "s"));
  }

  /**
   * Test set attribute overrides and removes old attributes, as opposed to
   * update.
   */
  public void testSetAttributes() {
    init("<p t=\"0\" s=\"hi\">hello</p>");
    Element e = (Element) doc.getFirstChild(doc.getDocumentElement());
    doc.setElementAttributes(e, new AttributesImpl("just", "this"));
    assertEquals(null, doc.getAttribute(e, "t"));
    assertEquals(null, doc.getAttribute(e, "s"));
    assertEquals("this", doc.getAttribute(e, "just"));
  }

  protected Point<Node> l(int location) {
    return doc.locate(location);
  }

  /** Init document state */
  protected void init(String initialContent) {
    indexed = DocProviders.POJO.parse(initialContent);

    // Get a mutable doc view of our target and hook it up with the
    // "remote" document as the sink of outgoing ops.
    doc = new MutableDocumentImpl<Node, Element, Text>(
        createSequencer(indexed), indexed);
  }

  /**
   * Check the content of both indexed documents is as expected
   * @param expectedContent
   */
  protected void assertResult(String expectedContent) {
    String result = DocOpUtil.toXmlString(indexed.asOperation());

    // Check the paste happened correctly
    assertEquals(expectedContent, result);

  }

  /**
   * Check the content of both indexed documents is as expected
   * @param expectedContent
   */
  protected void assertOperationResult(String expectedContent) {
    String result = DocOpUtil.toXmlString(indexed.asOperation());

    // Check the ops have been applied correctly
    assertEquals(expectedContent, result);
  }

  protected void assertCollapsedAt(
      int location, Range actual) {
    assertEquals(new Range(location, location), actual);
  }

  protected void assertCollapsedAt(
      int location, PointRange<Node> actual) {
    Point<Node> expected = l(location);
    assertEquals(new PointRange<Node>(expected, expected), actual);
  }

  protected void assertRangeAt(
      int start, int end, Range actual) {
    assertEquals(start, actual.getStart());
    assertEquals(end, actual.getEnd());
  }

  protected void assertRangeAt(
      int start, int end, PointRange<Node> actual) {
    PointRange<Node> expected = new PointRange<Node>(l(start), l(end));
    assertEquals(expected, actual);
  }

  /** Test a simple set annotation */
  public void testSetAnnotation() {
    init("<p>abcdef</p>");
    doc.setAnnotation(3, 6, "style/color", "stix");
    assertOperationResult(
        "<p>ab<?a \"style/color\"=\"stix\"?>cde<?a \"style/color\"?>f</p>");
  }

  /** Test adding two non-overlapping settings of the same annotation */
  public void testTwoNonOverlappingAnnotations() {
    init("<p>abcdef</p>");
    doc.setAnnotation(2, 4, "style/color", "lola");
    doc.setAnnotation(5, 6, "style/color", "lola");
    assertOperationResult(
        "<p>a<?a \"style/color\"=\"lola\"?>bc<?a \"style/color\"?>d" +
        "<?a \"style/color\"=\"lola\"?>e<?a \"style/color\"?>f</p>");
  }

  /** Test adding two overlapping settings of the same annotation */
  public void testTwoOverlappingAnnotations() {
    init("<p>abcdef</p>");
    doc.setAnnotation(2, 4, "style/color", "charlie");
    doc.setAnnotation(3, 6, "style/color", "charlie");
    assertOperationResult(
        "<p>a<?a \"style/color\"=\"charlie\"?>bcde<?a \"style/color\"?>f</p>");
  }

  /** Test adding an annotation over the whole document */
  public void testSetAnnotationOverWholeDocument() {
    init("<p>abcdef</p>");
    doc.setAnnotation(0, doc.size(), "style/color", "flim");
    assertOperationResult(
        "<?a \"style/color\"=\"flim\"?><p>abcdef</p><?a \"style/color\"?>");
  }

  /** Test a simple set and unset of an annotation */
  public void testSetAndUnsetAnnotation() {
    init("<p>abcdef</p>");
    doc.setAnnotation(3, 6, "style/color", "maisy");
    doc.setAnnotation(0, doc.size(), "style/color", null);
    assertOperationResult("<p>abcdef</p>");
  }

  /** Test that trying to add a zero range annotation does nothing */
  public void testZeroRangeSetAnnotation() {
    init("<p>abcdef</p>");
    doc.setAnnotation(3, 3, "style/color", "blum");
    assertOperationResult("<p>abcdef</p>");
  }

  /**
   * Test that trying to add an annotation with a negative start throws
   * an IndexOutOfBoundsException.
   */
  public void testNegativeStartSetAnnotationThrowsException() throws Exception {
    init("<p>abcdef</p>");
    try {
      doc.setAnnotation(-1, 4, "style/color", "frub");
      // Doh - no exception thrown. Fail the test
      assert false;
    } catch (IndexOutOfBoundsException iae) {
      // expected
    }
  }

  /**
   * Test that trying to add an annotation with an end past the size throws
   * an IndexOutOfBoundsException.
   */
  public void testSetAnnotationPastDocEndThrowsException() throws Exception {
    init("<p>abcdef</p>");
    try {
      doc.setAnnotation(1, doc.size() + 1, "style/color", "frub");
      // Doh - no exception thrown. Fail the test
      fail();
    } catch (IndexOutOfBoundsException iae) {
      // expected
    }
  }

  /**
   * Test that trying to add an annotation with a negative range throws
   * an IndexOutOfBoundsException.
   */
  public void testNegativeRangeSetAnnotationThrowsException() throws Exception {
    init("<p>abcdef</p>");
    try {
      doc.setAnnotation(4, 1, "style/color", "slarken");
      // Doh - no exception thrown. Fail the test
      fail();
    } catch (IndexOutOfBoundsException iae) {
      // expected
    }
  }

  /** Test a simple reset annotation */
  public void testResetAnnotation() {
    init("<p>abcdef</p>");
    doc.resetAnnotation(3, 6, "style/color", "pocoyo");
    assertOperationResult(
        "<p>ab<?a \"style/color\"=\"pocoyo\"?>cde<?a \"style/color\"?>f</p>");
  }

  /** Test a simple set and reset annotation */
  public void testSetAndResetAnnotation() {
    init("<p>abcdef</p>");
    doc.setAnnotation(0, doc.size(), "style/color", "pato");
    doc.resetAnnotation(3, 6, "style/color", "pato");
    assertOperationResult(
        "<p>ab<?a \"style/color\"=\"pato\"?>cde<?a \"style/color\"?>f</p>");
  }

  /**
   * Test that using a zero range reset annotation clears the annotation over
   * the whole document.
   */
  public void testZeroRangeResetAnnotationClearsDocument() {
    init("<p>abcdef</p>");
    doc.setAnnotation(1, 4, "style/color", "spot");
    doc.resetAnnotation(0, 0, "style/color", "spot");
    assertOperationResult("<p>abcdef</p>");

    init("<p>abcdef</p>");
    doc.setAnnotation(1, 4, "style/color", "spot");
    doc.resetAnnotation(2, 2, "style/color", "spot");
    assertOperationResult("<p>abcdef</p>");

    init("<p>abcdef</p>");
    doc.setAnnotation(1, 4, "style/color", "spot");
    doc.resetAnnotation(doc.size(), doc.size(), "style/color", "spot");
    assertOperationResult("<p>abcdef</p>");
  }

  /**
   * Test that trying to reset an annotation with a negative start throws
   * an IndexOutOfBoundsException.
   */
  public void testNegativeStartResetAnnotationThrowsException() throws Exception {
    init("<p>abcdef</p>");
    try {
      doc.resetAnnotation(-1, 4, "style/color", "frub");
      // Doh - no exception thrown. Fail the test
      assert false;
    } catch (IndexOutOfBoundsException iae) {
      // expected
    }
  }

  /**
   * Test that trying to reset an annotation with an end bigger than the document
   * an IndexOutOfBoundsException.
   */
  public void testResetAnnotationPastDocEndThrowsException() throws Exception {
    init("<p>abcdef</p>");
    try {
      doc.resetAnnotation(1, doc.size() + 1, "style/color", "frub");
      // Doh - no exception thrown. Fail the test
      assert false;
    } catch (IndexOutOfBoundsException iae) {
      // expected
    }
  }

  public void testMoveNodes() throws Exception {
    // simple move
    init("<root><before/><from/></root>");
    Element root = doc.getDocumentElement().getFirstChild().asElement();
    Node from = root.getLastChild();
    doc.moveSiblings(Point.start(doc, root), from, null);
    assertOperationResult("<root><from/><before/></root>");

    // move with attributes and children
    init("<root><before/> stuff <from> child <sub/></from> more <attr x=\"x\" y=\"z\"/> end</root>");
    root = doc.getDocumentElement().getFirstChild().asElement();
    Node stuff = root.getFirstChild().getNextSibling();
    from = stuff.getNextSibling();
    doc.moveSiblings(Point.before(doc, stuff), from, root.getLastChild());
    assertOperationResult(
        "<root><before/><from> child <sub/></from> more <attr x=\"x\" y=\"z\"/> stuff  end</root>");

    // move with annotations
    //    0     1  234  567   8   9    10 11     12
    init("<root><b>bo<i>ld</i></b><after/></root>");
    doc.setAnnotation(1, 9, "b", "B"); // around the bs
    doc.setAnnotation(4, 8, "i", "I"); // around the is
    doc.setAnnotation(0, 3, "s", "S"); // overlaps the start
    doc.setAnnotation(7, 12, "e", "E"); // overlaps the end, AND covers the new range

    /*
    <root><b>bo<i>ld</i></b><after/></root>
           B BB B BB  B   B
                I II  I
      S    S S
                      E   E    E E    E

    <root><after/><b>bo<i>ld</i></b></root>
                   B BB B BB  B   B
                        I II  I
      S            S S
             E E              E   E   E

    <?a "s"="S"?><root><?a "e"="E" "s"?><after/><?a "b"="B" "e" "s"="S"?><b>b<?a "s"?>o<?a "i"="I"?><i>ld<?a "e"="E"?></i><?a "i"?></b><?a "b"?></root><?a "e"?>
    */

    root = doc.getDocumentElement().getFirstChild().asElement();
    doc.moveSiblings(Point.end((Node) root), root.getFirstChild(), root.getLastChild());
    assertOperationResult("<?a \"s\"=\"S\"?><root><?a \"e\"=\"E\" \"s\"?><after/>"
        + "<?a \"b\"=\"B\" \"e\" \"s\"=\"S\"?><b>b<?a \"s\"?>o<?a \"i\"=\"I\"?><i>ld"
        + "<?a \"e\"=\"E\"?></i><?a \"i\"?></b><?a \"b\"?></root><?a \"e\"?>");
  }

  /**
   * Test we can atomically reset multiple annotations within a range.
   */
  @SuppressWarnings("deprecation") // resetAnnotationsInRange (method under test) is deprecated
  public void xtestSimpleResetAnnotations() {
    // TODO(user): Fix this test.

    init("<p>abcdef</p>");
    LinkedList<RangedValue<String>> annos = new LinkedList<RangedValue<String>>();
    annos.add(new RangedValue<String>(2, 4, "cyril"));
    annos.add(new RangedValue<String>(5, 6, "tallulah"));
    doc.resetAnnotationsInRange(0, doc.size(), "style/color", annos);
    assertOperationResult(
        "<p>a<?a \"style/color\"=\"cyril\"?>bc<?a \"style/color\"?>d" +
        "<?a \"style/color\"=\"tallulah\"?>e<?a \"style/color\"?>f</p>");
    // Just fail for now, so that we remember to come back to fix up this test.
    fail();

//    DocumentOperationChecker.Recorder recorder = new DocumentOperationChecker.Recorder();
//    recorder.begin();
//    recorder.skip(3);
//    recorder.startAnnotation("style/color", "cyril");
//    recorder.skip(2);
//    recorder.endAnnotation("style/color");
//    recorder.skip(1);
//    recorder.startAnnotation("style/color", "tallulah");
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    recorder.finish();
//    DocumentOperationChecker checker = recorder.finishRecording();
//    latestOp.apply(checker);
//    checker.checkCompleted();
  }

  /**
   * Test we can atomically extend an annotation to the right
   */
  @SuppressWarnings("deprecation") // resetAnnotationsInRange (method under test) is deprecated
  public void xtestExtendAnnotationsRight() {
    // TODO(user): Fix this test.

    // Test extending to the right
    init("<p>23456789</p>");
    LinkedList<RangedValue<String>> annos = new LinkedList<RangedValue<String>>();
    doc.setAnnotation(1, 2, "style/color", "marv");
    doc.setAnnotation(3, 4, "style/color", "eddie");
    annos.add(new RangedValue<String>(1, 2, "marv"));
    annos.add(new RangedValue<String>(3, 6, "eddie"));
    doc.resetAnnotationsInRange(1, 8, "style/color", annos);
    assertOperationResult(
        "<p>" +
        "<?a \"style/color\"=\"marv\"?>2<?a \"style/color\"?>" +
        "3" +
        "<?a \"style/color\"=\"eddie\"?>456<?a \"style/color\"?>" +
        "789" +
        "</p>");
    // Just fail for now, so that we remember to come back to fix up this test.
    fail();
//    DocumentOperationChecker.Recorder recorder = new DocumentOperationChecker.Recorder();
//    recorder.begin();
//    recorder.skip(5);
//    recorder.startAnnotation("style/color", "eddie");
//    recorder.skip(2);
//    recorder.endAnnotation("style/color");
//    recorder.finish();
//    DocumentOperationChecker checker = recorder.finishRecording();
//    latestOp.apply(checker);
//    checker.checkCompleted();
  }

  /**
   * Test we can atomically extend an annotation to the left
   */
  @SuppressWarnings("deprecation") // resetAnnotationsInRange (method under test) is deprecated
  public void xtestExtendAnnotationsLeft() {
    // TODO(user): Fix this test.

    init("<p>23456789</p>");
    LinkedList<RangedValue<String>> annos = new LinkedList<RangedValue<String>>();
    doc.setAnnotation(1, 2, "style/color", "lotta");
    doc.setAnnotation(3, 4, "style/color", "sizzles");
    annos.add(new RangedValue<String>(1, 2, "lotta"));
    annos.add(new RangedValue<String>(2, 4, "sizzles"));
    doc.resetAnnotationsInRange(0, 7, "style/color", annos);
    assertOperationResult(
        "<p>" +
        "<?a \"style/color\"=\"lotta\"?>2" +
        "<?a \"style/color\"=\"sizzles\"?>34<?a \"style/color\"?>" +
        "56789" +
        "</p>");
    // Just fail for now, so that we remember to come back to fix up this test.
    fail();
//    DocumentOperationChecker.Recorder recorder = new DocumentOperationChecker.Recorder();
//    recorder.begin();
//    recorder.skip(3);
//    recorder.startAnnotation("style/color", "sizzles");
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    recorder.finish();
//    DocumentOperationChecker checker = recorder.finishRecording();
//    latestOp.apply(checker);
//    checker.checkCompleted();
  }

  /**
   * Test we can atomically reset multiple annotations within a range that also clear
   * other existing annotations.
   */
  @SuppressWarnings("deprecation") // resetAnnotationsInRange (method under test) is deprecated
  public void xtestResetAnnotations() {
    // TODO(user): Fix this test.

    init("<p>23456789</p>");
    LinkedList<RangedValue<String>> annos = new LinkedList<RangedValue<String>>();
    doc.setAnnotation(0, 1, "style/color", "marv");
    doc.setAnnotation(3, 5, "style/color", "eddie");
    annos.add(new RangedValue<String>(2, 3, "charley"));
    annos.add(new RangedValue<String>(4, 6, "morten"));
    doc.resetAnnotationsInRange(0, 6, "style/color", annos);
    assertOperationResult(
        "<p>" +
        "2" +
        "<?a \"style/color\"=\"charley\"?>3<?a \"style/color\"?>" +
        "4" +
        "<?a \"style/color\"=\"morten\"?>56<?a \"style/color\"?>" +
        "789" +
        "</p></blip>");
    // Just fail for now, so that we remember to come back to fix up this test.
    fail();
//    DocumentOperationChecker.Recorder recorder = new DocumentOperationChecker.Recorder();
//    recorder.begin();
//    recorder.skip(1);
//    recorder.startAnnotation("style/color", null);
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    recorder.skip(1);
//    recorder.startAnnotation("style/color", "charley");
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    recorder.startAnnotation("style/color", null);
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    // TODO(user): optimise the below sequence to combine the two sets
//    // of the same value
//    recorder.startAnnotation("style/color", "morten");
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    recorder.startAnnotation("style/color", "morten");
//    recorder.skip(1);
//    recorder.endAnnotation("style/color");
//    recorder.finish();
//    DocumentOperationChecker checker = recorder.finishRecording();
//    latestOp.apply(checker);
//    checker.checkCompleted();
  }

  // TODO(danilatos): test all the other content manipulation methods.

  /**
   * Tests that createChildElement does as it says.
   */
  public void testCreateChildElement() {
    init("<p>first child</p>");
    Element root = doc.getDocumentElement();
    doc.createChildElement(root, "child", Collections.<String, String> emptyMap());
    assertOperationResult("<p>first child</p><child/>");
  }

  public void testCompareRangedValueByStartThenEndButIgnoreValue() {
    CompareRangedValueByStartThenEnd<String> comp =
      new CompareRangedValueByStartThenEnd<String>();
    {
      // first wholly to the left of second
      RangedValue<String> first = new RangedValue<String>(0, 3, "a");
      RangedValue<String> second = new RangedValue<String>(5, 6, null);
      assert(comp.compare(first, second) < 0);
    }
    {
      // End of first touching start of second
      RangedValue<String> first = new RangedValue<String>(0, 3, "a");
      RangedValue<String> second = new RangedValue<String>(3, 6, null);
      assert(comp.compare(first, second) < 0);
    }
    {
      // End of first within second
      RangedValue<String> first = new RangedValue<String>(0, 4, "a");
      RangedValue<String> second = new RangedValue<String>(3, 6, null);
      assert(comp.compare(first, second) < 0);
    }
    {
      // First and second start at the same place, first ends first
      RangedValue<String> first = new RangedValue<String>(3, 4, "a");
      RangedValue<String> second = new RangedValue<String>(3, 6, null);
      assert(comp.compare(first, second) < 0);
    }
    {
      // First equal to  second
      RangedValue<String> first = new RangedValue<String>(3, 4, "a");
      RangedValue<String> second = new RangedValue<String>(3, 4, null);
      assert(comp.compare(first, second) == 0);
    }
    {
      // First starts within second, ends are equal
      RangedValue<String> first = new RangedValue<String>(3, 4, "a");
      RangedValue<String> second = new RangedValue<String>(2, 4, null);
      assert(comp.compare(first, second) > 0);
    }
    {
      // First starts within second, first ends after second
      RangedValue<String> first = new RangedValue<String>(3, 5, "a");
      RangedValue<String> second = new RangedValue<String>(2, 4, null);
      assert(comp.compare(first, second) > 0);
    }
    {
      // First starts where second ends
      RangedValue<String> first = new RangedValue<String>(4, 5, "a");
      RangedValue<String> second = new RangedValue<String>(2, 4, null);
      assert(comp.compare(first, second) > 0);
    }
    {
      // First wholly to the right of second
      RangedValue<String> first = new RangedValue<String>(5, 6, "a");
      RangedValue<String> second = new RangedValue<String>(2, 4, null);
      assert(comp.compare(first, second) > 0);
    }
  }
}
TOP

Related Classes of org.waveprotocol.wave.model.document.MutableDocumentImplTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.