Author: mhermanto
Date: Fri Apr  8 17:35:32 2011
New Revision: 1090358

URL: http://svn.apache.org/viewvc?rev=1090358&view=rev
Log:
Incremental JS loading. Be able to serve partial JS, given that some libs have 
been loaded. Idea: accumulate exports and externs of loaded JS libs (and their 
transitively-dependent libs) for externs to requested JS libs (and their 
transitively-dependent libs, minus explicitly-requested JS libs).
http://codereview.appspot.com/4376045/

Modified:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/CompilationProcessor.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/SeparatorCommentingProcessor.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/DefaultJsCompiler.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
    
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CajaJsSubtractingProcessorTest.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CompilationProcessorTest.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/CompilationProcessor.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/CompilationProcessor.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/CompilationProcessor.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/CompilationProcessor.java
 Fri Apr  8 17:35:32 2011
@@ -17,7 +17,6 @@
  */
 package org.apache.shindig.gadgets.js;
 
-import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 import org.apache.shindig.gadgets.features.ApiDirective;
@@ -39,16 +38,16 @@ public class CompilationProcessor implem
    */
   public boolean process(JsRequest request, JsResponseBuilder builder)
       throws JsException {
-    ImmutableList.Builder<String> externsBuilder = ImmutableList.builder();
-    for (JsContent jsc : builder.build().getAllJsContent()) {
+    Iterable<JsContent> jsContents = builder.build().getAllJsContent();
+    for (JsContent jsc : jsContents) {
       FeatureBundle bundle = jsc.getFeatureBundle();
       if (bundle != null) {
-        externsBuilder.addAll(bundle.getApis(ApiDirective.Type.JS, false));
+        builder.appendExterns(bundle.getApis(ApiDirective.Type.JS, false));
       }
     }
 
-    JsResponse result = compiler.compile(request.getJsUri(),
-        builder.build().getAllJsContent(), externsBuilder.build());
+    JsResponse result = compiler.compile(request.getJsUri(), jsContents,
+        builder.build().getExterns());
     builder.clearJs().appendAllJs(result.getAllJsContent());
     return true;
   }

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java
 Fri Apr  8 17:35:32 2011
@@ -18,27 +18,29 @@
 
 package org.apache.shindig.gadgets.js;
 
-import java.util.Collection;
-import java.util.List;
-
 import com.google.common.base.Joiner;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.inject.Inject;
 
 import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.features.ApiDirective;
 import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureRegistry.FeatureBundle;
 import org.apache.shindig.gadgets.features.FeatureResource;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.rewrite.js.JsCompiler;
 import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
 import org.apache.shindig.gadgets.uri.UriStatus;
 
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
 /**
  * Retrieves the requested Javascript code using a {@link JsProcessor}.
  */
 public class GetJsContentProcessor implements JsProcessor {
-  private static final Collection<String> EMPTY_SET = Sets.newHashSet();
   private static final Joiner UNKNOWN_FEATURE_ERR = Joiner.on(", ");
 
   private final FeatureRegistry registry;
@@ -56,58 +58,68 @@ public class GetJsContentProcessor imple
     // Get JavaScript content from features aliases request.
     JsUri jsUri = request.getJsUri();
     GadgetContext ctx = new JsGadgetContext(jsUri);
-    Collection<String> needed = jsUri.getLibs();
 
-    List<String> unsupported = Lists.newLinkedList();
-    FeatureRegistry.LookupResult lookup = registry.getFeatureResources(ctx, 
needed, unsupported);
-    if (!unsupported.isEmpty()) {
-      throw new JsException(HttpResponse.SC_BAD_REQUEST,
-          "Unknown feature(s): " + UNKNOWN_FEATURE_ERR.join(unsupported));
-    }
+    List<FeatureBundle> requestedBundles = getAllBundles(ctx, jsUri.getLibs(), 
false);
+    List<FeatureBundle> loadedBundles = getAllBundles(ctx, 
jsUri.getLoadedLibs(), true);
 
-    // Quick-and-dirty implementation of incremental JS loading.
-    Collection<String> alreadyLoaded = EMPTY_SET;
-    Collection<String> alreadyHaveLibs = jsUri.getLoadedLibs();
-    if (!alreadyHaveLibs.isEmpty()) {
-      alreadyLoaded = registry.getFeatures(alreadyHaveLibs);
+    Set<String> loadedFeatures = Sets.newHashSet();
+    for (FeatureBundle bundle : loadedBundles) {
+      loadedFeatures.add(bundle.getName());
+      builder.appendExterns(bundle.getApis(ApiDirective.Type.JS, true));
+      builder.appendExterns(bundle.getApis(ApiDirective.Type.JS, false));
     }
 
     // Collate all JS desired for the current request.
     boolean isProxyCacheable = true;
 
-    // Pre-process each feature.
-    for (FeatureRegistry.FeatureBundle bundle : lookup.getBundles()) {
-      if (alreadyLoaded.contains(bundle.getName())) continue;
+    for (FeatureBundle bundle : requestedBundles) {
+      // Exclude all transitively-dependent loaded features.
+      if (loadedFeatures.contains(bundle.getName())) {
+        continue;
+      }
       builder.appendAllJs(compiler.getJsContent(jsUri, bundle));
       for (FeatureResource featureResource : bundle.getResources()) {
         isProxyCacheable = isProxyCacheable && 
featureResource.isProxyCacheable();
       }
     }
+
     builder.setProxyCacheable(isProxyCacheable);
     setResponseCacheTtl(builder, jsUri.getStatus());
     return true;
   }
 
+  private List<FeatureBundle> getAllBundles(GadgetContext ctx, 
Collection<String> requested,
+      boolean loaded) throws JsException {
+    List<String> unsupported = Lists.newLinkedList();
+    FeatureRegistry.LookupResult lookup = registry.getFeatureResources(ctx, 
requested, unsupported);
+    if (!unsupported.isEmpty()) {
+      String message = loaded ? "loaded" : "requested";
+      throw new JsException(HttpResponse.SC_BAD_REQUEST,
+          "Unknown " + message + " feature(s): " + 
UNKNOWN_FEATURE_ERR.join(unsupported));
+    }
+    return lookup.getBundles();
+  }
+
   /**
    * Sets the cache TTL depending on the value of the {@link UriStatus} object.
    *
-   * @param resp The {@link JsResponseBuilder} object.
+   * @param builder The {@link JsResponseBuilder} object.
    * @param vstatus The {@link UriStatus} object.
    */
-  protected void setResponseCacheTtl(JsResponseBuilder resp, UriStatus 
vstatus) {
+  protected void setResponseCacheTtl(JsResponseBuilder builder, UriStatus 
vstatus) {
     switch (vstatus) {
       case VALID_VERSIONED:
         // Versioned files get cached indefinitely
-        resp.setCacheTtlSecs(-1);
+        builder.setCacheTtlSecs(-1);
         break;
       case VALID_UNVERSIONED:
         // Unversioned files get cached for 1 hour.
-        resp.setCacheTtlSecs(60 * 60);
+        builder.setCacheTtlSecs(60 * 60);
         break;
       case INVALID_VERSION:
         // URL is invalid in some way, likely version mismatch.
         // Indicate no-cache forcing subsequent requests to regenerate URLs.
-        resp.setCacheTtlSecs(0);
+        builder.setCacheTtlSecs(0);
         break;
     }
   }

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java
 Fri Apr  8 17:35:32 2011
@@ -18,24 +18,26 @@
 
 package org.apache.shindig.gadgets.js;
 
-import javax.servlet.http.HttpServletResponse;
-
+import com.google.common.base.Joiner;
 import com.google.common.collect.Lists;
 
-import java.util.LinkedList;
-
 import java.util.List;
 
+import javax.servlet.http.HttpServletResponse;
+
 /**
  * A class with methods to create {@link JsResponse} objects.
  */
 public class JsResponseBuilder {
-  private LinkedList<JsContent> jsCode;
+  private static final String EXTERN_DELIM = ";\n";
+  private static final Joiner EXTERN_JOINER = Joiner.on(EXTERN_DELIM);
+
+  private List<JsContent> jsCode;
   private final List<String> errors;
   private int statusCode;
   private int cacheTtlSecs;
   private boolean proxyCacheable;
-  private String externs;
+  private final StringBuilder externs;
 
   public JsResponseBuilder() {
     jsCode = Lists.newLinkedList();
@@ -43,23 +45,30 @@ public class JsResponseBuilder {
     cacheTtlSecs = 0;
     proxyCacheable = false;
     errors = Lists.newLinkedList();
-    externs = null;
+    externs = new StringBuilder();
   }
 
   public JsResponseBuilder(JsResponse response) {
-    jsCode = Lists.newLinkedList(response.getAllJsContent());
-    errors = Lists.newLinkedList(response.getErrors());
+    this();
+    if (response.getAllJsContent() != null) {
+      jsCode.addAll(Lists.newArrayList(response.getAllJsContent()));
+    }
+    if (response.getErrors() != null) {
+      errors.addAll(Lists.newArrayList(response.getErrors()));
+    }
+    if (response.getExterns() != null) {
+      externs.append(response.getExterns());
+    }
     statusCode = response.getStatusCode();
     cacheTtlSecs = response.getCacheTtlSecs();
     proxyCacheable = response.isProxyCacheable();
-    externs = response.getExterns();
   }
 
   /**
    * Prepend a JS to the response.
    */
   public JsResponseBuilder prependJs(JsContent jsContent) {
-    jsCode.addFirst(jsContent);
+    jsCode.add(0, jsContent);
     return this;
   }
 
@@ -100,7 +109,7 @@ public class JsResponseBuilder {
    * Prepends JS to the output.
    */
   public JsResponseBuilder prependJs(String content, String name) {
-    jsCode.addFirst(JsContent.fromText(content, name));
+    jsCode.add(0, JsContent.fromText(content, name));
     return this;
   }
 
@@ -176,10 +185,26 @@ public class JsResponseBuilder {
   }
 
   /**
-   * Sets whether the compiled externs.
+   * Appends externs as string.
+   */
+  public JsResponseBuilder appendExterns(String externs) {
+    this.externs.append(externs).append(EXTERN_DELIM);
+    return this;
+  }
+
+  /**
+   * Appends externs as from list of strings.
+   */
+  public JsResponseBuilder appendExterns(List<String> externs) {
+    return appendExterns(EXTERN_JOINER.join(externs));
+  }
+
+  /**
+   * Deletes all externs in the builder.
    */
-  public JsResponseBuilder setExterns(String externs) {
-    this.externs = externs;
+  public JsResponseBuilder clearExterns() {
+    int last = externs.length();
+    this.externs.delete(0, last);
     return this;
   }
 
@@ -188,6 +213,6 @@ public class JsResponseBuilder {
    */
   public JsResponse build() {
     return new JsResponse(jsCode, statusCode, cacheTtlSecs, proxyCacheable,
-        errors, externs);
+        errors, externs.toString());
   }
 }
\ No newline at end of file

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/SeparatorCommentingProcessor.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/SeparatorCommentingProcessor.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/SeparatorCommentingProcessor.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/SeparatorCommentingProcessor.java
 Fri Apr  8 17:35:32 2011
@@ -25,7 +25,7 @@ public class SeparatorCommentingProcesso
 
   public boolean process(JsRequest jsRequest, JsResponseBuilder builder) {
     ImmutableList.Builder<JsContent> jsBuilder = ImmutableList.builder();
-    
+
     FeatureBundle lastFeature = null;
     for (JsContent js : builder.build().getAllJsContent()) {
       FeatureBundle feature = js.getFeatureBundle();
@@ -53,7 +53,7 @@ public class SeparatorCommentingProcesso
     builder.clearJs().appendAllJs(jsBuilder.build());
     return true;
   }
-  
+
   private JsContent newComment(FeatureBundle bundle, boolean start) {
     String tag = start ? "start" : "end";
     return JsContent.fromFeature(

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/DefaultJsCompiler.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/DefaultJsCompiler.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/DefaultJsCompiler.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/DefaultJsCompiler.java
 Fri Apr  8 17:35:32 2011
@@ -50,7 +50,7 @@ public class DefaultJsCompiler implement
     return jsContent;
   }
 
-  public JsResponse compile(JsUri jsUri, Iterable<JsContent> content, 
List<String> externs) {
+  public JsResponse compile(JsUri jsUri, Iterable<JsContent> content, String 
externs) {
     return new JsResponseBuilder().appendAllJs(content).build();
   }
 

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
 Fri Apr  8 17:35:32 2011
@@ -49,6 +49,6 @@ public interface JsCompiler {
    * @param externs The externs.
    * @return A compilation result object.
    */
-  JsResponse compile(JsUri jsUri, Iterable<JsContent> content, List<String> 
externs)
+  JsResponse compile(JsUri jsUri, Iterable<JsContent> content, String externs)
       throws JsException;
 }

Modified: 
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
 Fri Apr  8 17:35:32 2011
@@ -114,21 +114,12 @@ public class ClosureJsCompiler implement
     return new Compiler(errorManager);
   }
 
-  private String toExternString(List<String> externs) {
-    StringBuilder builder = new StringBuilder();
-    for (String extern : externs) {
-      builder.append(extern).append(";\n");
-    }
-    return builder.toString();
-  }
-
-  public JsResponse compile(JsUri jsUri, Iterable<JsContent> content, 
List<String> externs) 
+  public JsResponse compile(JsUri jsUri, Iterable<JsContent> content, String 
externs) 
       throws JsException {
     JsResponse exportResponse = defaultCompiler.compile(jsUri, content, 
externs);
     content = exportResponse.getAllJsContent();
 
-    String externStr = toExternString(externs);
-    String cacheKey = makeCacheKey(exportResponse.toJsString(), externStr, 
jsUri);
+    String cacheKey = makeCacheKey(exportResponse.toJsString(), externs, 
jsUri);
     JsResponse cachedResult = cache.getElement(cacheKey);
     if (cachedResult != null) {
       lastResult = cachedResult;
@@ -141,7 +132,7 @@ public class ClosureJsCompiler implement
     CompilerOptions options = getCompilerOptions();
     if (!jsUri.isDebug() || options.isExternExportsEnabled()) {
       List<JSSourceFile> allExterns = Lists.newArrayList();
-      allExterns.add(JSSourceFile.fromCode("externs", externStr));
+      allExterns.add(JSSourceFile.fromCode("externs", externs));
 
       List<JsContent> allContent = Lists.newLinkedList(content);
       if (options.isExternExportsEnabled()) {
@@ -165,7 +156,7 @@ public class ClosureJsCompiler implement
         cache.addElement(cacheKey, errorResult);
         return errorResult;
       }
-      
+
       String compiled = actualCompiler.toSource();
       if (outputCorrelatedJs()) {
         // Emit code correlated w/ original source.
@@ -177,7 +168,7 @@ public class ClosureJsCompiler implement
         builder.appendJs(compiled, "[compiled]");
       }
 
-      builder.setExterns(result.externExport);
+      builder.clearExterns().appendExterns(result.externExport);
     } else {
       // Otherwise, return original content and null exports.
       builder.appendAllJs(content);

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CajaJsSubtractingProcessorTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CajaJsSubtractingProcessorTest.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CajaJsSubtractingProcessorTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CajaJsSubtractingProcessorTest.java
 Fri Apr  8 17:35:32 2011
@@ -49,7 +49,7 @@ public class CajaJsSubtractingProcessorT
   private List<JsContent> contents = Lists.newArrayList();
   private JsResponse response;
   private JsResponseBuilder builder;
-  
+
   private CajaJsSubtractingProcessor processor;
 
   @Before
@@ -64,10 +64,10 @@ public class CajaJsSubtractingProcessorT
         mockFeatureResource(ImmutableMap.of(UriCommon.Param.CAJOLE.getKey(), 
"blah"))));
     contents.add(JsContent.fromFeature(CAJA_CONTENT_JS, null, null,
         mockFeatureResource(ImmutableMap.of(UriCommon.Param.CAJOLE.getKey(), 
ATTRIB_VALUE))));
-    
+
     response = new JsResponse(contents, -1, -1, false, ERRORS, null);
     builder = new JsResponseBuilder(response);
-    
+
     processor = new CajaJsSubtractingProcessor();
   }
 

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CompilationProcessorTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CompilationProcessorTest.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CompilationProcessorTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/CompilationProcessorTest.java
 Fri Apr  8 17:35:32 2011
@@ -67,7 +67,7 @@ public class CompilationProcessorTest {
     JsRequest request = control.createMock(JsRequest.class);
     expect(request.getJsUri()).andReturn(jsUri);
     expect(compiler.compile(same(jsUri), eq(builder.build().getAllJsContent()),
-        eq(Lists.newArrayList("extern3", 
"extern4")))).andReturn(outputResponse);
+        eq("extern3;\n" + "extern4;\n"))).andReturn(outputResponse);
 
     control.replay();
     boolean status = processor.process(request, builder);
@@ -94,7 +94,7 @@ public class CompilationProcessorTest {
     JsRequest request = control.createMock(JsRequest.class);
     expect(request.getJsUri()).andReturn(jsUri);
     List<String> emptyList = ImmutableList.of();
-    expect(compiler.compile(same(jsUri), 
eq(builder.build().getAllJsContent()), eq(emptyList)))
+    expect(compiler.compile(same(jsUri), 
eq(builder.build().getAllJsContent()), eq("")))
         .andThrow(new JsException(400, "foo"));
     control.replay();
     processor.process(request, builder);

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java?rev=1090358&r1=1090357&r2=1090358&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java
 Fri Apr  8 17:35:32 2011
@@ -40,14 +40,17 @@ import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-
+import org.apache.shindig.gadgets.features.ApiDirective;
 
 /**
  * Tests for {@link GetJsContentProcessor}.
  */
 public class GetJsContentProcessorTest {
-  private static final String JS_CODE1 = "some JS data";
-  private static final String JS_CODE2 = "some JS data";
+  private static final String JS_CODE1 = "js1";
+  private static final String JS_CODE2 = "js2";
+
+  private static final List<String> EMPTY_STRING_LIST = 
ImmutableList.<String>of();
+  private static final List<FeatureBundle> EMPTY_BUNDLE_LIST = 
ImmutableList.<FeatureBundle>of();
 
   private IMocksControl control;
   private FeatureRegistry registry;
@@ -70,88 +73,136 @@ public class GetJsContentProcessorTest {
 
   @Test
   public void testPopulatesResponseForUnversionedRequest() throws Exception {
-    setExpectations(true, UriStatus.VALID_UNVERSIONED);
+    setupForVersionAndProxy(true, UriStatus.VALID_UNVERSIONED);
     control.replay();
     processor.process(request, response);
-    checkResponse(true, 3600);
+    checkResponse(true, 3600, JS_CODE1 + JS_CODE2, "");
     control.verify();
   }
 
   @Test
   public void testPopulatesResponseForVersionedRequest() throws Exception {
-    setExpectations(true, UriStatus.VALID_VERSIONED);
+    setupForVersionAndProxy(true, UriStatus.VALID_VERSIONED);
     control.replay();
     processor.process(request, response);
-    checkResponse(true, -1);
+    checkResponse(true, -1, JS_CODE1 + JS_CODE2, "");
     control.verify();
   }
 
   @Test
   public void testPopulatesResponseForInvalidVersion() throws Exception {
-    setExpectations(true, UriStatus.INVALID_VERSION);
+    setupForVersionAndProxy(true, UriStatus.INVALID_VERSION);
     control.replay();
     processor.process(request, response);
-    checkResponse(true, 0);
+    checkResponse(true, 0, JS_CODE1 + JS_CODE2, "");
     control.verify();
   }
 
   @Test
   public void testPopulatesResponseForNoProxyCacheable() throws Exception {
-    setExpectations(false, UriStatus.VALID_UNVERSIONED);
+    setupForVersionAndProxy(false, UriStatus.VALID_UNVERSIONED);
     control.replay();
     processor.process(request, response);
-    checkResponse(false, 3600);
+    checkResponse(false, 3600, JS_CODE1 + JS_CODE2, "");
     control.verify();
   }
 
-  private void setExpectations(boolean proxyCacheable, UriStatus uriStatus) 
throws JsException {
+  @Test
+  public void testPopulateWithLoadedFeatures() throws Exception {
+    List<String> reqLibs = ImmutableList.of("feature1", "feature2");
+    List<String> loadLibs = ImmutableList.of("feature2");
+
+    FeatureResource resource1 = mockResource(true);
+    FeatureBundle bundle1 = mockBundle("feature1", null, null, 
Lists.newArrayList(resource1));
+    FeatureBundle bundle2 = mockBundle("feature2", "export2", "extern2", null);
+
+    setupJsUriAndRegistry(UriStatus.VALID_UNVERSIONED,
+        reqLibs, ImmutableList.of(bundle1, bundle2),
+        loadLibs, ImmutableList.of(bundle2));
+
+    expect(compiler.getJsContent(jsUri, bundle1))
+      .andReturn(ImmutableList.<JsContent>of(
+          JsContent.fromFeature(JS_CODE1, null, null, null)));
+
+    control.replay();
+    processor.process(request, response);
+    checkResponse(true, 3600, JS_CODE1, "export2;\nextern2;\n");
+    control.verify();
+  }
+
+  private void setupForVersionAndProxy(boolean proxyCacheable, UriStatus 
uriStatus) {
+    List<String> reqLibs = ImmutableList.of("feature");
+    List<String> loadLibs = EMPTY_STRING_LIST;
+
+    FeatureResource resource1 = mockResource(proxyCacheable);
+    FeatureResource resource2 = mockResource(proxyCacheable);
+    FeatureBundle bundle = mockBundle("feature", null, null,
+        Lists.newArrayList(resource1, resource2));
+
+    setupJsUriAndRegistry(uriStatus,
+        reqLibs, Lists.newArrayList(bundle),
+        loadLibs, EMPTY_BUNDLE_LIST);
+
+    expect(compiler.getJsContent(jsUri, bundle))
+        .andReturn(ImmutableList.<JsContent>of(
+            JsContent.fromFeature(JS_CODE1, null, bundle, resource1),
+            JsContent.fromFeature(JS_CODE2, null, bundle, resource2)));
+  }
+
+  private void setupJsUriAndRegistry(UriStatus uriStatus,
+      List<String> reqLibs, List<FeatureBundle> reqLookupBundles,
+      List<String> loadLibs, List<FeatureBundle> loadLookupBundles) {
+
     expect(jsUri.getStatus()).andReturn(uriStatus);
-    List<String> libs = ImmutableList.of("feature1", "feature2");
-    expect(jsUri.getLibs()).andReturn(libs);
     expect(jsUri.getContainer()).andReturn("container");
     expect(jsUri.getContext()).andReturn(RenderingContext.CONFIGURED_GADGET);
     expect(jsUri.isDebug()).andReturn(false);
-    expect(jsUri.getLoadedLibs()).andReturn(ImmutableList.<String>of());
+    expect(jsUri.getLibs()).andReturn(reqLibs);
+    expect(jsUri.getLoadedLibs()).andReturn(loadLibs);
+
     expect(request.getJsUri()).andReturn(jsUri);
 
-    List<FeatureBundle> bundles = mockBundles(proxyCacheable);
-    LookupResult lr = control.createMock(LookupResult.class);
-    expect(lr.getBundles()).andReturn(bundles);
-
-    expect(registry.getFeatureResources(isA(JsGadgetContext.class), eq(libs),
-        eq(ImmutableList.<String>of()))).andReturn(lr);
-    expect(compiler.getJsContent(jsUri, bundles.get(0)))
-        .andReturn(ImmutableList.<JsContent>of(
-            JsContent.fromFeature(JS_CODE1, "source1", bundles.get(0), null)));
-    expect(compiler.getJsContent(jsUri, bundles.get(1)))
-        .andReturn(ImmutableList.<JsContent>of(
-            JsContent.fromFeature(JS_CODE2, "source2", bundles.get(1), null)));
+    LookupResult reqLookup = mockLookupResult(reqLookupBundles);
+    LookupResult loadLookup = mockLookupResult(loadLookupBundles);
+
+    expect(registry.getFeatureResources(isA(JsGadgetContext.class), 
eq(reqLibs),
+        eq(EMPTY_STRING_LIST))).andReturn(reqLookup);
+    expect(registry.getFeatureResources(isA(JsGadgetContext.class), 
eq(loadLibs),
+        eq(EMPTY_STRING_LIST))).andReturn(loadLookup);
   }
 
-  private List<FeatureBundle> mockBundles(boolean proxyCacheable) {
-    FeatureBundle bundle1 = control.createMock(FeatureBundle.class);
-    expect(bundle1.getName()).andReturn("feature1");
-    FeatureResource resource1 = control.createMock(FeatureResource.class);
-    expect(resource1.isProxyCacheable()).andReturn(proxyCacheable);
-    List<FeatureResource> resources1 = Lists.newArrayList(resource1);
-    expect(bundle1.getResources()).andReturn(resources1);
-
-    FeatureBundle bundle2 = control.createMock(FeatureBundle.class);
-    expect(bundle2.getName()).andReturn("feature2");
-    FeatureResource resource2 = control.createMock(FeatureResource.class);
-    if (proxyCacheable) {
-      // Only consulted if the first bundle/resource is proxyCacheable.
-      expect(resource2.isProxyCacheable()).andReturn(proxyCacheable);
-    }
-    List<FeatureResource> resources2 = Lists.newArrayList(resource2);
-    expect(bundle2.getResources()).andReturn(resources2);
+  private LookupResult mockLookupResult(List<FeatureBundle> bundles) {
+    LookupResult result = control.createMock(LookupResult.class);
+    expect(result.getBundles()).andReturn(bundles);
+    return result;
+  }
+
+  private FeatureResource mockResource(boolean proxyCacheable) {
+    FeatureResource result = control.createMock(FeatureResource.class);
+    expect(result.isProxyCacheable()).andReturn(proxyCacheable).anyTimes();
+    return result;
+  }
 
-    return Lists.newArrayList(bundle1, bundle2);
+  private FeatureBundle mockBundle(String name, String export, String extern, 
List<FeatureResource> resources) {
+    FeatureBundle result = control.createMock(FeatureBundle.class);
+    expect(result.getName()).andReturn(name).anyTimes();
+    if (export != null) {
+      expect(result.getApis(ApiDirective.Type.JS, 
true)).andReturn(ImmutableList.of(export));
+    }
+    if (extern != null) {
+      expect(result.getApis(ApiDirective.Type.JS, 
false)).andReturn(ImmutableList.of(extern));
+    }
+    if (resources != null) {
+      expect(result.getResources()).andReturn(resources);
+    }
+    return result;
   }
 
-  private void checkResponse(boolean proxyCacheable, int expectedTtl) {
+  private void checkResponse(boolean proxyCacheable, int expectedTtl,
+      String jsString, String externsString) {
     assertEquals(proxyCacheable, response.isProxyCacheable());
     assertEquals(expectedTtl, response.getCacheTtlSecs());
-    assertEquals(JS_CODE1 + JS_CODE2, response.build().toJsString());
+    assertEquals(jsString, response.build().toJsString());
+    assertEquals(externsString, response.build().getExterns());
   }
 }


Reply via email to