/**
* Modified MIT License
*
* Copyright 2016 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.test.onesignal;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.ConnectivityManager;
import android.os.Bundle;
import com.onesignal.BuildConfig;
import com.onesignal.OSNotification;
import com.onesignal.OSNotificationAction;
import com.onesignal.OSNotificationOpenResult;
import com.onesignal.OSNotificationPayload;
import com.onesignal.OSPermissionObserver;
import com.onesignal.OSPermissionStateChanges;
import com.onesignal.OSPermissionSubscriptionState;
import com.onesignal.OSSubscriptionObserver;
import com.onesignal.OSSubscriptionStateChanges;
import com.onesignal.OneSignal;
import com.onesignal.OneSignalDbHelper;
import com.onesignal.ShadowAdvertisingIdProviderGPS;
import com.onesignal.ShadowBadgeCountUpdater;
import com.onesignal.ShadowCustomTabsClient;
import com.onesignal.ShadowCustomTabsSession;
import com.onesignal.ShadowGoogleApiClientBuilder;
import com.onesignal.ShadowGoogleApiClientCompatProxy;
import com.onesignal.ShadowFusedLocationApiWrapper;
import com.onesignal.ShadowNotificationManagerCompat;
import com.onesignal.ShadowOSUtils;
import com.onesignal.ShadowOneSignal;
import com.onesignal.ShadowOneSignalRestClient;
import com.onesignal.OneSignalPackagePrivateHelper;
import com.onesignal.ShadowPushRegistratorGPS;
import com.onesignal.ShadowRoboNotificationManager;
import com.onesignal.StaticResetHelper;
import com.onesignal.SyncService;
import com.onesignal.example.BlankActivity;
import junit.framework.Assert;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowConnectivityManager;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowSystemClock;
import org.robolectric.util.ActivityController;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static com.onesignal.OneSignalPackagePrivateHelper.GcmBroadcastReceiver_processBundle;
import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor_Process;
import static com.onesignal.OneSignalPackagePrivateHelper.NotificationOpenedProcessor_processFromContext;
import static com.onesignal.OneSignalPackagePrivateHelper.bundleAsJSONObject;
import static com.test.onesignal.GenerateNotificationRunner.getBaseNotifBundle;
import static org.robolectric.Shadows.shadowOf;
@Config(packageName = "com.onesignal.example",
shadows = {ShadowOneSignalRestClient.class,
ShadowPushRegistratorGPS.class,
ShadowOSUtils.class,
ShadowAdvertisingIdProviderGPS.class,
ShadowCustomTabsClient.class, ShadowCustomTabsSession.class,
ShadowNotificationManagerCompat.class},
instrumentedPackages = {"com.onesignal"},
constants = BuildConfig.class,
sdk = 21)
@RunWith(RobolectricTestRunner.class)
// Enable to ensure test order to consistency debug flaky test.
// @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MainOneSignalClassRunner {
private static final String ONESIGNAL_APP_ID = "b2f7f966-d8cc-11e4-bed1-df8f05be55ba";
@SuppressLint("StaticFieldLeak")
private static Activity blankActivity;
private static String callBackUseId, getCallBackRegId;
private static String notificationOpenedMessage;
private static JSONObject lastGetTags;
private static ActivityController<BlankActivity> blankActivityController;
private static void GetIdsAvailable() {
OneSignal.idsAvailable(new OneSignal.IdsAvailableHandler() {
@Override
public void idsAvailable(String userId, String registrationId) {
callBackUseId = userId;
getCallBackRegId = registrationId;
}
});
}
private static OneSignal.NotificationOpenedHandler getNotificationOpenedHandler() {
return new OneSignal.NotificationOpenedHandler() {
@Override
public void notificationOpened(OSNotificationOpenResult openedResult) {
notificationOpenedMessage = openedResult.notification.payload.body;
}
};
}
private static void GetTags() {
OneSignal.getTags(new OneSignal.GetTagsHandler() {
@Override
public void tagsAvailable(JSONObject tags) {
lastGetTags = tags;
}
});
}
private static void cleanUp() {
callBackUseId = getCallBackRegId = null;
notificationOpenedMessage = null;
lastGetTags = null;
TestHelpers.betweenTestsCleanup();
}
@BeforeClass // Runs only once, before any tests
public static void setUpClass() throws Exception {
ShadowLog.stream = System.out;
Field OneSignal_CurrentSubscription = OneSignal.class.getDeclaredField("subscribableStatus");
OneSignal_CurrentSubscription.setAccessible(true);
OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE);
StaticResetHelper.saveStaticValues();
}
@Before
public void beforeEachTest() throws Exception {
blankActivityController = Robolectric.buildActivity(BlankActivity.class).create();
blankActivity = blankActivityController.get();
RemoveDisableNotificationOpenedToManifest();
cleanUp();
}
@AfterClass
public static void afterEverything() {
cleanUp();
}
@Test
public void testInitFromApplicationContext() throws Exception {
// Application.onCreate
OneSignal.init(RuntimeEnvironment.application, "123456789", ONESIGNAL_APP_ID);
threadAndTaskWait();
Assert.assertNotNull(ShadowOneSignalRestClient.lastPost);
ShadowOneSignalRestClient.lastPost = null;
restartAppAndElapseTimeToNextSession();
// Restart app, should not send onSession automatically
OneSignal.init(RuntimeEnvironment.application, "123456789", ONESIGNAL_APP_ID);
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
// Starting of first Activity should trigger onSession
blankActivityController.resume();
threadAndTaskWait();
Assert.assertNotNull(ShadowOneSignalRestClient.lastPost);
}
@Test
public void testOnSessionCalledOnlyOncePer30Sec() throws Exception {
// Will call create
ShadowSystemClock.setCurrentTimeMillis(60 * 60 * 1000);
OneSignalInit();
threadAndTaskWait();
blankActivityController.resume();
Assert.assertEquals("players", ShadowOneSignalRestClient.lastUrl);
// Shouldn't call on_session if just resuming app with a short delay
blankActivityController.pause();
threadAndTaskWait();
ShadowOneSignalRestClient.lastUrl = null;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastUrl);
// Or when restarting the app quickly.
ShadowOneSignalRestClient.lastPost = null;
StaticResetHelper.restSetStaticFields();
OneSignalInit();
threadAndTaskWait();
blankActivityController.resume();
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastUrl);
blankActivityController.pause();
threadAndTaskWait();
ShadowSystemClock.setCurrentTimeMillis(121 * 60 * 1000);
ShadowOneSignalRestClient.lastUrl = null;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertTrue(ShadowOneSignalRestClient.lastUrl.matches("players/.*/on_session"));
Assert.assertEquals("{\"app_id\":\"b2f7f966-d8cc-11e4-bed1-df8f05be55ba\"}", ShadowOneSignalRestClient.lastPost.toString());
}
@Test
public void testAlwaysUseRemoteProjectNumberOverLocal() throws Exception {
ShadowSystemClock.setCurrentTimeMillis(60 * 60 * 1000);
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals("87654321", ShadowPushRegistratorGPS.lastProjectNumber);
// A 2nd init call
OneSignalInit();
blankActivityController.pause();
threadAndTaskWait();
ShadowSystemClock.setCurrentTimeMillis(121 * 60 * 1000);
blankActivityController.resume();
threadAndTaskWait();
// Make sure when we try to register again before our on_session call it is with the remote
// project number instead of the local one.
Assert.assertEquals("87654321", ShadowPushRegistratorGPS.lastProjectNumber);
}
@Test
public void testPutStillCalledOnChanges() throws Exception {
// Will call create
ShadowSystemClock.setCurrentTimeMillis(60 * 60 * 1000);
OneSignalInit();
threadAndTaskWait();
blankActivityController.resume();
Assert.assertEquals("players", ShadowOneSignalRestClient.lastUrl);
// Shouldn't call on_session if just resuming app with a short delay
blankActivityController.pause();
threadAndTaskWait();
ShadowOneSignalRestClient.lastUrl = null;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastUrl);
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
ShadowOSUtils.carrierName = "test2";
// Should make PUT call with changes on app restart
ShadowOneSignalRestClient.lastPost = null;
StaticResetHelper.restSetStaticFields();
OneSignalInit();
threadAndTaskWait();
blankActivityController.resume();
threadAndTaskWait();
Assert.assertEquals(4, ShadowOneSignalRestClient.networkCallCount);
GetIdsAvailable();
Assert.assertEquals("players/" + callBackUseId, ShadowOneSignalRestClient.lastUrl);
Assert.assertEquals("{\"carrier\":\"test2\",\"app_id\":\"b2f7f966-d8cc-11e4-bed1-df8f05be55ba\"}", ShadowOneSignalRestClient.lastPost.toString());
}
@Test
public void testPutCallsMadeWhenUserStateChangesOnAppResume() throws Exception {
// Will call create
ShadowSystemClock.setCurrentTimeMillis(60 * 60 * 1000);
OneSignalInit();
threadAndTaskWait();
blankActivityController.resume();
Assert.assertEquals("players", ShadowOneSignalRestClient.lastUrl);
}
@Test
public void testOpenFromNotificationWhenAppIsDead() throws Exception {
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Robo test message\", \"custom\": { \"i\": \"UUID\" } }]"), false);
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
threadAndTaskWait();
Assert.assertEquals("Robo test message", notificationOpenedMessage);
}
@Test
public void testAndroidParamsProjectNumberOverridesLocal() throws Exception {
OneSignalInit();
threadAndTaskWait();
Assert.assertNotSame("123456789", ShadowPushRegistratorGPS.lastProjectNumber);
}
@Test
public void testNullProjectNumberSetsErrorType() throws Exception {
// Get call will not return a Google project number if it hasn't been entered on the OneSignal dashboard.
ShadowOneSignalRestClient.nextSuccessResponse = "{\"awl_list\": {}}";
// Don't fire the mock callback, it will be done from the real class.
ShadowPushRegistratorGPS.skipComplete = true;
OneSignal.init(blankActivity, null, ONESIGNAL_APP_ID);
threadAndTaskWait();
Assert.assertEquals(-6, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
}
@Test
public void shouldCorrectlyRemoveOpenedHandlerAndFireMissedOnesWhenAddedBack() throws Exception {
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
threadAndTaskWait();
OneSignal.removeNotificationOpenedHandler();
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Robo test message\", \"custom\": { \"i\": \"UUID\" } }]"), false);
Assert.assertNull(notificationOpenedMessage);
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
Assert.assertEquals("Robo test message", notificationOpenedMessage);
}
@Test
public void shouldNotFireNotificationOpenAgainAfterAppRestart() throws Exception {
AddLauncherIntentFilter();
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
threadAndTaskWait();
Bundle bundle = getBaseNotifBundle();
OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromGCMIntentService(blankActivity, bundle, null);
threadAndTaskWait();
notificationOpenedMessage = null;
// Restart app - Should omit notification_types
StaticResetHelper.restSetStaticFields();
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
threadAndTaskWait();
Assert.assertEquals(null, notificationOpenedMessage);
}
@Test
public void testOpenFromNotificationWhenAppIsInBackground() throws Exception {
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
Assert.assertNull(notificationOpenedMessage);
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false);
Assert.assertEquals("Test Msg", notificationOpenedMessage);
threadAndTaskWait();
}
@Test
public void testOpeningLauncherActivity() throws Exception {
AddLauncherIntentFilter();
// From app launching normally
Assert.assertNotNull(shadowOf(blankActivity).getNextStartedActivity());
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false);
Assert.assertNotNull(shadowOf(blankActivity).getNextStartedActivity());
Assert.assertNull(shadowOf(blankActivity).getNextStartedActivity());
}
@Test
public void testOpeningLaunchUrl() throws Exception {
// Removes app launch
shadowOf(blankActivity).getNextStartedActivity();
// No OneSignal init here to test case where it is located in an Activity.
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\", \"u\": \"http://google.com\" } }]"), false);
Intent intent = shadowOf(blankActivity).getNextStartedActivity();
Assert.assertEquals("android.intent.action.VIEW", intent.getAction());
Assert.assertEquals("http://google.com", intent.getData().toString());
Assert.assertNull(shadowOf(blankActivity).getNextStartedActivity());
}
@Test
public void testOpeningLaunchUrlWithDisableDefault() throws Exception {
AddDisableNotificationOpenedToManifest();
// Removes app launch
shadowOf(blankActivity).getNextStartedActivity();
// No OneSignal init here to test case where it is located in an Activity.
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\", \"u\": \"http://google.com\" } }]"), false);
Assert.assertNull(shadowOf(blankActivity).getNextStartedActivity());
}
@Test
public void testDisableOpeningLauncherActivityOnNotifiOpen() throws Exception {
AddDisableNotificationOpenedToManifest();
AddLauncherIntentFilter();
// From app launching normally
Assert.assertNotNull(shadowOf(blankActivity).getNextStartedActivity());
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler());
Assert.assertNull(notificationOpenedMessage);
OneSignal.handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false);
Assert.assertNull(shadowOf(blankActivity).getNextStartedActivity());
Assert.assertEquals("Test Msg", notificationOpenedMessage);
}
private static String notificationReceivedBody;
private static int androidNotificationId;
@Test
public void testNotificationReceivedWhenAppInFocus() throws Exception {
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID, getNotificationOpenedHandler(), new OneSignal.NotificationReceivedHandler() {
@Override
public void notificationReceived(OSNotification notification) {
androidNotificationId = notification.androidNotificationId;
notificationReceivedBody = notification.payload.body;
}
});
threadAndTaskWait();
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification);
Bundle bundle = getBaseNotifBundle();
boolean processResult = GcmBroadcastReceiver_processBundle(blankActivity, bundle);
threadAndTaskWait(); threadAndTaskWait();
Assert.assertEquals(null, notificationOpenedMessage);
Assert.assertFalse(processResult);
// NotificationBundleProcessor.Process(...) will be called if processResult is true as a service
NotificationBundleProcessor_Process(blankActivity, false, bundleAsJSONObject(bundle), null);
Assert.assertEquals("Robo test message", notificationReceivedBody);
Assert.assertFalse(0 == androidNotificationId);
// Don't fire for duplicates
notificationOpenedMessage = null;
notificationReceivedBody = null;
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.None);
Assert.assertNull(notificationOpenedMessage);
GcmBroadcastReceiver_processBundle(blankActivity, bundle);
threadAndTaskWait();
Assert.assertEquals(null, notificationOpenedMessage);
Assert.assertEquals(null, notificationReceivedBody);
// Test that only NotificationReceivedHandler fires
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.None);
bundle = getBaseNotifBundle("UUID2");
notificationOpenedMessage = null;
notificationReceivedBody = null;
GcmBroadcastReceiver_processBundle(blankActivity, bundle);
threadAndTaskWait(); threadAndTaskWait();
Assert.assertEquals(null, notificationOpenedMessage);
Assert.assertNull(notificationOpenedMessage);
Assert.assertEquals("Robo test message", notificationReceivedBody);
}
@Test
@Config(shadows = {ShadowBadgeCountUpdater.class})
public void testBadgeClearOnFirstStart() throws Exception {
ShadowBadgeCountUpdater.lastCount = -1;
// First run should set badge to 0
OneSignal.init(RuntimeEnvironment.application, "123456789", ONESIGNAL_APP_ID);
threadAndTaskWait();
Assert.assertEquals(0, ShadowBadgeCountUpdater.lastCount);
// Resume should have no effect on badges.
ShadowBadgeCountUpdater.lastCount = -1;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertEquals(-1, ShadowBadgeCountUpdater.lastCount);
// Nor an app restart
StaticResetHelper.restSetStaticFields();
OneSignal.init(RuntimeEnvironment.application, "123456789", ONESIGNAL_APP_ID);
threadAndTaskWait();
Assert.assertEquals(-1, ShadowBadgeCountUpdater.lastCount);
}
@Test
public void testUnsubscribeStatusShouldBeSetIfGCMErrored() throws Exception {
ShadowPushRegistratorGPS.fail = true;
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(-7, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
}
@Test
public void testInvalidGoogleProjectNumberWithSuccessfulRegisterResponse() throws Exception {
GetIdsAvailable();
// A more real test would be "missing support library" but bad project number is an easier setup
// and is testing the same logic.
OneSignalInitWithBadProjectNum();
threadAndTaskWait();
Robolectric.getForegroundThreadScheduler().runOneTask();
Assert.assertEquals(-6, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
// Test that idsAvailable still fires
Assert.assertEquals(ShadowOneSignalRestClient.testUserId, callBackUseId);
}
@Test
public void testGMSErrorsAfterSuccessfulSubscribeDoNotUnsubscribeTheDevice() throws Exception {
OneSignalInit();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
ShadowOneSignalRestClient.lastPost = null;
restartAppAndElapseTimeToNextSession();
ShadowPushRegistratorGPS.fail = true;
OneSignalInit();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
}
@Test
public void testInvalidGoogleProjectNumberWithFailedRegisterResponse() throws Exception {
// Ensures lower number notification_types do not over right higher numbered ones.
ShadowPushRegistratorGPS.fail = true;
GetIdsAvailable();
OneSignalInitWithBadProjectNum();
threadAndTaskWait();
Robolectric.getForegroundThreadScheduler().runOneTask();
Assert.assertEquals(-6, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
// Test that idsAvailable still fires
Assert.assertEquals(ShadowOneSignalRestClient.testUserId, callBackUseId);
}
@Test
public void testUnsubcribedShouldMakeRegIdNullToIdsAvailable() throws Exception {
GetIdsAvailable();
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(ShadowPushRegistratorGPS.regId, ShadowOneSignalRestClient.lastPost.getString("identifier"));
Robolectric.getForegroundThreadScheduler().runOneTask();
Assert.assertEquals(ShadowPushRegistratorGPS.regId, getCallBackRegId);
OneSignal.setSubscription(false);
GetIdsAvailable();
threadAndTaskWait();
Assert.assertNull(getCallBackRegId);
}
@Test
public void testSetSubscriptionShouldNotOverrideSubscribeError() throws Exception {
OneSignalInitWithBadProjectNum();
threadAndTaskWait();
// Should not try to update server
ShadowOneSignalRestClient.lastPost = null;
OneSignal.setSubscription(true);
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
// Restart app - Should omit notification_types
restartAppAndElapseTimeToNextSession();
OneSignalInitWithBadProjectNum();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
}
@Test
public void shouldNotResetSubscriptionOnSession() throws Exception {
OneSignalInit();
OneSignal.setSubscription(false);
threadAndTaskWait();
Assert.assertEquals(-2, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
restartAppAndElapseTimeToNextSession();
OneSignalInit();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
}
@Test
public void shouldSetSubscriptionCorrectlyEvenAfterFirstOneSignalRestInitFail() throws Exception {
// Failed to register with OneSignal but SetSubscription was called with false
ShadowOneSignalRestClient.failAll = true;
OneSignalInit();
OneSignal.setSubscription(false);
threadAndTaskWait();
ShadowOneSignalRestClient.failAll = false;
// Restart app - Should send unsubscribe with create player call.
restartAppAndElapseTimeToNextSession();
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(-2, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
// Restart app again - Value synced last time so don't send again.
restartAppAndElapseTimeToNextSession();
OneSignalInit();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
}
@Test
public void shouldUpdateNotificationTypesCorrectlyEvenWhenSetSubscriptionIsCalledInAnErrorState() throws Exception {
OneSignalInitWithBadProjectNum();
threadAndTaskWait();
OneSignal.setSubscription(true);
// Restart app - Should send subscribe with on_session call.
StaticResetHelper.restSetStaticFields();
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
}
@Test
public void shouldAllowMultipleSetSubscription() throws Exception {
OneSignalInit();
threadAndTaskWait();
OneSignal.setSubscription(false);
threadAndTaskWait();
Assert.assertEquals(-2, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
// Should not resend same value
ShadowOneSignalRestClient.lastPost = null;
OneSignal.setSubscription(false);
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
OneSignal.setSubscription(true);
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
// Should not resend same value
ShadowOneSignalRestClient.lastPost = null;
OneSignal.setSubscription(true);
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
}
private static boolean userIdWasNull = false;
@Test
public void shouldNotFireIdsAvailableWithoutUserId() throws Exception {
ShadowOneSignalRestClient.failNext = true;
ShadowPushRegistratorGPS.fail = true;
OneSignal.idsAvailable(new OneSignal.IdsAvailableHandler() {
@Override
public void idsAvailable(String userId, String registrationId) {
if (userId == null)
userIdWasNull = true;
}
});
OneSignalInit();
Assert.assertFalse(userIdWasNull);
threadAndTaskWait();
}
@Test
public void testGCMTimeOutThenSuccessesLater() throws Exception {
// Init with a bad connection to Google.
ShadowPushRegistratorGPS.fail = true;
OneSignalInit();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("identifier"));
// Registers for GCM after a retry
ShadowPushRegistratorGPS.fail = false;
ShadowPushRegistratorGPS.manualFireRegisterForPush();
threadAndTaskWait();
Assert.assertEquals(ShadowPushRegistratorGPS.regId, ShadowOneSignalRestClient.lastPost.getString("identifier"));
// Cold restart app, should not send the same identifier again.
ShadowOneSignalRestClient.lastPost = null;
restartAppAndElapseTimeToNextSession();
OneSignalInit();
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("identifier"));
}
@Test
public void testChangeAppId() throws Exception {
OneSignalInit();
threadAndTaskWait();
int normalCreateFieldCount = ShadowOneSignalRestClient.lastPost.length();
StaticResetHelper.restSetStaticFields();
OneSignal.init(blankActivity, "123456789", "99f7f966-d8cc-11e4-bed1-df8f05be55b2");
threadAndTaskWait();
Assert.assertEquals(normalCreateFieldCount, ShadowOneSignalRestClient.lastPost.length());
}
@Test
public void testUserDeletedFromServer() throws Exception {
// First cold boot normal
OneSignalInit();
threadAndTaskWait();
int normalCreateFieldCount = ShadowOneSignalRestClient.lastPost.length();
ShadowOneSignalRestClient.lastPost = null;
// Developer deletes user, cold boots apps should resend all fields
restartAppAndElapseTimeToNextSession();
ShadowOneSignalRestClient.failNext = true;
ShadowOneSignalRestClient.failResponse = "{\"errors\":[\"Device type is not a valid device_type. Valid options are: 0 = iOS, 1 = Android, 2 = Amazon, 3 = WindowsPhone(MPNS), 4 = ChromeApp, 5 = ChromeWebsite, 6 = WindowsPhone(WNS), 7 = Safari(APNS), 8 = Firefox\"]}";
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(normalCreateFieldCount, ShadowOneSignalRestClient.lastPost.length());
// Developer deletes users again from dashboard while app is running.
ShadowOneSignalRestClient.lastPost = null;
ShadowOneSignalRestClient.failNext = true;
ShadowOneSignalRestClient.failResponse = "{\"errors\":[\"No user with this id found\"]}";
OneSignal.sendTag("key1", "value1");
threadAndTaskWait();
Assert.assertEquals(normalCreateFieldCount, ShadowOneSignalRestClient.lastPost.length() - 1);
}
@Test
public void testOfflineCrashes() throws Exception {
ConnectivityManager connectivityManager = (ConnectivityManager)RuntimeEnvironment.application.getSystemService(Context.CONNECTIVITY_SERVICE);
ShadowConnectivityManager shadowConnectivityManager = shadowOf(connectivityManager);
shadowConnectivityManager.setActiveNetworkInfo(null);
OneSignalInit();
threadAndTaskWait();
OneSignal.sendTag("key", "value");
threadAndTaskWait();
OneSignal.setSubscription(false);
threadAndTaskWait();
}
// ####### SendTags Tests ########
@Test
public void shouldSendTags() throws Exception {
OneSignalInit();
OneSignal.sendTags(new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"));
threadAndTaskWait();
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
Assert.assertEquals(ONESIGNAL_APP_ID, ShadowOneSignalRestClient.lastPost.getString("app_id"));
Assert.assertEquals("value1", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").getString("test1"));
Assert.assertEquals("value2", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").getString("test2"));
// Should omit sending repeated tags
ShadowOneSignalRestClient.lastPost = null;
OneSignal.sendTags(new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"));
threadAndTaskWait();
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
// Should only send changed and new tags
OneSignal.sendTags(new JSONObject("{\"test1\": \"value1.5\", \"test2\": \"value2\", \"test3\": \"value3\"}"));
threadAndTaskWait();
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
JSONObject sentTags = ShadowOneSignalRestClient.lastPost.getJSONObject("tags");
Assert.assertEquals("value1.5", sentTags.getString("test1"));
Assert.assertFalse(sentTags.has(("test2")));
Assert.assertEquals("value3", sentTags.getString("test3"));
}
@Test
public void shouldNotSendTagOnRepeats() throws Exception {
OneSignalInit();
OneSignal.sendTag("test1", "value1");
threadAndTaskWait();
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
Assert.assertEquals(ONESIGNAL_APP_ID, ShadowOneSignalRestClient.lastPost.getString("app_id"));
Assert.assertEquals("value1", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").getString("test1"));
// Should only send new tag
ShadowOneSignalRestClient.lastPost = null;
OneSignal.sendTag("test2", "value2");
threadAndTaskWait();
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
Assert.assertEquals("value2", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").getString("test2"));
// Should not resend first tags
ShadowOneSignalRestClient.lastPost = null;
OneSignal.sendTag("test1", "value1");
threadAndTaskWait();
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
}
@Test
public void shouldSendTagsWithRequestBatching() throws Exception {
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
OneSignal.sendTags(new JSONObject("{\"test1\": \"value1\"}"));
OneSignal.sendTags(new JSONObject("{\"test2\": \"value2\"}"));
GetTags();
threadAndTaskWait();
threadAndTaskWait();
Assert.assertEquals("value1", lastGetTags.getString("test1"));
Assert.assertEquals("value2", lastGetTags.getString("test2"));
Assert.assertEquals(4, ShadowOneSignalRestClient.networkCallCount);
}
@Test
public void shouldNotAttemptToSendTagsBeforeGettingPlayerId() throws Exception {
ShadowPushRegistratorGPS.skipComplete = true;
OneSignalInit();
GetIdsAvailable();
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.networkCallCount);
// Should not attempt to make a network call yet as we don't have a player_id
OneSignal.sendTags(new JSONObject("{\"test1\": \"value1\"}"));
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.networkCallCount);
ShadowPushRegistratorGPS.fireLastCallback();
threadAndTaskWait(); threadAndTaskWait();
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
Assert.assertNotNull(callBackUseId);
}
@Test
public void testOldIntValues() throws Exception {
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", "{\"tags\": {\"int\": 123}}");
editor.putString("ONESIGNAL_USERSTATE_SYNCVALYES_TOSYNC_STATE", "{\"tags\": {\"int\": 123}}");
editor.apply();
OneSignalInit();
threadAndTaskWait();
OneSignal.deleteTag("int");
threadAndTaskWait();
GetTags();
Assert.assertNull(lastGetTags);
}
@Test
public void testSendTagNonStringValues() throws Exception {
OneSignalInit();
OneSignal.sendTags("{\"int\": 122, \"bool\": true, \"null\": null, \"array\": [123], \"object\": {}}");
GetTags();
threadAndTaskWait(); threadAndTaskWait(); threadAndTaskWait();
Assert.assertEquals(String.class, lastGetTags.get("int").getClass());
Assert.assertEquals("122", lastGetTags.get("int"));
Assert.assertEquals(String.class, lastGetTags.get("bool").getClass());
Assert.assertEquals("true", lastGetTags.get("bool"));
// null should be the same as a blank string.
Assert.assertFalse(lastGetTags.has("null"));
Assert.assertFalse(lastGetTags.has("array"));
Assert.assertFalse(lastGetTags.has("object"));
}
private static boolean failedCurModTest;
@Test
public void testSendTagsConcurrentModificationException() throws Exception {
OneSignalInit();
threadAndTaskWait();
for(int a = 0; a < 10; a++) {
List<Thread> threadList = new ArrayList<>(30);
for (int i = 0; i < 30; i++) {
Thread lastThread = newSendTagTestThread(Thread.currentThread(), i);
lastThread.start();
threadList.add(lastThread);
Assert.assertFalse(failedCurModTest);
}
for(Thread thread : threadList)
thread.join();
Assert.assertFalse(failedCurModTest);
}
}
private static Thread newSendTagTestThread(final Thread mainThread, final int id) {
return new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
if (failedCurModTest)
break;
OneSignal.sendTags("{\"key" + id + "\": " + i + "}");
// OneSignalPackagePrivateHelper.OneSignalStateSynchronizer_syncUserState(false);
}
} catch (Throwable t) {
// Ignore the flaky Robolectric null error.
if (t.getStackTrace()[0].getClassName().equals("org.robolectric.shadows.ShadowMessageQueue"))
return;
failedCurModTest = true;
mainThread.interrupt();
throw t;
}
}
});
}
@Test
public void shouldSaveToSyncIfKilledBeforeDelayedCompare() throws Exception {
OneSignalInit();
threadAndTaskWait();
Service service = Robolectric.buildService(SyncService.class).create().get();
OneSignal.sendTag("key", "value");
// Swipe app away from Recent Apps list, should save unsynced data.
OneSignalPackagePrivateHelper.SyncService_onTaskRemoved(service);
OneSignalPackagePrivateHelper.resetRunnables();
// Network call for android params and player create should have been made.
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
// App is re-opened.
StaticResetHelper.restSetStaticFields();
OneSignalInit();
threadAndTaskWait();
// Un-synced tag should now sync.
Assert.assertEquals("value", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").getString("key"));
}
@Test
public void shouldSyncPendingChangesFromSyncService() throws Exception {
OneSignalInit();
threadAndTaskWait();
OneSignal.sendTag("key", "value");
// App is swiped away
Service service = Robolectric.buildService(SyncService.class).create().get();
OneSignalPackagePrivateHelper.SyncService_onTaskRemoved(service);
OneSignalPackagePrivateHelper.resetRunnables();
threadAndTaskWait();
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
StaticResetHelper.restSetStaticFields();
// There were unsynced changes so service should have been scheduled for a restart.
AlarmManager alarmManager = (AlarmManager)RuntimeEnvironment.application.getSystemService(Context.ALARM_SERVICE);
Assert.assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size());
Assert.assertEquals(SyncService.class, shadowOf(shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent()).getIntentClass());
shadowOf(alarmManager).getScheduledAlarms().clear();
// Service is restarted
Intent intent = new Intent();
intent.putExtra("task", 1); // TASK_SYNC
service = Robolectric.buildService(SyncService.class, intent).startCommand(0, 0).get();
threadAndTaskWait();
Assert.assertEquals("value", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").getString("key"));
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
OneSignalInit();
threadAndTaskWait();
// No new changes, don't schedule another restart.
OneSignalPackagePrivateHelper.SyncService_onTaskRemoved(service);
Assert.assertEquals(0, shadowOf(alarmManager).getScheduledAlarms().size());
}
@Test
public void shouldNotCrashIfOnTaskRemovedIsCalledBeforeInitIsDone() {
Service service = Robolectric.buildService(SyncService.class).create().get();
OneSignalPackagePrivateHelper.SyncService_onTaskRemoved(service);
}
@Test
public void testMethodCallsBeforeInit() throws Exception {
GetTags();
OneSignal.sendTag("key", "value");
OneSignal.sendTags("{\"key\": \"value\"}");
OneSignal.deleteTag("key");
OneSignal.deleteTags("[\"key1\", \"key2\"]");
OneSignal.setSubscription(false);
OneSignal.enableVibrate(false);
OneSignal.enableSound(false);
OneSignal.promptLocation();
OneSignal.postNotification("{}", new OneSignal.PostNotificationResponseHandler() {
@Override
public void onSuccess(JSONObject response) {}
@Override
public void onFailure(JSONObject response) {}
});
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification);
OneSignal.removeNotificationOpenedHandler();
OneSignal.removeNotificationReceivedHandler();
OneSignal.startInit(blankActivity).init();
// Checks that Notification setting worked.
Field OneSignal_mInitBuilder = OneSignal.class.getDeclaredField("mInitBuilder");
OneSignal_mInitBuilder.setAccessible(true);
OneSignal.Builder builder = (OneSignal.Builder)OneSignal_mInitBuilder.get(null);
Field OneSignal_Builder_mDisplayOption = builder.getClass().getDeclaredField("mDisplayOption");
OneSignal_Builder_mDisplayOption.setAccessible(true);
OneSignal.OSInFocusDisplayOption inFocusDisplayOption = (OneSignal.OSInFocusDisplayOption)OneSignal_Builder_mDisplayOption.get(builder);
Assert.assertEquals(inFocusDisplayOption, OneSignal.OSInFocusDisplayOption.Notification);
threadAndTaskWait();
}
// ####### DeleteTags Tests ######
@Test
public void testDeleteTagWithNonexistingKey() throws Exception {
OneSignalInit();
OneSignal.deleteTag("int");
}
@Test
public void testDeleteTags() throws Exception {
OneSignalInit();
OneSignal.sendTags("{\"str\": \"str1\", \"int\": 122, \"bool\": true}");
OneSignal.deleteTag("int");
GetTags();
threadAndTaskWait();
Assert.assertFalse(lastGetTags.has("int"));
lastGetTags = null;
// Should only send the tag we added back.
OneSignal.sendTags("{\"str\": \"str1\", \"int\": 122, \"bool\": true}");
threadAndTaskWait();
Assert.assertEquals("{\"int\":\"122\"}", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").toString());
// Make sure a single delete works.
OneSignal.deleteTag("int");
GetTags();
threadAndTaskWait();
Assert.assertFalse(lastGetTags.has("int"));
// Delete all other tags, the 'tags' key should not exists in local storage.
OneSignal.deleteTags(Arrays.asList("bool", "str"));
threadAndTaskWait();
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
String syncValues = prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null);
Assert.assertFalse(new JSONObject(syncValues).has("tags"));
}
@Test
public void testDeleteTagsAfterSync() throws Exception {
OneSignalInit();
OneSignal.sendTags("{\"foo\": \"bar\", \"fuz\": \"baz\"}");
threadAndTaskWait();
Assert.assertEquals("bar", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").get("foo"));
Assert.assertEquals("baz", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").get("fuz"));
OneSignal.deleteTags("[\"foo\", \"fuz\"]");
threadAndTaskWait();
Assert.assertEquals("", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").get("foo"));
Assert.assertEquals("", ShadowOneSignalRestClient.lastPost.getJSONObject("tags").get("fuz"));
GetTags();
Assert.assertNull(lastGetTags);
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
JSONObject syncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
Assert.assertFalse(syncValues.has("tags"));
}
@Test
public void testOmitDeletesOfNonExistingKeys() throws Exception {
OneSignalInit();
OneSignal.deleteTag("this_key_does_not_exist");
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("tags"));
}
// ####### GetTags Tests ########
@Test
public void testGetTagsWithNoTagsShouldBeNull() throws Exception {
OneSignalInit();
threadAndTaskWait();
GetTags();
threadAndTaskWait();
Assert.assertNull(lastGetTags);
String lastUrl = ShadowOneSignalRestClient.lastUrl;
Assert.assertEquals("?app_id=" + ONESIGNAL_APP_ID, lastUrl.substring(lastUrl.lastIndexOf("?")));
}
@Test
public void testGetTagNullCheck() throws Exception {
OneSignalInit();
OneSignal.getTags(null);
}
@Test
public void shouldGetTags() throws Exception {
OneSignalInit();
OneSignal.sendTags(new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"));
threadAndTaskWait();
GetTags();
threadAndTaskWait();
Assert.assertEquals("value1", lastGetTags.getString("test1"));
Assert.assertEquals("value2", lastGetTags.getString("test2"));
}
@Test
public void shouldGetTagsFromServerOnFirstCallAndMergeLocalAndRemote() throws Exception {
OneSignalInit();
threadAndTaskWait();
ShadowOneSignalRestClient.nextSuccessfulGETResponse = "{\"tags\": {\"test1\": \"value1\", \"test2\": \"value2\"}}";
GetTags();
threadAndTaskWait();
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
Assert.assertEquals("value1", lastGetTags.getString("test1"));
Assert.assertEquals("value2", lastGetTags.getString("test2"));
// Makes sure a 2nd call to GetTags correctly uses existing tags and merges new local changes.
lastGetTags = null;
OneSignal.sendTag("test3", "value3");
GetTags();
threadAndTaskWait();
Assert.assertEquals("value1", lastGetTags.getString("test1"));
Assert.assertEquals("value2", lastGetTags.getString("test2"));
Assert.assertEquals("value3", lastGetTags.getString("test3"));
threadAndTaskWait();
// Also ensure only 1 network call is made to just send the new tags only.
Assert.assertEquals(4, ShadowOneSignalRestClient.networkCallCount);
StaticResetHelper.restSetStaticFields();
OneSignalInit();
threadAndTaskWait();
// Test that local pending changes are still applied but new changes made server side a respected.
lastGetTags = null;
ShadowOneSignalRestClient.failNextPut = true;
OneSignal.deleteTag("test2");
OneSignal.sendTag("test4", "value4");
ShadowOneSignalRestClient.nextSuccessfulGETResponse = "{\"tags\": {\"test1\": \"value1\", \"test2\": \"value2\",\"test3\": \"ShouldOverride\",\"test4\": \"RemoteShouldNotOverwriteLocalPending\"}}";
GetTags();
threadAndTaskWait();
Assert.assertEquals("value1", lastGetTags.getString("test1"));
System.out.println("lastGetTags: " + lastGetTags);
Assert.assertFalse(lastGetTags.has("test2"));
Assert.assertEquals("ShouldOverride", lastGetTags.getString("test3"));
Assert.assertEquals("value4", lastGetTags.getString("test4"));
Assert.assertEquals(8, ShadowOneSignalRestClient.networkCallCount);
// Sending 'test1' and 'test3' could be omitted but this is a negotiable performance hit.
Assert.assertEquals("{\"test1\":\"value1\",\"test2\":\"\",\"test3\":\"ShouldOverride\",\"test4\":\"value4\"}",
ShadowOneSignalRestClient.lastPost.optJSONObject("tags").toString());
}
@Test
public void getTagsDelayedAfterRegistering() throws Exception {
ShadowOneSignalRestClient.nextSuccessfulGETResponse = "{\"tags\": {\"test1\": \"value1\"}}";
OneSignalInit();
GetTags();
threadAndTaskWait(); threadAndTaskWait(); threadAndTaskWait();
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
Assert.assertEquals("value1", lastGetTags.getString("test1"));
Assert.assertTrue(ShadowOneSignalRestClient.lastUrl.contains(ShadowOneSignalRestClient.testUserId));
}
@Test
public void syncHashedEmailTest() throws Exception {
OneSignalInit();
// Casing should be forced to lower.
OneSignal.syncHashedEmail("Test@tEst.CoM");
threadAndTaskWait();
Assert.assertEquals("b642b4217b34b1e8d3bd915fc65c4452" ,ShadowOneSignalRestClient.lastPost.getString("em_m"));
Assert.assertEquals("a6ad00ac113a19d953efb91820d8788e2263b28a" ,ShadowOneSignalRestClient.lastPost.getString("em_s"));
// Test email update
ShadowOneSignalRestClient.lastPost = null;
OneSignal.syncHashedEmail("test@test2.com");
threadAndTaskWait();
Assert.assertEquals("3e1163777d25d2b935057c3ae393efee" ,ShadowOneSignalRestClient.lastPost.getString("em_m"));
Assert.assertEquals("69e9ca5af84bc88bc185136cd6f782ee889be5c8" ,ShadowOneSignalRestClient.lastPost.getString("em_s"));
// Test trim on email
ShadowOneSignalRestClient.lastPost = null;
OneSignal.syncHashedEmail(" test@test2.com ");
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
// Test invalid email.
OneSignal.syncHashedEmail("aaaaaa");
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
// Test invalid email.
OneSignal.syncHashedEmail(null);
threadAndTaskWait();
Assert.assertNull(ShadowOneSignalRestClient.lastPost);
}
// ####### on_focus Tests ########
@Test
public void sendsOnFocus() throws Exception {
OneSignalInit();
threadAndTaskWait();
blankActivityController.resume();
ShadowSystemClock.setCurrentTimeMillis(60 * 1000);
blankActivityController.pause();
threadAndTaskWait();
Assert.assertEquals(60, ShadowOneSignalRestClient.lastPost.getInt("active_time"));
Assert.assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
}
/*
// Can't get test to work from a app flow due to the main thread being locked one way or another in a robolectric env.
// Running ActivityLifecycleListener.focusHandlerThread...advanceToNextPostedRunnable waits on the main thread.
// If it is put in its own thread then synchronized that is run when messages a runnable is added / removed hangs the main thread here too.
@Test
public void shouldNotDoubleCountFocusTime() throws Exception {
System.out.println("TEST IS RUNNING ONE THREAD: " + Thread.currentThread());
// Start app normally
OneSignalInit();
threadAndTaskWait();
// Press home button after 30 sec
blankActivityController.resume();
ShadowSystemClock.setCurrentTimeMillis(30 * 1000);
blankActivityController.pause();
threadAndTaskWait();
// Press home button after 30 more sec, with a network hang
blankActivityController.resume();
ShadowSystemClock.setCurrentTimeMillis(60 * 1000);
ShadowOneSignalRestClient.interruptibleDelayNext = true;
blankActivityController.pause();
System.out.println("HERE1");
threadAndTaskWait();
System.out.println("HERE2" + Thread.currentThread());
// Open app and press home button again right away.
blankActivityController.resume();
System.out.println("HERE3: " + Thread.currentThread());
blankActivityController.pause();
System.out.println("HERE4");
threadAndTaskWait();
System.out.println("HERE5");
ShadowOneSignalRestClient.interruptHTTPDelay();
System.out.println("HERE6");
threadWait();
System.out.println("ShadowOneSignalRestClient.lastPost: " + ShadowOneSignalRestClient.lastPost);
System.out.println("ShadowOneSignalRestClient.networkCallCount: " + ShadowOneSignalRestClient.networkCallCount);
Assert.assertEquals(60, ShadowOneSignalRestClient.lastPost.getInt("active_time"));
Assert.assertEquals(2, ShadowOneSignalRestClient.networkCallCount);
}
*/
// ####### Unit Test Location ########
@Test
@Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class})
public void shouldUpdateAllLocationFieldsWhenAnyFieldsChange() throws Exception {
ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION");
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(1.0, ShadowOneSignalRestClient.lastPost.getDouble("lat"));
Assert.assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long"));
Assert.assertEquals(3.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc"));
Assert.assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type"));
ShadowOneSignalRestClient.lastPost = null;
StaticResetHelper.restSetStaticFields();
ShadowFusedLocationApiWrapper.lat = 30.0;
ShadowFusedLocationApiWrapper.accuracy = 5.0f;
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(30.0, ShadowOneSignalRestClient.lastPost.getDouble("lat"));
Assert.assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long"));
Assert.assertEquals(5.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc"));
Assert.assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type"));
}
@Test
@Config(shadows = {ShadowOneSignal.class})
@SuppressWarnings("unchecked") // getDeclaredMethod
public void testLocationTimeout() throws Exception {
//ShadowApplication.getInstance().grantPermissions(new String[]{"android.permission.YOUR_PERMISSION"});
OneSignalInit();
threadAndTaskWait();
Class klass = Class.forName("com.onesignal.LocationGMS");
Method method = klass.getDeclaredMethod("startFallBackThread");
method.setAccessible(true);
method.invoke(null);
method = klass.getDeclaredMethod("fireFailedComplete");
method.setAccessible(true);
method.invoke(null);
threadAndTaskWait();
Assert.assertFalse(ShadowOneSignal.messages.contains("GoogleApiClient timedout"));
}
@Test
@Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class})
public void testLocationSchedule() throws Exception {
ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_FINE_LOCATION");
ShadowFusedLocationApiWrapper.lat = 1.0d; ShadowFusedLocationApiWrapper.log = 2.0d;
ShadowFusedLocationApiWrapper.accuracy = 3.0f;
ShadowFusedLocationApiWrapper.time = 12345L;
// location if we have permission
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat"));
Assert.assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long"));
Assert.assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc"));
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type"));
// Checking make sure an update is scheduled.
AlarmManager alarmManager = (AlarmManager)RuntimeEnvironment.application.getSystemService(Context.ALARM_SERVICE);
Assert.assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size());
Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent();
Assert.assertEquals(SyncService.class, shadowOf(intent).getIntentClass());
// Setting up a new point and testing it is sent
ShadowFusedLocationApiWrapper.lat = 1.1d; ShadowFusedLocationApiWrapper.log = 2.2d;
ShadowFusedLocationApiWrapper.accuracy = 3.3f;
ShadowFusedLocationApiWrapper.time = 12346L;
Robolectric.buildService(SyncService.class, intent).startCommand(0, 0);
threadAndTaskWait();
Assert.assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat"));
Assert.assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long"));
Assert.assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc"));
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("loc_bg"));
Assert.assertEquals("11111111-2222-3333-4444-555555555555", ShadowOneSignalRestClient.lastPost.opt("ad_id"));
// Testing loc_bg
blankActivityController.pause();
threadAndTaskWait();
ShadowFusedLocationApiWrapper.time = 12347L;
Robolectric.buildService(SyncService.class, intent).startCommand(0, 0);
threadAndTaskWait();
Assert.assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat"));
Assert.assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long"));
Assert.assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc"));
Assert.assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg"));
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type"));
Assert.assertEquals("11111111-2222-3333-4444-555555555555", ShadowOneSignalRestClient.lastPost.opt("ad_id"));
}
@Test
@Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class})
public void testLocationFromSyncAlarm() throws Exception {
ShadowFusedLocationApiWrapper.lat = 1.0d; ShadowFusedLocationApiWrapper.log = 2.0d;
ShadowFusedLocationApiWrapper.accuracy = 3.0f;
ShadowFusedLocationApiWrapper.time = 12345L;
OneSignalInit();
threadAndTaskWait();
StaticResetHelper.restSetStaticFields();
AlarmManager alarmManager = (AlarmManager)RuntimeEnvironment.application.getSystemService(Context.ALARM_SERVICE);
shadowOf(alarmManager).getScheduledAlarms().clear();
ShadowOneSignalRestClient.lastPost = null;
ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION");
Intent intent = new Intent();
intent.putExtra("task", 1); // Sync
Robolectric.buildService(SyncService.class, intent).startCommand(0, 0);
threadAndTaskWait();
Assert.assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat"));
Assert.assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long"));
Assert.assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc"));
Assert.assertEquals(0, ShadowOneSignalRestClient.lastPost.optInt("loc_type"));
Assert.assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg"));
// Checking make sure an update is scheduled.
alarmManager = (AlarmManager)RuntimeEnvironment.application.getSystemService(Context.ALARM_SERVICE);
Assert.assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size());
intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent();
Assert.assertEquals(SyncService.class, shadowOf(intent).getIntentClass());
shadowOf(alarmManager).getScheduledAlarms().clear();
}
@Test
public void testAppl() throws Exception {
AddLauncherIntentFilter();
RuntimeEnvironment.getRobolectricPackageManager().addPackage("org.robolectric.default");
OneSignalInit();
threadAndTaskWait();
String baseKey = "pkgs";
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.getJSONArray(baseKey + "_a").length());
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
JSONObject syncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
Assert.assertFalse(syncValues.has(baseKey + "_a"));
Assert.assertEquals(1, syncValues.getJSONArray(baseKey).length());
JSONObject toSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_TOSYNC_STATE", null));
Assert.assertFalse(toSyncValues.has(baseKey + "_a"));
Assert.assertEquals(1, toSyncValues.getJSONArray(baseKey).length());
restartAppAndElapseTimeToNextSession();
ShadowOneSignalRestClient.lastPost = null;
RuntimeEnvironment.getRobolectricPackageManager().addPackage("org.test.app2");
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.getJSONArray(baseKey + "_a").length());
restartAppAndElapseTimeToNextSession();
ShadowOneSignalRestClient.lastPost = null;
RuntimeEnvironment.getRobolectricPackageManager().removePackage("org.test.app2");
OneSignalInit();
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.getJSONArray(baseKey + "_d").length());
restartAppAndElapseTimeToNextSession();
ShadowOneSignalRestClient.lastPost = null;
OneSignalInit();
threadAndTaskWait();
System.out.println("ShadowOneSignalRestClient.lastPost: " + ShadowOneSignalRestClient.lastPost);
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has(baseKey + "_d"));
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has(baseKey + "_a"));
}
// ####### Unit test postNotification #####
private static JSONObject postNotificationSuccess = null, postNotificationFailure = null;
@Test
public void testPostNotification() throws Exception {
OneSignalInit();
OneSignal.PostNotificationResponseHandler handler = new OneSignal.PostNotificationResponseHandler() {
@Override
public void onSuccess(JSONObject response) {
postNotificationSuccess = response;
}
@Override
public void onFailure(JSONObject response) {
postNotificationFailure = response;
}
};
// Not testing input here, just that HTTP 200 fires a success.
OneSignal.postNotification("{}", handler);
threadAndTaskWait();
Assert.assertNotNull(postNotificationSuccess);
Assert.assertNull(postNotificationFailure);
postNotificationSuccess = postNotificationFailure = null;
ShadowOneSignalRestClient.nextSuccessResponse = "{\"id\":\"\",\"recipients\":0,\"errors\":[\"All included players are not subscribed\"]}";
OneSignal.postNotification("{}", handler);
Assert.assertNull(postNotificationSuccess);
Assert.assertNotNull(postNotificationFailure);
}
@Test
@Config(shadows = { ShadowRoboNotificationManager.class, ShadowBadgeCountUpdater.class })
public void shouldCancelAndClearNotifications() throws Exception {
ShadowRoboNotificationManager.notifications.clear();
OneSignalInitFromApplication();
threadAndTaskWait();
// Create 2 notifications
Bundle bundle = getBaseNotifBundle();
OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromGCMIntentService(blankActivity, bundle, null);
bundle = getBaseNotifBundle("UUID2");
OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromGCMIntentService(blankActivity, bundle, null);
// Test canceling
Map<Integer, ShadowRoboNotificationManager.PostedNotification> postedNotifs = ShadowRoboNotificationManager.notifications;
Iterator<Map.Entry<Integer, ShadowRoboNotificationManager.PostedNotification>> postedNotifsIterator = postedNotifs.entrySet().iterator();
ShadowRoboNotificationManager.PostedNotification postedNotification = postedNotifsIterator.next().getValue();
OneSignal.cancelNotification(postedNotification.id);
Assert.assertEquals(1, ShadowBadgeCountUpdater.lastCount);
Assert.assertEquals(1, ShadowRoboNotificationManager.notifications.size());
OneSignal.clearOneSignalNotifications();
Assert.assertEquals(0, ShadowBadgeCountUpdater.lastCount);
Assert.assertEquals(0, ShadowRoboNotificationManager.notifications.size());
// Make sure they are marked dismissed.
SQLiteDatabase readableDb = OneSignalDbHelper.getInstance(blankActivity).getReadableDatabase();
Cursor cursor = readableDb.query(OneSignalPackagePrivateHelper.NotificationTable.TABLE_NAME, new String[] { "created_time" },
OneSignalPackagePrivateHelper.NotificationTable.COLUMN_NAME_DISMISSED + " = 1", null, null, null, null);
Assert.assertEquals(2, cursor.getCount());
cursor.close();
}
// ####### Unit test toJSONObject methods
@Test
public void testOSNotificationToJSONObject() throws Exception {
OSNotification osNotification = createTestOSNotification();
JSONObject testJsonObj = osNotification.toJSONObject();
Assert.assertEquals("msg_body", testJsonObj.optJSONObject("payload").optString("body"));
JSONObject firstActionButton = (JSONObject)testJsonObj.optJSONObject("payload").optJSONArray("actionButtons").get(0);
Assert.assertEquals("text", firstActionButton.optString("text"));
JSONObject additionalData = testJsonObj.optJSONObject("payload").optJSONObject("additionalData");
Assert.assertEquals("bar", additionalData.optString("foo"));
}
@Test
public void testOSNotificationOpenResultToJSONObject() throws Exception {
OSNotificationOpenResult osNotificationOpenResult = new OSNotificationOpenResult();
osNotificationOpenResult.notification = createTestOSNotification();
osNotificationOpenResult.action = new OSNotificationAction();
osNotificationOpenResult.action.type = OSNotificationAction.ActionType.Opened;
JSONObject testJsonObj = osNotificationOpenResult.toJSONObject();
JSONObject additionalData = testJsonObj.optJSONObject("notification").optJSONObject("payload").optJSONObject("additionalData");
Assert.assertEquals("bar", additionalData.optString("foo"));
JSONObject firstGroupedNotification = (JSONObject)testJsonObj.optJSONObject("notification").optJSONArray("groupedNotifications").get(0);
Assert.assertEquals("collapseId1", firstGroupedNotification.optString("collapseId"));
}
@Test
public void testNotificationOpenedProcessorHandlesEmptyIntent() {
NotificationOpenedProcessor_processFromContext(blankActivity, new Intent());
}
@Test
public void shouldOpenChromeTab() throws Exception {
OneSignalInit();
threadAndTaskWait();
Assert.assertTrue(ShadowCustomTabsClient.bindCustomTabsServiceCalled);
Assert.assertTrue(ShadowCustomTabsSession.lastURL.toString().contains("https://onesignal.com/android_frame.html?app_id=b2f7f966-d8cc-11e4-bed1-df8f05be55ba&user_id=a2f7f967-e8cc-11e4-bed1-118f05be4511&ad_id=11111111-2222-3333-4444-555555555555&cbs_id="));
}
@Test
public void shouldHandleChromeNullNewSession() throws Exception {
ShadowCustomTabsClient.nullNewSession = true;
OneSignalInit();
threadAndTaskWait();
}
private OSPermissionStateChanges lastPermissionStateChanges;
private boolean currentPermission;
// Firing right away to match iOS behavior for wrapper SDKs.
@Test
public void shouldFirePermissionObserverOnFirstAdd() throws Exception {
OneSignalInit();
threadAndTaskWait();
OSPermissionObserver permissionObserver = new OSPermissionObserver() {
@Override
public void onOSPermissionChanged(OSPermissionStateChanges stateChanges) {
lastPermissionStateChanges = stateChanges;
currentPermission = stateChanges.getTo().getEnabled();
}
};
OneSignal.addPermissionObserver(permissionObserver);
Assert.assertFalse(lastPermissionStateChanges.getFrom().getEnabled());
Assert.assertTrue(lastPermissionStateChanges.getTo().getEnabled());
// Test to make sure object was correct at the time of firing.
Assert.assertTrue(currentPermission);
}
@Test
public void shouldFirePermissionObserverWhenUserDisablesNotifications() throws Exception {
OneSignalInit();
threadAndTaskWait();
OSPermissionObserver permissionObserver = new OSPermissionObserver() {
@Override
public void onOSPermissionChanged(OSPermissionStateChanges stateChanges) {
lastPermissionStateChanges = stateChanges;
currentPermission = stateChanges.getTo().getEnabled();
}
};
OneSignal.addPermissionObserver(permissionObserver);
lastPermissionStateChanges = null;
// Make sure garbage collection doesn't nuke any observers.
Runtime.getRuntime().gc();
blankActivityController.pause();
threadAndTaskWait();
ShadowNotificationManagerCompat.enabled = false;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertTrue(lastPermissionStateChanges.getFrom().getEnabled());
Assert.assertFalse(lastPermissionStateChanges.getTo().getEnabled());
// Test to make sure object was correct at the time of firing.
Assert.assertFalse(currentPermission);
// unsubscribeWhenNotificationsAreDisabled is not set so don't send notification_types.
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
}
@Test
public void shouldSetNotificationTypesToZeroWhenUnsubscribeWhenNotificationsAreDisabledIsEnabled() throws Exception {
ShadowNotificationManagerCompat.enabled = false;
OneSignal.startInit(blankActivity).unsubscribeWhenNotificationsAreDisabled(true).init();
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID);
threadAndTaskWait();
Assert.assertEquals(0, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
blankActivityController.pause();
threadAndTaskWait();
ShadowNotificationManagerCompat.enabled = true;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertEquals(1, ShadowOneSignalRestClient.lastPost.getInt("notification_types"));
}
@Test
@Config(shadows = {ShadowBadgeCountUpdater.class})
public void shouldClearBadgesWhenPermissionIsDisabled() throws Exception {
OneSignalInit();
threadAndTaskWait();
ShadowBadgeCountUpdater.updateCount(1, blankActivity);
blankActivityController.pause();
threadAndTaskWait();
ShadowNotificationManagerCompat.enabled = false;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertEquals(0, ShadowBadgeCountUpdater.lastCount);
}
private OSSubscriptionStateChanges lastSubscriptionStateChanges;
private boolean currentSubscription;
@Test
public void shouldFireSubscriptionObserverOnFirstAdd() throws Exception {
OneSignalInit();
threadAndTaskWait();
OSSubscriptionObserver permissionObserver = new OSSubscriptionObserver() {
@Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
lastSubscriptionStateChanges = stateChanges;
currentSubscription = stateChanges.getTo().getSubscribed();
}
};
OneSignal.addSubscriptionObserver(permissionObserver);
Assert.assertFalse(lastSubscriptionStateChanges.getFrom().getSubscribed());
Assert.assertTrue(lastSubscriptionStateChanges.getTo().getSubscribed());
// Test to make sure object was correct at the time of firing.
Assert.assertTrue(currentSubscription);
}
@Test
public void shouldFireSubscriptionObserverWhenUserDisablesNotifications() throws Exception {
OneSignalInit();
threadAndTaskWait();
OSSubscriptionObserver subscriptionObserver = new OSSubscriptionObserver() {
@Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
lastSubscriptionStateChanges = stateChanges;
currentSubscription = stateChanges.getTo().getSubscribed();
}
};
OneSignal.addSubscriptionObserver(subscriptionObserver);
lastSubscriptionStateChanges = null;
// Make sure garbage collection doesn't nuke any observers.
Runtime.getRuntime().gc();
blankActivityController.pause();
threadAndTaskWait();
ShadowNotificationManagerCompat.enabled = false;
blankActivityController.resume();
threadAndTaskWait();
Assert.assertTrue(lastSubscriptionStateChanges.getFrom().getSubscribed());
Assert.assertFalse(lastSubscriptionStateChanges.getTo().getSubscribed());
// Test to make sure object was correct at the time of firing.
Assert.assertFalse(currentSubscription);
// unsubscribeWhenNotificationsAreDisabled is not set so don't send notification_types.
Assert.assertFalse(ShadowOneSignalRestClient.lastPost.has("notification_types"));
}
@Test
public void shouldFireSubscriptionObserverWhenChangesHappen() throws Exception {
OneSignalInit();
OSSubscriptionObserver permissionObserver = new OSSubscriptionObserver() {
@Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
lastSubscriptionStateChanges = stateChanges;
currentSubscription = stateChanges.getTo().getSubscribed();
}
};
OneSignal.addSubscriptionObserver(permissionObserver);
threadAndTaskWait();
Assert.assertFalse(lastSubscriptionStateChanges.getFrom().getSubscribed());
Assert.assertTrue(lastSubscriptionStateChanges.getTo().getSubscribed());
// Test to make sure object was correct at the time of firing.
Assert.assertTrue(currentSubscription);
Assert.assertTrue(lastSubscriptionStateChanges.getTo().getUserSubscriptionSetting());
Assert.assertEquals(ShadowPushRegistratorGPS.regId, lastSubscriptionStateChanges.getTo().getPushToken());
Assert.assertEquals(ShadowOneSignalRestClient.testUserId, lastSubscriptionStateChanges.getTo().getUserId());
}
@Test
public void shouldNotFireSubscriptionObserverWhenChangesHappenIfRemoved() throws Exception {
OneSignalInit();
OSSubscriptionObserver permissionObserver = new OSSubscriptionObserver() {
@Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
lastSubscriptionStateChanges = stateChanges;
currentSubscription = stateChanges.getTo().getSubscribed();
}
};
OneSignal.addSubscriptionObserver(permissionObserver);
lastSubscriptionStateChanges = null;
OneSignal.removeSubscriptionObserver(permissionObserver);
threadAndTaskWait();
Assert.assertFalse(currentSubscription);
Assert.assertNull(lastSubscriptionStateChanges);
}
@Test
public void shouldReturnCorrectGetPermissionSubscriptionState() throws Exception {
OneSignalInit();
threadAndTaskWait();
OSPermissionSubscriptionState permissionSubscriptionState = OneSignal.getPermissionSubscriptionState();
Assert.assertTrue(permissionSubscriptionState.getPermissionStatus().getEnabled());
Assert.assertTrue(permissionSubscriptionState.getSubscriptionStatus().getSubscribed());
}
// ####### Unit test helper methods ########
private static OSNotification createTestOSNotification() throws Exception {
OSNotification osNotification = new OSNotification();
osNotification.payload = new OSNotificationPayload();
osNotification.payload.body = "msg_body";
osNotification.payload.additionalData = new JSONObject("{\"foo\": \"bar\"}");
osNotification.payload.actionButtons = new ArrayList<>();
OSNotificationPayload.ActionButton actionButton = new OSNotificationPayload.ActionButton();
actionButton.text = "text";
actionButton.id = "id";
osNotification.payload.actionButtons.add(actionButton);
osNotification.displayType = OSNotification.DisplayType.None;
osNotification.groupedNotifications = new ArrayList<>();
OSNotificationPayload groupedPayload = new OSNotificationPayload();
groupedPayload.collapseId = "collapseId1";
osNotification.groupedNotifications.add(groupedPayload);
return osNotification;
}
private static void threadAndTaskWait() throws Exception {
TestHelpers.threadAndTaskWait();
}
private void OneSignalInit() {
OneSignal.setLogLevel(OneSignal.LOG_LEVEL.DEBUG, OneSignal.LOG_LEVEL.NONE);
ShadowOSUtils.subscribableStatus = 1;
OneSignal.init(blankActivity, "123456789", ONESIGNAL_APP_ID);
}
private void OneSignalInitFromApplication() {
OneSignal.setLogLevel(OneSignal.LOG_LEVEL.DEBUG, OneSignal.LOG_LEVEL.NONE);
OneSignal.init(blankActivity.getApplicationContext(), "123456789", ONESIGNAL_APP_ID);
}
private void OneSignalInitWithBadProjectNum() {
ShadowOSUtils.subscribableStatus = -6;
OneSignal.init(blankActivity, "NOT A VALID Google project number", ONESIGNAL_APP_ID);
}
// For some reason Roboelctric does not automatically add this when it reads the AndroidManifest.xml
// Also it seems it has to be done in the test itself instead of the setup process.
private static void AddLauncherIntentFilter() {
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.setPackage("com.onesignal.example");
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.packageName = "com.onesignal.example";
resolveInfo.activityInfo.name = "MainActivity";
RuntimeEnvironment.getRobolectricPackageManager().addResolveInfoForIntent(launchIntent, resolveInfo);
RuntimeEnvironment.getRobolectricPackageManager().addManifest(shadowOf(RuntimeEnvironment.application).getAppManifest(), 0);
}
private static void AddDisableNotificationOpenedToManifest() {
ShadowApplication.getInstance().getAppManifest().getApplicationMetaData().put("com.onesignal.NotificationOpened.DEFAULT", "DISABLE");
RuntimeEnvironment.getRobolectricPackageManager().addManifest(shadowOf(RuntimeEnvironment.application).getAppManifest(), 0);
}
private static void RemoveDisableNotificationOpenedToManifest() {
ShadowApplication.getInstance().getAppManifest().getApplicationMetaData().remove("com.onesignal.NotificationOpened.DEFAULT");
RuntimeEnvironment.getRobolectricPackageManager().addManifest(shadowOf(RuntimeEnvironment.application).getAppManifest(), 0);
}
private static int sessionCountOffset = 1;
private static void restartAppAndElapseTimeToNextSession() {
StaticResetHelper.restSetStaticFields();
ShadowSystemClock.setCurrentTimeMillis(System.currentTimeMillis() + 1000 * 31 * sessionCountOffset++);
}
}