/* * 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.thirdparty.guava.common.collect.Lists; import junit.framework.TestCase; import java.io.UnsupportedEncodingException; 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 { @Override public SortedSet<ConfigurationProperty> getConfigurationProperties() { return new TreeSet<ConfigurationProperty>(); } @Override public String getModuleFunctionName() { return MOCK_MODULE_NAME; } @Override public long getModuleLastModified() { return MOCK_MODULE_LAST_MODIFIED; } @Override public String getModuleName() { return MOCK_MODULE_NAME; } @Override public SortedSet<SelectionProperty> getProperties() { return new TreeSet<SelectionProperty>(); } @Override public boolean isOutputCompact() { return true; } @Override public String optimizeJavaScript(TreeLogger logger, String jsProgram) { return jsProgram; } } 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 getModuleSuffix2(TreeLogger logger, LinkerContext context, String strongName) { 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 Development Mode, it should match * the module's timestamp. For Production Mode, it should be current. */ public void testTimestampOnSelectionScript() throws UnableToCompleteException { // Development Mode { 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()); } // Production Mode { 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() { byte[][] js = new byte[][] {getBytes("code for fragment 0"), getBytes("code for fragment 1")}; String jsStrongName = "fooStrongName"; StatementRanges[] statementRanges = new StatementRanges[] { new StandardStatementRanges(Lists.newArrayList(0), Lists.newArrayList(10)), new StandardStatementRanges(Lists.newArrayList(0), Lists.newArrayList(20))}; byte[] symbolMapBytes = getBytes("symbol map"); StandardCompilationResult result = new StandardCompilationResult( new MockPermutationResult(js, jsStrongName, statementRanges, symbolMapBytes)); 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; } }