From 0e6d5f603b9b0fb9ae9a2844f74e596d9b66e691 Mon Sep 17 00:00:00 2001 From: Joe Hansche Date: Fri, 24 Mar 2017 16:26:34 -0700 Subject: [PATCH 1/2] Add tests for hetergeneous subscriptions Ensures that we are able to subscribe to multiple ParseQuery, regardless of class. The current tests work fine, because the client is declared as ParseLiveQueryClient, so subclasses are accepted. But many method parameters have to be cast to more specific (or in some cases, less specific) class types. The problem with this approach is that you are forced to use the top-level `ParseLiveQueryClient` type parameter, which means all other type parameters must match `ParseObject`, including `ParseQuery`, and the callback events all have to take `ParseObject` and then it would have to be cast to the desired heterogeneous types. The goal would be that the type parameter is only enforced during subscription, not at the Client declaration level: ParseLiveQueryClient client = ... handle1 = client.subscribe(ParseQuery.getQuery(Subclass1.class)); handle2 = client.subscribe(ParseQuery.getQuery(Subclass2.class)); In its current state, `handle1` and `handle2` would be forced to use an event callback locked to ``, and cast the objects to `Subclass1` and `Subclass2`. OR the only other option is to create two separate ParseLiveQueryClient objects (which therefore requires multiple websocket connections). --- .../com/parse/TestParseLiveQueryClient.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java index fc60c45..8c01e89 100644 --- a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java +++ b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java @@ -163,6 +163,48 @@ public void testErrorWhenSubscribedToCallback() throws Exception { assertEquals(serverError.isReconnect(), true); } + @Test + public void testHeterogeneousSubscriptions() throws Exception { + ParseObject.registerSubclass(MockClassA.class); + ParseObject.registerSubclass(MockClassB.class); + + ParseQuery query1 = ParseQuery.getQuery(MockClassA.class); + ParseQuery query2 = ParseQuery.getQuery(MockClassB.class); + SubscriptionHandling handle1 = parseLiveQueryClient.subscribe((ParseQuery) query1); + SubscriptionHandling handle2 = parseLiveQueryClient.subscribe((ParseQuery) query2); + + handle1.handleError(new SubscriptionHandling.HandleErrorCallback() { + @Override + public void onError(ParseQuery query, LiveQueryException exception) { + throw new RuntimeException(exception); + } + }); + handle2.handleError(new SubscriptionHandling.HandleErrorCallback() { + @Override + public void onError(ParseQuery query, LiveQueryException exception) { + throw new RuntimeException(exception); + } + }); + + SubscriptionHandling.HandleEventCallback eventMockCallback1 = mock(SubscriptionHandling.HandleEventCallback.class); + SubscriptionHandling.HandleEventCallback eventMockCallback2 = mock(SubscriptionHandling.HandleEventCallback.class); + + handle1.handleEvent(SubscriptionHandling.Event.CREATE, eventMockCallback1); + handle2.handleEvent(SubscriptionHandling.Event.CREATE, eventMockCallback2); + + ParseObject parseObject1 = new MockClassA(); + parseObject1.setObjectId("testId1"); + + ParseObject parseObject2 = new MockClassB(); + parseObject2.setObjectId("testId2"); + + webSocketClientCallback.onMessage(createObjectCreateMessage(handle1.getRequestId(), parseObject1).toString()); + webSocketClientCallback.onMessage(createObjectCreateMessage(handle2.getRequestId(), parseObject2).toString()); + + validateSameObject((SubscriptionHandling.HandleEventCallback) eventMockCallback1, (ParseQuery) query1, parseObject1); + validateSameObject((SubscriptionHandling.HandleEventCallback) eventMockCallback2, (ParseQuery) query2, parseObject2); + } + @Test public void testCreateEventWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); @@ -370,6 +412,7 @@ private void validateSameObject(SubscriptionHandling.HandleEventCallback Date: Fri, 24 Mar 2017 15:28:22 -0700 Subject: [PATCH 2/2] Refactor the client's generic type parameters The way it was written, a single client could only ever be used for a single `` type parameter. That means to have multiple LiveQuery objects for multiple ParseObject classes, we're forced to create multiple ParseLiveQueryClient objects, each with its own `` type parameter. Since the client itself does not actually require being locked to a single type, we can move those type parameters to individual methods instead. --- .../java/com/parse/ParseLiveQueryClient.java | 16 ++++++------ .../com/parse/ParseLiveQueryClientImpl.java | 25 ++++++++++--------- .../com/parse/TestParseLiveQueryClient.java | 7 +++--- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClient.java b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClient.java index 7a3d546..e6b9e8b 100644 --- a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClient.java +++ b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClient.java @@ -3,13 +3,13 @@ import java.net.URI; import java.util.concurrent.Executor; -public interface ParseLiveQueryClient { +public interface ParseLiveQueryClient { - SubscriptionHandling subscribe(ParseQuery query); + SubscriptionHandling subscribe(ParseQuery query); - void unsubscribe(final ParseQuery query); + void unsubscribe(final ParseQuery query); - void unsubscribe(final ParseQuery query, final SubscriptionHandling subscriptionHandling); + void unsubscribe(final ParseQuery query, final SubscriptionHandling subscriptionHandling); void reconnect(); @@ -17,13 +17,13 @@ public interface ParseLiveQueryClient { class Factory { - public static ParseLiveQueryClient getClient(URI uri) { - return new ParseLiveQueryClientImpl<>(uri); + public static ParseLiveQueryClient getClient(URI uri) { + return new ParseLiveQueryClientImpl(uri); } /* package */ - static ParseLiveQueryClient getClient(URI uri, WebSocketClientFactory webSocketClientFactory, Executor taskExecutor) { - return new ParseLiveQueryClientImpl<>(uri, webSocketClientFactory, taskExecutor); + static ParseLiveQueryClient getClient(URI uri, WebSocketClientFactory webSocketClientFactory, Executor taskExecutor) { + return new ParseLiveQueryClientImpl(uri, webSocketClientFactory, taskExecutor); } } diff --git a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java index ca5f7a2..b8970ed 100644 --- a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java +++ b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java @@ -15,14 +15,14 @@ import static com.parse.Parse.checkInit; -/* package */ class ParseLiveQueryClientImpl implements ParseLiveQueryClient { +/* package */ class ParseLiveQueryClientImpl implements ParseLiveQueryClient { private static final String LOG_TAG = "ParseLiveQueryClient"; private final Executor taskExecutor; private final String applicationId; private final String clientKey; - private final SparseArray> subscriptions = new SparseArray<>(); + private final SparseArray> subscriptions = new SparseArray<>(); private final URI uri; private final WebSocketClientFactory webSocketClientFactory; private final WebSocketClient.WebSocketClientCallback webSocketClientCallback; @@ -46,7 +46,7 @@ } @Override - public SubscriptionHandling subscribe(ParseQuery query) { + public SubscriptionHandling subscribe(ParseQuery query) { int requestId = requestIdGenerator(); Subscription subscription = new Subscription<>(requestId, query); subscriptions.append(requestId, subscription); @@ -63,7 +63,7 @@ public SubscriptionHandling subscribe(ParseQuery query) { } @Override - public void unsubscribe(final ParseQuery query) { + public void unsubscribe(final ParseQuery query) { if (query != null) { for (int i = 0; i < subscriptions.size(); i++) { Subscription subscription = subscriptions.valueAt(i); @@ -75,7 +75,7 @@ public void unsubscribe(final ParseQuery query) { } @Override - public void unsubscribe(final ParseQuery query, final SubscriptionHandling subscriptionHandling) { + public void unsubscribe(final ParseQuery query, final SubscriptionHandling subscriptionHandling) { if (query != null && subscriptionHandling != null) { for (int i = 0; i < subscriptions.size(); i++) { Subscription subscription = subscriptions.valueAt(i); @@ -199,7 +199,7 @@ private void parseMessage(String message) throws LiveQueryException { } } - private void handleSubscribedEvent(JSONObject jsonObject) throws JSONException { + private void handleSubscribedEvent(JSONObject jsonObject) throws JSONException { final int requestId = jsonObject.getInt("requestId"); final Subscription subscription = subscriptionForRequestId(requestId); if (subscription != null) { @@ -207,7 +207,7 @@ private void handleSubscribedEvent(JSONObject jsonObject) throws JSONException { } } - private void handleUnsubscribedEvent(JSONObject jsonObject) throws JSONException { + private void handleUnsubscribedEvent(JSONObject jsonObject) throws JSONException { final int requestId = jsonObject.getInt("requestId"); final Subscription subscription = subscriptionForRequestId(requestId); if (subscription != null) { @@ -216,7 +216,7 @@ private void handleUnsubscribedEvent(JSONObject jsonObject) throws JSONException } } - private void handleObjectEvent(Subscription.Event event, JSONObject jsonObject) throws JSONException { + private void handleObjectEvent(Subscription.Event event, JSONObject jsonObject) throws JSONException { final int requestId = jsonObject.getInt("requestId"); final Subscription subscription = subscriptionForRequestId(requestId); if (subscription != null) { @@ -225,7 +225,7 @@ private void handleObjectEvent(Subscription.Event event, JSONObject jsonObject) } } - private void handleErrorEvent(JSONObject jsonObject) throws JSONException { + private void handleErrorEvent(JSONObject jsonObject) throws JSONException { int requestId = jsonObject.getInt("requestId"); int code = jsonObject.getInt("code"); String error = jsonObject.getString("error"); @@ -236,11 +236,12 @@ private void handleErrorEvent(JSONObject jsonObject) throws JSONException { } } - private Subscription subscriptionForRequestId(int requestId) { - return subscriptions.get(requestId); + private Subscription subscriptionForRequestId(int requestId) { + //noinspection unchecked + return (Subscription) subscriptions.get(requestId); } - private void sendSubscription(final Subscription subscription) { + private void sendSubscription(final Subscription subscription) { ParseUser.getCurrentSessionTokenAsync().onSuccess(new Continuation() { @Override public Void then(Task task) throws Exception { diff --git a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java index 8c01e89..e66aaeb 100644 --- a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java +++ b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java @@ -7,6 +7,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.robolectric.RobolectricGradleTestRunner; @@ -42,7 +43,7 @@ public class TestParseLiveQueryClient { private PauseableExecutor executor; private WebSocketClient webSocketClient; private WebSocketClient.WebSocketClientCallback webSocketClientCallback; - private ParseLiveQueryClient parseLiveQueryClient; + private ParseLiveQueryClient parseLiveQueryClient; private ParseUser mockUser; @@ -170,8 +171,8 @@ public void testHeterogeneousSubscriptions() throws Exception { ParseQuery query1 = ParseQuery.getQuery(MockClassA.class); ParseQuery query2 = ParseQuery.getQuery(MockClassB.class); - SubscriptionHandling handle1 = parseLiveQueryClient.subscribe((ParseQuery) query1); - SubscriptionHandling handle2 = parseLiveQueryClient.subscribe((ParseQuery) query2); + SubscriptionHandling handle1 = parseLiveQueryClient.subscribe(query1); + SubscriptionHandling handle2 = parseLiveQueryClient.subscribe(query2); handle1.handleError(new SubscriptionHandling.HandleErrorCallback() { @Override