/** * Copyright 2009 Google Inc. * * 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 org.waveprotocol.wave.client.editor.util; import static org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil.clearAnnotationsOverRange; import static org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil.getAnnotationOverRangeIfFull; import static org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil.getFirstCoveringAnnotationOverRange; import static org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil.setAnnotationOverRange; import static org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil.supplementAnnotations; import org.waveprotocol.wave.client.editor.content.misc.CaretAnnotations; import org.waveprotocol.wave.client.editor.content.misc.CaretAnnotations.AnnotationResolver; import org.waveprotocol.wave.model.document.util.DocProviders; import junit.framework.TestCase; import org.waveprotocol.wave.model.document.MutableDocument; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.StringSet; /** * Tests the methods in EditorAnnotationUtilTest, paired up with the context and direct versions * tested at the same time. * * TODO(patcoleman): These tests should be properly split into large and small tests after this CL, * or alternatively those with buildContext could be made small, which requires nodelet-free * CMutableDocuments to be created. As the context methods call the pojo methods, it should be * safe to just test the pojo ones for now. * * @author patcoleman@google.com (Pat Coleman) */ public class EditorAnnotationUtilTest extends TestCase { // Parameters to make generating the EditorContext wrappers easier private int start; private int end; private MutableDocument<?, ?, ?> doc; private CaretAnnotations caret; // Resolve the annotations by left-biasing within the document, stores it in doc. AnnotationResolver annotationResolver = new AnnotationResolver() { public String getAnnotation(String key) { return start == 0 || doc.getAnnotation(start - 1, key) == null ? null : doc.getAnnotation(start - 1, key).toString(); } }; // Builds a fake editor context out of the state of the test. /*private EditorContext buildContext() { return new FakeEditorContext(doc, caret, "", new DocumentFreeSelectionHelper(start, end)); }*/ // Converts the document and annotations into a CMutableDocument and stores in doc private void useDocument(String docContent, String... annotations) { MutableDocument<?, ?, ?> mutable = DocProviders.MOJO.parse("<body><line></line>" + docContent + "</body>"); // format: key:value:start:end for (String value : annotations) { String[] vals = value.split(":"); mutable.setAnnotation(Integer.parseInt(vals[2]), Integer.parseInt(vals[3]), vals[0], vals[1]); } doc = mutable; } // Converts the annotations passed into a CaretAnnotations bundle and stores in caret private void useCaretAnnotations(String... annotations) { CaretAnnotations localCaret = new CaretAnnotations(); for (String value : annotations) { String[] dup = value.split(":"); localCaret.setAnnotation(dup[0], dup[1]); } caret = localCaret; caret.setAnnotationResolver(annotationResolver); } // Stores the start and end of the selection range to simulate. private void useSelection(int begin, int finish) { start = begin; end = finish; } /// /// tests begin (anything with buildContext() currently disabled) /// /** check getting first cover for (null, non-null) x (range, collapsed) */ public void testGetFirstCover() { // cover the 'owd', offsets 4 to 7 useDocument("howdy", "A:a:5:7", "X:x:4:7", "Y:y:3:8"); useCaretAnnotations("W:w"); useSelection(4, 7); // null return for range selection //assertNull(getFirstAnnotationOverSelection(buildContext(), "A", "B")); assertNull(getFirstCoveringAnnotationOverRange(doc, caret, new String[]{"A", "B"}, start, end)); // non-null return for range selection //assertEquals(getFirstAnnotationOverSelection(buildContext(), "A", "X", "Y"), "x"); //assertEquals("x", getFirstCoveringAnnotationOverRange(doc, caret, new String[]{"A", "X", "Y"}, // start, end)); // collapse: useSelection(5, 5); // null for collapsed selection //assertNull(getFirstAnnotationOverSelection(buildContext(), "Q", "A")); assertNull(getFirstCoveringAnnotationOverRange(doc, caret, new String[]{"Q", "A"}, start, end)); // non-null for collapsed selection, using direct caret annotation //assertEquals(getFirstAnnotationOverSelection(buildContext(), "W", "X", "Y"), "w"); assertEquals("w", getFirstCoveringAnnotationOverRange(doc, caret, new String[]{"W", "X", "Y"}, start, end)); // non-null for collapsed selection, using annotation resolver //assertEquals(getFirstAnnotationOverSelection(buildContext(), "X", "Y", "W"), "x"); assertEquals("x", getFirstCoveringAnnotationOverRange(doc, caret, new String[]{"X", "Y", "W"}, start, end)); } /** check getting an annotation cover for (null, non-null) x (range, collapsed) */ public void testGetAnnotationIfFull() { // cover the 'owd', offsets 4 to 7 useDocument("howdy", "A:a:5:7", "X:x:4:7", "Y:y:3:8"); useCaretAnnotations("W:w"); useSelection(4, 7); // null for range: //assertNull(getAnnotationOverSelectionIfFull(buildContext(), "A")); assertNull(getAnnotationOverRangeIfFull(doc, caret, "A", start, end)); // non-null for range: //assertEquals(getAnnotationOverSelectionIfFull(buildContext(), "Y"), "y"); assertEquals(getAnnotationOverRangeIfFull(doc, caret, "Y", start, end), "y"); // collapse selection useSelection(5, 5); // null for collapsed selection //assertNull(getAnnotationOverSelectionIfFull(buildContext(), "$")); assertNull(getAnnotationOverRangeIfFull(doc, caret, "$", start, end)); // non-null for collapsed selection, using direct annotation //assertEquals(getAnnotationOverSelectionIfFull(buildContext(), "W"), "w"); assertEquals(getAnnotationOverRangeIfFull(doc, caret, "W", start, end), "w"); // non-null for collapsed selection, using annotation resolver //assertEquals(getAnnotationOverSelectionIfFull(buildContext(), "Y"), "y"); assertEquals(getAnnotationOverRangeIfFull(doc, caret, "Y", start, end), "y"); } /** check setting an annotation for (null, non-null) x (range, collapsed) */ public void testSetAnnotation() { // cover the 'owd', offsets 4 to 7 useCaretAnnotations("W:w"); useSelection(4, 7); // null over ranged selection //useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); //setAnnotationOverSelection(buildContext(), "Y", null); //assertNull(doc.getAnnotation(start, "Y")); //assertEquals(doc.firstAnnotationChange(start, end + 1, "Y", null), end); useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); setAnnotationOverRange(doc, caret, "Y", null, start, end); assertNull(doc.getAnnotation(start, "Y")); assertEquals(doc.firstAnnotationChange(start, end + 1, "Y", null), end); // non-null over ranged selection //useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); //setAnnotationOverSelection(buildContext(), "A", "a"); //assertEquals(doc.getAnnotation(start, "A"), "a"); //assertEquals(doc.firstAnnotationChange(start, end, "A", "a"), -1); useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); setAnnotationOverRange(doc, caret, "A", "a", start, end); assertEquals(doc.getAnnotation(start, "A"), "a"); assertEquals(doc.firstAnnotationChange(start, end, "A", "a"), -1); // null over collapsed selection useSelection(5, 5); //useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); //setAnnotationOverSelection(buildContext(), "A", null); //assertTrue(caret.hasAnnotation("A")); //assertNull(caret.getAnnotation("A")); useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); setAnnotationOverRange(doc, caret, "A", null, start, end); assertTrue(caret.hasAnnotation("A")); assertNull(caret.getAnnotation("A")); // non-null over collapsed selection //useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); //setAnnotationOverSelection(buildContext(), "A", "!"); //assertEquals(caret.getAnnotation("A"), "!"); useDocument("howdy", "A:a:5:6", "X:x:4:7", "Y:y:3:8"); setAnnotationOverRange(doc, caret, "A", "!", start, end); assertEquals(caret.getAnnotation("A"), "!"); } /** check clearing annotations for (range, collapsed) */ public void testClearAnnotations() { // cover the 'owd', offsets 4 to 7 useCaretAnnotations("W:w"); useSelection(4, 7); // clear ranged annotations //useDocument("howdy", "A:a:5:7", "X:x:4:7", "Y:y:3:8"); //assertFalse(clearAnnotationsOverSelection(buildContext(), "?")); //assertTrue(clearAnnotationsOverSelection(buildContext(), "Q", "X", "Y")); //assertNull(doc.getAnnotation(start, "X")); //assertEquals(doc.firstAnnotationChange(start, end + 1, "Y", null), end); useDocument("howdy", "A:a:5:7", "X:x:4:7", "Y:y:3:8"); assertFalse(clearAnnotationsOverRange(doc, caret, new String[]{"?"}, start, end)); assertTrue(clearAnnotationsOverRange(doc, caret, new String[]{"Q", "X", "Y"}, start, end)); assertNull(doc.getAnnotation(start, "X")); assertEquals(doc.firstAnnotationChange(start, end + 1, "Y", null), end); // clear over caret annotation useSelection(5, 5); //assertFalse(clearAnnotationsOverSelection(buildContext(), "P")); // clearing a direct annotation: useDocument("howdy", "A:a:5:7", "X:x:4:7", "Y:y:3:8"); assertTrue(clearAnnotationsOverRange(doc, caret, new String[]{"Y"}, start, end)); assertTrue(caret.hasAnnotation("Y")); assertNull(caret.getAnnotation("Y")); // clearing one that was already a caret annotation: useDocument("howdy", "A:a:5:7", "X:x:4:7", "Y:y:3:8"); assertTrue(clearAnnotationsOverRange(doc, caret, new String[]{"W"}, start, end)); assertTrue(caret.hasAnnotation("W")); assertNull(caret.getAnnotation("W")); } /** Check whether caret annotations are applied correctly. */ public void testSupplementAnnotationsRightAligned() { // NOTE(patcoleman): currently, nothing happens on supplementing annotations from the left, as // the behaviour of annotations is to do this anyway. If this behaviour changes, a test // should be added to test left-alignment too. useDocument("howdy", "A:a:5:7", "X:x:2:8"); useCaretAnnotations("X:?", "Y:y"); // caret annotation already exists, don't supplement: StringSet keys = CollectionUtils.createStringSet(); keys.add("X"); supplementAnnotations(doc, caret, keys, 5, false); assertTrue(caret.hasAnnotation("X")); assertEquals("?", caret.getAnnotation("X")); // keeps the caret annotation assertTrue(caret.hasAnnotation("Y")); // subset of annotations are supplemented, make sure new value is right: caret.removeAnnotation("X"); keys.add("A"); supplementAnnotations(doc, caret, keys, 5, false); assertFalse(caret.hasAnnotation("X")); assertTrue(caret.hasAnnotation("A")); assertEquals("a", caret.getAnnotation("A")); } }