All Downloads are FREE. Search and download functionalities are using the official Maven repository.

test.java.com.cloudant.sync.replication.ReplicationServiceTest Maven / Gradle / Ivy

There is a newer version: 2.4.1
Show newest version
package com.cloudant.sync.replication;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.wifi.WifiManager;
import android.os.SystemClock;
import android.test.ServiceTestCase;

import com.cloudant.android.TestReplicationService;

import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.io.File;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class ReplicationServiceTest extends ServiceTestCase {

    private static final long ALARM_TOLERANCE_MS = 500;
    private Context mMockContext;
    private SharedPreferences mMockPreferences;
    private SharedPreferences.Editor mMockPreferencesEditor;
    private AlarmManager mMockAlarmManager;
    private WifiManager mMockWifiManager;
    private WifiManager.WifiLock mMockWifiLock;

    ReplicationPolicyManager mMockReplicationPolicyManager;
    private TestReplicationService mService;

    public ReplicationServiceTest() {
        super(TestReplicationService.class);
    }

    @BeforeClass
    public void setUp() {
        mMockContext = mock(Context.class);
        mMockPreferences = mock(SharedPreferences.class);
        when(mMockContext.getSharedPreferences("com.cloudant.preferences", Context.MODE_PRIVATE)).thenReturn(mMockPreferences);
        when(mMockContext.getPackageName()).thenReturn("cloudant.com.androidtest");
        when(mMockContext.getDir(anyString(), anyInt())).thenReturn(new File("/data/data/cloudant.com.androidtest/files"));
        when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
        mMockPreferencesEditor = mock(SharedPreferences.Editor.class);
        when(mMockPreferences.edit()).thenReturn(mMockPreferencesEditor);
        mMockAlarmManager = mock(AlarmManager.class);
        mMockWifiManager = mock(WifiManager.class);
        mMockWifiLock = mock(WifiManager.WifiLock.class);
        mMockReplicationPolicyManager = mock(ReplicationPolicyManager.class);
    }

    @Test
    public void testServiceStart() {
        Intent intent = new Intent(getContext(), TestReplicationService.class);
        startService(intent);
    }

    @Test
    public void testServiceBind() {
        Intent intent = new Intent(getContext(), TestReplicationService.class);
        bindService(intent);
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * the device has been rebooted, if the value for the next alarm scheduled in SharedPreferences
     * is in the past, then the SharedPreferences are updated to indicate that the next
     * alarm should be triggered immediately.
     */
    @Test
    public void testOnStartCommandRebootImmediateAlarm() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(ReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_DEVICE_REBOOTED);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_DEVICE_REBOOTED) {
                    latch.countDown();
                }
            }
        });

        ArgumentCaptor captorPrefKeys = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor captorPrefValues = ArgumentCaptor.forClass(Long.class);
        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            verify(mMockPreferencesEditor, times(2)).putLong(captorPrefKeys.capture(), captorPrefValues.capture());
            List prefsKeys = captorPrefKeys.getAllValues();
            List prefsValues = captorPrefValues.getAllValues();
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueElapsed", prefsKeys.get(0));
            assertTrue("Alarm elapsed time not within " + ALARM_TOLERANCE_MS + "ms of current time",
                Math.abs(SystemClock.elapsedRealtime() - prefsValues.get(0)) < ALARM_TOLERANCE_MS);
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueClock", prefsKeys.get(1));
            assertTrue("Alarm clock time not within " + ALARM_TOLERANCE_MS + "ms of current time",
                    Math.abs(System.currentTimeMillis() - prefsValues.get(1)) < ALARM_TOLERANCE_MS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * the device has been rebooted, if the next alarm scheduled in SharedPreferences is within
     * the alarm period of the current time, the alarm scheduled in SharedPreferences is unchanged.
     */
    public void testOnStartCommandRebootDelayedAlarm() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(ReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_DEVICE_REBOOTED);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        long timeReturned = System.currentTimeMillis() + ((1000 * service.getUnboundIntervalInSeconds()) / 2);
        when(mMockPreferences.getLong("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueClock", 0)).thenReturn(timeReturned);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_DEVICE_REBOOTED) {
                    latch.countDown();
                }
            }
        });

        ArgumentCaptor captorPrefKeys = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor captorPrefValues = ArgumentCaptor.forClass(Long.class);
        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            verify(mMockPreferencesEditor, never()).putLong(captorPrefKeys.capture(), captorPrefValues.capture());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * the device has been rebooted, if the next alarm scheduled in SharedPreferences is scheduled
     * more than the alarm period in the future, the next alarm is scheduled one alarm period from
     * the current time.
     */
    public void testOnStartCommandRebootLateAlarm() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_DEVICE_REBOOTED);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        long timeReturned = System.currentTimeMillis() + ((1000 * service.getUnboundIntervalInSeconds()) * 10);
        when(mMockPreferences.getLong("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueClock", 0)).thenReturn(timeReturned);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_DEVICE_REBOOTED) {
                    latch.countDown();
                }
            }
        });

        ArgumentCaptor captorPrefKeys = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor captorPrefValues = ArgumentCaptor.forClass(Long.class);
        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            verify(mMockPreferencesEditor, times(2)).putLong(captorPrefKeys.capture(), captorPrefValues.capture());
            List prefsKeys = captorPrefKeys.getAllValues();
            List prefsValues = captorPrefValues.getAllValues();
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueElapsed", prefsKeys.get(0));
            long expectedElapsedTime = SystemClock.elapsedRealtime() + (1000 * service.getUnboundIntervalInSeconds());
            long expectedRealTime = System.currentTimeMillis() + (1000 * service.getUnboundIntervalInSeconds());
            assertTrue("Alarm elapsed time not within " + ALARM_TOLERANCE_MS + "ms of expected time",
                Math.abs(expectedElapsedTime - prefsValues.get(0)) < ALARM_TOLERANCE_MS);
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueClock", prefsKeys.get(1));
            assertTrue("Alarm clock time not within " + ALARM_TOLERANCE_MS + "ms of expected time",
                Math.abs(expectedRealTime - prefsValues.get(1)) < ALARM_TOLERANCE_MS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * periodic replications should be started, the {@link AlarmManager}, is setup to fire
     * at the correct time and with the correct frequency.
     */
    public void testOnStartCommandStartPeriodicReplications() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_START_PERIODIC_REPLICATION);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        long timeReturned = SystemClock.elapsedRealtime();
        when(mMockPreferences.getBoolean("com.cloudant.sync.replication.PeriodicReplicationService.periodicReplicationsActive", false)).thenReturn(false);
        when(mMockContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mMockAlarmManager);
        when(mMockPreferences.getLong("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueElapsed", 0)).thenReturn(timeReturned);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_START_PERIODIC_REPLICATION) {
                    latch.countDown();
                }
            }
        });

        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            ArgumentCaptor captorPrefKeys = ArgumentCaptor.forClass(String.class);
            ArgumentCaptor captorPrefValues = ArgumentCaptor.forClass(Boolean.class);
            verify(mMockPreferencesEditor, times(1)).putBoolean(captorPrefKeys.capture(), captorPrefValues.capture());
            String prefsKey = captorPrefKeys.getValue();
            Boolean prefsValue = captorPrefValues.getValue();
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.periodicReplicationsActive", prefsKey);
            assertTrue("Alarm manager should be set in running state", prefsValue);


            ArgumentCaptor captorType = ArgumentCaptor.forClass(Integer.class);
            ArgumentCaptor captorTriggerAtMillis = ArgumentCaptor.forClass(Long.class);
            ArgumentCaptor captorIntervalMillis = ArgumentCaptor.forClass(Long.class);

            verify(mMockAlarmManager, times(1)).setInexactRepeating(captorType.capture(), captorTriggerAtMillis.capture(), captorIntervalMillis.capture(), Mockito.any(PendingIntent.class));
            assertEquals("Incorrect alarm type", AlarmManager.ELAPSED_REALTIME_WAKEUP, (int) captorType.getValue());
            assertEquals("Incorrect initial trigger time", timeReturned, (long) captorTriggerAtMillis.getValue());
            assertEquals("Incorrect alarm period", service.getUnboundIntervalInSeconds() * 1000, (long) captorIntervalMillis.getValue());

            // Unfortunately, we can't do much testing of the PendingIntent itself as there aren't
            // methods to extract anything useful.
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * periodic replications should be started, if they have already been started, the
     * {@link AlarmManager} is not invoked to restart the periodic replications..
     */
    public void testOnStartCommandStartPeriodicReplicationsAlreadyStarted() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_START_PERIODIC_REPLICATION);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        when(mMockPreferences.getBoolean("com.cloudant.sync.replication.PeriodicReplicationService.periodicReplicationsActive", false)).thenReturn(true);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_START_PERIODIC_REPLICATION) {
                    latch.countDown();
                }
            }
        });

        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
             verify(mMockAlarmManager, never()).setInexactRepeating(Mockito.anyInt(), Mockito.anyLong(), Mockito.anyLong(), Mockito.any(PendingIntent.class));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * periodic replications should be stopped, the {@link AlarmManager}, is cancelled.
     */
    public void testOnStartCommandStopPeriodicReplications() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_STOP_PERIODIC_REPLICATION);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        long timeReturned = SystemClock.elapsedRealtime();
        when(mMockPreferences.getBoolean("com.cloudant.sync.replication.PeriodicReplicationService.periodicReplicationsActive", false)).thenReturn(true);
        when(mMockContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mMockAlarmManager);
        when(mMockPreferences.getLong("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueElapsed", 0)).thenReturn(timeReturned);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_STOP_PERIODIC_REPLICATION) {
                    latch.countDown();
                }
            }
        });

        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            ArgumentCaptor captorPrefKeys = ArgumentCaptor.forClass(String.class);
            ArgumentCaptor captorPrefValues = ArgumentCaptor.forClass(Boolean.class);
            verify(mMockPreferencesEditor, times(1)).putBoolean(captorPrefKeys.capture(), captorPrefValues.capture());
            String prefsKey = captorPrefKeys.getValue();
            Boolean prefsValue = captorPrefValues.getValue();
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.periodicReplicationsActive", prefsKey);
            assertFalse("Alarm manager should be set in stopped state", prefsValue);

            verify(mMockAlarmManager, times(1)).cancel(Mockito.any(PendingIntent.class));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * periodic replications should be stopped, if they have already been stopped, the
     * {@link AlarmManager} is not cancelled again.
     */
    public void testOnStartCommandStopPeriodicReplicationsAlreadyStopped() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_STOP_PERIODIC_REPLICATION);
        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        long timeReturned = SystemClock.elapsedRealtime();
        when(mMockPreferences.getBoolean("com.cloudant.sync.replication.PeriodicReplicationService.periodicReplicationsActive", false)).thenReturn(false);
        when(mMockContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mMockAlarmManager);
        when(mMockPreferences.getLong("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueElapsed", 0)).thenReturn(timeReturned);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_STOP_PERIODIC_REPLICATION) {
                    latch.countDown();
                }
            }
        });

        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            verify(mMockAlarmManager, never()).cancel(Mockito.any(PendingIntent.class));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * a replication should be started, a {@link android.net.wifi.WifiManager.WifiLock} is acquired,
     * the {@link ReplicationPolicyManager} is started and the next alarm time is stored in
     * SharedPreferences.
     */
    public void testOnStartCommandStartReplication() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        Intent intent = new Intent(mMockContext, TestReplicationService.class);
        intent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_START_REPLICATION);
        service.setReplicationPolicyManager(mMockReplicationPolicyManager);

        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
        when(mMockWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "ReplicationService")).thenReturn(mMockWifiLock);

        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(1);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_START_REPLICATION) {
                    latch.countDown();
                }
            }
        });

        service.onStartCommand(intent, 0, 0);
        try {
            latch.await();
            verify(mMockWifiLock, times(1)).acquire();
            verify(mMockReplicationPolicyManager, times(1)).startReplications();
            ArgumentCaptor captorPrefKeys = ArgumentCaptor.forClass(String.class);
            ArgumentCaptor captorPrefValues = ArgumentCaptor.forClass(Long.class);
            verify(mMockPreferencesEditor, times(2)).putLong(captorPrefKeys.capture(), captorPrefValues.capture());
            List prefsKeys = captorPrefKeys.getAllValues();
            List prefsValues = captorPrefValues.getAllValues();
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueElapsed", prefsKeys.get(0));
            assertTrue("Alarm elapsed time not within " + ALARM_TOLERANCE_MS + "ms of current time",
                Math.abs(SystemClock.elapsedRealtime() + (service.getUnboundIntervalInSeconds() * 1000) - prefsValues.get(0)) < ALARM_TOLERANCE_MS);
            assertEquals("com.cloudant.sync.replication.PeriodicReplicationService.alarmDueClock", prefsKeys.get(1));
            assertTrue("Alarm clock time not within " + ALARM_TOLERANCE_MS + "ms of current time",
                Math.abs(System.currentTimeMillis() + (service.getUnboundIntervalInSeconds() * 1000) - prefsValues.get(1)) < ALARM_TOLERANCE_MS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Check that when an intent is sent to the {@link PeriodicReplicationService} indicating that
     * a replication should be stopped, the {@link android.net.wifi.WifiManager.WifiLock} is
     * released and the {@link ReplicationPolicyManager} is stopped.
     */
    public void testOnStartCommandStopReplication() {
        PeriodicReplicationService service = new TestReplicationService(mMockContext);
        service.setReplicationPolicyManager(mMockReplicationPolicyManager);

        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
        when(mMockWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "ReplicationService")).thenReturn(mMockWifiLock);
        when(mMockWifiLock.isHeld()).thenReturn(true);

        service.onCreate();
        final CountDownLatch latch = new CountDownLatch(2);

        service.setOperationStartedListener(new PeriodicReplicationService
                .OperationStartedListener() {
            @Override
            public void operationStarted(int operationId) {
                if (operationId == PeriodicReplicationService.COMMAND_START_REPLICATION && latch.getCount() == 2) {
                    latch.countDown();
                }
                else if (operationId == PeriodicReplicationService.COMMAND_STOP_REPLICATION && latch.getCount() == 1) {
                    latch.countDown();
                }
            }
        });

        Intent startIntent = new Intent(mMockContext, TestReplicationService.class);
        startIntent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_START_REPLICATION);
        service.onStartCommand(startIntent, 0, 0);
        Intent stopIntent = new Intent(mMockContext, TestReplicationService.class);
        stopIntent.putExtra(PeriodicReplicationService.EXTRA_COMMAND, PeriodicReplicationService.COMMAND_STOP_REPLICATION);
        service.onStartCommand(stopIntent, 0, 0);
        try {
            latch.await();
            verify(mMockWifiLock, times(1)).release();
            verify(mMockReplicationPolicyManager, times(1)).stopReplications();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy