blob: d28ac61ed000cd034bb60feed28936e6b67a5329 [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.dev.cfg;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.cfg.Libraries.IncompatibleLibraryVersionException;
import com.google.gwt.dev.javac.testing.impl.MockResource;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.UnitTestTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import junit.framework.TestCase;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
/**
* Test for the module def loading
*/
public class ModuleDefLoaderTest extends TestCase {
private CompilerContext compilerContext;
private CompilerContext.Builder compilerContextBuilder;
private MockLibraryWriter mockLibraryWriter = new MockLibraryWriter();
public void assertHonorsStrictResources(boolean strictResources)
throws UnableToCompleteException {
TreeLogger logger = TreeLogger.NULL;
compilerContext.getOptions().setEnforceStrictSourceResources(strictResources);
compilerContext.getOptions().setEnforceStrictPublicResources(strictResources);
ModuleDef emptyModule = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.merging.Empty");
Resource sourceFile =
emptyModule.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InOne.java");
Resource publicFile = emptyModule.findPublicFile("Public.java");
if (strictResources) {
// Empty.gwt.xml did not register any source or public paths and the strictResource setting is
// blocking the implicit addition of any default entries. So these resource searches should
// fail.
assertNull(sourceFile);
assertNull(publicFile);
} else {
assertNotNull(sourceFile);
assertNotNull(publicFile);
}
}
public void testAllowsImpreciseResources() throws Exception {
assertHonorsStrictResources(false);
}
public void testErrorReporting_badXml() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadModule",
"Line 3, column 1 : Element type \"inherits\" must be followed by either "
+ "attribute specifications, \">\" or \"/>\".");
}
public void testErrorReporting_badLinker() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadLinker",
"Line 2: Invalid linker name 'X'");
}
public void testErrorReporting_badProperty() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadProperty",
"Line 2: Property 'X' not found");
}
public void testErrorReporting_badPropertyValue() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadPropertyValue",
"Line 3: Value 'z' in not a valid value for property 'X'");
}
public void testErrorReporting_deepError() {
UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
builder.setLowestLogLevel(TreeLogger.DEBUG);
builder.expectDebug(
"Loading inherited module 'com.google.gwt.dev.cfg.testdata.errors.DeepInheritsError0'",
null);
builder.expectDebug(
"Loading inherited module 'com.google.gwt.dev.cfg.testdata.errors.DeepInheritsError1'",
null);
builder.expectDebug(
"Loading inherited module 'com.google.gwt.dev.cfg.testdata.errors.BadModule'", null);
builder.expectError("Line 3, column 1 : Element type \"inherits\" must be followed by either "
+ "attribute specifications, \">\" or \"/>\".", null);
UnitTestTreeLogger logger = builder.createLogger();
try {
ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.errors.DeepInheritsError0");
fail("Should have failed to load module.");
} catch (UnableToCompleteException e) {
// failure is expected.
}
logger.assertLogEntriesContainExpected();
}
public void testErrorReporting_inheritNotFound() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.InheritNotFound",
"Unable to find 'com/google/gwt/dev/cfg/testdata/NonExistentModule.gwt.xml' on your "
+ "classpath; could be a typo, or maybe you forgot to include a classpath entry "
+ "for source?");
}
public void testErrorReporting_invalidName() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.InvalidName",
"Line 2: Invalid property name '123:33'");
}
public void testErrorReporting_multipleErrors() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.MultipleErrors",
"Line 1: Unexpected attribute 'blah' in element 'module'");
}
public void testErrorReporting_unexpectedAttribute() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.UnexpectedAttribute",
"Line 2: Unexpected attribute 'blah' in element 'inherits'");
}
public void testErrorReporting_unexpectedTag() {
assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.UnexpectedTag",
"Line 2: Unexpected element 'inherited'");
}
public void testLoadFromLibraryGroup() throws UnableToCompleteException, IOException,
IncompatibleLibraryVersionException {
PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
logger.setMaxDetail(TreeLogger.INFO);
// Create the library zip file.
File zipFile = File.createTempFile("FooLib", ".gwtlib");
zipFile.deleteOnExit();
// Put data in the library and save it.
ZipLibraryWriter zipLibraryWriter = new ZipLibraryWriter(zipFile.getPath());
zipLibraryWriter.setLibraryName("FooLib");
MockResource userXmlResource = new MockResource("com/google/gwt/user/User.gwt.xml") {
@Override
public CharSequence getContent() {
return "<module></module>";
}
};
zipLibraryWriter.addBuildResource(userXmlResource);
zipLibraryWriter.write();
// Read data back from disk.
ZipLibrary zipLibrary = new ZipLibrary(zipFile.getPath());
// Prepare the LibraryGroup and ResourceLoader.
compilerContext = compilerContextBuilder.libraryGroup(
LibraryGroup.fromLibraries(Lists.<Library> newArrayList(zipLibrary), false)).build();
ResourceLoader resourceLoader = ResourceLoaders.forPathAndFallback(Lists.newArrayList(zipFile),
ResourceLoaders.forClassLoader(Thread.currentThread()));
// Will throw an exception if com.google.gwt.user.User can't be found and parsed.
ModuleDefLoader.loadFromResources(logger, compilerContext, "com.google.gwt.user.User",
resourceLoader, false);
}
/**
* Tests that the tree representation for a module is correct.
*/
public void testLibraryTree() throws Exception {
TreeLogger logger = TreeLogger.NULL;
ModuleDef six = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.dependents.Six");
Collection<String> sixActualDependencies =
six.getDirectDependencies("com.google.gwt.dev.cfg.testdata.dependents.Six");
Collection<String> fiveActualDependencies =
six.getDirectDependencies("com.google.gwt.dev.cfg.testdata.dependents.Five");
assertEquals(ImmutableSet.of("com.google.gwt.core.Core",
"com.google.gwt.dev.cfg.testdata.dependents.Five"), sixActualDependencies);
assertEquals("Contains " + fiveActualDependencies, ImmutableSet.of(
"com.google.gwt.core.Core", "com.google.gwt.dev.cfg.testdata.dependents.One",
"com.google.gwt.dev.cfg.testdata.dependents.Two",
"com.google.gwt.dev.cfg.testdata.dependents.Four"), fiveActualDependencies);
}
/**
* Test of merging multiple modules in the same package space.
* This exercises the interaction of include, exclude, and skip attributes.
*/
public void testModuleMerging() throws Exception {
TreeLogger logger = TreeLogger.NULL;
ModuleDef one = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.merging.One");
assertNotNull(one.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InOne.java"));
assertNotNull(one.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/Shared.java"));
assertNull(one.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InTwo.java"));
assertNull(one.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/Toxic.java"));
ModuleDef two = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.merging.Two");
assertNotNull(two.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InOne.java"));
assertNotNull(two.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/Shared.java"));
assertNotNull(two.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InTwo.java"));
assertNull(two.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/Toxic.java"));
ModuleDef three = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.merging.Three");
assertNotNull(three.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InOne.java"));
assertNotNull(three.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/Shared.java"));
assertNull(three.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/InTwo.java"));
assertNull(three.findSourceFile("com/google/gwt/dev/cfg/testdata/merging/client/Toxic.java"));
}
/**
* The top level module has an invalid name.
*/
public void testModuleNamingInvalid() {
UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
builder.setLowestLogLevel(TreeLogger.ERROR);
builder.expectError("Invalid module name: 'com.google.gwt.dev.cfg.testdata.naming.Invalid..Foo'", null);
UnitTestTreeLogger logger = builder.createLogger();
try {
ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.naming.Invalid..Foo");
fail("Expected exception from invalid module name.");
} catch (UnableToCompleteException expected) {
}
logger.assertLogEntriesContainExpected();
}
public void testModuleNamingValid() throws Exception {
TreeLogger logger = TreeLogger.NULL;
ModuleDef module;
module = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.naming.Foo-test");
assertNotNull(module.findSourceFile("com/google/gwt/dev/cfg/testdata/naming/client/Mock.java"));
module = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.naming.7Foo");
assertNotNull(module.findSourceFile("com/google/gwt/dev/cfg/testdata/naming/client/Mock.java"));
module = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.naming.Nested7Foo");
assertNotNull(module.findSourceFile("com/google/gwt/dev/cfg/testdata/naming/client/Mock.java"));
module = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.naming.Nested7Foo");
assertNotNull(module.findSourceFile("com/google/gwt/dev/cfg/testdata/naming/client/Mock.java"));
}
/**
* The top level module has a valid name, but the inherited one does not.
*/
public void testModuleNestedNamingInvalid() {
UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
builder.setLowestLogLevel(TreeLogger.ERROR);
builder.expectError("Invalid module name: 'com.google.gwt.dev.cfg.testdata.naming.Invalid..Foo'", null);
UnitTestTreeLogger logger = builder.createLogger();
try {
ModuleDefLoader.loadFromClassPath(logger,
compilerContext, "com.google.gwt.dev.cfg.testdata.naming.NestedInvalid", false);
fail("Expected exception from invalid module name.");
} catch (UnableToCompleteException expected) {
}
logger.assertLogEntriesContainExpected();
}
public void testRequiresStrictResources() throws Exception {
assertHonorsStrictResources(true);
}
public void testResourcesVisible() throws Exception {
TreeLogger logger = TreeLogger.NULL;
ModuleDef one = ModuleDefLoader.loadFromClassPath(logger, compilerContext,
"com.google.gwt.dev.cfg.testdata.merging.One");
Set<String> visibleResourcePaths = one.getBuildResourceOracle().getPathNames();
// Sees the logo.png image.
assertTrue(visibleResourcePaths.contains(
"com/google/gwt/dev/cfg/testdata/merging/resources/logo.png"));
// But not the java source file.
assertFalse(visibleResourcePaths.contains(
"com/google/gwt/dev/cfg/testdata/merging/resources/NotAResource.java"));
}
public void testSeparateLibraryModuleReferences() throws UnableToCompleteException {
compilerContext = compilerContextBuilder.compileMonolithic(false).build();
ModuleDefLoader.loadFromClassPath(TreeLogger.NULL, compilerContext,
"com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne", false);
// The library writer was given the module and it's direct fileset module xml files as build
// resources.
assertEquals(Sets.newHashSet(
"com/google/gwt/dev/cfg/testdata/separate/filesetone/FileSetOne.gwt.xml",
"com/google/gwt/dev/cfg/testdata/separate/libraryone/LibraryOne.gwt.xml"),
mockLibraryWriter.getBuildResourcePaths());
// The library writer was given LibraryTwo as a dependency library.
assertEquals(Sets.newHashSet("com.google.gwt.core.Core",
"com.google.gwt.dev.cfg.testdata.separate.librarytwo.LibraryTwo"),
mockLibraryWriter.getDependencyLibraryNames());
}
public void testSeparateLibraryName() throws UnableToCompleteException {
compilerContext = compilerContextBuilder.compileMonolithic(false).build();
ModuleDefLoader.loadFromClassPath(TreeLogger.NULL, compilerContext,
"com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne", false);
assertEquals("com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne",
mockLibraryWriter.getLibraryName());
}
public void testSeparateModuleReferences() throws UnableToCompleteException {
compilerContext = compilerContextBuilder.compileMonolithic(false).build();
ModuleDef libraryOneModule = ModuleDefLoader.loadFromClassPath(TreeLogger.NULL, compilerContext,
"com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne", false);
// The module sees itself and it's direct fileset module as "target" modules.
assertEquals(Sets.newHashSet("com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne",
"com.google.gwt.dev.cfg.testdata.separate.filesetone.FileSetOne"),
libraryOneModule.getTargetLibraryCanonicalModuleNames());
// The module sees the referenced library module as a "library" module.
assertEquals(Sets.newHashSet("com.google.gwt.core.Core",
"com.google.gwt.dev.cfg.testdata.separate.librarytwo.LibraryTwo"),
libraryOneModule.getExternalLibraryCanonicalModuleNames());
}
public void testSeparateModuleResourcesLibraryOne() throws UnableToCompleteException {
compilerContext = compilerContextBuilder.compileMonolithic(false).build();
ModuleDef libraryOneModule = ModuleDefLoader.loadFromClassPath(TreeLogger.NULL, compilerContext,
"com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne", false);
// Includes own source.
assertNotNull(libraryOneModule.findSourceFile(
"com/google/gwt/dev/cfg/testdata/separate/libraryone/client/LibraryOne.java"));
// Cascades to include the subtree of fileset sources.
assertNotNull(libraryOneModule.findSourceFile(
"com/google/gwt/dev/cfg/testdata/separate/filesetone/client/FileSetOne.java"));
// Does not include source from referenced libraries.
assertNull(libraryOneModule.findSourceFile(
"com/google/gwt/dev/cfg/testdata/separate/librarytwo/client/LibraryTwo.java"));
}
public void testSeparateRootFilesetFail() {
compilerContext = compilerContextBuilder.compileMonolithic(false).build();
TreeLogger logger = TreeLogger.NULL;
try {
ModuleDefLoader.loadFromClassPath(logger,
compilerContext, "com.google.gwt.dev.cfg.testdata.separate.filesetone.FileSetOne", false);
fail("Expected a fileset loaded as the root of a module tree to fail, but it didn't.");
} catch (UnableToCompleteException e) {
// Expected behavior.
}
}
public void testWritesTargetLibraryProperties() throws UnableToCompleteException {
compilerContext = compilerContextBuilder.compileMonolithic(false).build();
ModuleDef libraryOneModule = ModuleDefLoader.loadFromClassPath(TreeLogger.NULL, compilerContext,
"com.google.gwt.dev.cfg.testdata.separate.libraryone.LibraryOne", false);
// Library one sees all defined values for the "libraryTwoProperty" binding property and knows
// which one was defined in this target library.
for (BindingProperty bindingProperty :
libraryOneModule.getProperties().getBindingProperties()) {
if (!bindingProperty.getName().equals("libraryTwoProperty")) {
continue;
}
assertEquals(Sets.newHashSet(bindingProperty.getDefinedValues()),
Sets.newHashSet("yes", "no", "maybe"));
}
// Library one sees all defined values for the "libraryTwoConfigProperty" property and knows
// which one was defined in this target library.
for (ConfigurationProperty configurationProperty :
libraryOneModule.getProperties().getConfigurationProperties()) {
if (!configurationProperty.getName().equals("libraryTwoConfigProperty")) {
continue;
}
assertEquals(Sets.newHashSet(configurationProperty.getValues()), Sets.newHashSet("false"));
assertEquals(Sets.newHashSet(configurationProperty.getTargetLibraryValues()),
Sets.newHashSet("false"));
}
}
private void assertErrorsWhenLoading(String moduleName, String... errorMessages) {
UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
builder.setLowestLogLevel(TreeLogger.WARN);
for (String errorMessage : errorMessages) {
builder.expectError(errorMessage, null);
UnitTestTreeLogger logger = builder.createLogger();
try {
ModuleDefLoader.loadFromClassPath(
logger, compilerContext, moduleName);
fail("Should have failed to load module.");
} catch (UnableToCompleteException e) {
// failure is expected.
}
logger.assertCorrectLogEntries();
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
ModuleDefLoader.getModulesCache().clear();
compilerContextBuilder = new CompilerContext.Builder();
compilerContext = compilerContextBuilder.libraryWriter(mockLibraryWriter).build();
}
}