blob: 934ee076e3ea1b1a7ae070d1f4050823e382e3fc [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.requestfactory.server;
import com.google.web.bindery.requestfactory.server.RequestFactoryInterfaceValidator.ClassLoaderLoader;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.Locator;
import com.google.web.bindery.requestfactory.shared.ProxyFor;
import com.google.web.bindery.requestfactory.shared.ProxyForName;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.RequestFactory;
import com.google.web.bindery.requestfactory.shared.Service;
import com.google.web.bindery.requestfactory.shared.ServiceName;
import com.google.web.bindery.requestfactory.shared.SimpleRequestFactory;
import com.google.web.bindery.requestfactory.shared.SkipInterfaceValidation;
import com.google.web.bindery.requestfactory.shared.ValueProxy;
import com.google.web.bindery.requestfactory.shared.impl.FindRequest;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* JRE tests for {@link RequestFactoryInterfaceValidator}.
*/
@SkipInterfaceValidation
public class RequestFactoryInterfaceValidatorTest extends TestCase {
static class ClinitEntity {
static ClinitEntity findClinitEntity(@SuppressWarnings("unused") String key) {
return null;
}
static ClinitEntity request() {
return null;
}
Object OBJECT = new Object();
String getId() {
return null;
}
int getVersion() {
return 0;
}
}
@ProxyFor(ClinitEntity.class)
interface ClinitEntityProxy extends EntityProxy {
Object OBJECT = new Object();
}
@Service(ClinitEntity.class)
interface ClinitRequestContext extends RequestContext {
Object OBJECT = new Object();
Request<ClinitEntityProxy> request();
}
interface ClinitRequestFactory extends RequestFactory {
Object OBJECT = new Object();
ClinitRequestContext context();
}
static class Domain {
static int fooStatic(@SuppressWarnings("unused") int a) {
return 0;
}
int foo(@SuppressWarnings("unused") int a) {
return 0;
}
java.sql.Date getSqlDate() {
return null;
}
}
@ProxyFor(Domain.class)
interface DomainProxy extends EntityProxy {
}
interface DomainProxyMissingAnnotation extends EntityProxy {
}
static class DomainWithOverloads {
void foo() {
}
void foo(@SuppressWarnings("unused") int a) {
}
String getId() {
return null;
}
int getVersion() {
return 0;
}
}
@ProxyFor(DomainWithOverloads.class)
interface DomainWithOverloadsProxy extends EntityProxy {
void foo();
}
@ProxyFor(Domain.class)
interface DomainWithSqlDateProxy extends EntityProxy {
java.sql.Date getSqlDate();
}
/**
* Tests that the validator reports non-static finder methods.
*/
static class EntityWithInstanceFind {
public String getId() {
return null;
}
public int getVersion() {
return 0;
}
/**
* This method should be static.
*/
EntityWithInstanceFind findEntityWithInstanceFind(@SuppressWarnings("unused") String key) {
return null;
}
}
@ProxyFor(EntityWithInstanceFind.class)
interface EntityWithInstanceFindProxy extends EntityProxy {
}
class Foo {
}
@ProxyFor(HasListDomain.class)
interface HasList extends EntityProxy {
List<ReachableOnlyThroughReturnedList> getList();
void setList(List<ReachableOnlyThroughParamaterList> list);
}
static class HasListDomain extends Domain {
public String getId() {
return null;
}
public int getVersion() {
return 0;
}
List<Domain> getList() {
return null;
}
void setList(@SuppressWarnings("unused") List<Domain> value) {
}
}
/**
* An entity type without the usual boilerplate.
*/
class LocatorEntity {
}
class LocatorEntityLocator extends Locator<LocatorEntity, String> {
@Override
public LocatorEntity create(Class<? extends LocatorEntity> clazz) {
return null;
}
@Override
public LocatorEntity find(Class<? extends LocatorEntity> clazz, String id) {
return null;
}
@Override
public Class<LocatorEntity> getDomainType() {
return null;
}
@Override
public String getId(LocatorEntity domainObject) {
return null;
}
@Override
public Class<String> getIdType() {
return null;
}
@Override
public Object getVersion(LocatorEntity domainObject) {
return null;
}
}
@ProxyFor(value = LocatorEntity.class, locator = LocatorEntityLocator.class)
interface LocatorEntityProxy extends EntityProxy {
}
@ProxyForName(value = "com.google.web.bindery.requestfactory.server.RequestFactoryInterfaceValidatorTest.LocatorEntity", locator = "badLocator")
interface LocatorEntityProxyWithBadLocator extends EntityProxy {
}
@ProxyForName(value = "badDomainType", locator = "com.google.web.bindery.requestfactory.server.RequestFactoryInterfaceValidatorTest.LocatorEntityProxyWithBadServiceName")
interface LocatorEntityProxyWithBadServiceName extends EntityProxy {
}
@ProxyFor(value = Value.class)
interface MyValueProxy extends ValueProxy {
}
@ProxyFor(Domain.class)
interface ReachableOnlyThroughParamaterList extends EntityProxy {
}
@ProxyFor(Domain.class)
interface ReachableOnlyThroughReturnedList extends EntityProxy {
}
interface RequestContextMissingAnnotation extends RequestContext {
}
@Service(Domain.class)
interface ServiceRequestMismatchedArity extends RequestContext {
InstanceRequest<DomainProxy, Integer> foo(int a, int b);
Request<Integer> fooStatic(int a, int b);
}
@Service(Domain.class)
interface ServiceRequestMismatchedParam extends RequestContext {
Request<Integer> foo(long a);
}
@Service(Domain.class)
interface ServiceRequestMismatchedReturn extends RequestContext {
Request<Long> foo(int a);
}
@Service(Domain.class)
interface ServiceRequestMismatchedStatic extends RequestContext {
Request<Integer> foo(int a);
InstanceRequest<DomainProxy, Integer> fooStatic(int a);
}
@Service(Domain.class)
interface ServiceRequestMissingMethod extends RequestContext {
Request<Integer> doesNotExist(int a);
}
@Service(Domain.class)
interface SkipValidationChecksReferredProxies extends ValueProxy {
@SkipInterfaceValidation
// still validates other proxies
DomainProxyMissingAnnotation getDomainProxyMissingAnnotation();
}
@Service(Domain.class)
interface SkipValidationContext extends RequestContext {
@SkipInterfaceValidation
Request<Integer> doesNotExist(int a);
@SkipInterfaceValidation
Request<Long> foo(int a);
}
@Service(Domain.class)
interface SkipValidationProxy extends ValueProxy {
@SkipInterfaceValidation
boolean doesNotExist();
}
@ProxyFor(Domain.class)
@ProxyForName("Domain")
@Service(Domain.class)
@ServiceName("Domain")
interface TooManyAnnotations extends RequestContext {
}
static class UnexpectedIdAndVersionDomain {
Random getId() {
return null;
}
Random getVersion() {
return null;
}
}
@ProxyFor(UnexpectedIdAndVersionDomain.class)
interface UnexpectedIdAndVersionProxy extends EntityProxy {
}
static class Value {
}
static class VisibleErrorContext extends RequestFactoryInterfaceValidator.ErrorContext {
final List<String> logs;
public VisibleErrorContext(Logger logger) {
super(logger);
logs = new ArrayList<String>();
}
public VisibleErrorContext(VisibleErrorContext that) {
super(that);
this.logs = that.logs;
}
@Override
public void poison(String msg, Object... args) {
logs.add(String.format(msg, args));
super.poison(msg, args);
}
@Override
public void poison(String msg, Throwable t) {
logs.add(msg);
super.poison(msg, t);
}
@Override
protected VisibleErrorContext fork() {
return new VisibleErrorContext(this);
}
}
RequestFactoryInterfaceValidator v;;
private static final boolean DUMP_PAYLOAD = Boolean.getBoolean("gwt.rpc.dumpPayload");;
private VisibleErrorContext errors;
/**
* Ensure that calling {@link RequestFactoryInterfaceValidator#antidote()}
* doesn't cause information to be lost.
*/
public void testAntidote() {
v.validateRequestContext(RequestContextMissingAnnotation.class.getName());
assertTrue(v.isPoisoned());
v.antidote();
assertFalse(v.isPoisoned());
v.validateRequestContext(RequestContextMissingAnnotation.class.getName());
assertTrue(v.isPoisoned());
}
public void testBadLocatorName() {
v.validateEntityProxy(LocatorEntityProxyWithBadLocator.class.getName());
assertTrue(v.isPoisoned());
assertTrue(errors.logs.contains("Cannot find locator named badLocator"));
}
public void testBadServiceName() {
v.validateEntityProxy(LocatorEntityProxyWithBadServiceName.class.getName());
assertTrue(v.isPoisoned());
assertTrue(errors.logs.contains("Cannot find domain type named badDomainType"));
}
/**
* Test that subclasses of {@code java.util.Date} are not transportable.
*/
public void testDateSubclass() {
v.validateEntityProxy(DomainWithSqlDateProxy.class.getName());
assertTrue(v.isPoisoned());
}
public void testFindMustBeStatic() {
v.validateEntityProxy(EntityWithInstanceFindProxy.class.getName());
assertTrue(v.isPoisoned());
assertTrue(errors.logs.contains("The findEntityWithInstanceFind method must be static"));
}
/**
* Test the {@link FindRequest} context used to implement find().
*/
public void testFindRequestContext() {
v.validateRequestContext(FindRequest.class.getName());
}
/**
* Make sure that proxy types referenced through type parameters of method
* return types and paramaters types are examined.
*/
public void testFollowingTypeParameters() {
v.validateEntityProxy(HasList.class.getName());
Deobfuscator d = v.getDeobfuscator();
assertNotNull(d.getClientProxies(HasListDomain.class.getName()));
assertTrue(d.getClientProxies(Domain.class.getName()).contains(
ReachableOnlyThroughParamaterList.class.getName()));
assertTrue(d.getClientProxies(Domain.class.getName()).contains(
ReachableOnlyThroughReturnedList.class.getName()));
}
/**
* Ensure that the &lt;clinit> methods don't interfere with validation.
*/
public void testIntecfacesWithClinits() {
v.validateRequestFactory(ClinitRequestFactory.class.getName());
assertFalse(v.isPoisoned());
}
public void testLocatorProxy() {
v.validateEntityProxy(LocatorEntityProxy.class.getName());
assertFalse(v.isPoisoned());
}
public void testMismatchedArity() {
v.validateRequestContext(ServiceRequestMismatchedArity.class.getName());
assertTrue(v.isPoisoned());
}
public void testMismatchedParamType() {
v.validateRequestContext(ServiceRequestMismatchedParam.class.getName());
assertTrue(v.isPoisoned());
}
public void testMismatchedReturnType() {
v.validateRequestContext(ServiceRequestMismatchedReturn.class.getName());
assertTrue(v.isPoisoned());
}
public void testMismatchedStatic() {
v.validateRequestContext(ServiceRequestMismatchedStatic.class.getName());
assertTrue(v.isPoisoned());
}
public void testMissingDomainAnnotations() {
v.validateEntityProxy(DomainProxyMissingAnnotation.class.getName());
assertTrue(v.isPoisoned());
}
public void testMissingIdAndVersion() {
v.validateEntityProxy(DomainProxy.class.getName());
assertTrue(v.isPoisoned());
}
public void testMissingMethod() {
v.validateRequestContext(ServiceRequestMissingMethod.class.getName());
assertTrue(v.isPoisoned());
}
public void testMissingServiceAnnotations() {
v.validateRequestContext(RequestContextMissingAnnotation.class.getName());
assertTrue(v.isPoisoned());
}
public void testOverloadedMethod() {
v.validateEntityProxy(DomainWithOverloadsProxy.class.getName());
assertTrue(v.isPoisoned());
}
public void testSkipValidationChecksReferredProxies() {
v.validateValueProxy(SkipValidationChecksReferredProxies.class.getName());
assertTrue(v.isPoisoned());
}
public void testSkipValidationContext() {
v.validateRequestContext(SkipValidationContext.class.getName());
assertFalse(v.isPoisoned());
}
public void testSkipValidationProxy() {
v.validateValueProxy(SkipValidationProxy.class.getName());
assertFalse(v.isPoisoned());
}
/**
* Perform a full test of the RequestFactory used for most tests.
*/
public void testTestCodeFactories() {
v.validateRequestFactory(SimpleRequestFactory.class.getName());
assertFalse(v.isPoisoned());
}
public void testTooManyAnnotations() {
v.validateRequestContext(TooManyAnnotations.class.getName());
assertTrue(v.isPoisoned());
}
public void testUnexpectedIdAndVersion() {
v.validateEntityProxy(UnexpectedIdAndVersionProxy.class.getName());
assertTrue(v.isPoisoned());
}
public void testValueType() {
v.validateValueProxy(MyValueProxy.class.getName());
assertFalse(v.isPoisoned());
}
@Override
protected void setUp() throws Exception {
Logger logger = Logger.getLogger("");
logger.setLevel(DUMP_PAYLOAD ? Level.ALL : Level.OFF);
errors = new VisibleErrorContext(logger);
v =
new RequestFactoryInterfaceValidator(errors, new ClassLoaderLoader(Thread.currentThread()
.getContextClassLoader()));
}
}