/** * 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 org.waveprotocol.wave.client.gadget.renderer; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.junit.client.GWTTestCase; /** * Tests Gadget Controller functions that relay JavaScript calls to GWT. * */ public class ControllerGwtTest extends GWTTestCase { private static class TestController extends Controller { private final static JavaScriptObject LIBRARY = getFakeRpcLibrary(); /** * Test controller constructor. */ public TestController() { super(LIBRARY); } /** * Issues a simulated gadget RPC call at JS level. * * @param gadgetId ID of the gadget that would generate this call. * @param service name of the service. * @param args call arguments. */ public void simulateCallFromGadget(String gadgetId, String service, String ... args) { JsArrayMixed arr = JsArrayMixed.create(); for (int i = 0; i < args.length; ++i) { arr.put(i, args[i]); } simulateCallFromGadgetHelper(gadgetId, service, arr); } /** * Issues a simulated gadget RPC call at JS level. * * @param gadgetId ID of the gadget that would generate this call. * @param service name of the service. * @param args call arguments. */ public void simulateCallFromGadget(String gadgetId, String service, JavaScriptObject ... args) { JsArrayMixed arr = JsArrayMixed.create(); for (int i = 0; i < args.length; ++i) { arr.put(i, args[i]); } simulateCallFromGadgetHelper(gadgetId, service, arr); } private native void simulateCallFromGadgetHelper(String gadgetId, String service, JsArrayMixed args) /*-{ var hub = @org.waveprotocol.wave.client.gadget.renderer.ControllerGwtTest.TestController::LIBRARY; hub.callFrom(gadgetId, service, null, args); }-*/; /** * Sets singleton value for testing. This is necessary evil because the * registered JS functions require a reference to the Controller instance in * the static context. Because of that a completely clean testing of the * controller is not possible. * TODO(user): Explore other options for binding JS and Java contexts. * * @param controller controller that substitutes the singleton. */ public static void setTestInstance(Controller controller) { instance = controller; } /** * Returns a fake gadget RPC library. * * @return gadget RPC library as JS object. */ private static native JavaScriptObject getFakeRpcLibrary() /*-{ return function() { var serviceRegistry = {}; return { // Registers a callback function for the given service. register: function(serviceName, handler) { serviceRegistry[serviceName] = handler; }, // Simulates call from a gadget. callFrom: function(from, serviceName, callback, var_args) { var rpc = { f: from, s: serviceName, c: callback ? 1 : 0, a: var_args }; (serviceRegistry[serviceName]).apply(rpc, rpc.a); }, // Simulates call to a gadget. Called from within the Controller. // Bounces the call back to the '..' listener with the target ID // set as the only argument. call: function(targetId, serviceName, callback, var_args) { var rpc = { f: '..', s: serviceName, c: callback ? 1 : 0, a: [targetId] }; (serviceRegistry[serviceName]).apply(rpc, rpc.a); } }; }(); }-*/; } /** * Test gadget RPC listener with methods to receive service calls and verify * the results. * * TODO(user): Convert to mockito implementation. */ private static class Listener implements GadgetRpcListener { private String gadgetTitle = ""; private int setTitleCounter = 0; private String[] prefKeyValue = {}; private int setPrefCounter = 0; private String iframeHeight = ""; private int setIframeHeightCounter = 0; private String iframeWidth = ""; private int setIframeWidthCounter = 0; private String navigateUrl = ""; private int requestNavigateToCounter = 0; private String podiumState = ""; private int updatePodiumStateCounter = 0; private String apiVersion = ""; private int waveEnableCounter = 0; private JavaScriptObject stateDelta = JavaScriptObject.createObject(); private int waveGadgetStateUpdateCounter = 0; private JavaScriptObject statePrivateDelta = JavaScriptObject.createObject(); private int wavePrivateGadgetStateUpdateCounter = 0; private String logMessage = ""; private int logMessageCounter = 0; private String gadgetSnippet = ""; private int setSnippetCounter = 0; @Override public void setTitle(String title) { gadgetTitle = title; setTitleCounter++; } @Override public void setPrefs(String ... keyValue) { prefKeyValue = keyValue; setPrefCounter++; } @Override public void setIframeHeight(String height) { iframeHeight = height; setIframeHeightCounter++; } @Override public void setIframeWidth(String width) { iframeWidth = width; setIframeWidthCounter++; } @Override public void requestNavigateTo(String url) { navigateUrl = url; requestNavigateToCounter++; } @Override public void updatePodiumState(String state) { podiumState = state; updatePodiumStateCounter++; } @Override public void waveEnable(String waveApiVersion) { apiVersion = waveApiVersion; waveEnableCounter++; } @Override public void waveGadgetStateUpdate(JavaScriptObject delta) { stateDelta = delta; waveGadgetStateUpdateCounter++; } @Override public void logMessage(String message) { logMessage = message; logMessageCounter++; } @Override public void wavePrivateGadgetStateUpdate(JavaScriptObject delta) { statePrivateDelta = delta; wavePrivateGadgetStateUpdateCounter++; } @Override public void setSnippet(String snippet) { gadgetSnippet = snippet; setSnippetCounter++; } /** * Verify set title results. * * @param title * @param counter */ public void assertSetTitle(String title, int counter) { assertEquals(title, gadgetTitle); assertEquals(counter, setTitleCounter); } /** * Verify set preferences results. * * @param counter * @param keyValue */ public void assertSetPref(int counter, String ... keyValue) { if (keyValue == null) { assertTrue((prefKeyValue == null) || (prefKeyValue.length == 0)); } else if (prefKeyValue == null) { assertTrue((keyValue == null) || (keyValue.length == 0)); } else { assertEquals(keyValue.length, prefKeyValue.length); for (int i = 0; i < keyValue.length; ++i) { assertEquals(keyValue[i], prefKeyValue[i]); } } assertEquals(counter, setPrefCounter); } /** * Verify set IFrame height results. * @param height * @param counter */ public void assertSetIframeHeight(String height, int counter) { assertEquals(height, iframeHeight); assertEquals(counter, setIframeHeightCounter); } /** * Verify set IFrame width results. * @param width * @param counter */ public void assertSetIframeWidth(String width, int counter) { assertEquals(width, iframeWidth); assertEquals(counter, setIframeWidthCounter); } /** * Verify request navigate to results. * * @param url * @param counter */ public void assertRequestNavigateTo(String url, int counter) { assertEquals(url, navigateUrl); assertEquals(counter, requestNavigateToCounter); } /** * Verify update Podium state results. * * @param state * @param counter */ public void assertUpdatePodiumState(String state, int counter) { assertEquals(state, podiumState); assertEquals(counter, updatePodiumStateCounter); } /** * Verify wave enable results. * * @param waveApiVersion * @param counter */ public void assertWaveEnable(String waveApiVersion, int counter) { assertEquals(waveApiVersion, apiVersion); assertEquals(counter, waveEnableCounter); } /** * Verify wave gadget state update results. * * @param delta * @param counter */ public void assertWaveGadgetStateUpdate(JavaScriptObject delta, int counter) { assertEquals(delta.toString(), stateDelta.toString()); assertEquals(counter, waveGadgetStateUpdateCounter); } /** * Verify wave gadget state update results. * * @param delta * @param counter */ public void assertWavePrivateGadgetStateUpdate(JavaScriptObject delta, int counter) { assertEquals(delta.toString(), statePrivateDelta.toString()); assertEquals(counter, wavePrivateGadgetStateUpdateCounter); } /** * Verify logMessage results. * * @param message * @param counter */ public void assertLogMessage(String message, int counter) { assertEquals(message, logMessage); assertEquals(counter, logMessageCounter); } /** * Verify setSnippet results. * * @param snippet * @param counter */ public void assertSetSnippet(String snippet, int counter) { assertEquals(snippet, gadgetSnippet); assertEquals(counter, setSnippetCounter); } } private static native JavaScriptObject getFakeStateObject() /*-{ return {"state":"test"}; }-*/; private static native JavaScriptObject getFakePrivateStateObject() /*-{ return {"private_state":"private_test"}; }-*/; /** * Runs a mixed chain of service calls to make sure individual calls are * correct and do not interfere. */ public void testController() { TestController controller = new TestController(); TestController.setTestInstance(controller); Listener listener1 = new Listener(); Listener listener2 = new Listener(); Listener hostListener = new Listener(); controller.registerGadgetListener("1", listener1); controller.registerGadgetListener("2", listener2); controller.registerGadgetListener("..", hostListener); // Run a mix of service calls. controller.simulateCallFromGadget("1", "set_title", "Title 1"); listener1.assertSetTitle("Title 1", 1); controller.simulateCallFromGadget("2", "set_title", "Title 2"); listener2.assertSetTitle("Title 2", 1); controller.simulateCallFromGadget("1", "set_title", "Title 1 again"); controller.simulateCallFromGadget("2", "set_title", "Title 2 again"); controller.call("Target ID", "set_title", callArgument(null)); controller.simulateCallFromGadget( "1", "set_pref", "ignoredToken", "pref 1", "value 1", "pref 2", "value 2"); controller.simulateCallFromGadget("2", "resize_iframe", "200"); controller.simulateCallFromGadget("2", "setIframeWidth", "100"); controller.simulateCallFromGadget("1", "requestNavigateTo", "url 1"); controller.simulateCallFromGadget("2", "updateState", "state 2"); controller.simulateCallFromGadget("1", "wave_enable", "1.0"); controller.simulateCallFromGadget("2", "wave_gadget_state", getFakeStateObject()); controller.simulateCallFromGadget("1", "wave_log", "Log it!"); controller.simulateCallFromGadget("2", "set_snippet", "Snip it!"); controller.simulateCallFromGadget( "1", "wave_private_gadget_state", getFakePrivateStateObject()); // Verify the service call results. listener1.assertSetTitle("Title 1 again", 2); listener2.assertSetTitle("Title 2 again", 2); hostListener.assertSetTitle("Target ID", 1); listener1.assertSetPref(1, "pref 1", "value 1", "pref 2", "value 2"); listener2.assertSetPref(0); listener1.assertSetIframeHeight("", 0); listener1.assertSetIframeWidth("", 0); listener2.assertSetIframeHeight("200", 1); listener2.assertSetIframeWidth("100", 1); listener1.assertRequestNavigateTo("url 1", 1); listener2.assertRequestNavigateTo("", 0); listener1.assertUpdatePodiumState("", 0); listener2.assertUpdatePodiumState("state 2", 1); listener1.assertWaveEnable("1.0", 1); listener2.assertWaveEnable("", 0); listener1.assertWaveGadgetStateUpdate(JavaScriptObject.createObject(), 0); listener2.assertWaveGadgetStateUpdate(getFakeStateObject(), 1); listener1.assertLogMessage("Log it!", 1); listener2.assertLogMessage("", 0); listener1.assertSetSnippet("", 0); listener2.assertSetSnippet("Snip it!", 1); listener1.assertWavePrivateGadgetStateUpdate(getFakePrivateStateObject(), 1); controller.simulateCallFromGadget("2", "set_pref", "ignoredToken"); listener2.assertSetPref(1); controller.simulateCallFromGadget("2", "set_pref", "ignoredToken", "pref 1", "value 1"); listener2.assertSetPref(2, "pref 1", "value 1"); } @Override public String getModuleName() { return "org.waveprotocol.wave.client.gadget.tests"; } @Override protected void gwtTearDown() { TestController.setTestInstance(null); } /** * @param argument Call argument. * @returns Call argument as JsArrayMixed. */ private native Controller.JsArrayMixed callArgument(String argument) /*-{ return [ argument ]; }-*/; }