Move AutoBean package to com.google.web.bindery.autobean package. http://code.google.com/p/google-web-toolkit/issues/detail?id=6253 Review at http://gwt-code-reviews.appspot.com/1414803 Patch by: bobv Review by: rjrjr git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10007 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/test/com/google/web/bindery/autobean/AutoBeanSuite.java b/user/test/com/google/web/bindery/autobean/AutoBeanSuite.java new file mode 100644 index 0000000..387dc9d --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/AutoBeanSuite.java
@@ -0,0 +1,43 @@ +/* + * 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 com.google.web.bindery.autobean; + +import com.google.web.bindery.autobean.gwt.client.AutoBeanTest; +import com.google.web.bindery.autobean.shared.AutoBeanCodexTest; +import com.google.web.bindery.autobean.shared.SplittableTest; +import com.google.web.bindery.autobean.vm.AutoBeanCodexJreTest; +import com.google.web.bindery.autobean.vm.AutoBeanJreTest; +import com.google.web.bindery.autobean.vm.SplittableJreTest; +import com.google.gwt.junit.tools.GWTTestSuite; + +import junit.framework.Test; + +/** + * Tests of the Editor framework. These tests focus on core Editor behaviors, + * rather than on integration with backing stores. + */ +public class AutoBeanSuite { + public static Test suite() { + GWTTestSuite suite = new GWTTestSuite("Test suite for AutoBean functions"); + suite.addTestSuite(AutoBeanCodexJreTest.class); + suite.addTestSuite(AutoBeanCodexTest.class); + suite.addTestSuite(AutoBeanJreTest.class); + suite.addTestSuite(AutoBeanTest.class); + suite.addTestSuite(SplittableJreTest.class); + suite.addTestSuite(SplittableTest.class); + return suite; + } +}
diff --git a/user/test/com/google/web/bindery/autobean/gwt/client/AutoBeanTest.java b/user/test/com/google/web/bindery/autobean/gwt/client/AutoBeanTest.java new file mode 100644 index 0000000..3124590 --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/gwt/client/AutoBeanTest.java
@@ -0,0 +1,568 @@ +/* + * 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 com.google.web.bindery.autobean.gwt.client; + +import com.google.web.bindery.autobean.shared.AutoBean; +import com.google.web.bindery.autobean.shared.AutoBeanFactory; +import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category; +import com.google.web.bindery.autobean.shared.AutoBeanUtils; +import com.google.web.bindery.autobean.shared.AutoBeanVisitor; +import com.google.web.bindery.autobean.shared.AutoBeanVisitor.ParameterizationVisitor; +import com.google.gwt.core.client.GWT; +import com.google.gwt.junit.client.GWTTestCase; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * Tests runtime behavior of AutoBean framework. + */ +public class AutoBeanTest extends GWTTestCase { + + /** + * Static implementation of {@link HasCall}. + */ + public static class CallImpl { + public static Object seen; + + public static <T> T __intercept(AutoBean<HasCall> bean, T value) { + seen = value; + return value; + } + + public static int add(AutoBean<HasCall> bean, int a, int b) { + assertNotNull(bean); + return ((Integer) bean.getTag("offset")) + a + b; + } + } + + /** + * The factory being tested. + */ + @Category(CallImpl.class) + protected interface Factory extends AutoBeanFactory { + AutoBean<HasBoolean> hasBoolean(); + + AutoBean<HasCall> hasCall(); + + AutoBean<HasChainedSetters> hasChainedSetters(); + + AutoBean<HasList> hasList(); + + AutoBean<HasComplexTypes> hasListOfList(); + + AutoBean<HasMoreChainedSetters> hasMoreChainedSetters(); + + AutoBean<Intf> intf(); + + AutoBean<Intf> intf(RealIntf wrapped); + + AutoBean<OtherIntf> otherIntf(); + } + + interface HasBoolean { + boolean getGet(); + + boolean hasHas(); + + boolean isIs(); + + void setGet(boolean value); + + void setHas(boolean value); + + void setIs(boolean value); + } + + interface HasCall { + int add(int a, int b); + } + + interface HasChainedSetters { + int getInt(); + + String getString(); + + HasChainedSetters setInt(int value); + + HasChainedSetters setString(String value); + } + + interface HasComplexTypes { + List<List<Intf>> getList(); + + List<Map<String, Intf>> getListOfMap(); + + Map<Map<String, String>, List<List<Intf>>> getMap(); + } + + interface HasList { + List<Intf> getList(); + + void setList(List<Intf> list); + } + + interface HasMoreChainedSetters extends HasChainedSetters { + boolean isBoolean(); + + HasMoreChainedSetters setBoolean(boolean value); + + HasMoreChainedSetters setInt(int value); + } + + interface Intf { + int getInt(); + + String getString(); + + void setInt(int number); + + void setString(String value); + } + + interface OtherIntf { + HasBoolean getHasBoolean(); + + Intf getIntf(); + + UnreferencedInFactory getUnreferenced(); + + void setHasBoolean(HasBoolean value); + + void setIntf(Intf intf); + } + + static class RealIntf implements Intf { + int i; + String string; + + @Override + public boolean equals(Object o) { + return (o instanceof Intf) && (((Intf) o).getInt() == getInt()); + } + + public int getInt() { + return i; + } + + public String getString() { + return string; + } + + @Override + public int hashCode() { + return i; + } + + public void setInt(int number) { + this.i = number; + } + + public void setString(String value) { + this.string = value; + } + + public String toString() { + return "toString"; + } + } + + interface UnreferencedInFactory { + } + + private static class ParameterizationTester extends ParameterizationVisitor { + private final StringBuilder sb; + private Stack<Boolean> isOpen = new Stack<Boolean>(); + + private ParameterizationTester(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void endVisitType(Class<?> type) { + if (isOpen.pop()) { + sb.append(">"); + } + } + + @Override + public boolean visitParameter() { + if (isOpen.peek()) { + sb.append(","); + } else { + sb.append("<"); + isOpen.pop(); + isOpen.push(true); + } + return true; + } + + @Override + public boolean visitType(Class<?> type) { + sb.append(type.getName()); + isOpen.push(false); + return true; + } + } + + protected Factory factory; + + @Override + public String getModuleName() { + return "com.google.web.bindery.autobean.AutoBean"; + } + + public void testBooleanIsHasMethods() { + HasBoolean b = factory.hasBoolean().as(); + assertFalse(b.getGet()); + assertFalse(b.hasHas()); + assertFalse(b.isIs()); + + b.setGet(true); + b.setHas(true); + b.setIs(true); + + assertTrue(b.getGet()); + assertTrue(b.hasHas()); + assertTrue(b.isIs()); + } + + public void testCategory() { + AutoBean<HasCall> call = factory.hasCall(); + call.setTag("offset", 1); + assertEquals(6, call.as().add(2, 3)); + assertEquals(6, CallImpl.seen); + } + + public void testChainedSetters() { + AutoBean<HasChainedSetters> bean = factory.hasChainedSetters(); + bean.as().setInt(42).setString("Blah"); + assertEquals(42, bean.as().getInt()); + assertEquals("Blah", bean.as().getString()); + + AutoBean<HasMoreChainedSetters> more = factory.hasMoreChainedSetters(); + more.as().setInt(42).setBoolean(true).setString("Blah"); + assertEquals(42, more.as().getInt()); + assertTrue(more.as().isBoolean()); + assertEquals("Blah", more.as().getString()); + } + + public void testDiff() { + AutoBean<Intf> a1 = factory.intf(); + AutoBean<Intf> a2 = factory.intf(); + + assertTrue(AutoBeanUtils.diff(a1, a2).isEmpty()); + + a2.as().setInt(42); + Map<String, Object> diff = AutoBeanUtils.diff(a1, a2); + assertEquals(1, diff.size()); + assertEquals(42, diff.get("int")); + } + + public void testDiffWithListPropertyAssignment() { + AutoBean<HasList> a1 = factory.hasList(); + AutoBean<HasList> a2 = factory.hasList(); + + assertTrue(AutoBeanUtils.diff(a1, a2).isEmpty()); + + List<Intf> l1 = new ArrayList<Intf>(); + a1.as().setList(l1); + List<Intf> l2 = new ArrayList<Intf>(); + a2.as().setList(l2); + + assertTrue(AutoBeanUtils.diff(a1, a2).isEmpty()); + + l2.add(factory.intf().as()); + Map<String, Object> diff = AutoBeanUtils.diff(a1, a2); + assertEquals(1, diff.size()); + assertEquals(l2, diff.get("list")); + + l1.add(l2.get(0)); + assertTrue(AutoBeanUtils.diff(a1, a2).isEmpty()); + } + + public void testDynamicMethods() { + AutoBean<Intf> intf = factory.create(Intf.class); + assertNotNull(intf); + + RealIntf real = new RealIntf(); + real.i = 42; + intf = factory.create(Intf.class, real); + assertNotNull(intf); + assertEquals(42, intf.as().getInt()); + } + + public void testEquality() { + AutoBean<Intf> a1 = factory.intf(); + AutoBean<Intf> a2 = factory.intf(); + + assertNotSame(a1, a2); + assertFalse(a1.equals(a2)); + + // Make sure as() is stable + assertSame(a1.as(), a1.as()); + assertEquals(a1.as(), a1.as()); + + // When wrapping, use underlying object's equality + RealIntf real = new RealIntf(); + real.i = 42; + AutoBean<Intf> w = factory.intf(real); + // AutoBean interface never equals wrapped object + assertFalse(w.equals(real)); + // Wrapper interface should delegate hashCode(), equals(), and toString() + assertEquals(real.hashCode(), w.as().hashCode()); + assertEquals(real, w.as()); + assertEquals(real.toString(), w.as().toString()); + assertEquals(w.as(), real); + } + + public void testFactory() { + AutoBean<Intf> auto = factory.intf(); + assertSame(factory, auto.getFactory()); + } + + public void testFreezing() { + AutoBean<Intf> auto = factory.intf(); + Intf intf = auto.as(); + intf.setInt(42); + auto.setFrozen(true); + try { + intf.setInt(55); + fail("Should have thrown an exception"); + } catch (IllegalStateException expected) { + } + + assertTrue(auto.isFrozen()); + assertEquals(42, intf.getInt()); + } + + public void testNested() { + AutoBean<OtherIntf> auto = factory.otherIntf(); + OtherIntf other = auto.as(); + + assertNull(other.getIntf()); + + Intf intf = new RealIntf(); + intf.setString("Hello world!"); + other.setIntf(intf); + Intf retrieved = other.getIntf(); + assertEquals("Hello world!", retrieved.getString()); + assertNotNull(AutoBeanUtils.getAutoBean(retrieved)); + } + + public void testParameterizationVisitor() { + AutoBean<HasComplexTypes> auto = factory.hasListOfList(); + auto.accept(new AutoBeanVisitor() { + int count = 0; + + @Override + public void endVisit(AutoBean<?> bean, Context ctx) { + assertEquals(3, count); + } + + @Override + public void endVisitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, + CollectionPropertyContext ctx) { + check(propertyName, ctx); + } + + @Override + public void endVisitMapProperty(String propertyName, AutoBean<Map<?, ?>> value, + MapPropertyContext ctx) { + check(propertyName, ctx); + } + + private void check(String propertyName, PropertyContext ctx) { + count++; + StringBuilder sb = new StringBuilder(); + ctx.accept(new ParameterizationTester(sb)); + + if ("list".equals(propertyName)) { + // List<List<Intf>> + assertEquals(List.class.getName() + "<" + List.class.getName() + "<" + + Intf.class.getName() + ">>", sb.toString()); + } else if ("listOfMap".equals(propertyName)) { + // List<Map<String, Intf>> + assertEquals(List.class.getName() + "<" + Map.class.getName() + "<" + + String.class.getName() + "," + Intf.class.getName() + ">>", sb.toString()); + } else if ("map".equals(propertyName)) { + // Map<Map<String, String>, List<List<Intf>>> + assertEquals(Map.class.getName() + "<" + Map.class.getName() + "<" + + String.class.getName() + "," + String.class.getName() + ">," + List.class.getName() + + "<" + List.class.getName() + "<" + Intf.class.getName() + ">>>", sb.toString()); + } else { + throw new RuntimeException(propertyName); + } + } + }); + } + + /** + * Make sure primitive properties can be returned. + */ + public void testPrimitiveProperty() { + AutoBean<Intf> auto = factory.intf(); + Intf intf = auto.as(); + + assertNull(intf.getString()); + intf.setString("Hello world!"); + assertEquals("Hello world!", intf.getString()); + + assertEquals(0, intf.getInt()); + intf.setInt(42); + assertEquals(42, intf.getInt()); + } + + public void testTags() { + AutoBean<Intf> auto = factory.intf(); + auto.setTag("test", 42); + assertEquals(42, auto.getTag("test")); + } + + public void testTraversal() { + final AutoBean<OtherIntf> other = factory.otherIntf(); + final AutoBean<Intf> intf = factory.intf(); + final AutoBean<HasBoolean> hasBoolean = factory.hasBoolean(); + other.as().setIntf(intf.as()); + other.as().setHasBoolean(hasBoolean.as()); + intf.as().setInt(42); + hasBoolean.as().setGet(true); + hasBoolean.as().setHas(true); + hasBoolean.as().setIs(true); + + class Checker extends AutoBeanVisitor { + boolean seenHasBoolean; + boolean seenIntf; + boolean seenOther; + + @Override + public void endVisitReferenceProperty(String propertyName, AutoBean<?> value, + PropertyContext ctx) { + if ("hasBoolean".equals(propertyName)) { + assertSame(hasBoolean, value); + assertEquals(HasBoolean.class, ctx.getType()); + } else if ("intf".equals(propertyName)) { + assertSame(intf, value); + assertEquals(Intf.class, ctx.getType()); + } else if ("unreferenced".equals(propertyName)) { + assertNull(value); + assertEquals(UnreferencedInFactory.class, ctx.getType()); + } else { + fail("Unexpecetd property " + propertyName); + } + } + + @Override + public void endVisitValueProperty(String propertyName, Object value, PropertyContext ctx) { + if ("int".equals(propertyName)) { + assertEquals(42, value); + assertEquals(int.class, ctx.getType()); + } else if ("string".equals(propertyName)) { + assertNull(value); + assertEquals(String.class, ctx.getType()); + } else if ("get".equals(propertyName) || "has".equals(propertyName) + || "is".equals(propertyName)) { + assertEquals(boolean.class, ctx.getType()); + assertTrue((Boolean) value); + } else { + fail("Unknown value property " + propertyName); + } + } + + @Override + public boolean visit(AutoBean<?> bean, Context ctx) { + if (bean == hasBoolean) { + seenHasBoolean = true; + } else if (bean == intf) { + seenIntf = true; + } else if (bean == other) { + seenOther = true; + } else { + fail("Unknown AutoBean"); + } + return true; + } + + void check() { + assertTrue(seenHasBoolean); + assertTrue(seenIntf); + assertTrue(seenOther); + } + } + Checker c = new Checker(); + other.accept(c); + c.check(); + } + + public void testType() { + assertEquals(Intf.class, factory.intf().getType()); + } + + /** + * Ensure that a totally automatic bean can't be unwrapped, since the + * generated mapper depends on the AutoBean. + */ + public void testUnwrappingSimpleBean() { + AutoBean<Intf> auto = factory.intf(); + try { + auto.unwrap(); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testWrapped() { + RealIntf real = new RealIntf(); + AutoBean<Intf> auto = factory.intf(real); + Intf intf = auto.as(); + + assertNotSame(real, intf); + assertNull(intf.getString()); + assertEquals(0, intf.getInt()); + + real.string = "blah"; + assertEquals("blah", intf.getString()); + real.i = 42; + assertEquals(42, intf.getInt()); + + intf.setString("bar"); + assertEquals("bar", real.string); + + intf.setInt(41); + assertEquals(41, real.i); + + AutoBean<Intf> rewrapped = factory.intf(real); + assertSame(auto, rewrapped); + + // Disconnect the wrapper, make sure it shuts down correctly. + Intf unwrapped = auto.unwrap(); + assertSame(real, unwrapped); + assertNull(AutoBeanUtils.getAutoBean(real)); + try { + intf.setInt(42); + fail("Should have thrown exception"); + } catch (IllegalStateException expected) { + } + } + + @Override + protected void gwtSetUp() throws Exception { + factory = GWT.create(Factory.class); + } +}
diff --git a/user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java b/user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java new file mode 100644 index 0000000..13f69b3 --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java
@@ -0,0 +1,379 @@ +/* + * 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 com.google.web.bindery.autobean.shared; + +import com.google.web.bindery.autobean.shared.AutoBean.PropertyName; +import com.google.web.bindery.autobean.shared.impl.EnumMap; +import com.google.gwt.core.client.GWT; +import com.google.gwt.junit.client.GWTTestCase; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Simple encoding / decoding tests for the AutoBeanCodex. + */ +public class AutoBeanCodexTest extends GWTTestCase { + /** + * Protected so that the JRE-only test can instantiate instances. + */ + protected interface Factory extends AutoBeanFactory { + AutoBean<HasSplittable> hasAutoBean(); + + AutoBean<HasCycle> hasCycle(); + + AutoBean<HasEnum> hasEnum(); + + AutoBean<HasList> hasList(); + + AutoBean<HasMap> hasMap(); + + AutoBean<HasSimple> hasSimple(); + + AutoBean<Simple> simple(); + } + + /* + * These enums are used to verify that a List<Enum> or Map<Enum, Enum> pulls + * in the necessary metadata. + */ + enum EnumReachableThroughList { + FOO_LIST + } + + enum EnumReachableThroughMapKey { + FOO_KEY + } + + enum EnumReachableThroughMapValue { + FOO_VALUE + } + + /** + * Used to test that cycles are detected. + */ + interface HasCycle { + List<HasCycle> getCycle(); + + void setCycle(List<HasCycle> cycle); + } + + interface HasEnum { + MyEnum getEnum(); + + List<MyEnum> getEnums(); + + Map<MyEnum, Integer> getMap(); + + List<EnumReachableThroughList> getParameterizedList(); + + Map<EnumReachableThroughMapKey, EnumReachableThroughMapValue> getParameterizedMap(); + + void setEnum(MyEnum value); + + void setEnums(List<MyEnum> value); + + void setMap(Map<MyEnum, Integer> value); + } + + interface HasList { + List<Integer> getIntList(); + + List<Simple> getList(); + + void setIntList(List<Integer> list); + + void setList(List<Simple> list); + } + + interface HasMap { + Map<Simple, Simple> getComplexMap(); + + Map<Map<String, String>, Map<String, String>> getNestedMap(); + + Map<String, Simple> getSimpleMap(); + + void setComplexMap(Map<Simple, Simple> map); + + void setNestedMap(Map<Map<String, String>, Map<String, String>> map); + + void setSimpleMap(Map<String, Simple> map); + } + + interface HasSimple { + Simple getSimple(); + + void setSimple(Simple s); + } + + interface HasSplittable { + Splittable getSimple(); + + List<Splittable> getSimpleList(); + + Map<Splittable, Splittable> getSplittableMap(); + + Splittable getString(); + + void setSimple(Splittable simple); + + void setSimpleList(List<Splittable> simple); + + void setSplittableMap(Map<Splittable, Splittable> map); + + void setString(Splittable s); + } + + enum MyEnum { + FOO, BAR, + // The eclipse formatter wants to put this annotation inline + @PropertyName("quux") + BAZ; + } + + interface ReachableOnlyFromParameterization extends Simple { + } + + interface Simple { + int getInt(); + + String getString(); + + Boolean hasOtherBoolean(); + + boolean isBoolean(); + + void setBoolean(boolean b); + + void setInt(int i); + + void setOtherBoolean(Boolean b); + + void setString(String s); + } + + protected Factory f; + + @Override + public String getModuleName() { + return "com.google.web.bindery.autobean.AutoBean"; + } + + public void testCycle() { + AutoBean<HasCycle> bean = f.hasCycle(); + bean.as().setCycle(Arrays.asList(bean.as())); + try { + checkEncode(bean); + fail("Should not have encoded"); + } catch (UnsupportedOperationException expected) { + } + } + + public void testEmptyList() { + AutoBean<HasList> bean = f.hasList(); + bean.as().setList(Collections.<Simple> emptyList()); + AutoBean<HasList> decodedBean = checkEncode(bean); + assertNotNull(decodedBean.as().getList()); + assertTrue(decodedBean.as().getList().isEmpty()); + } + + public void testEnum() { + EnumMap map = (EnumMap) f; + assertEquals("BAR", map.getToken(MyEnum.BAR)); + assertEquals("quux", map.getToken(MyEnum.BAZ)); + assertEquals(MyEnum.BAR, map.getEnum(MyEnum.class, "BAR")); + assertEquals(MyEnum.BAZ, map.getEnum(MyEnum.class, "quux")); + + List<MyEnum> arrayValue = Arrays.asList(MyEnum.FOO, MyEnum.BAR, null, MyEnum.BAZ); + Map<MyEnum, Integer> mapValue = new HashMap<MyEnum, Integer>(); + mapValue.put(MyEnum.FOO, 0); + mapValue.put(MyEnum.BAR, 1); + mapValue.put(MyEnum.BAZ, 2); + + AutoBean<HasEnum> bean = f.hasEnum(); + bean.as().setEnum(MyEnum.BAZ); + bean.as().setEnums(arrayValue); + bean.as().setMap(mapValue); + + Splittable split = AutoBeanCodex.encode(bean); + // Make sure the overridden form is always used + assertFalse(split.getPayload().contains("BAZ")); + + AutoBean<HasEnum> decoded = checkEncode(bean); + assertEquals(MyEnum.BAZ, decoded.as().getEnum()); + assertEquals(arrayValue, decoded.as().getEnums()); + assertEquals(mapValue, decoded.as().getMap()); + + assertEquals(MyEnum.BAZ, AutoBeanUtils.getAllProperties(bean).get("enum")); + bean.as().setEnum(null); + assertNull(bean.as().getEnum()); + assertNull(AutoBeanUtils.getAllProperties(bean).get("enum")); + decoded = checkEncode(bean); + assertNull(decoded.as().getEnum()); + } + + /** + * Ensures that enum types that are reachable only through a method + * parameterization are included in the enum map. + */ + public void testEnumReachableOnlyThroughParameterization() { + EnumMap map = (EnumMap) f; + assertEquals("FOO_LIST", map.getToken(EnumReachableThroughList.FOO_LIST)); + assertEquals("FOO_KEY", map.getToken(EnumReachableThroughMapKey.FOO_KEY)); + assertEquals("FOO_VALUE", map.getToken(EnumReachableThroughMapValue.FOO_VALUE)); + assertEquals(EnumReachableThroughList.FOO_LIST, map.getEnum(EnumReachableThroughList.class, + "FOO_LIST")); + assertEquals(EnumReachableThroughMapKey.FOO_KEY, map.getEnum(EnumReachableThroughMapKey.class, + "FOO_KEY")); + assertEquals(EnumReachableThroughMapValue.FOO_VALUE, map.getEnum( + EnumReachableThroughMapValue.class, "FOO_VALUE")); + } + + public void testMap() { + AutoBean<HasMap> bean = f.hasMap(); + Map<String, Simple> map = new HashMap<String, Simple>(); + Map<Simple, Simple> complex = new HashMap<Simple, Simple>(); + bean.as().setSimpleMap(map); + bean.as().setComplexMap(complex); + + for (int i = 0, j = 5; i < j; i++) { + Simple s = f.simple().as(); + s.setInt(i); + map.put(String.valueOf(i), s); + + Simple key = f.simple().as(); + key.setString(String.valueOf(i)); + complex.put(key, s); + } + + AutoBean<HasMap> decoded = checkEncode(bean); + map = decoded.as().getSimpleMap(); + complex = decoded.as().getComplexMap(); + assertEquals(5, map.size()); + for (int i = 0, j = 5; i < j; i++) { + Simple s = map.get(String.valueOf(i)); + assertNotNull(s); + assertEquals(i, s.getInt()); + } + assertEquals(5, complex.size()); + for (Map.Entry<Simple, Simple> entry : complex.entrySet()) { + assertEquals(entry.getKey().getString(), String.valueOf(entry.getValue().getInt())); + } + } + + /** + * Verify that arbitrarily complicated Maps of Maps work. + */ + public void testNestedMap() { + Map<String, String> key = new HashMap<String, String>(); + key.put("a", "b"); + + Map<String, String> value = new HashMap<String, String>(); + value.put("c", "d"); + + Map<Map<String, String>, Map<String, String>> test = + new HashMap<Map<String, String>, Map<String, String>>(); + test.put(key, value); + + AutoBean<HasMap> bean = f.hasMap(); + bean.as().setNestedMap(test); + + AutoBean<HasMap> decoded = checkEncode(bean); + assertEquals(1, decoded.as().getNestedMap().size()); + } + + public void testNull() { + AutoBean<Simple> bean = f.simple(); + AutoBean<Simple> decodedBean = checkEncode(bean); + assertNull(decodedBean.as().getString()); + } + + public void testSimple() { + AutoBean<Simple> bean = f.simple(); + Simple simple = bean.as(); + simple.setBoolean(true); + simple.setInt(42); + simple.setOtherBoolean(true); + simple.setString("Hello World!"); + + AutoBean<Simple> decodedBean = checkEncode(bean); + assertTrue(AutoBeanUtils.diff(bean, decodedBean).isEmpty()); + assertTrue(decodedBean.as().isBoolean()); + assertTrue(decodedBean.as().hasOtherBoolean()); + + AutoBean<HasSimple> bean2 = f.hasSimple(); + bean2.as().setSimple(simple); + + AutoBean<HasSimple> decodedBean2 = checkEncode(bean2); + assertNotNull(decodedBean2.as().getSimple()); + assertTrue(AutoBeanUtils.diff(bean, AutoBeanUtils.getAutoBean(decodedBean2.as().getSimple())) + .isEmpty()); + + AutoBean<HasList> bean3 = f.hasList(); + bean3.as().setIntList(Arrays.asList(1, 2, 3, null, 4, 5)); + bean3.as().setList(Arrays.asList(simple)); + + AutoBean<HasList> decodedBean3 = checkEncode(bean3); + assertNotNull(decodedBean3.as().getIntList()); + assertEquals(Arrays.asList(1, 2, 3, null, 4, 5), decodedBean3.as().getIntList()); + assertNotNull(decodedBean3.as().getList()); + assertEquals(1, decodedBean3.as().getList().size()); + assertTrue(AutoBeanUtils.diff(bean, + AutoBeanUtils.getAutoBean(decodedBean3.as().getList().get(0))).isEmpty()); + } + + public void testSplittable() { + AutoBean<Simple> simple = f.simple(); + simple.as().setString("Simple"); + AutoBean<HasSplittable> bean = f.hasAutoBean(); + bean.as().setSimple(AutoBeanCodex.encode(simple)); + bean.as().setString(ValueCodex.encode("Hello ['\"] world")); + List<Splittable> testList = + Arrays.asList(AutoBeanCodex.encode(simple), null, AutoBeanCodex.encode(simple)); + bean.as().setSimpleList(testList); + Map<Splittable, Splittable> testMap = + Collections.singletonMap(ValueCodex.encode("12345"), ValueCodex.encode("5678")); + bean.as().setSplittableMap(testMap); + + AutoBean<HasSplittable> decoded = checkEncode(bean); + Splittable toDecode = decoded.as().getSimple(); + AutoBean<Simple> decodedSimple = AutoBeanCodex.decode(f, Simple.class, toDecode); + assertEquals("Simple", decodedSimple.as().getString()); + assertEquals("Hello ['\"] world", ValueCodex.decode(String.class, decoded.as().getString())); + assertEquals("12345", decoded.as().getSplittableMap().keySet().iterator().next().asString()); + assertEquals("5678", decoded.as().getSplittableMap().values().iterator().next().asString()); + + List<Splittable> list = decoded.as().getSimpleList(); + assertEquals(3, list.size()); + assertNull(list.get(1)); + assertEquals("Simple", AutoBeanCodex.decode(f, Simple.class, list.get(2)).as().getString()); + } + + @Override + protected void gwtSetUp() throws Exception { + f = GWT.create(Factory.class); + } + + private <T> AutoBean<T> checkEncode(AutoBean<T> bean) { + Splittable split = AutoBeanCodex.encode(bean); + AutoBean<T> decoded = AutoBeanCodex.decode(f, bean.getType(), split); + assertTrue(AutoBeanUtils.deepEquals(bean, decoded)); + return decoded; + } +}
diff --git a/user/test/com/google/web/bindery/autobean/shared/SplittableTest.java b/user/test/com/google/web/bindery/autobean/shared/SplittableTest.java new file mode 100644 index 0000000..46a6baf --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/shared/SplittableTest.java
@@ -0,0 +1,330 @@ +/* + * 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)); + } +}
diff --git a/user/test/com/google/web/bindery/autobean/vm/AutoBeanCodexJreTest.java b/user/test/com/google/web/bindery/autobean/vm/AutoBeanCodexJreTest.java new file mode 100644 index 0000000..46adb69 --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/vm/AutoBeanCodexJreTest.java
@@ -0,0 +1,35 @@ +/* + * 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 com.google.web.bindery.autobean.vm; + +import com.google.web.bindery.autobean.shared.AutoBeanCodexTest; + +/** + * Runs AutoBeanCodexTest in pure-JRE mode. + */ +public class AutoBeanCodexJreTest extends AutoBeanCodexTest { + + @Override + public String getModuleName() { + return null; + } + + @Override + protected void gwtSetUp() throws Exception { + f = AutoBeanFactorySource.create(Factory.class); + } + +}
diff --git a/user/test/com/google/web/bindery/autobean/vm/AutoBeanJreTest.java b/user/test/com/google/web/bindery/autobean/vm/AutoBeanJreTest.java new file mode 100644 index 0000000..e56be29 --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/vm/AutoBeanJreTest.java
@@ -0,0 +1,34 @@ +/* + * 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 com.google.web.bindery.autobean.vm; + +import com.google.web.bindery.autobean.gwt.client.AutoBeanTest; + +/** + * Runs the AutoBeanTests against the JRE implementation. + */ +public class AutoBeanJreTest extends AutoBeanTest { + + @Override + public String getModuleName() { + return null; + } + + @Override + protected void gwtSetUp() throws Exception { + factory = AutoBeanFactorySource.create(Factory.class); + } +}
diff --git a/user/test/com/google/web/bindery/autobean/vm/SplittableJreTest.java b/user/test/com/google/web/bindery/autobean/vm/SplittableJreTest.java new file mode 100644 index 0000000..ddb9126 --- /dev/null +++ b/user/test/com/google/web/bindery/autobean/vm/SplittableJreTest.java
@@ -0,0 +1,28 @@ +/* + * 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.vm; + +import com.google.web.bindery.autobean.shared.SplittableTest; + +/** + * A JRE-only version of SplittableTest. + */ +public class SplittableJreTest extends SplittableTest { + @Override + public String getModuleName() { + return null; + } +}