| /* |
| * 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.gwt.core.client.GWT; |
| import com.google.gwt.junit.client.GWTTestCase; |
| 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 java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| 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) { |
| assertNotNull(bean); |
| 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); |
| |
| @Override |
| HasMoreChainedSetters setInt(int value); |
| } |
| |
| interface Intf { |
| int getInt(); |
| |
| String getProperty(); |
| |
| String getString(); |
| |
| void setInt(int number); |
| |
| // Avoid name conflicts in AbstractAutoBean |
| void setProperty(String value); |
| |
| 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 property; |
| String string; |
| |
| @Override |
| public boolean equals(Object o) { |
| return (o instanceof Intf) && (((Intf) o).getInt() == getInt()); |
| } |
| |
| @Override |
| public int getInt() { |
| return i; |
| } |
| |
| @Override |
| public String getProperty() { |
| return property; |
| } |
| |
| @Override |
| public String getString() { |
| return string; |
| } |
| |
| @Override |
| public int hashCode() { |
| return i; |
| } |
| |
| @Override |
| public void setInt(int number) { |
| this.i = number; |
| } |
| |
| @Override |
| public void setProperty(String value) { |
| this.property = value; |
| } |
| |
| @Override |
| public void setString(String value) { |
| this.string = value; |
| } |
| |
| @Override |
| 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 testListShim() { |
| HasList hl = factory.hasList().as(); |
| hl.setList(new ArrayList<Intf>()); |
| hl.getList().add(factory.intf().as()); |
| hl.getList().add(factory.intf().as()); |
| |
| Iterator<Intf> iter = hl.getList().iterator(); |
| assertEquals(hl.getList().get(0), iter.next()); |
| assertTrue(iter.hasNext()); |
| assertEquals(hl.getList().get(1), iter.next()); |
| // Iterator#remove became a 'default' method in Java 8; test non-regression |
| iter.remove(); |
| assertFalse(iter.hasNext()); |
| assertEquals(1, hl.getList().size()); |
| |
| // Same with ListIterator |
| hl.getList().add(factory.intf().as()); |
| iter = hl.getList().listIterator(); |
| assertEquals(hl.getList().get(0), iter.next()); |
| assertTrue(iter.hasNext()); |
| assertEquals(hl.getList().get(1), iter.next()); |
| // ListIterator#remove became a 'default' method in Java 8; test non-regression |
| iter.remove(); |
| assertFalse(iter.hasNext()); |
| assertEquals(1, hl.getList().size()); |
| } |
| |
| 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, (int) auto.<Integer>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) || "property".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) { |
| } |
| } |
| |
| public void testFactoryToString() { |
| assertNotNull(factory.toString()); |
| } |
| |
| public void testFactoryHashCode() { |
| assertTrue(factory.hashCode() != 0); |
| assertEquals(factory.hashCode(), factory.hashCode()); |
| } |
| |
| public void testFactoryEquals() { |
| assertFalse(factory.equals(null)); |
| assertTrue(factory.equals(factory)); |
| } |
| |
| @Override |
| protected void gwtSetUp() throws Exception { |
| factory = GWT.create(Factory.class); |
| } |
| } |