blob: 433c38adfbaa5afabe65a0dc3181309df52d3bc6 [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.core.ext.linker.impl;
import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.ConfigurationProperty;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.dev.Permutation;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.jjs.PermutationResult;
import junit.framework.TestCase;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A regular JUnit test case for {@link SelectionScriptLinker}.
*/
public class SelectionScriptLinkerUnitTest extends TestCase {
private static class MockLinkerContext implements LinkerContext {
public SortedSet<ConfigurationProperty> getConfigurationProperties() {
return new TreeSet<ConfigurationProperty>();
}
public String getModuleFunctionName() {
return MOCK_MODULE_NAME;
}
public long getModuleLastModified() {
return MOCK_MODULE_LAST_MODIFIED;
}
public String getModuleName() {
return MOCK_MODULE_NAME;
}
public SortedSet<SelectionProperty> getProperties() {
return new TreeSet<SelectionProperty>();
}
public boolean isOutputCompact() {
return true;
}
public String optimizeJavaScript(TreeLogger logger, String jsProgram) {
return jsProgram;
}
}
private static class MockPermutationResult implements PermutationResult {
private ArtifactSet artifacts = new ArtifactSet();
public void addArtifacts(Collection<? extends Artifact<?>> newArtifacts) {
artifacts.addAll(newArtifacts);
}
public ArtifactSet getArtifacts() {
return artifacts;
}
public byte[][] getJs() {
return new byte[][] {
getBytes("code for fragment 0"), getBytes("code for fragment 1")};
}
public Permutation getPermutation() {
return new Permutation(0, new StaticPropertyOracle(
new BindingProperty[0], new String[0],
new com.google.gwt.dev.cfg.ConfigurationProperty[0]));
}
public byte[] getSerializedSymbolMap() {
return getBytes("symbol map");
}
public StatementRanges[] getStatementRanges() {
ArrayList<StatementRanges> ranges = new ArrayList<StatementRanges>();
for (byte[] js : getJs()) {
ranges.add(new MockStatementRanges(js.length));
}
return ranges.toArray(new StatementRanges[0]);
}
}
private static class MockStatementRanges implements StatementRanges {
private int length;
MockStatementRanges(int length) {
this.length = length;
}
public int end(int i) {
return length;
}
public int numStatements() {
return 1;
}
public int start(int i) {
return 0;
}
}
private static class NonShardableSelectionScriptLinker extends
SelectionScriptLinker {
@Override
public String getDescription() {
return getClass().getName();
}
@Override
protected String getCompilationExtension(TreeLogger logger,
LinkerContext context) {
return ".js";
}
@Override
protected String getModulePrefix(TreeLogger logger, LinkerContext context,
String strongName) {
return "MODULE_PREFIX";
}
@Override
protected String getModuleSuffix(TreeLogger logger, LinkerContext context) {
return "MODULE_SUFFIX";
}
@Override
protected String getSelectionScriptTemplate(TreeLogger logger,
LinkerContext context) {
return SelectionScriptLinkerUnitTest.class.getPackage().getName().replace(
'.', '/')
+ "/MockTemplate.js";
}
}
@Shardable
private static class ShardableSelectionScriptLinker extends
NonShardableSelectionScriptLinker {
}
private static final long MOCK_MODULE_LAST_MODIFIED = 1234;
private static final String MOCK_MODULE_NAME = "test";
private static byte[] getBytes(String string) {
try {
return string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Test that running a non-shardable linker in simulated sharding mode does
* not lose the permutations.
*/
public void testNonShardableHasPermutations()
throws UnableToCompleteException {
ArtifactSet artifacts = new ArtifactSet();
StandardCompilationResult result = createCompilationResult();
artifacts.add(result);
ArtifactSet updated = new NonShardableSelectionScriptLinker().link(
TreeLogger.NULL, new MockLinkerContext(), artifacts);
SortedSet<SelectionInformation> selectionInfos = updated.find(SelectionInformation.class);
assertEquals(1, selectionInfos.size());
}
/**
* Test timestamps on the selection script. For hosted mode, it should match
* the module's timestamp. For dev mode, it should be current.
*/
public void testTimestampOnSelectionScript() throws UnableToCompleteException {
// hosted
{
ArtifactSet artifacts = new ArtifactSet();
ArtifactSet updated = new ShardableSelectionScriptLinker().link(
TreeLogger.NULL, new MockLinkerContext(), artifacts, false);
EmittedArtifact selectionScript = findSelectionScript(updated);
assertEquals(MOCK_MODULE_LAST_MODIFIED, selectionScript.getLastModified());
}
// compiled
{
ArtifactSet artifacts = new ArtifactSet();
artifacts.add(createCompilationResult());
ArtifactSet updated = new ShardableSelectionScriptLinker().link(
TreeLogger.NULL, new MockLinkerContext(), artifacts, true);
updated = transferThinning(updated);
updated = new ShardableSelectionScriptLinker().link(TreeLogger.NULL,
new MockLinkerContext(), updated, false);
EmittedArtifact selectionScript = findSelectionScript(updated);
assertTrue(MOCK_MODULE_LAST_MODIFIED != selectionScript.getLastModified());
}
}
private StandardCompilationResult createCompilationResult() {
StandardCompilationResult result = new StandardCompilationResult(
new MockPermutationResult());
result.addSelectionPermutation(new TreeMap<SelectionProperty, String>());
result.addSoftPermutation(Collections.<SelectionProperty, String> emptyMap());
return result;
}
private EmittedArtifact findEmittedArtifact(ArtifactSet updated,
String partialPath) {
for (EmittedArtifact art : updated.find(EmittedArtifact.class)) {
if (art.getPartialPath().equals(partialPath)) {
return art;
}
}
return null;
}
private EmittedArtifact findSelectionScript(ArtifactSet updated) {
return findEmittedArtifact(updated, MOCK_MODULE_NAME + ".nocache.js");
}
/**
* Thin down the artifact set as if it were being transmitted back. The method
* in Link cannot be used because it wants a StandardLinkerContext, not an
* arbitrary LinkerContext.
*/
@SuppressWarnings("unchecked")
private ArtifactSet transferThinning(ArtifactSet artifacts) {
ArtifactSet updated = new ArtifactSet();
// The raw type Artifact is to work around a Java compiler bug:
// http://bugs.sun.com/view_bug.do?bug_id=6548436
for (Artifact art : artifacts) {
if (art.isTransferableFromShards() || (art instanceof EmittedArtifact)) {
updated.add(art);
}
}
return updated;
}
}