You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by mh...@apache.org on 2011/05/06 03:57:07 UTC

svn commit: r1100024 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/js/ test/java/org/apache/shindig/gadgets/js/

Author: mhermanto
Date: Fri May  6 01:57:06 2011
New Revision: 1100024

URL: http://svn.apache.org/viewvc?rev=1100024&view=rev
Log:
JSL beacon now keeps track of loaded features.
http://codereview.appspot.com/4486043/

Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessor.java
      - copied, changed from r1099980, shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessor.java
      - copied, changed from r1099942, shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessor.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessorTest.java
      - copied, changed from r1099980, shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessorTest.java
      - copied, changed from r1099942, shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessorTest.java
Removed:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessor.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessorTest.java
Modified:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java

Copied: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessor.java (from r1099980, shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessor.java)
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessor.java?p2=shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessor.java&p1=shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessor.java&r1=1099980&r2=1100024&rev=1100024&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessor.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessor.java Fri May  6 01:57:06 2011
@@ -22,7 +22,7 @@ import com.google.common.annotations.Vis
 
 import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
 
-public class AddJsLoadCallbackProcessor implements JsProcessor {
+public class AddJslCallbackProcessor implements JsProcessor {
   private static final String CODE_ID = "[jsload-callback]";
   
   @VisibleForTesting

Copied: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessor.java (from r1099942, shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessor.java)
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessor.java?p2=shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessor.java&p1=shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessor.java&r1=1099942&r2=1100024&rev=1100024&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessor.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessor.java Fri May  6 01:57:06 2011
@@ -36,7 +36,7 @@ import java.util.List;
  * 
  * Used when loading embedded JS configuration in core.config/config.js.
  */
-public class InjectJsInfoVariableProcessor implements JsProcessor {
+public class AddJslInfoVariableProcessor implements JsProcessor {
   private static final String CODE_ID = "[jsload-code-info]";
 
   @VisibleForTesting
@@ -48,7 +48,7 @@ public class InjectJsInfoVariableProcess
   private final JsUriManager jsUriManager;
 
   @Inject
-  public InjectJsInfoVariableProcessor(JsUriManager jsUriManager) {
+  public AddJslInfoVariableProcessor(JsUriManager jsUriManager) {
     this.jsUriManager = jsUriManager;
   }
 

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessor.java?rev=1100024&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessor.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessor.java Fri May  6 01:57:06 2011
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.js;
+
+import com.google.common.annotations.VisibleForTesting;
+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.commons.lang.StringEscapeUtils;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureRegistry.FeatureBundle;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Injects a global ___jsl.l variable with information about the JS request.
+ * 
+ * Used when loading embedded JS configuration in core.config/config.js.
+ */
+public class AddJslLoadedVariableProcessor implements JsProcessor {
+  private static final String CODE_ID = "[jsload-loaded-info]";
+  private static final Joiner UNKNOWN_FEATURE_ERR = Joiner.on(", ");
+
+  @VisibleForTesting
+  static final String TEMPLATE =
+      "window['___jsl']['l'] = (window['___jsl']['l'] || []).concat(%s);";
+
+  private final FeatureRegistry registry;
+
+  @Inject
+  public AddJslLoadedVariableProcessor(FeatureRegistry featureRegistry) {
+    this.registry = featureRegistry;
+  }
+
+  public boolean process(JsRequest jsRequest, JsResponseBuilder builder) throws JsException {
+    JsUri jsUri = jsRequest.getJsUri();
+    if (!jsUri.isNohint()) {
+      Set<String> result = getBundleNames(jsUri, false);
+      result.removeAll(getBundleNames(jsUri, true));
+      String array = toArrayString(result);
+      builder.appendJs(String.format(TEMPLATE, array), CODE_ID);
+    }
+    return true;
+  }
+
+  // TODO: factor this logic into somewhere shared. it's now used in GetJsContentProcessor.
+  private Set<String> getBundleNames(JsUri jsUri, boolean loaded) throws JsException {
+    GadgetContext ctx = new JsGadgetContext(jsUri);
+    Collection<String> libs = loaded ? jsUri.getLoadedLibs() : jsUri.getLibs(); 
+    List<String> unsupported = Lists.newLinkedList();
+    FeatureRegistry.LookupResult lookup = registry.getFeatureResources(ctx, libs, 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));
+    }
+    Set<String> ret = Sets.newLinkedHashSet(); // ordered set for testability.
+    for (FeatureBundle bundle : lookup.getBundles()) {
+      ret.add(bundle.getName());
+    }
+    return ret;
+  }
+  
+  private String toArrayString(Set<String> bundles) {
+    StringBuilder builder = new StringBuilder();
+    for (String bundle : bundles) {
+      if (builder.length() > 0) builder.append(",");
+      builder.append("'").append(StringEscapeUtils.escapeJavaScript(bundle)).append("'");
+    }
+    return "[" + builder.toString() + "]";
+  }
+}

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java?rev=1100024&r1=1100023&r2=1100024&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java Fri May  6 01:57:06 2011
@@ -38,7 +38,7 @@ public class JsServingPipelineModule ext
   @Provides
   @Inject
   public List<JsProcessor> provideProcessors(
-      InjectJsInfoVariableProcessor injectJsInfoVariableProcessor,
+      AddJslInfoVariableProcessor addJslInfoVariableProcessor,
       JsLoadProcessor jsLoaderGeneratorProcessor,
       IfModifiedSinceProcessor ifModifiedSinceProcessor,
       GetJsContentProcessor getJsContentProcessor,
@@ -46,12 +46,13 @@ public class JsServingPipelineModule ext
       ExportJsProcessor exportJsProcessor,
       SeparatorCommentingProcessor separatorCommentingProcessor,
       ConfigInjectionProcessor configInjectionProcessor,
+      AddJslLoadedVariableProcessor addJslLoadedVariableProcessor,
       AddOnloadFunctionProcessor addOnloadFunctionProcessor,
-      AddJsLoadCallbackProcessor addJsLoadCallbackProcessor,
+      AddJslCallbackProcessor addJslCallbackProcessor,
       CompilationProcessor compilationProcessor,
       AnonFuncWrappingProcessor anonFuncProcessor) {
     return ImmutableList.of(
-        injectJsInfoVariableProcessor,
+        addJslInfoVariableProcessor,
         jsLoaderGeneratorProcessor,
         ifModifiedSinceProcessor,
         getJsContentProcessor,
@@ -59,8 +60,9 @@ public class JsServingPipelineModule ext
         exportJsProcessor,
         separatorCommentingProcessor,
         configInjectionProcessor,
+        addJslLoadedVariableProcessor,
         addOnloadFunctionProcessor,
-        addJsLoadCallbackProcessor,
+        addJslCallbackProcessor,
         compilationProcessor,
         anonFuncProcessor);
   }

Copied: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessorTest.java (from r1099980, shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessorTest.java)
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessorTest.java?p2=shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessorTest.java&p1=shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessorTest.java&r1=1099980&r2=1100024&rev=1100024&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJsLoadCallbackProcessorTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslCallbackProcessorTest.java Fri May  6 01:57:06 2011
@@ -26,13 +26,13 @@ import org.easymock.IMocksControl;
 import org.junit.Before;
 import org.junit.Test;
 
-public class AddJsLoadCallbackProcessorTest {
+public class AddJslCallbackProcessorTest {
   
   private IMocksControl control;
   private JsUri jsUri;
   private JsRequest request;
   private JsResponseBuilder response;
-  private AddJsLoadCallbackProcessor processor;  
+  private AddJslCallbackProcessor processor;  
   
   @Before
   public void setUp() {
@@ -40,7 +40,7 @@ public class AddJsLoadCallbackProcessorT
     jsUri = control.createMock(JsUri.class);
     request = control.createMock(JsRequest.class);
     response = new JsResponseBuilder();
-    processor = new AddJsLoadCallbackProcessor();
+    processor = new AddJslCallbackProcessor();
     EasyMock.expect(request.getJsUri()).andReturn(jsUri);
   }
 
@@ -49,7 +49,7 @@ public class AddJsLoadCallbackProcessorT
     EasyMock.expect(jsUri.isNohint()).andReturn(false);
     control.replay();
     assertTrue(processor.process(request, response));
-    assertEquals(AddJsLoadCallbackProcessor.JSL_CALLBACK_JS,
+    assertEquals(AddJslCallbackProcessor.JSL_CALLBACK_JS,
         response.build().toJsString());
     control.verify();
   }

Copied: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessorTest.java (from r1099942, shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessorTest.java)
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessorTest.java?p2=shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessorTest.java&p1=shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessorTest.java&r1=1099942&r2=1100024&rev=1100024&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/InjectJsInfoVariableProcessorTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslInfoVariableProcessorTest.java Fri May  6 01:57:06 2011
@@ -36,9 +36,9 @@ import java.util.List;
 
 
 /**
- * Tests for {@link InjectJsInfoVariableProcessor}.
+ * Tests for {@link AddJslInfoVariableProcessor}.
  */
-public class InjectJsInfoVariableProcessorTest {
+public class AddJslInfoVariableProcessorTest {
 
   private static final String URI = "http://localhost";
   private static final List<String> LIBS = ImmutableList.of("fo'o", "bar", "baz");
@@ -52,7 +52,7 @@ public class InjectJsInfoVariableProcess
   private JsUriManager jsUriManager;
   private JsUri jsUri;
   private JsResponseBuilder response;
-  private InjectJsInfoVariableProcessor processor;
+  private AddJslInfoVariableProcessor processor;
 
   @Before
   public void setUp() {
@@ -60,7 +60,7 @@ public class InjectJsInfoVariableProcess
     request = control.createMock(JsRequest.class);
     jsUriManager = control.createMock(JsUriManager.class);
     response = new JsResponseBuilder();
-    processor = new InjectJsInfoVariableProcessor(jsUriManager);
+    processor = new AddJslInfoVariableProcessor(jsUriManager);
   }
 
   @Test
@@ -77,7 +77,7 @@ public class InjectJsInfoVariableProcess
     setJsUri(URI);
     control.replay();
     processor.process(request, response);
-    String expected = String.format(InjectJsInfoVariableProcessor.HINT_TEMPLATE, URI_JS, LIBS_JS);
+    String expected = String.format(AddJslInfoVariableProcessor.HINT_TEMPLATE, URI_JS, LIBS_JS);
     assertEquals(expected, response.build().toJsString());
     control.verify();    
   }
@@ -89,7 +89,7 @@ public class InjectJsInfoVariableProcess
     EasyMock.expect(jsUriManager.makeExternJsUri(EasyMock.capture(captureJsUri))).andReturn(Uri.parse(GENERATED_URI));
     control.replay();
     processor.process(request, response);
-    String expected = String.format(InjectJsInfoVariableProcessor.HINT_TEMPLATE, GENERATED_URI_JS, LIBS_JS);
+    String expected = String.format(AddJslInfoVariableProcessor.HINT_TEMPLATE, GENERATED_URI_JS, LIBS_JS);
     assertEquals(expected, response.build().toJsString());
     assertFalse(captureJsUri.getValue().isJsload());
     assertTrue(captureJsUri.getValue().isNohint());

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessorTest.java?rev=1100024&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessorTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddJslLoadedVariableProcessorTest.java Fri May  6 01:57:06 2011
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.js;
+
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureRegistry.FeatureBundle;
+import org.apache.shindig.gadgets.features.FeatureRegistry.LookupResult;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.apache.shindig.gadgets.uri.UriStatus;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Tests for {@link AddJslLoadedVariableProcessor}.
+ */
+public class AddJslLoadedVariableProcessorTest {
+  private static final List<String> EMPTY_STRING_LIST = ImmutableList.<String>of();
+
+  private static final String REQ_1_LIB = "foo";
+  private static final String REQ_1_DEP_LIB = "bar";
+  private static final String REQ_2_LIB = "gig";
+  private static final String LOAD_LIB = "bar";
+  private static final String URI = "http://localhost";
+  private static final List<String> REQ_LIBS = ImmutableList.of(
+      REQ_1_LIB, REQ_2_LIB, REQ_1_LIB, REQ_2_LIB);
+  private static final List<String> LOAD_LIBS = ImmutableList.of(
+      LOAD_LIB, LOAD_LIB);
+
+  private IMocksControl control;
+  private JsRequest request;
+  private FeatureRegistry registry;
+  private JsUri jsUri;
+  private JsResponseBuilder response;
+  private AddJslLoadedVariableProcessor processor;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    request = control.createMock(JsRequest.class);
+    registry = control.createMock(FeatureRegistry.class);
+    response = new JsResponseBuilder();
+    processor = new AddJslLoadedVariableProcessor(registry);
+  }
+
+  @Test
+  public void testSucceeds() throws Exception {
+    setUpJsUri(URI + "/" + REQ_1_LIB + ".js");
+    setUpRegistry();
+    control.replay();
+    processor.process(request, response);
+    assertEquals(String.format(AddJslLoadedVariableProcessor.TEMPLATE,
+        "['foo','gig']"), response.build().toJsString());
+    control.verify();
+  }
+
+  @Test
+  public void testSkips() throws Exception {
+    setUpJsUri(URI + "?nohint=1");
+    control.replay();
+    processor.process(request, response);
+    assertEquals("", response.build().toJsString());
+    control.verify();
+  }
+
+  private void setUpJsUri(String uri) {
+    jsUri = new JsUri(UriStatus.VALID_UNVERSIONED, Uri.parse(uri), REQ_LIBS, LOAD_LIBS);
+    EasyMock.expect(request.getJsUri()).andReturn(jsUri);
+  }
+
+  private void setUpRegistry() {
+    FeatureBundle bundle1 = mockBundle(REQ_1_LIB);
+    FeatureBundle bundle2 = mockBundle(REQ_1_DEP_LIB);
+    FeatureBundle bundle3 = mockBundle(REQ_2_LIB);
+    FeatureBundle bundle4 = mockBundle(LOAD_LIB);
+    LookupResult reqResult = mockLookupResult(Lists.newArrayList(bundle1, bundle2, bundle3));
+    expect(registry.getFeatureResources(isA(JsGadgetContext.class), eq(REQ_LIBS),
+        eq(EMPTY_STRING_LIST))).andReturn(reqResult);
+    LookupResult loadResult = mockLookupResult(Lists.newArrayList(bundle4));
+    expect(registry.getFeatureResources(isA(JsGadgetContext.class), eq(LOAD_LIBS),
+        eq(EMPTY_STRING_LIST))).andReturn(loadResult);
+  }
+
+  private LookupResult mockLookupResult(List<FeatureBundle> bundles) {
+    LookupResult result = control.createMock(LookupResult.class);
+    expect(result.getBundles()).andReturn(bundles);
+    return result;
+  }
+
+  private FeatureBundle mockBundle(String name) {
+    FeatureBundle result = control.createMock(FeatureBundle.class);
+    expect(result.getName()).andReturn(name);
+    return result;
+  }
+}