diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index 373ee8ecb9b..24876dfc099 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -13,7 +13,9 @@ import javax.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.concurrent.TimeUnit; import android.content.Context; @@ -28,7 +30,8 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.common.network.OkHttpCallUtil; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; import com.facebook.react.modules.systeminfo.AndroidInfoHelpers; -import com.facebook.react.packagerconnection.JSPackagerWebSocketClient; +import com.facebook.react.packagerconnection.JSPackagerClient; +import com.facebook.react.packagerconnection.ReconnectingWebSocket; import okhttp3.Call; import okhttp3.Callback; @@ -86,7 +89,7 @@ public class DevServerHelper { public interface PackagerCommandListener { void onPackagerReloadCommand(); void onCaptureHeapCommand(); - void onPokeSamplingProfilerCommand(@Nullable final JSPackagerWebSocketClient.WebSocketSender webSocket); + void onPokeSamplingProfilerCommand(@Nullable final ReconnectingWebSocket.WebSocketSender webSocket); } private final DevInternalSettings mSettings; @@ -94,7 +97,7 @@ public class DevServerHelper { private final Handler mRestartOnChangePollingHandler; private boolean mOnChangePollingEnabled; - private @Nullable JSPackagerWebSocketClient mPackagerConnection; + private @Nullable JSPackagerClient mPackagerClient; private @Nullable InspectorPackagerConnection mInspectorPackagerConnection; private @Nullable OkHttpClient mOnChangePollingClient; private @Nullable OnServerContentChangeListener mOnServerContentChangeListener; @@ -112,32 +115,40 @@ public class DevServerHelper { } public void openPackagerConnection(final PackagerCommandListener commandListener) { - if (mPackagerConnection != null) { + if (mPackagerClient != null) { FLog.w(ReactConstants.TAG, "Packager connection already open, nooping."); return; } new AsyncTask() { @Override protected Void doInBackground(Void... params) { - mPackagerConnection = new JSPackagerWebSocketClient(getPackagerConnectionURL(), - new JSPackagerWebSocketClient.JSPackagerCallback() { - @Override - public void onMessage( - @Nullable JSPackagerWebSocketClient.WebSocketSender webSocket, - String target, - String action) { - if (commandListener != null && "bridge".equals(target)) { - if ("reload".equals(action)) { - commandListener.onPackagerReloadCommand(); - } else if ("captureHeap".equals(action)) { - commandListener.onCaptureHeapCommand(); - } else if ("pokeSamplingProfiler".equals(action)) { - commandListener.onPokeSamplingProfilerCommand(webSocket); - } - } - } - }); - mPackagerConnection.connect(); + Map handlers = + new HashMap(); + handlers.put("reload", new JSPackagerClient.RequestHandler() { + @Override + public void onNotification( + @Nullable ReconnectingWebSocket.WebSocketSender webSocket) { + commandListener.onPackagerReloadCommand(); + } + }); + handlers.put("captureHeap", new JSPackagerClient.RequestHandler() { + @Override + public void onNotification( + @Nullable ReconnectingWebSocket.WebSocketSender webSocket) { + commandListener.onCaptureHeapCommand(); + } + }); + handlers.put("pokeSamplingProfiler", new JSPackagerClient.RequestHandler() { + @Override + public void onNotification( + @Nullable ReconnectingWebSocket.WebSocketSender webSocket) { + commandListener.onPokeSamplingProfilerCommand(webSocket); + } + }); + + mPackagerClient = new JSPackagerClient(getPackagerConnectionURL(), handlers); + mPackagerClient.init(); + return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -147,9 +158,9 @@ public class DevServerHelper { new AsyncTask() { @Override protected Void doInBackground(Void... params) { - if (mPackagerConnection != null) { - mPackagerConnection.closeQuietly(); - mPackagerConnection = null; + if (mPackagerClient != null) { + mPackagerClient.close(); + mPackagerClient = null; } return null; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java index 95e3d9a7023..bd34a37f9ad 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java @@ -56,7 +56,7 @@ import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; import com.facebook.react.devsupport.interfaces.StackFrame; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; -import com.facebook.react.packagerconnection.JSPackagerWebSocketClient; +import com.facebook.react.packagerconnection.ReconnectingWebSocket; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -694,7 +694,7 @@ public class DevSupportManagerImpl implements @Override public void onPokeSamplingProfilerCommand( - @Nullable final JSPackagerWebSocketClient.WebSocketSender webSocket) { + @Nullable final ReconnectingWebSocket.WebSocketSender webSocket) { UiThreadUtil.runOnUiThread(new Runnable() { @Override public void run() { @@ -710,7 +710,7 @@ public class DevSupportManagerImpl implements } private void handlePokeSamplingProfiler( - @Nullable JSPackagerWebSocketClient.WebSocketSender webSocket) { + @Nullable ReconnectingWebSocket.WebSocketSender webSocket) { try { List pokeResults = JSCSamplingProfiler.poke(60000); for (String result : pokeResults) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java new file mode 100644 index 00000000000..5e08ee9ac54 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerClient.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the LICENSE file in the root + * directory of this source tree. An additional grant of patent rights can be found in the PATENTS + * file in the same directory. + */ + +package com.facebook.react.packagerconnection; + +import javax.annotation.Nullable; + +import java.io.IOException; +import java.util.Map; + +import android.util.JsonReader; +import android.util.JsonToken; + +import com.facebook.common.logging.FLog; + +import okhttp3.ResponseBody; +import okhttp3.ws.WebSocket; + +/** + * A client for packager that uses WebSocket connection. + */ +final public class JSPackagerClient implements ReconnectingWebSocket.MessageCallback { + private static final String TAG = JSPackagerClient.class.getSimpleName(); + + public interface RequestHandler { + public void onNotification(@Nullable ReconnectingWebSocket.WebSocketSender webSocket); + } + + private ReconnectingWebSocket mWebSocket; + private Map mRequestHandlers; + + public JSPackagerClient(String url, Map requestHandlers) { + super(); + mWebSocket = new ReconnectingWebSocket(url, this); + mRequestHandlers = requestHandlers; + } + + public void init() { + mWebSocket.connect(); + } + + public void close() { + mWebSocket.closeQuietly(); + } + + @Override + public void onMessage(@Nullable ReconnectingWebSocket.WebSocketSender webSocket, ResponseBody response) { + if (response.contentType() != WebSocket.TEXT) { + FLog.w(TAG, "Websocket received unexpected message with payload of type " + response.contentType()); + return; + } + + try { + JsonReader reader = new JsonReader(response.charStream()); + + Integer version = null; + String target = null; + String action = null; + + reader.beginObject(); + while (reader.hasNext()) { + String field = reader.nextName(); + + if (JsonToken.NULL == reader.peek()) { + reader.skipValue(); + continue; + } + + if ("version".equals(field)) { + version = reader.nextInt(); + } else if ("target".equals(field)) { + target = reader.nextString(); + } else if ("action".equals(field)) { + action = reader.nextString(); + } + } + reader.close(); + + if (version == null || target == null || action == null || version != 1) { + return; + } + + if ("bridge".equals(target) && mRequestHandlers.containsKey(action)) { + mRequestHandlers.get(action).onNotification(webSocket); + } + } catch (IOException e) { + FLog.e(TAG, "Parsing response message from websocket failed", e); + } finally { + response.close(); + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerWebSocketClient.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java similarity index 54% rename from ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerWebSocketClient.java rename to ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java index d5e905afed7..40ec85951fd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/JSPackagerWebSocketClient.java +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java @@ -15,8 +15,6 @@ import java.util.concurrent.TimeUnit; import android.os.Handler; import android.os.Looper; -import android.util.JsonReader; -import android.util.JsonToken; import com.facebook.common.logging.FLog; @@ -31,19 +29,14 @@ import okhttp3.ws.WebSocketListener; import okio.Buffer; /** - * A wrapper around WebSocketClient that recognizes packager's message format. + * A wrapper around WebSocketClient that reconnects automatically */ -public class JSPackagerWebSocketClient implements WebSocketListener { - private static final String TAG = "JSPackagerWebSocketClient"; +final public class ReconnectingWebSocket implements WebSocketListener { + private static final String TAG = ReconnectingWebSocket.class.getSimpleName(); private static final int RECONNECT_DELAY_MS = 2000; - private final String mUrl; - private final Handler mHandler; - private boolean mClosed = false; - private boolean mSuppressConnectionErrors; - - final public class WebSocketSender { + static public class WebSocketSender { private WebSocket mWebSocket; public WebSocketSender(WebSocket webSocket) { @@ -55,14 +48,18 @@ public class JSPackagerWebSocketClient implements WebSocketListener { } } - public interface JSPackagerCallback { - void onMessage(@Nullable WebSocketSender webSocket, String target, String action); + public interface MessageCallback { + void onMessage(@Nullable WebSocketSender webSocket, ResponseBody message); } + private final String mUrl; + private final Handler mHandler; + private boolean mClosed = false; + private boolean mSuppressConnectionErrors; private @Nullable WebSocket mWebSocket; - private @Nullable JSPackagerCallback mCallback; + private @Nullable MessageCallback mCallback; - public JSPackagerWebSocketClient(String url, JSPackagerCallback callback) { + public ReconnectingWebSocket(String url, MessageCallback callback) { super(); mUrl = url; mCallback = callback; @@ -73,33 +70,40 @@ public class JSPackagerWebSocketClient implements WebSocketListener { if (mClosed) { throw new IllegalStateException("Can't connect closed client"); } - OkHttpClient httpClient = new OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read - .build(); + + OkHttpClient httpClient = new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read + .build(); Request request = new Request.Builder().url(mUrl).build(); WebSocketCall call = WebSocketCall.create(httpClient, request); call.enqueue(this); } + private synchronized void delayedReconnect() { + // check that we haven't been closed in the meantime + if (!mClosed) { + connect(); + } + } + private void reconnect() { if (mClosed) { throw new IllegalStateException("Can't reconnect closed client"); } + if (!mSuppressConnectionErrors) { - FLog.w(TAG, "Couldn't connect to packager, will silently retry"); + FLog.w(TAG, "Couldn't connect to \"" + mUrl + "\", will silently retry"); mSuppressConnectionErrors = true; } + mHandler.postDelayed( new Runnable() { @Override public void run() { - // check that we haven't been closed in the meantime - if (!mClosed) { - connect(); - } + delayedReconnect(); } }, RECONNECT_DELAY_MS); @@ -108,6 +112,7 @@ public class JSPackagerWebSocketClient implements WebSocketListener { public void closeQuietly() { mClosed = true; closeWebSocketQuietly(); + mCallback = null; } private void closeWebSocketQuietly() { @@ -121,65 +126,19 @@ public class JSPackagerWebSocketClient implements WebSocketListener { } } - private void triggerMessageCallback(String target, String action) { - if (mCallback != null) { - WebSocketSender webSocketSender = mWebSocket == null - ? null - : new WebSocketSender(mWebSocket); - mCallback.onMessage(webSocketSender, target, action); - } + private void abort(String message, Throwable cause) { + FLog.e(TAG, "Error occurred, shutting down websocket connection: " + message, cause); + closeWebSocketQuietly(); } @Override - public void onMessage(ResponseBody response) throws IOException { - if (response.contentType() != WebSocket.TEXT) { - FLog.w(TAG, "Websocket received unexpected message with payload of type " + response.contentType()); - return; - } - - try { - JsonReader reader = new JsonReader(response.charStream()); - - Integer version = null; - String target = null; - String action = null; - - reader.beginObject(); - while (reader.hasNext()) { - String field = reader.nextName(); - - if (JsonToken.NULL == reader.peek()) { - reader.skipValue(); - continue; - } - - if ("version".equals(field)) { - version = reader.nextInt(); - } else if ("target".equals(field)) { - target = reader.nextString(); - } else if ("action".equals(field)) { - action = reader.nextString(); - } - } - reader.close(); - - if (version == null || version != 1) { - return; - } - if (target == null || action == null) { - return; - } - - triggerMessageCallback(target, action); - } catch (IOException e) { - abort("Parsing response message from websocket failed", e); - } finally { - response.close(); - } + public synchronized void onOpen(WebSocket webSocket, Response response) { + mWebSocket = webSocket; + mSuppressConnectionErrors = false; } @Override - public void onFailure(IOException e, Response response) { + public synchronized void onFailure(IOException e, Response response) { if (mWebSocket != null) { abort("Websocket exception", e); } @@ -189,26 +148,23 @@ public class JSPackagerWebSocketClient implements WebSocketListener { } @Override - public void onOpen(WebSocket webSocket, Response response) { - mWebSocket = webSocket; - mSuppressConnectionErrors = false; + public synchronized void onMessage(ResponseBody message) { + if (mCallback != null) { + WebSocketSender webSocketSender = mWebSocket == null + ? null + : new WebSocketSender(mWebSocket); + mCallback.onMessage(webSocketSender, message); + } } @Override - public void onClose(int code, String reason) { + public synchronized void onPong(Buffer payload) { } + + @Override + public synchronized void onClose(int code, String reason) { mWebSocket = null; if (!mClosed) { reconnect(); } } - - @Override - public void onPong(Buffer payload) { - // ignore - } - - private void abort(String message, Throwable cause) { - FLog.e(TAG, "Error occurred, shutting down websocket connection: " + message, cause); - closeWebSocketQuietly(); - } } diff --git a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java new file mode 100644 index 00000000000..dcca178ae0d --- /dev/null +++ b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerClientTest.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.packagerconnection; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import okhttp3.ResponseBody; +import okhttp3.ws.WebSocket; + +import static org.mockito.Mockito.*; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class JSPackagerClientTest { + private static Map createRH( + String action, JSPackagerClient.RequestHandler handler) { + Map m = + new HashMap(); + m.put(action, handler); + return m; + } + + @Test + public void test_onMessage_ShouldTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.TEXT, + "{\"version\": 1, \"target\": \"bridge\", \"action\": \"actionValue\"}")); + verify(handler).onNotification(wbs); + } + + @Test + public void test_onMessage_WithInvalidContentType_ShouldNotTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.BINARY, + "{\"version\": 1, \"target\": \"bridge\", \"action\": \"actionValue\"}")); + verify(handler, never()).onNotification(wbs); + } + + @Test + public void test_onMessage_WithoutTarget_ShouldNotTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.TEXT, + "{\"version\": 1, \"action\": \"actionValue\"}")); + verify(handler, never()).onNotification(wbs); + } + + @Test + public void test_onMessage_With_Null_Target_ShouldNotTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.TEXT, + "{\"version\": 1, \"target\": null, \"action\": \"actionValue\"}")); + verify(handler, never()).onNotification(wbs); + } + + @Test + public void test_onMessage_WithoutAction_ShouldNotTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.TEXT, + "{\"version\": 1, \"target\": \"bridge\"}")); + verify(handler, never()).onNotification(wbs); + } + + @Test + public void test_onMessage_With_Null_Action_ShouldNotTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.TEXT, + "{\"version\": 1, \"target\": \"bridge\", \"action\": null}")); + verify(handler, never()).onNotification(wbs); + } + + @Test + public void test_onMessage_WrongVersion_ShouldNotTriggerCallback() throws IOException { + JSPackagerClient.RequestHandler handler = mock(JSPackagerClient.RequestHandler.class); + final JSPackagerClient client = new JSPackagerClient("ws://not_needed", createRH("actionValue", handler)); + WebSocket webSocket = mock(WebSocket.class); + ReconnectingWebSocket.WebSocketSender wbs = new ReconnectingWebSocket.WebSocketSender(webSocket); + + client.onMessage( + wbs, + ResponseBody.create( + WebSocket.TEXT, + "{\"version\": 2, \"target\": \"bridge\", \"action\": \"actionValue\"}")); + verify(handler, never()).onNotification(wbs); + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerWebSocketClientTest.java b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerWebSocketClientTest.java deleted file mode 100644 index 4e6c885ce2f..00000000000 --- a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/JSPackagerWebSocketClientTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -package com.facebook.react.packagerconnection; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.IOException; - -import okhttp3.ResponseBody; -import okhttp3.ws.WebSocket; - -import static org.mockito.Mockito.*; -import org.robolectric.RobolectricTestRunner; - -@RunWith(RobolectricTestRunner.class) -public class JSPackagerWebSocketClientTest { - - @Test - public void test_onMessage_ShouldTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.TEXT, - "{\"version\": 1, \"target\": \"targetValue\", \"action\": \"actionValue\"}")); - verify(callback).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), eq("targetValue"), eq("actionValue")); - } - - @Test - public void test_onMessage_WithInvalidContentType_ShouldNotTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.BINARY, - "{\"version\": 1, \"target\": \"targetValue\", \"action\": \"actionValue\"}")); - verify(callback, never()).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), anyString(), anyString()); - } - - @Test - public void test_onMessage_WithoutTarget_ShouldNotTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.TEXT, - "{\"version\": 1, \"action\": \"actionValue\"}")); - verify(callback, never()).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), anyString(), anyString()); - } - - @Test - public void test_onMessage_With_Null_Target_ShouldNotTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.TEXT, - "{\"version\": 1, \"target\": null, \"action\": \"actionValue\"}")); - verify(callback, never()).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), anyString(), anyString()); - } - - @Test - public void test_onMessage_WithoutAction_ShouldNotTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.TEXT, - "{\"version\": 1, \"target\": \"targetValue\"}")); - verify(callback, never()).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), anyString(), anyString()); - } - - @Test - public void test_onMessage_With_Null_Action_ShouldNotTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.TEXT, - "{\"version\": 1, \"target\": \"targetValue\", \"action\": null}")); - verify(callback, never()).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), anyString(), anyString()); - } - - @Test - public void test_onMessage_WrongVersion_ShouldNotTriggerCallback() throws IOException { - final JSPackagerWebSocketClient.JSPackagerCallback callback = - mock(JSPackagerWebSocketClient.JSPackagerCallback.class); - final JSPackagerWebSocketClient client = new JSPackagerWebSocketClient("ws://not_needed", callback); - client.onMessage(ResponseBody.create( - WebSocket.TEXT, - "{\"version\": 2, \"target\": \"targetValue\", \"action\": \"actionValue\"}")); - verify(callback, never()).onMessage(any(JSPackagerWebSocketClient.WebSocketSender.class), anyString(), anyString()); - } -}