blob: 048e8a5e1b0d94bcc41c8ff873b64ea487ba84b4 [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.gwt.editor.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.AutoBeanFactory.Category;
import com.google.gwt.junit.client.GWTTestCase;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 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 int add(AutoBean<HasCall> bean, int a, int b) {
assertNotNull(bean);
return ((Integer) bean.getTag("offset")) + a + b;
}
public static <T> T __intercept(AutoBean<HasCall> bean, T value) {
seen = value;
return value;
}
}
@Category(CallImpl.class)
interface Factory extends AutoBeanFactory {
AutoBean<HasCall> hasCall();
AutoBean<HasList> hasList();
AutoBean<Intf> intf();
AutoBean<Intf> intf(RealIntf wrapped);
AutoBean<OtherIntf> otherIntf();
}
interface HasCall {
int add(int a, int b);
}
interface HasList {
List<Intf> getList();
void setList(List<Intf> list);
}
interface Intf {
int getInt();
String getString();
void setInt(int number);
void setString(String value);
}
interface OtherIntf {
Intf getIntf();
UnreferencedInFactory getUnreferenced();
void setIntf(Intf intf);
}
static class RealIntf implements Intf {
String string;
int i;
@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;
}
}
interface UnreferencedInFactory {
}
private Factory factory;
@Override
public String getModuleName() {
return "com.google.gwt.editor.Editor";
}
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 testClone() {
AutoBean<Intf> a1 = factory.intf();
Intf i1 = a1.as();
i1.setInt(42);
AutoBean<Intf> a2 = a1.clone(false);
Intf i2 = a2.as();
// Copies have the same values
assertNotSame(i1, i2);
assertFalse(i1.equals(i2));
assertEquals(i1.getInt(), i2.getInt());
// Cloned instances do not affect one another
i1.setInt(41);
assertEquals(41, i1.getInt());
assertEquals(42, i2.getInt());
}
public void testCloneDeep() {
AutoBean<OtherIntf> a1 = factory.otherIntf();
OtherIntf o1 = a1.as();
o1.setIntf(factory.intf().as());
o1.getIntf().setInt(42);
AutoBean<OtherIntf> a2 = a1.clone(true);
OtherIntf o2 = a2.as();
assertNotSame(o1.getIntf(), o2.getIntf());
assertEquals(o1.getIntf().getInt(), o2.getIntf().getInt());
o1.getIntf().setInt(41);
assertEquals(42, o2.getIntf().getInt());
}
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"));
}
/**
* Tests that lists are in fact aliased between AutoBeans after a shallow
* clone.
*/
public void testDiffWithCloneAndListMutation() {
AutoBean<HasList> a1 = factory.hasList();
List<Intf> list = new ArrayList<Intf>();
list.add(factory.intf().as());
a1.as().setList(list);
AutoBean<HasList> a2 = a1.clone(false);
assertTrue(AutoBeanUtils.diff(a1, a2).isEmpty());
// Lists are aliased between AutoBeans
assertSame(a1.as().getList(), a2.as().getList());
assertEquals(a1.as().getList(), a2.as().getList());
}
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() and equals()
assertEquals(real.hashCode(), w.as().hashCode());
assertEquals(real, w.as());
assertEquals(w.as(), real);
}
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));
}
/**
* 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();
other.as().setIntf(intf.as());
intf.as().setInt(42);
class Checker extends AutoBeanVisitor {
boolean seenOther;
boolean seenIntf;
@Override
public void endVisitReferenceProperty(String propertyName,
AutoBean<?> value, PropertyContext ctx) {
if ("intf".equals(propertyName)) {
assertSame(intf, value);
} else if ("unreferenced".equals(propertyName)) {
assertNull(value);
} else {
fail("Unexpecetd property " + propertyName);
}
}
@Override
public void endVisitValueProperty(String propertyName, Object value,
PropertyContext ctx) {
if ("int".equals(propertyName)) {
assertEquals(42, value);
} else if ("string".equals(propertyName)) {
assertNull(value);
} else {
fail("Unknown value property " + propertyName);
}
}
@Override
public boolean visit(AutoBean<?> bean, Context ctx) {
if (bean == other) {
seenOther = true;
} else if (bean == intf) {
seenIntf = true;
} else {
fail("Unknown AutoBean");
}
return true;
}
void check() {
assertTrue(seenOther);
assertTrue(seenIntf);
}
}
Checker c = new Checker();
other.accept(c);
c.check();
}
/**
* 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);
}
}