SelectionScriptLinker was setting the wrong timestamp on
the generated selection script when used in production mode.
It mistakenly thought it was in dev mode.

Review at http://gwt-code-reviews.appspot.com/287801

Review by: scottb@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7824 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 9580913..cb62803 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -216,7 +216,7 @@
      * mode, we just set it to now.
      */
     long lastModified;
-    if (artifacts.find(CompilationResult.class).size() == 0) {
+    if (propMapsByPermutation.isEmpty()) {
       lastModified = context.getModuleLastModified();
     } else {
       lastModified = System.currentTimeMillis();
diff --git a/user/test/com/google/gwt/core/ext/linker/impl/SelectionScriptLinkerUnitTest.java b/user/test/com/google/gwt/core/ext/linker/impl/SelectionScriptLinkerUnitTest.java
index a1b3bf4..433c38a 100644
--- a/user/test/com/google/gwt/core/ext/linker/impl/SelectionScriptLinkerUnitTest.java
+++ b/user/test/com/google/gwt/core/ext/linker/impl/SelectionScriptLinkerUnitTest.java
@@ -22,7 +22,9 @@
 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;
@@ -43,54 +45,22 @@
  * A regular JUnit test case for {@link SelectionScriptLinker}.
  */
 public class SelectionScriptLinkerUnitTest extends TestCase {
-  private static class NonShardableSelectionScriptLinker extends
-      SelectionScriptLinker {
-    @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";
-    }
-
-    @Override
-    public String getDescription() {
-      return getClass().getName();
-    }
-  }
-
   private static class MockLinkerContext implements LinkerContext {
+
     public SortedSet<ConfigurationProperty> getConfigurationProperties() {
       return new TreeSet<ConfigurationProperty>();
     }
 
     public String getModuleFunctionName() {
-      return "test";
+      return MOCK_MODULE_NAME;
     }
 
     public long getModuleLastModified() {
-      return 0;
+      return MOCK_MODULE_LAST_MODIFIED;
     }
 
     public String getModuleName() {
-      return "test";
+      return MOCK_MODULE_NAME;
     }
 
     public SortedSet<SelectionProperty> getProperties() {
@@ -161,6 +131,48 @@
     }
   }
 
+  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");
@@ -177,10 +189,7 @@
       throws UnableToCompleteException {
     ArtifactSet artifacts = new ArtifactSet();
 
-    StandardCompilationResult result = new StandardCompilationResult(
-        new MockPermutationResult());
-    result.addSelectionPermutation(new TreeMap<SelectionProperty, String>());
-    result.addSoftPermutation(Collections.<SelectionProperty, String> emptyMap());
+    StandardCompilationResult result = createCompilationResult();
     artifacts.add(result);
 
     ArtifactSet updated = new NonShardableSelectionScriptLinker().link(
@@ -189,4 +198,72 @@
     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;
+  }
 }