/* * Copyright 2011 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 com.google.web.bindery.autobean.shared; import com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable; import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl; import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder; import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; import com.google.web.bindery.autobean.shared.impl.SplittableList; import com.google.web.bindery.autobean.shared.impl.SplittableSimpleMap; import com.google.web.bindery.autobean.shared.impl.StringQuoter; import com.google.gwt.core.client.GWT; import com.google.gwt.junit.client.GWTTestCase; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; /** * Tests for the underlying Splittable implementation. This test class is not * indicative of code that users would write, it's simply doing spot-checks of * functionality that AbstractAutoBean depends on. */ public class SplittableTest extends GWTTestCase { /** * */ private static final EncodeState testState = EncodeState.forTesting(); @Override public String getModuleName() { return "com.google.web.bindery.autobean.AutoBean"; } public void testBasicProperties() { Splittable data = StringQuoter.split("{\"a\":true, \"b\":3, \"c\":\"string\", \"d\":null}"); assertTrue("isBoolean", data.get("a").isBoolean()); assertTrue("asBoolean", data.get("a").asBoolean()); assertTrue("isNumber", data.get("b").isNumber()); assertEquals(3.0, data.get("b").asNumber()); assertTrue("isString", data.get("c").isString()); assertEquals("string", data.get("c").asString()); assertTrue("isNull", data.isNull("d")); assertNull("should be null", data.get("d")); } /** * Ensure that hashcodes don't leak into the payload. */ public void testHashCode() { Splittable data = StringQuoter.split("{\"a\":\"b\"}"); int hash = data.hashCode(); String payload = data.getPayload(); assertFalse(payload, payload.contains("$H")); assertFalse(payload, payload.contains(String.valueOf(hash))); assertEquals(hash, data.hashCode()); } /** * Splittables are implemented by a couple of different concrete types. We'll * use this method to make sure that the correct implementation type is being * used in various circumstances. */ public void testImplementationChoice() { Splittable s = StringQuoter.split("[1,false,\"true\"]"); if (GWT.isScript()) { assertTrue("s should be JsoSplittable", s instanceof JsoSplittable); assertTrue("s[0] should be JsoSplittable", s.get(0) instanceof JsoSplittable); assertTrue("s[1] should be JsoSplittable", s.get(1) instanceof JsoSplittable); assertTrue("s[2] should be JsoSplittable", s.get(2) instanceof JsoSplittable); } else { // Using the same types in both pure-JRE and DevMode to avoid JSNI // overhead assertTrue("s should be JsonSplittable", s.getClass().getName().endsWith("JsonSplittable")); assertTrue("s[0] should be JsonSplittable", s.get(0).getClass().getName().endsWith( "JsonSplittable")); assertTrue("s[1] should be JsonSplittable", s.get(1).getClass().getName().endsWith( "JsonSplittable")); assertTrue("s[2] should be JsonSplittable", s.get(2).getClass().getName().endsWith( "JsonSplittable")); } } public void testIndexed() { Splittable s = StringQuoter.createIndexed(); assertTrue(s.isIndexed()); assertFalse(s.isKeyed()); assertFalse(s.isString()); assertEquals(0, s.size()); string("foo").assign(s, 0); string("bar").assign(s, 1); string("baz").assign(s, 2); assertEquals(3, s.size()); assertEquals("[\"foo\",\"bar\",\"baz\"]", s.getPayload()); string("quux").assign(s, 1); assertEquals("[\"foo\",\"quux\",\"baz\"]", s.getPayload()); Splittable s2 = s.deepCopy(); assertNotSame(s, s2); assertEquals(s.size(), s2.size()); for (int i = 0, j = s.size(); i < j; i++) { assertEquals(s.get(i).asString(), s2.get(i).asString()); } s.setSize(2); assertEquals(2, s.size()); assertEquals("[\"foo\",\"quux\"]", s.getPayload()); // Make sure reified values aren't in the payload Object o = new Object(); s.setReified("reified", o); assertFalse(s.getPayload().contains("reified")); assertFalse(s.getPayload().contains("__s")); assertSame(o, s.getReified("reified")); } public void testKeyed() { Splittable s = StringQuoter.createSplittable(); assertFalse(s.isIndexed()); assertTrue(s.isKeyed()); assertFalse(s.isString()); assertTrue(s.getPropertyKeys().isEmpty()); string("bar").assign(s, "foo"); string("quux").assign(s, "baz"); // Actual iteration order is undefined assertEquals(new HashSet<String>(Arrays.asList("foo", "baz")), new HashSet<String>(s .getPropertyKeys())); assertFalse(s.isNull("foo")); assertTrue(s.isNull("bar")); assertEquals("bar", s.get("foo").asString()); assertEquals("quux", s.get("baz").asString()); String payload = s.getPayload(); assertTrue(payload.startsWith("{")); assertTrue(payload.endsWith("}")); assertTrue(payload.contains("\"foo\":\"bar\"")); assertTrue(payload.contains("\"baz\":\"quux\"")); Splittable s2 = s.deepCopy(); assertNotSame(s, s2); assertEquals("bar", s2.get("foo").asString()); assertEquals("quux", s2.get("baz").asString()); // Make sure reified values aren't in the payload Object o = new Object(); s.setReified("reified", o); assertFalse("Should not see reified in " + s.getPayload(), s.getPayload().contains("reified")); assertFalse("Should not see __s in " + s.getPayload(), s.getPayload().contains("__s")); assertSame(o, s.getReified("reified")); } public void testNested() { Splittable s = StringQuoter.split("{\"a\":{\"foo\":\"bar\"}}"); Splittable a = s.get("a"); assertNotNull(a); assertEquals("bar", a.get("foo").asString()); assertSame(a, s.get("a")); assertEquals(a, s.get("a")); } /** * Tests attributes of the {@link Splittable#NULL} field. */ public void testNull() { Splittable n = Splittable.NULL; if (GWT.isScript()) { assertNull(n); } else { assertNotNull(n); } assertFalse("boolean", n.isBoolean()); assertFalse("indexed", n.isIndexed()); assertFalse("keyed", n.isKeyed()); assertFalse("string", n.isString()); assertEquals("null", n.getPayload()); } /** * Extra tests in here due to potential to confuse {@code false} and * {@code null} values. */ public void testSplittableListBoolean() { Coder boolCoder = AutoBeanCodexImpl.valueCoder(Boolean.class); Splittable s = StringQuoter.createIndexed(); bool(false).assign(s, 0); assertFalse("0 should not be null", s.isNull(0)); assertTrue("s[0] should be a boolean", s.get(0).isBoolean()); assertFalse("s[0] should be false", s.get(0).asBoolean()); assertNotNull("Null decode", ValueCodex.decode(Boolean.class, s.get(0))); Object decodedBoolean = boolCoder.decode(testState, s.get(0)); assertNotNull("decode should not return null", decodedBoolean); assertFalse("decoded value should be false", (Boolean) decodedBoolean); bool(true).assign(s, 1); assertTrue("s[1] should be a boolean", s.get(1).isBoolean()); assertTrue("s[1] should be true", s.get(1).asBoolean()); assertTrue("boolCoder 1", (Boolean) boolCoder.decode(testState, s.get(1))); Splittable.NULL.assign(s, 2); assertTrue("3 should be null", s.isNull(3)); assertEquals("payload", "[false,true,null]", s.getPayload()); List<Boolean> boolList = new SplittableList<Boolean>(s, boolCoder, testState); assertEquals("boolList", Arrays.<Boolean> asList(false, true, null), boolList); } /** * Extra tests in here due to potential to confuse 0 and {@code null} values. */ public void testSplittableListNumbers() { Coder intCoder = AutoBeanCodexImpl.valueCoder(Integer.class); Coder doubleCoder = AutoBeanCodexImpl.valueCoder(Double.class); Splittable s = StringQuoter.createIndexed(); number(0).assign(s, 0); assertFalse("0 should not be null", s.isNull(0)); assertTrue("s[0] should be a number", s.get(0).isNumber()); assertNotNull("Null decode", ValueCodex.decode(Integer.class, s.get(0))); Object decodedInt = intCoder.decode(testState, s.get(0)); assertNotNull("decode should not return null", decodedInt); assertEquals("intCoder 0", Integer.valueOf(0), decodedInt); assertEquals("doubleCoder 0", Double.valueOf(0), doubleCoder.decode(testState, s.get(0))); number(3.141592).assign(s, 1); assertEquals("intCoder 1", Integer.valueOf(3), intCoder.decode(testState, s.get(1))); assertEquals("doubleCoder 1", Double.valueOf(3.141592), doubleCoder.decode(testState, s.get(1))); number(42).assign(s, 2); Splittable.NULL.assign(s, 3); assertTrue("3 should be null", s.isNull(3)); assertEquals("payload", "[0,3.141592,42,null]", s.getPayload()); List<Double> doubleList = new SplittableList<Double>(s, doubleCoder, testState); assertEquals(Double.valueOf(0), doubleList.get(0)); assertEquals("doubleList", Arrays.<Double> asList(0d, 3.141592, 42d, null), doubleList); // Don't share backing data between lists s = StringQuoter.split("[0,3.141592,42,null]"); List<Integer> intList = new SplittableList<Integer>(s, intCoder, testState); assertEquals("intList", Arrays.<Integer> asList(0, 3, 42, null), intList); } public void testSplittableListString() { Splittable data = StringQuoter.split("[\"Hello\",\"World\"]"); SplittableList<String> list = new SplittableList<String>(data, AutoBeanCodexImpl.valueCoder(String.class), testState); assertEquals(2, list.size()); assertEquals(Arrays.asList("Hello", "World"), list); list.set(0, "Goodbye"); assertEquals(Arrays.asList("Goodbye", "World"), list); assertEquals("[\"Goodbye\",\"World\"]", data.getPayload()); list.remove(0); assertEquals(Arrays.asList("World"), list); assertEquals("[\"World\"]", data.getPayload()); list.add("Wide"); list.add("Web"); assertEquals(Arrays.asList("World", "Wide", "Web"), list); assertEquals("[\"World\",\"Wide\",\"Web\"]", data.getPayload()); } public void testSplittableMapStringString() { Splittable data = StringQuoter.split("{\"foo\":\"bar\",\"baz\":\"quux\",\"isNull\":null}"); assertTrue("isNull should be null", data.isNull("isNull")); assertFalse("isNull should not be undefined", data.isUndefined("isNull")); Map<String, String> map = new SplittableSimpleMap<String, String>(data, AutoBeanCodexImpl.valueCoder(String.class), AutoBeanCodexImpl.valueCoder(String.class), testState); assertEquals(3, map.size()); assertEquals("bar", map.get("foo")); assertEquals("quux", map.get("baz")); assertTrue("Map should have isNull key", map.containsKey("isNull")); assertNull(map.get("isNull")); assertFalse("Map should not have unknown key", map.containsKey("unknown")); map.put("bar", "foo2"); assertEquals("foo2", map.get("bar")); String payload = data.getPayload(); assertTrue(payload.contains("\"bar\":\"foo2\"")); assertTrue(payload.contains("\"isNull\":null")); } public void testString() { Splittable s = string("Hello '\" World!"); assertFalse(s.isIndexed()); assertFalse(s.isKeyed()); assertTrue(s.isString()); assertEquals("Hello '\" World!", s.asString()); assertEquals("\"Hello '\\\" World!\"", s.getPayload()); } public void testStringEmpty() { Splittable s = string(""); assertFalse(s.isIndexed()); assertFalse(s.isKeyed()); assertTrue(s.isString()); assertEquals("", s.asString()); assertEquals("\"\"", s.getPayload()); } private Splittable bool(boolean value) { return StringQuoter.split(String.valueOf(value)); } private Splittable number(double number) { return StringQuoter.split(String.valueOf(number)); } private Splittable string(String value) { return StringQuoter.split(StringQuoter.quote(value)); } }