blob: 9b207801ccfac9ac47620ace9a800c67e086c9f5 [file] [log] [blame]
/*
* Copyright 2008 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.junit.rebind;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.BadPropertyValueException;
import com.google.gwt.core.ext.ConfigurationProperty;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.SelectionProperty;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.junit.client.GWTTestCase.TestModuleInfo;
import com.google.gwt.junit.client.impl.GWTRunner;
import com.google.gwt.junit.client.impl.JUnitHost.TestInfo;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import java.io.PrintWriter;
import java.util.Set;
import java.util.TreeSet;
/**
* This class generates a stub class for classes that derive from GWTTestCase.
* This stub class provides the necessary bridge between our Hosted or Hybrid
* mode classes and the JUnit system.
*/
public class GWTRunnerGenerator extends Generator {
private static final String GWT_RUNNER_NAME = GWTRunner.class.getName();
private static String getPackagePrefix(JClassType classType) {
String name = classType.getPackage().getName();
return (name.length() == 0) ? name : (name + '.');
}
/**
* Create a new type that satisfies the rebind request.
*/
@Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
if (!GWT_RUNNER_NAME.equals(typeName)) {
logger.log(TreeLogger.ERROR, "This generator may only be used with "
+ GWT_RUNNER_NAME, null);
throw new UnableToCompleteException();
}
JClassType requestedClass;
try {
requestedClass = context.getTypeOracle().getType(typeName);
} catch (NotFoundException e) {
logger.log(
TreeLogger.ERROR,
"Could not find type '"
+ typeName
+ "'; please see the log, as this usually indicates a previous error ",
e);
throw new UnableToCompleteException();
}
String moduleName;
try {
ConfigurationProperty prop = context.getPropertyOracle().getConfigurationProperty(
"junit.moduleName");
moduleName = prop.getValues().get(0);
} catch (BadPropertyValueException e) {
logger.log(TreeLogger.ERROR,
"Could not resolve junit.moduleName property", e);
throw new UnableToCompleteException();
}
String userAgent;
try {
SelectionProperty prop = context.getPropertyOracle().getSelectionProperty(
logger, "user.agent");
userAgent = prop.getCurrentValue();
} catch (BadPropertyValueException e) {
logger.log(TreeLogger.ERROR, "Could not resolve user.agent property", e);
throw new UnableToCompleteException();
}
// Get the stub class name, and see if its source file exists.
//
String generatedClass = requestedClass.getName().replace('.', '_') + "Impl"
+ userAgent;
String packageName = requestedClass.getPackage().getName();
String qualifiedStubClassName = packageName + "." + generatedClass;
SourceWriter sourceWriter = getSourceWriter(logger, context, packageName,
generatedClass, GWT_RUNNER_NAME);
if (sourceWriter != null) {
// Check the global set of active tests for this module.
TestModuleInfo moduleInfo = GWTTestCase.getTestsForModule(moduleName);
Set<TestInfo> moduleTests = (moduleInfo == null) ? null : moduleInfo.getTests();
Set<String> testClasses;
if (moduleTests == null || moduleTests.isEmpty()) {
// Fall back to pulling in all types in the module.
JClassType[] allTestTypes = getAllPossibleTestTypes(context.getTypeOracle());
testClasses = getTestTypesForModule(logger, moduleName, allTestTypes);
} else {
// Must use sorted set to prevent nondeterminism.
testClasses = new TreeSet<String>();
for (TestInfo testInfo : moduleTests) {
testClasses.add(testInfo.getTestClass());
}
}
writeCreateNewTestCaseMethod(testClasses, sourceWriter);
writeGetUserAgentPropertyMethod(userAgent, sourceWriter);
sourceWriter.commit(logger);
}
return qualifiedStubClassName;
}
private JClassType[] getAllPossibleTestTypes(TypeOracle typeOracle) {
JClassType gwtTestType = typeOracle.findType(GWTTestCase.class.getName());
if (gwtTestType != null) {
return gwtTestType.getSubtypes();
} else {
return new JClassType[0];
}
}
private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx,
String packageName, String className, String superclassName) {
PrintWriter printWriter = ctx.tryCreate(logger, packageName, className);
if (printWriter == null) {
return null;
}
ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
packageName, className);
composerFactory.setSuperclass(superclassName);
composerFactory.addImport(GWTTestCase.class.getName());
composerFactory.addImport(GWT.class.getName());
return composerFactory.createSourceWriter(ctx, printWriter);
}
private Set<String> getTestTypesForModule(TreeLogger logger,
String moduleName, JClassType[] allTestTypes) {
// Must use sorted set to prevent nondeterminism.
Set<String> testClasses = new TreeSet<String>();
for (JClassType classType : allTestTypes) {
if (!classType.isPublic() || classType.isAbstract()
|| !classType.isDefaultInstantiable()) {
continue;
}
String className = getPackagePrefix(classType)
+ classType.getName().replace('.', '$');
try {
Class<?> testClass = Class.forName(className);
GWTTestCase instantiated = (GWTTestCase) testClass.newInstance();
if (!moduleName.equals(instantiated.getModuleName())) {
continue;
}
} catch (Throwable e) {
logger.log(
TreeLogger.INFO,
"Error determining if test class '"
+ className
+ "' is a part of the current module; skipping; expect subsequent errors if this test class is run",
e);
continue;
}
testClasses.add(classType.getQualifiedSourceName());
}
return testClasses;
}
private void writeCreateNewTestCaseMethod(Set<String> testClasses,
SourceWriter sw) {
sw.println();
sw.println("protected final GWTTestCase createNewTestCase(String testClass) {");
sw.indent();
boolean isFirst = true;
for (String className : testClasses) {
if (isFirst) {
isFirst = false;
} else {
sw.print("else ");
}
sw.println("if (testClass.equals(\"" + className + "\")) {");
sw.indentln("return GWT.create(" + className + ".class);");
sw.println("}");
}
sw.println("return null;");
sw.outdent();
sw.println("}");
}
private void writeGetUserAgentPropertyMethod(String userAgent, SourceWriter sw) {
sw.println();
sw.println("protected final String getUserAgentProperty() {");
sw.indent();
sw.println("return \"" + userAgent + "\";");
sw.outdent();
sw.println("}");
}
}