package com.zegoggles.smssync.service; import android.app.Notification; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.telephony.TelephonyManager; import com.fsck.k9.mail.MessagingException; import com.zegoggles.smssync.contacts.ContactGroup; import com.zegoggles.smssync.mail.DataType; import com.zegoggles.smssync.preferences.AuthPreferences; import com.zegoggles.smssync.preferences.Preferences; import com.zegoggles.smssync.service.exception.BackupDisabledException; import com.zegoggles.smssync.service.exception.NoConnectionException; import com.zegoggles.smssync.service.exception.RequiresBackgroundDataException; import com.zegoggles.smssync.service.exception.RequiresLoginException; import com.zegoggles.smssync.service.exception.RequiresWifiException; import com.zegoggles.smssync.service.state.SmsSyncState; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowConnectivityManager; import org.robolectric.shadows.ShadowNetworkInfo; import org.robolectric.shadows.ShadowNotification; import org.robolectric.shadows.ShadowNotificationManager; import org.robolectric.shadows.ShadowWifiManager; import java.util.List; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import static org.robolectric.Robolectric.shadowOf; @RunWith(RobolectricTestRunner.class) public class SmsBackupServiceTest { SmsBackupService service; ShadowConnectivityManager shadowConnectivityManager; ShadowNotificationManager shadowNotificationManager; ShadowWifiManager shadowWifiManager; @Mock AuthPreferences authPreferences; @Mock Preferences preferences; @Mock BackupTask backupTask; @Mock Alarms alarms; @Before public void before() { initMocks(this); service = new SmsBackupService() { @Override protected BackupTask getBackupTask() { return backupTask; } @Override protected Alarms getAlarms() { return alarms; } @Override protected Preferences getPreferences() { return preferences; } @Override protected AuthPreferences getAuthPreferences() { return authPreferences; } }; shadowNotificationManager = shadowOf(service.getNotifier()); shadowConnectivityManager = shadowOf(service.getConnectivityManager()); shadowWifiManager = shadowOf(service.getWifiManager()); service.onCreate(); when(authPreferences.getStoreUri()).thenReturn("imap+ssl+://xoauth:foooo@imap.gmail.com:993"); when(authPreferences.isLoginInformationSet()).thenReturn(true); when(preferences.getBackupContactGroup()).thenReturn(ContactGroup.EVERYBODY); } @After public void after() { service.onDestroy(); } @Test public void shouldTriggerBackupWithManualIntent() throws Exception { Intent intent = new Intent(); intent.putExtra(BackupType.EXTRA, BackupType.MANUAL.name()); service.handleIntent(intent); verify(backupTask).execute(any(BackupConfig.class)); } @Test public void shouldRespectBackgroundDataSetting() throws Exception { Intent intent = new Intent(); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(RequiresBackgroundDataException.class); shadowConnectivityManager.setBackgroundDataSetting(true); service.handleIntent(intent); verify(backupTask).execute(any(BackupConfig.class)); } @Test public void shouldCheckForConnectivityBeforeBackingUp() throws Exception { Intent intent = new Intent(); intent.putExtra(BackupType.EXTRA, BackupType.MANUAL.name()); shadowConnectivityManager.setActiveNetworkInfo(null); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(NoConnectionException.class); } @Test public void shouldCheckForWifiConnectivity() throws Exception { Intent intent = new Intent(); when(preferences.isWifiOnly()).thenReturn(true); shadowConnectivityManager.setBackgroundDataSetting(true); shadowWifiManager.setWifiEnabled(false); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(RequiresWifiException.class); } @Test public void shouldCheckForWifiConnectivityAndNetworkType() throws Exception { Intent intent = new Intent(); when(preferences.isWifiOnly()).thenReturn(true); shadowConnectivityManager.setBackgroundDataSetting(true); shadowConnectivityManager.setActiveNetworkInfo(connectedViaEdge()); shadowWifiManager.setWifiEnabled(true); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(RequiresWifiException.class); } @Test public void shouldCheckForLoginCredentials() throws Exception { Intent intent = new Intent(); when(authPreferences.isLoginInformationSet()).thenReturn(false); shadowConnectivityManager.setBackgroundDataSetting(true); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(RequiresLoginException.class); } @Test public void shouldCheckForEnabledDataTypes() throws Exception { for (DataType type : DataType.values()) { type.setBackupEnabled(Robolectric.application, false); } Intent intent = new Intent(); when(authPreferences.isLoginInformationSet()).thenReturn(true); shadowConnectivityManager.setBackgroundDataSetting(true); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(BackupDisabledException.class); assertThat(service.getState().state).isEqualTo(SmsSyncState.FINISHED_BACKUP); } @Test public void shouldPassInCorrectBackupConfig() throws Exception { Intent intent = new Intent(); intent.putExtra(BackupType.EXTRA, BackupType.MANUAL.name()); ArgumentCaptor<BackupConfig> config = ArgumentCaptor.forClass(BackupConfig.class); service.handleIntent(intent); verify(backupTask).execute(config.capture()); BackupConfig backupConfig = config.getValue(); assertThat(backupConfig.backupType).isEqualTo(BackupType.MANUAL); assertThat(backupConfig.currentTry).isEqualTo(0); assertThat(backupConfig.skip).isFalse(); } @Test public void shouldScheduleNextBackupAfterFinished() throws Exception { shadowConnectivityManager.setBackgroundDataSetting(true); Intent intent = new Intent(); intent.putExtra(BackupType.EXTRA, BackupType.REGULAR.name()); service.handleIntent(intent); verify(backupTask).execute(any(BackupConfig.class)); service.backupStateChanged(service.transition(SmsSyncState.FINISHED_BACKUP, null)); verify(alarms).scheduleRegularBackup(); assertThat(shadowOf(service).isStoppedBySelf()); assertThat(shadowOf(service).isForegroundStopped()); } @Test public void shouldCheckForValidStore() throws Exception { when(authPreferences.getStoreUri()).thenReturn("invalid"); Intent intent = new Intent(); intent.putExtra(BackupType.EXTRA, BackupType.MANUAL.name()); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertThat(service.getState().exception).isExactlyInstanceOf(MessagingException.class); } @Test public void shouldNotifyUserAboutErrorInManualMode() throws Exception { when(authPreferences.getStoreUri()).thenReturn("invalid"); Intent intent = new Intent(); intent.putExtra(BackupType.EXTRA, BackupType.MANUAL.name()); service.handleIntent(intent); verifyZeroInteractions(backupTask); assertNotificationShown("SMSBackup+ error", "No valid IMAP URI: invalid"); assertThat(shadowOf(service).isStoppedBySelf()); assertThat(shadowOf(service).isForegroundStopped()); } private void assertNotificationShown(String title, String message) { List<Notification> notifications = shadowNotificationManager.getAllNotifications(); assertThat(notifications).hasSize(1); ShadowNotification n = shadowOf(notifications.get(0)); assertThat(n.getLatestEventInfo().getContentTitle()).isEqualTo(title); assertThat(n.getLatestEventInfo().getContentText()).isEqualTo(message); } private NetworkInfo connectedViaEdge() { return ShadowNetworkInfo.newInstance( null, /* detailed state */ ConnectivityManager.TYPE_MOBILE_HIPRI, TelephonyManager.NETWORK_TYPE_EDGE, true, /* available */ true /* connected */ ); } }