blob: 76150f1d7380c0f8020ae526dc5d88a661572039 [file] [log] [blame]
/*
* 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.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 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);
}
}