/* * 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, /** * Contains a method that cannot even be called, but is enough to make * MyEnum.BAR.getClass().isEnum()==false, because BAR's class is now an * anonymous subclass of MyEnum. */ BAR { @SuppressWarnings("unused") private void dummy() { } }, // 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; } }