/** * Copyright 2010 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.model.document.util; import static org.waveprotocol.wave.model.document.util.DocOpScrub.CHARS_TO_LEAVE; import static org.waveprotocol.wave.model.document.util.DocOpScrub.JIA; import static org.waveprotocol.wave.model.document.util.DocOpScrub.PSI; import static org.waveprotocol.wave.model.document.util.DocOpScrub.WO; import junit.framework.TestCase; import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap; import org.waveprotocol.wave.model.document.operation.Attributes; import org.waveprotocol.wave.model.document.operation.AttributesUpdate; import org.waveprotocol.wave.model.document.operation.DocOp; import org.waveprotocol.wave.model.document.operation.impl.AttributesImpl; import org.waveprotocol.wave.model.document.operation.impl.AttributesUpdateImpl; import org.waveprotocol.wave.model.document.operation.impl.DocOpBuilder; import org.waveprotocol.wave.model.document.util.DocOpScrub.UncheckedAnnotationBoundary; import org.waveprotocol.wave.model.operation.OpComparators; /** * @author danilatos@google.com (Daniel Danilatos) */ public class DocOpScrubTest extends TestCase { static { DocOpScrub.setShouldScrubByDefault(false); } // Sprinkling from a few random character ranges static final String VALID = " abcDEF^#!(_+90" + "\u03e0\u042e\u6202\u05f3\u0610\u0710\u3045\u00ee\u0175\u0a00"; static final String VALID_SCRUBBED = "aaaaaaaaaaaaaaa" + PSI + JIA + WO + "HA?Jbc?"; static final String INVALID = "\u200e\u200f\u202a\u202e\u0000\u001f\u007f\u009f\u206a\u206f" + "\ud800\udbff\udc00\udfff\uffff\ufffe\ufdd0\ufdef"; static final String INVALID_SCRUBBED = "||||^^^^##<<>>!!!!"; static final String BOTH = VALID + INVALID; static final String BOTH_SCRUBBED = VALID_SCRUBBED + INVALID_SCRUBBED; static final String VALID_PARTIAL = leaveInitial(VALID, VALID_SCRUBBED); static final String INVALID_PARTIAL = INVALID_SCRUBBED; // Always scrub invalid characters static final Attributes ATTRS = AttributesImpl.fromUnsortedPairsUnchecked( VALID, VALID + "x", INVALID, INVALID + "y", "w", "a", // test robust against duplicate keys (ill-formed op proto) "w", "a", "xxxa", "a", // test multiple similar keys to ensure they are unique-ified "xxxb", "b", "xxxc", "c", "joe.bloe@example.com", "john.smith@foobar.net", "@", "@x", "a@b", "c@d", "a@@@b@@@c", "d@@e", "b", "bb", "", ""); static final Attributes ATTRS_SCRUBBED = AttributesImpl.fromUnsortedPairsUnchecked( VALID_PARTIAL, VALID_PARTIAL +"a", INVALID_PARTIAL, INVALID_PARTIAL + "a", "w", "a", "w", "a", "xxxa", "a", // test multiple similar keys to ensure they are unique-ified "xxxa_1", "b", "xxxa_2", "c", "aaaaaaaa@aaaaaaaaaaa", "aaaaaaaaaa@aaaaaaaaaa", "@", "@a", "a@a", "a@a", "a@@aaaaaa", "d@@a", "b", "bb", "", ""); static final AttributesUpdate ATTRSUP = AttributesUpdateImpl.fromUnsortedTripletsUnchecked( VALID, VALID + "w", VALID + "x", INVALID, INVALID + "y", INVALID + "z", "w", "a", "b", // test robust against duplicate keys (ill-formed op proto) "w", "a", "b", "xxxa", "a", "aa", // test multiple similar keys to ensure they are unique-ified "xxxb", "b", "bb", "xxxc", "c", "cc", "joe.bloe@example.com", "john.smith@foobar.net", "joe.shmoe@fubar.org", "a", null, "x@", "@", "@x", null, "a@b", "c@d", "e@f", "a@@@b@@@c", "d@@e", "@@", "b", "bb", "ccc", "", "", ""); static final AttributesUpdate ATTRSUP_SCRUBBED = AttributesUpdateImpl.fromUnsortedTripletsUnchecked( VALID_PARTIAL, VALID_PARTIAL + "a", VALID_PARTIAL + "a", INVALID_PARTIAL, INVALID_PARTIAL + "a", INVALID_PARTIAL + "a", "w", "a", "b", // test duplicate keys (ill-formed op) to ensure they are unique-ified "w", "a", "b", "xxxa", "a", "aa", // test multiple similar keys to ensure they are unique-ified "xxxa_1", "b", "bb", "xxxa_2", "c", "cc", "aaaaaaaa@aaaaaaaaaaa", "aaaaaaaaaa@aaaaaaaaaa", "aaaaaaaaa@aaaaaaaaa", "a", null, "a@", "@", "@a", null, "a@a", "a@a", "a@a", "a@@aaaaaa", "d@@a", "@@", "b", "bb", "ccc", "", "", ""); static final AnnotationBoundaryMap ANNO = new UncheckedAnnotationBoundary(new String[]{ "x", "y", VALID, "y", null, INVALID, "", "", null, "/qwerty", "/qwerty/", "//qwert/y", VALID + "/" + INVALID + "/c@@b//c@b/johno@example.com", "x", "y", "w", "a", "b", // test robust against duplicate keys (ill-formed op proto) "w", "a", "b", "xxxa", "a", "aa", // test multiple similar keys to ensure they are unique-ified "xxxb", "b", "bb", "xxxc", "c", "cc", }, new String[] { "x", // test robust against non-empty intersection of change & end keys "ww", // test duplicate keys (ill-formed op) to ensure they are unique-ified "ww", "yyya", // test multiple similar keys to ensure they are unique-ified "yyyb", "yyyc", VALID + "/" + INVALID + "/c@@b/c@b/johno@example.com/" }); static final AnnotationBoundaryMap ANNO_SCRUBBED = new UncheckedAnnotationBoundary(new String[]{ "x", "y", VALID_PARTIAL, "y", null, INVALID_PARTIAL, "", "", null, "/qweaaa", "/qwaaaaa", "//qaaaaaa", // slashes only meaningful for keys VALID_PARTIAL + "/" + INVALID_PARTIAL + "/c@@a//a@a/aaaaa@aaaaaaaaaaa", "x", "y", "w", "a", "b", // test duplicate keys (ill-formed op) to ensure they are unique-ified "w", "a", "b", "xxxa", "a", "aa", // test multiple similar keys to ensure they are unique-ified "xxxa_3", "b", "bb", "xxxa_4", "c", "cc", }, new String[] { "x", "ww", "ww", "yyya", "yyya_1", "yyya_2", VALID_PARTIAL + "/" + INVALID_PARTIAL + "/c@@a/a@a/aaaaa@aaaaaaaaaaa/" }); public void testScrubs() { DocOpBuilder b1 = new DocOpBuilder(); DocOpBuilder b2 = new DocOpBuilder(); b1.characters(VALID); b2.characters(VALID_SCRUBBED); b1.deleteCharacters(VALID); b2.deleteCharacters(VALID_SCRUBBED); b1.characters(INVALID); b2.characters(INVALID_SCRUBBED); b1.deleteCharacters(INVALID); b2.deleteCharacters(INVALID_SCRUBBED); b1.elementStart("abc", ATTRS); b2.elementStart("abc", ATTRS_SCRUBBED); b1.elementEnd(); b2.elementEnd(); b1.deleteElementStart("abc", ATTRS); b2.deleteElementStart("abc", ATTRS_SCRUBBED); b1.deleteElementEnd(); b2.deleteElementEnd(); b1.retain(5); b2.retain(5); b1.replaceAttributes(ATTRS, ATTRS); b2.replaceAttributes(ATTRS_SCRUBBED, ATTRS_SCRUBBED); b1.updateAttributes(ATTRSUP); b2.updateAttributes(ATTRSUP_SCRUBBED); b1.annotationBoundary(ANNO); b2.annotationBoundary(ANNO_SCRUBBED); DocOp o1 = DocOpScrub.scrub(b1.buildUnchecked()); DocOp o2 = b2.buildUnchecked(); assertTrue("\n" + o1 + "\n but expected \n" + o2, OpComparators.SYNTACTIC_IDENTITY.equal(o1, o2)); } static String leaveInitial(String original, String scrubbed) { return original.substring(0, DocOpScrub.CHARS_TO_LEAVE) + scrubbed.substring(CHARS_TO_LEAVE); } }