blob: 42a821b7bbb09d7f95a4ef1cf8d3176b842783c6 [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.requestfactory.rebind.model;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationStateBuilder;
import com.google.gwt.dev.javac.impl.JavaResourceBase;
import com.google.gwt.dev.javac.impl.MockJavaResource;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.UnitTestTreeLogger;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.requestfactory.server.TestContextImpl;
import com.google.gwt.requestfactory.server.TestContextNoIdImpl;
import com.google.gwt.requestfactory.server.TestContextNoVersionImpl;
import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.InstanceRequest;
import com.google.gwt.requestfactory.shared.ProxyFor;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.Request;
import com.google.gwt.requestfactory.shared.RequestContext;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.Service;
import com.google.gwt.requestfactory.shared.impl.Property;
import junit.framework.TestCase;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
/**
* Test case for {@link com.google.gwt.requestfactory.rebind.model.RequestFactoryModel}
* that uses mock CompilationStates.
*/
public class RequestFactoryModelTest extends TestCase {
/**
* Constructs an empty interface representation of a type.
*/
private static class EmptyMockJavaResource extends MockJavaResource {
private final StringBuilder code = new StringBuilder();
public EmptyMockJavaResource(Class<?> clazz) {
super(clazz.getName());
code.append("package ").append(clazz.getPackage().getName())
.append(";\n");
code.append("public interface ").append(clazz.getSimpleName());
int numParams = clazz.getTypeParameters().length;
if (numParams != 0) {
code.append("<");
for (int i = 0; i < numParams; i++) {
if (i != 0) {
code.append(",");
}
code.append("T").append(i);
}
code.append(">");
}
code.append("{}\n");
}
@Override
protected CharSequence getContent() {
return code;
}
}
/**
* Loads the actual source of a type. This should be used only for types
* directly tested by this test. Note that use of this class requires your
* source files to be on your classpath.
*/
private static class RealJavaResource extends MockJavaResource {
public RealJavaResource(Class<?> clazz) {
super(clazz.getName());
}
@Override
protected CharSequence getContent() {
String resourceName = getTypeName().replace('.', '/') + ".java";
InputStream stream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(resourceName);
return Util.readStreamAsString(stream);
}
}
private static TreeLogger createCompileLogger() {
PrintWriterTreeLogger logger = new PrintWriterTreeLogger(
new PrintWriter(System.err, true));
logger.setMaxDetail(TreeLogger.ERROR);
return logger;
}
private TreeLogger logger;
@Override
public void setUp() throws Exception {
super.setUp();
logger = createCompileLogger();
}
public void testBadCollectionType() {
testModelWithMethodDecl("Request<SortedSet<Integer>> badReturnType();",
"Requests that return collections may be declared with java.util.List or java.util.Set only");
}
public void testBadCollectionTypeNotParameterized() {
testModelWithMethodDecl("Request<SortedSet> badReturnType();",
"Requests that return collections of List or Set must be parameterized");
}
public void testBadReturnType() {
testModelWithMethodDecl("Request<Iterable> badReturnType();",
"Invalid Request parameterization java.lang.Iterable");
}
public void testMismatchedArityInstance() {
testModelWithMethodDecl(
"InstanceRequest<TestProxy, String> mismatchedArityInstance(TestProxy p, int x);",
"Parameter 0 of method TestContext.mismatchedArityInstance does not match method com.google.gwt.requestfactory.server.TestContextImpl.mismatchedArityInstance");
}
public void testMismatchedArityStatic() {
testModelWithMethodDecl("Request<String> mismatchedArityStatic(int x);",
"Method TestContext.mismatchedArityStatic parameters do not match same method on com.google.gwt.requestfactory.server.TestContextImpl");
}
public void testMismatchedModifierNonStatic() {
testModelWithMethodDecl(
"InstanceRequest<TestProxy, String> mismatchedNonStatic();",
"Method TestContext.mismatchedNonStatic is an instance method, while the corresponding method on com.google.gwt.requestfactory.server.TestContextImpl is static");
}
public void testMismatchedModifierStatic() {
testModelWithMethodDecl("Request<String> mismatchedStatic();",
"Method TestContext.mismatchedStatic is a static method, while the corresponding method on com.google.gwt.requestfactory.server.TestContextImpl is not");
}
public void testMismatchedParamType() {
testModelWithMethodDecl("Request<String> mismatchedParamType(Integer x);",
"Parameter 0 of method TestContext.mismatchedParamType does not match method com.google.gwt.requestfactory.server.TestContextImpl.mismatchedParamType");
}
public void testMismatchedReturnType() {
testModelWithMethodDecl("Request<String> mismatchedReturnType();",
"Return type of method TestContext.mismatchedReturnType does not match method com.google.gwt.requestfactory.server.TestContextImpl.mismatchedReturnType");
}
public void testMissingId() {
testModelWithMethodDeclArgs("Request<TestProxy> okMethodProxy();",
TestContextNoIdImpl.class.getName(),
TestContextNoIdImpl.class.getName(),
"The class com.google.gwt.requestfactory.server.TestContextNoIdImpl is missing method getId()");
}
public void testMissingMethod() {
testModelWithMethodDecl("Request<String> missingMethod();",
"Method t.TestContext.missingMethod has no corresponding public method on"
+ " com.google.gwt.requestfactory.server.TestContextImpl");
}
public void testMissingProxyFor() {
testModelWithMethodDeclArgs("Request<TestProxy> okMethodProxy();",
TestContextImpl.class.getName(), null,
"The t.TestProxy type does not have a @ProxyFor annotation");
}
public void testMissingService() {
testModelWithMethodDeclArgs("Request<String> okMethod();", null,
TestContextImpl.class.getName(),
"RequestContext subtype t.TestContext is missing a @Service annotation");
}
public void testMissingVersion() {
testModelWithMethodDeclArgs("Request<TestProxy> okMethodProxy();",
TestContextNoVersionImpl.class.getName(),
TestContextNoVersionImpl.class.getName(),
"The class com.google.gwt.requestfactory.server.TestContextNoVersionImpl is missing method getVersion()");
}
public void testModelWithMethodDecl(final String clientMethodDecls,
String... expected) {
testModelWithMethodDeclArgs(clientMethodDecls,
TestContextImpl.class.getName(), TestContextImpl.class.getName(),
expected);
}
public void testModelWithMethodDeclArgs(final String clientMethodDecls,
final String serviceClass, String proxyClass, String... expected) {
Set<Resource> javaResources = getJavaResources(proxyClass);
javaResources.add(new MockJavaResource("t.TestRequestFactory") {
@Override
protected CharSequence getContent() {
StringBuilder code = new StringBuilder();
code.append("package t;\n");
code.append("import " + RequestFactory.class.getName() + ";\n");
code.append("interface TestRequestFactory extends RequestFactory {\n");
code.append("TestContext testContext();");
code.append("}");
return code;
}
});
javaResources.add(new MockJavaResource("t.TestContext") {
@Override
protected CharSequence getContent() {
StringBuilder code = new StringBuilder();
code.append("package t;\n");
code.append("import " + Request.class.getName() + ";\n");
code.append("import " + InstanceRequest.class.getName() + ";\n");
code.append("import " + RequestContext.class.getName() + ";\n");
code.append("import " + SortedSet.class.getName() + ";\n");
code.append("import " + List.class.getName() + ";\n");
code.append("import " + Set.class.getName() + ";\n");
code.append("import " + Service.class.getName() + ";\n");
code.append("import " + TestContextImpl.class.getName() + ";\n");
if (serviceClass != null) {
code.append("@Service(" + serviceClass + ".class)");
}
code.append("interface TestContext extends RequestContext {\n");
code.append(clientMethodDecls);
code.append("}");
return code;
}
});
CompilationState state = CompilationStateBuilder
.buildFrom(logger, javaResources);
UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
builder.setLowestLogLevel(TreeLogger.ERROR);
for (String expectedMsg : expected) {
builder.expectError(expectedMsg, null);
}
builder.expectError(RequestFactoryModel.poisonedMessage(), null);
UnitTestTreeLogger testLogger = builder.createLogger();
try {
new RequestFactoryModel(testLogger,
state.getTypeOracle().findType("t.TestRequestFactory"));
fail("Should have complained");
} catch (UnableToCompleteException e) {
}
testLogger.assertCorrectLogEntries();
}
public void testOverloadedMethod() {
testModelWithMethodDecl("Request<String> overloadedMethod();",
"Method t.TestContext.overloadedMethod is overloaded on com.google.gwt.requestfactory.server.TestContextImpl");
}
private Set<Resource> getJavaResources(final String proxyClass) {
MockJavaResource[] javaFiles = {new MockJavaResource("t.AddressProxy") {
@Override
protected CharSequence getContent() {
StringBuilder code = new StringBuilder();
code.append("package t;\n");
code.append("import " + ProxyFor.class.getName() + ";\n");
code.append("import " + EntityProxy.class.getName() + ";\n");
if (proxyClass != null) {
code.append("@ProxyFor(" + proxyClass + ".class)");
}
code.append("interface TestProxy extends EntityProxy {\n");
code.append("}");
return code;
}
}, new MockJavaResource("java.util.List") {
// Tests a Driver interface that extends more than RFED
@Override
protected CharSequence getContent() {
StringBuilder code = new StringBuilder();
code.append("package java.util;\n");
code.append("public interface List<T> extends Collection<T> {\n");
code.append("}");
return code;
}
}, new MockJavaResource("java.util.Set") {
// Tests a Driver interface that extends more than RFED
@Override
protected CharSequence getContent() {
StringBuilder code = new StringBuilder();
code.append("package java.util;\n");
code.append("public interface Set<T> extends Collection<T> {\n");
code.append("}");
return code;
}
}, new MockJavaResource("java.util.SortedSet") {
// Tests a Driver interface that extends more than RFED
@Override
protected CharSequence getContent() {
StringBuilder code = new StringBuilder();
code.append("package java.util;\n");
code.append("public interface SortedSet<T> extends Set<T> {\n");
code.append("}");
return code;
}
}};
Set<Resource> toReturn = new HashSet<Resource>(Arrays.asList(javaFiles));
toReturn.addAll(Arrays.asList(
new Resource[]{new EmptyMockJavaResource(Iterable.class),
new EmptyMockJavaResource(Property.class),
new EmptyMockJavaResource(EntityProxy.class),
new EmptyMockJavaResource(InstanceRequest.class),
new EmptyMockJavaResource(RequestFactory.class),
new EmptyMockJavaResource(Receiver.class),
new RealJavaResource(Request.class),
new RealJavaResource(Service.class),
new RealJavaResource(ProxyFor.class),
new EmptyMockJavaResource(RequestContext.class),}));
toReturn.addAll(Arrays.asList(JavaResourceBase.getStandardResources()));
return toReturn;
}
}