diff --git a/pom.xml b/pom.xml
index dfd425ef0..0a67f6c14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -482,5 +482,11 @@
2.1.1
test
+
+ org.hamcrest
+ hamcrest
+ 2.2
+ test
+
diff --git a/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java b/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
index 95366eaa7..882fec8fc 100644
--- a/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
+++ b/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
@@ -20,18 +20,22 @@
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
+import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.internal.CallableOperation;
import com.google.firebase.internal.FirebaseService;
import com.google.firebase.internal.NonNull;
+import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
* This class is the entry point for all server-side Firebase Cloud Messaging actions.
@@ -91,7 +95,7 @@ public String send(@NonNull Message message) throws FirebaseMessagingException {
*
*
If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
* FCM performs all the necessary validations and emulates the send operation. The {@code dryRun}
- * option is useful for determining whether an FCM registration has been deleted. However, it
+ * option is useful for determining whether an FCM registration has been deleted. However, it
* cannot be used to validate APNs tokens.
*
* @param message A non-null {@link Message} to be sent.
@@ -139,6 +143,191 @@ protected String execute() throws FirebaseMessagingException {
};
}
+ /**
+ * Sends each message in the given list via Firebase Cloud Messaging.
+ * Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the
+ * given array.
+ *
+ *
The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
+ * value is in the same order as the input list.
+ *
+ * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @return A {@link BatchResponse} indicating the result of the operation.
+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
+ * failure, meaning that none of the messages in the list could be sent. Partial failures or
+ * no failures are only indicated by a {@link BatchResponse}.
+ */
+ public BatchResponse sendEach(@NonNull List messages) throws FirebaseMessagingException {
+ return sendEachOp(messages, false).call();
+ }
+
+
+ /**
+ * Sends each message in the given list via Firebase Cloud Messaging.
+ * Unlike {@link #sendAll(List)}, this method makes an HTTP call for each message in the
+ * given array.
+ *
+ * If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
+ * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun}
+ * option is useful for determining whether an FCM registration has been deleted. But it cannot be
+ * used to validate APNs tokens.
+ *
+ *
The list of responses obtained by calling {@link BatchResponse#getResponses()} on the return
+ * value is in the same order as the input list.
+ *
+ * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
+ * @return A {@link BatchResponse} indicating the result of the operation.
+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
+ * failure, meaning that none of the messages in the list could be sent. Partial failures or
+ * no failures are only indicated by a {@link BatchResponse}.
+ */
+ public BatchResponse sendEach(
+ @NonNull List messages, boolean dryRun) throws FirebaseMessagingException {
+ return sendEachOp(messages, dryRun).call();
+ }
+
+ /**
+ * Similar to {@link #sendEach(List)} but performs the operation asynchronously.
+ *
+ * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
+ * the messages have been sent.
+ */
+ public ApiFuture sendEachAsync(@NonNull List messages) {
+ return sendEachOp(messages, false).callAsync(app);
+ }
+
+ /**
+ * Similar to {@link #sendEach(List, boolean)} but performs the operation asynchronously.
+ *
+ * @param messages A non-null, non-empty list containing up to 500 messages.
+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
+ * the messages have been sent.
+ */
+ public ApiFuture sendEachAsync(@NonNull List messages, boolean dryRun) {
+ return sendEachOp(messages, dryRun).callAsync(app);
+ }
+
+ private CallableOperation sendEachOp(
+ final List messages, final boolean dryRun) {
+ final List immutableMessages = ImmutableList.copyOf(messages);
+ checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
+ checkArgument(immutableMessages.size() <= 500,
+ "messages list must not contain more than 500 elements");
+
+ return new CallableOperation() {
+ @Override
+ protected BatchResponse execute() throws FirebaseMessagingException {
+ List> list = new ArrayList<>();
+ for (Message message : immutableMessages) {
+ ApiFuture messageId = sendOpForSendResponse(message, dryRun).callAsync(app);
+ list.add(messageId);
+ }
+ try {
+ List responses = ApiFutures.allAsList(list).get();
+ return new BatchResponseImpl(responses);
+ } catch (InterruptedException | ExecutionException e) {
+ throw new FirebaseMessagingException(ErrorCode.CANCELLED, SERVICE_ID);
+ }
+ }
+ };
+ }
+
+ private CallableOperation sendOpForSendResponse(
+ final Message message, final boolean dryRun) {
+ checkNotNull(message, "message must not be null");
+ final FirebaseMessagingClient messagingClient = getMessagingClient();
+ return new CallableOperation() {
+ @Override
+ protected SendResponse execute() {
+ try {
+ String messageId = messagingClient.send(message, dryRun);
+ return SendResponse.fromMessageId(messageId);
+ } catch (FirebaseMessagingException e) {
+ return SendResponse.fromException(e);
+ }
+ }
+ };
+ }
+
+ /**
+ * Sends the given multicast message to all the FCM registration tokens specified in it.
+ *
+ * This method uses the {@link #sendEach(List)} API under the hood to send the given
+ * message to all the target recipients. The list of responses obtained by calling
+ * {@link BatchResponse#getResponses()} on the return value is in the same order as the
+ * tokens in the {@link MulticastMessage}.
+ *
+ * @param message A non-null {@link MulticastMessage}
+ * @return A {@link BatchResponse} indicating the result of the operation.
+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
+ * failure, meaning that none of the messages in the list could be sent. Partial failures or
+ * no failures are only indicated by a {@link BatchResponse}.
+ */
+ public BatchResponse sendEachForMulticast(
+ @NonNull MulticastMessage message) throws FirebaseMessagingException {
+ return sendEachForMulticast(message, false);
+ }
+
+ /**
+ * Sends the given multicast message to all the FCM registration tokens specified in it.
+ *
+ *
If the {@code dryRun} option is set to true, the message will not be actually sent. Instead
+ * FCM performs all the necessary validations, and emulates the send operation. The {@code dryRun}
+ * option is useful for determining whether an FCM registration has been deleted. But it cannot be
+ * used to validate APNs tokens.
+ *
+ *
This method uses the {@link #sendEach(List)} API under the hood to send the given
+ * message to all the target recipients. The list of responses obtained by calling
+ * {@link BatchResponse#getResponses()} on the return value is in the same order as the
+ * tokens in the {@link MulticastMessage}.
+ *
+ * @param message A non-null {@link MulticastMessage}.
+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
+ * @return A {@link BatchResponse} indicating the result of the operation.
+ * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
+ * delivery. An exception here or a {@link BatchResponse} with all failures indicates a total
+ * failure, meaning that none of the messages in the list could be sent. Partial failures or
+ * no failures are only indicated by a {@link BatchResponse}.
+ */
+ public BatchResponse sendEachForMulticast(@NonNull MulticastMessage message, boolean dryRun)
+ throws FirebaseMessagingException {
+ checkNotNull(message, "multicast message must not be null");
+ return sendEach(message.getMessageList(), dryRun);
+ }
+
+ /**
+ * Similar to {@link #sendEachForMulticast(MulticastMessage)} but performs the operation
+ * asynchronously.
+ *
+ * @param message A non-null {@link MulticastMessage}.
+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
+ * the messages have been sent.
+ */
+ public ApiFuture sendEachForMulticastAsync(@NonNull MulticastMessage message) {
+ return sendEachForMulticastAsync(message, false);
+ }
+
+ /**
+ * Similar to {@link #sendEachForMulticast(MulticastMessage, boolean)} but performs the operation
+ * asynchronously.
+ *
+ * @param message A non-null {@link MulticastMessage}.
+ * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
+ * the messages have been sent.
+ */
+ public ApiFuture sendEachForMulticastAsync(
+ @NonNull MulticastMessage message, boolean dryRun) {
+ checkNotNull(message, "multicast message must not be null");
+ return sendEachAsync(message.getMessageList(), dryRun);
+ }
+
/**
* Sends all the messages in the given list via Firebase Cloud Messaging. Employs batching to
* send the entire list as a single RPC call. Compared to the {@link #send(Message)} method, this
@@ -150,8 +339,10 @@ protected String execute() throws FirebaseMessagingException {
* @param messages A non-null, non-empty list containing up to 500 messages.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
- * delivery. An exception here indicates a total failure -- i.e. none of the messages in the
- * list could be sent. Partial failures are indicated by a {@link BatchResponse} return value.
+ * delivery. An exception here indicates a total failure, meaning that none of the messages in
+ * the list could be sent. Partial failures are indicated by a {@link BatchResponse} return
+ * value.
+ * @deprecated Use {@link #sendEach(List)} instead.
*/
public BatchResponse sendAll(
@NonNull List messages) throws FirebaseMessagingException {
@@ -175,8 +366,10 @@ public BatchResponse sendAll(
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
- * delivery. An exception here indicates a total failure -- i.e. none of the messages in the
- * list could be sent. Partial failures are indicated by a {@link BatchResponse} return value.
+ * delivery. An exception here indicates a total failure, meaning that none of the messages in
+ * the list could be sent. Partial failures are indicated by a {@link BatchResponse} return
+ * value.
+ * @deprecated Use {@link #sendEach(List, boolean)} instead.
*/
public BatchResponse sendAll(
@NonNull List messages, boolean dryRun) throws FirebaseMessagingException {
@@ -187,8 +380,9 @@ public BatchResponse sendAll(
* Similar to {@link #sendAll(List)} but performs the operation asynchronously.
*
* @param messages A non-null, non-empty list containing up to 500 messages.
- * @return @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
+ * @deprecated Use {@link #sendEachAsync(List)} instead.
*/
public ApiFuture sendAllAsync(@NonNull List messages) {
return sendAllAsync(messages, false);
@@ -199,8 +393,9 @@ public ApiFuture sendAllAsync(@NonNull List messages) {
*
* @param messages A non-null, non-empty list containing up to 500 messages.
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
- * @return @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
+ * @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent, or when the emulation has finished.
+ * @deprecated Use {@link #sendEachAsync(List, boolean)} instead.
*/
public ApiFuture sendAllAsync(
@NonNull List messages, boolean dryRun) {
@@ -218,9 +413,10 @@ public ApiFuture sendAllAsync(
* @param message A non-null {@link MulticastMessage}
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
- * delivery. An exception here indicates a total failure -- i.e. the messages could not be
- * delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
+ * delivery. An exception here indicates a total failure, meaning that the messages could not
+ * be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
* return value.
+ * @deprecated Use {@link #sendEachForMulticast(MulticastMessage)} instead.
*/
public BatchResponse sendMulticast(
@NonNull MulticastMessage message) throws FirebaseMessagingException {
@@ -244,9 +440,10 @@ public BatchResponse sendMulticast(
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return A {@link BatchResponse} indicating the result of the operation.
* @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
- * delivery. An exception here indicates a total failure -- i.e. the messages could not be
- * delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
+ * delivery. An exception here indicates a total failure, meaning that the messages could not
+ * be delivered to any recipient. Partial failures are indicated by a {@link BatchResponse}
* return value.
+ * @deprecated Use {@link #sendEachForMulticast(MulticastMessage, boolean)} instead.
*/
public BatchResponse sendMulticast(
@NonNull MulticastMessage message, boolean dryRun) throws FirebaseMessagingException {
@@ -261,6 +458,7 @@ public BatchResponse sendMulticast(
* @param message A non-null {@link MulticastMessage}.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
+ * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage)} instead.
*/
public ApiFuture sendMulticastAsync(@NonNull MulticastMessage message) {
return sendMulticastAsync(message, false);
@@ -274,6 +472,7 @@ public ApiFuture sendMulticastAsync(@NonNull MulticastMessage mes
* @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
* @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
* the messages have been sent.
+ * @deprecated Use {@link #sendEachForMulticastAsync(MulticastMessage, boolean)} instead.
*/
public ApiFuture sendMulticastAsync(
@NonNull MulticastMessage message, boolean dryRun) {
diff --git a/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java b/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
index 9ae7bba24..bebae6e4d 100644
--- a/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
+++ b/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
@@ -96,6 +96,103 @@ public void testSendError() throws InterruptedException {
}
}
+ @Test
+ public void testSendEach() throws Exception {
+ List messages = new ArrayList<>();
+ messages.add(
+ Message.builder()
+ .setNotification(Notification.builder()
+ .setTitle("Title")
+ .setBody("Body")
+ .build())
+ .setTopic("foo-bar")
+ .build());
+ messages.add(
+ Message.builder()
+ .setNotification(Notification.builder()
+ .setTitle("Title")
+ .setBody("Body")
+ .build())
+ .setTopic("foo-bar")
+ .build());
+ messages.add(
+ Message.builder()
+ .setNotification(Notification.builder()
+ .setTitle("Title")
+ .setBody("Body")
+ .build())
+ .setToken("not-a-token")
+ .build());
+
+ BatchResponse response = FirebaseMessaging.getInstance().sendEach(messages, true);
+
+ assertEquals(2, response.getSuccessCount());
+ assertEquals(1, response.getFailureCount());
+
+ List responses = response.getResponses();
+ assertEquals(3, responses.size());
+ assertTrue(responses.get(0).isSuccessful());
+ String id = responses.get(0).getMessageId();
+ assertTrue(id != null && id.matches("^projects/.*/messages/.*$"));
+
+ assertTrue(responses.get(1).isSuccessful());
+ id = responses.get(1).getMessageId();
+ assertTrue(id != null && id.matches("^projects/.*/messages/.*$"));
+
+ assertFalse(responses.get(2).isSuccessful());
+ assertNull(responses.get(2).getMessageId());
+ FirebaseMessagingException exception = responses.get(2).getException();
+ assertNotNull(exception);
+ assertEquals(ErrorCode.INVALID_ARGUMENT, exception.getErrorCode());
+ }
+
+ @Test
+ public void testSendFiveHundredWithSendEach() throws Exception {
+ List messages = new ArrayList<>();
+ for (int i = 0; i < 500; i++) {
+ messages.add(Message.builder().setTopic("foo-bar-" + (i % 10)).build());
+ }
+
+ BatchResponse response = FirebaseMessaging.getInstance().sendEach(messages, true);
+
+ assertEquals(500, response.getResponses().size());
+ assertEquals(500, response.getSuccessCount());
+ assertEquals(0, response.getFailureCount());
+ for (SendResponse sendResponse : response.getResponses()) {
+ if (!sendResponse.isSuccessful()) {
+ sendResponse.getException().printStackTrace();
+ }
+ assertTrue(sendResponse.isSuccessful());
+ String id = sendResponse.getMessageId();
+ assertTrue(id != null && id.matches("^projects/.*/messages/.*$"));
+ assertNull(sendResponse.getException());
+ }
+ }
+
+ @Test
+ public void testSendEachForMulticast() throws Exception {
+ MulticastMessage multicastMessage = MulticastMessage.builder()
+ .setNotification(Notification.builder()
+ .setTitle("Title")
+ .setBody("Body")
+ .build())
+ .addToken("not-a-token")
+ .addToken("also-not-a-token")
+ .build();
+
+ BatchResponse response = FirebaseMessaging.getInstance().sendEachForMulticast(
+ multicastMessage, true);
+
+ assertEquals(0, response.getSuccessCount());
+ assertEquals(2, response.getFailureCount());
+ assertEquals(2, response.getResponses().size());
+ for (SendResponse sendResponse : response.getResponses()) {
+ assertFalse(sendResponse.isSuccessful());
+ assertNull(sendResponse.getMessageId());
+ assertNotNull(sendResponse.getException());
+ }
+ }
+
@Test
public void testSendAll() throws Exception {
List messages = new ArrayList<>();
diff --git a/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java b/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
index 8e8e79e07..dc07ce002 100644
--- a/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
+++ b/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
@@ -16,6 +16,9 @@
package com.google.firebase.messaging;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -27,13 +30,19 @@
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.TestOnlyImplFirebaseTrampolines;
import com.google.firebase.auth.MockGoogleCredentials;
+import com.google.firebase.internal.Nullable;
+
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
+
import org.junit.After;
import org.junit.Test;
@@ -46,6 +55,9 @@ public class FirebaseMessagingTest {
private static final Message EMPTY_MESSAGE = Message.builder()
.setTopic("test-topic")
.build();
+ private static final Message EMPTY_MESSAGE_2 = Message.builder()
+ .setTopic("test-topic2")
+ .build();
private static final MulticastMessage TEST_MULTICAST_MESSAGE = MulticastMessage.builder()
.addToken("test-fcm-token1")
.addToken("test-fcm-token2")
@@ -262,6 +274,277 @@ public void testSendAsyncFailure() throws InterruptedException {
assertFalse(client.isLastDryRun);
}
+ @Test
+ public void testSendEachWithNull() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ try {
+ messaging.sendEach(null);
+ fail("No error thrown for null message list");
+ } catch (NullPointerException expected) {
+ // expected
+ }
+
+ assertNull(client.lastMessage);
+ }
+
+ @Test
+ public void testSendEachWithEmptyList() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ try {
+ messaging.sendEach(ImmutableList.of());
+ fail("No error thrown for empty message list");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+
+ assertNull(client.lastMessage);
+ }
+
+ @Test
+ public void testSendEachWithTooManyMessages() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+ ImmutableList.Builder listBuilder = ImmutableList.builder();
+ for (int i = 0; i < 501; i++) {
+ listBuilder.add(Message.builder().setTopic("topic").build());
+ }
+
+ try {
+ messaging.sendEach(listBuilder.build(), false);
+ fail("No error thrown for too many messages in the list");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+
+ assertNull(client.lastMessage);
+ }
+
+ @Test
+ public void testSendEach() throws FirebaseMessagingException {
+ ImmutableList messages = ImmutableList.of(EMPTY_MESSAGE, EMPTY_MESSAGE_2);
+ ImmutableList messageIds = ImmutableList.of("test1", "test2");
+ Map messageMap = new HashMap<>();
+ for (int i = 0; i < 2; i++) {
+ messageMap.put(messages.get(i), SendResponse.fromMessageId(messageIds.get(i)));
+ }
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageMap(messageMap);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEach(messages);
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals(messageIds.get(i), response.getResponses().get(i).getMessageId());
+ }
+ assertThat(client.lastMessage, anyOf(is(EMPTY_MESSAGE), is(EMPTY_MESSAGE_2)));
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachDryRun() throws FirebaseMessagingException {
+ ImmutableList messages = ImmutableList.of(EMPTY_MESSAGE, EMPTY_MESSAGE_2);
+ ImmutableList messageIds = ImmutableList.of("test1", "test2");
+ Map messageMap = new HashMap<>();
+ for (int i = 0; i < 2; i++) {
+ messageMap.put(messages.get(i), SendResponse.fromMessageId(messageIds.get(i)));
+ }
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageMap(messageMap);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEach(messages, true);
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals(messageIds.get(i), response.getResponses().get(i).getMessageId());
+ }
+ assertThat(client.lastMessage, anyOf(is(EMPTY_MESSAGE), is(EMPTY_MESSAGE_2)));
+ assertTrue(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachFailure() throws FirebaseMessagingException {
+ ImmutableList messages = ImmutableList.of(EMPTY_MESSAGE, EMPTY_MESSAGE_2);
+ Map messageMap = new HashMap<>();
+ messageMap.put(messages.get(0), SendResponse.fromMessageId("test"));
+ messageMap.put(messages.get(1), SendResponse.fromException(TEST_EXCEPTION));
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageMap(messageMap);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEach(messages);
+
+ assertEquals(1, response.getFailureCount());
+ assertEquals(1, response.getSuccessCount());
+ assertEquals("test", response.getResponses().get(0).getMessageId());
+ assertEquals(TEST_EXCEPTION, response.getResponses().get(1).getException());
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachAsync() throws Exception {
+ ImmutableList messages = ImmutableList.of(EMPTY_MESSAGE, EMPTY_MESSAGE_2);
+ ImmutableList messageIds = ImmutableList.of("test1", "test2");
+ Map messageMap = new HashMap<>();
+ for (int i = 0; i < 2; i++) {
+ messageMap.put(messages.get(i), SendResponse.fromMessageId(messageIds.get(i)));
+ }
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageMap(messageMap);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachAsync(messages).get();
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals(messageIds.get(i), response.getResponses().get(i).getMessageId());
+ }
+ assertThat(client.lastMessage, anyOf(is(EMPTY_MESSAGE), is(EMPTY_MESSAGE_2)));
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachAsyncDryRun() throws Exception {
+ ImmutableList messages = ImmutableList.of(EMPTY_MESSAGE, EMPTY_MESSAGE_2);
+ ImmutableList messageIds = ImmutableList.of("test1", "test2");
+ Map messageMap = new HashMap<>();
+ for (int i = 0; i < 2; i++) {
+ messageMap.put(messages.get(i), SendResponse.fromMessageId(messageIds.get(i)));
+ }
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageMap(messageMap);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachAsync(messages, true).get();
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals(messageIds.get(i), response.getResponses().get(i).getMessageId());
+ }
+ assertThat(client.lastMessage, anyOf(is(EMPTY_MESSAGE), is(EMPTY_MESSAGE_2)));
+ assertTrue(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachAsyncFailure() throws Exception {
+ ImmutableList messages = ImmutableList.of(EMPTY_MESSAGE, EMPTY_MESSAGE_2);
+ Map messageMap = new HashMap<>();
+ messageMap.put(messages.get(0), SendResponse.fromMessageId("test"));
+ messageMap.put(messages.get(1), SendResponse.fromException(TEST_EXCEPTION));
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageMap(messageMap);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachAsync(messages).get();
+
+ assertEquals(1, response.getFailureCount());
+ assertEquals(1, response.getSuccessCount());
+ assertEquals("test", response.getResponses().get(0).getMessageId());
+ assertEquals(TEST_EXCEPTION, response.getResponses().get(1).getException());
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachForMulticastWithNull() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ try {
+ messaging.sendEachForMulticast(null);
+ fail("No error thrown for null multicast message");
+ } catch (NullPointerException expected) {
+ // expected
+ }
+
+ assertNull(client.lastMessage);
+ }
+
+ @Test
+ public void testSendEachForMulticast() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId("test");
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachForMulticast(TEST_MULTICAST_MESSAGE);
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals("test", response.getResponses().get(i).getMessageId());
+ }
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachForMulticastDryRun() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId("test");
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachForMulticast(TEST_MULTICAST_MESSAGE, true);
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals("test", response.getResponses().get(i).getMessageId());
+ }
+ assertTrue(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachForMulticastFailure() throws FirebaseMessagingException {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromException(TEST_EXCEPTION);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachForMulticast(TEST_MULTICAST_MESSAGE);
+
+ assertEquals(2, response.getFailureCount());
+ assertEquals(0, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals(TEST_EXCEPTION, response.getResponses().get(i).getException());
+ }
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachForMulticastAsync() throws Exception {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId("test");
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachForMulticastAsync(TEST_MULTICAST_MESSAGE).get();
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals("test", response.getResponses().get(i).getMessageId());
+ }
+ assertFalse(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachForMulticastAsyncDryRun() throws Exception {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId("test");
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachForMulticastAsync(
+ TEST_MULTICAST_MESSAGE, true).get();
+
+ assertEquals(2, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals("test", response.getResponses().get(i).getMessageId());
+ }
+ assertTrue(client.isLastDryRun);
+ }
+
+ @Test
+ public void testSendEachForMulticastAsyncFailure() throws Exception {
+ MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromException(TEST_EXCEPTION);
+ FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
+
+ BatchResponse response = messaging.sendEachForMulticastAsync(TEST_MULTICAST_MESSAGE).get();
+
+ assertEquals(2, response.getFailureCount());
+ assertEquals(0, response.getSuccessCount());
+ for (int i = 0; i < 2; i++) {
+ assertEquals(TEST_EXCEPTION, response.getResponses().get(i).getException());
+ }
+ assertFalse(client.isLastDryRun);
+ }
+
@Test
public void testSendAllWithNull() throws FirebaseMessagingException {
MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
@@ -681,6 +964,7 @@ private static class MockFirebaseMessagingClient implements FirebaseMessagingCli
private Message lastMessage;
private List lastBatch;
private boolean isLastDryRun;
+ private ImmutableMap messageMap;
private MockFirebaseMessagingClient(
String messageId, BatchResponse batchResponse, FirebaseMessagingException exception) {
@@ -689,10 +973,20 @@ private MockFirebaseMessagingClient(
this.exception = exception;
}
+ private MockFirebaseMessagingClient(
+ Map messageMap, FirebaseMessagingException exception) {
+ this.messageMap = ImmutableMap.copyOf(messageMap);
+ this.exception = exception;
+ }
+
static MockFirebaseMessagingClient fromMessageId(String messageId) {
return new MockFirebaseMessagingClient(messageId, null, null);
}
+ static MockFirebaseMessagingClient fromMessageMap(Map messageMap) {
+ return new MockFirebaseMessagingClient(messageMap, null);
+ }
+
static MockFirebaseMessagingClient fromBatchResponse(BatchResponse batchResponse) {
return new MockFirebaseMessagingClient(null, batchResponse, null);
}
@@ -702,13 +996,23 @@ static MockFirebaseMessagingClient fromException(FirebaseMessagingException exce
}
@Override
+ @Nullable
public String send(Message message, boolean dryRun) throws FirebaseMessagingException {
lastMessage = message;
isLastDryRun = dryRun;
if (exception != null) {
throw exception;
}
- return messageId;
+ if (messageMap == null) {
+ return messageId;
+ }
+ if (!messageMap.containsKey(message)) {
+ return null;
+ }
+ if (messageMap.get(message).getException() != null) {
+ throw messageMap.get(message).getException();
+ }
+ return messageMap.get(message).getMessageId();
}
@Override