| /* |
| * 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; |
| } |
| } |