package com.novoda.downloadmanager.lib;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.net.Uri;
import android.support.annotation.NonNull;
import com.novoda.downloadmanager.lib.DownloadContract.Batches;
import com.novoda.downloadmanager.lib.DownloadContract.Downloads;
import com.novoda.notils.string.StringUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.InOrder;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollection;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.whenNew;
@RunWith(Enclosed.class)
public class BatchStatusRepositoryTests {
private static final long ANY_BATCH_ID = 1;
private static final long ANY_DOWNLOAD_ID = 2l;
private static final int ANY_BATCH_STATUS = DownloadStatus.RUNNING;
private static final Uri PUBLICLY_ACCESSIBLE_DOWNLOADS_URI = mock(Uri.class);
private static final Uri DOWNLOADS_BY_BATCH_URI = mock(Uri.class);
private static final Uri ALL_DOWNLOADS_URI = mock(Uri.class);
private static final Uri BATCHES_URI = mock(Uri.class);
private static final Uri BATCH_BY_ID_URI = mock(Uri.class);
private static final Uri CONTENT_URI = mock(Uri.class);
private static final Uri DOWNLOADS_WITHOUT_PROGRESS_URI = mock(Uri.class);
private static final Uri BATCHES_WITHOUT_PROGRESS_URI = mock(Uri.class);
private static final long CURRENT_TIME_MILLIS = 1l;
@RunWith(Parameterized.class)
public static class GetBatchStatus {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{DownloadStatus.PAUSING},
{DownloadStatus.QUEUED_DUE_CLIENT_RESTRICTIONS},
{DownloadStatus.DELETING},
{DownloadStatus.SUBMITTED},
{DownloadStatus.PENDING},
{DownloadStatus.RUNNING},
{DownloadStatus.PAUSED_BY_APP},
{DownloadStatus.WAITING_TO_RETRY},
{DownloadStatus.WAITING_FOR_NETWORK},
{DownloadStatus.QUEUED_FOR_WIFI},
{DownloadStatus.INSUFFICIENT_SPACE_ERROR},
{DownloadStatus.DEVICE_NOT_FOUND_ERROR},
{DownloadStatus.SUCCESS},
{DownloadStatus.BAD_REQUEST},
{DownloadStatus.NOT_ACCEPTABLE},
{DownloadStatus.LENGTH_REQUIRED},
{DownloadStatus.PRECONDITION_FAILED},
{DownloadStatus.MIN_ARTIFICIAL_ERROR_STATUS},
{DownloadStatus.FILE_ALREADY_EXISTS_ERROR},
{DownloadStatus.CANNOT_RESUME},
{DownloadStatus.CANCELED},
{DownloadStatus.UNKNOWN_ERROR},
{DownloadStatus.FILE_ERROR},
{DownloadStatus.UNHANDLED_REDIRECT},
{DownloadStatus.UNHANDLED_HTTP_CODE},
{DownloadStatus.HTTP_DATA_ERROR},
{DownloadStatus.HTTP_EXCEPTION},
{DownloadStatus.TOO_MANY_REDIRECTS},
{DownloadStatus.BATCH_FAILED},
});
}
private int expectedStatus;
public GetBatchStatus(int expectedStatus) {
this.expectedStatus = expectedStatus;
}
@Test
public void givenAStatusThenThatStatusIsRetrieved() throws Exception {
BatchStatusRepository service = givenABatchWithStatus(ANY_BATCH_ID, expectedStatus);
int batchStatus = service.getBatchStatus(ANY_BATCH_ID);
assertThat(batchStatus).isEqualTo(expectedStatus);
}
private BatchStatusRepository givenABatchWithStatus(long batchId, int status) {
SystemFacade mockSystemFacade = mock(SystemFacade.class);
DownloadsUriProvider mockDownloadsUriProvider = mock(DownloadsUriProvider.class);
when(mockDownloadsUriProvider.getSingleBatchUri(batchId)).thenReturn(BATCH_BY_ID_URI);
MockCursorWithStatuses mockCursorWithStatuses = new MockCursorWithStatuses(status);
ContentResolver mockResolver = mock(ContentResolver.class);
String[] projection = {Batches.COLUMN_STATUS};
when(mockResolver.query(BATCH_BY_ID_URI, projection, null, null, null))
.thenReturn(mockCursorWithStatuses);
return new BatchStatusRepository(mockResolver, mockDownloadsUriProvider, mockSystemFacade);
}
}
@RunWith(Parameterized.class)
public static class CalculateBatchStatus {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{new Integer[]{DownloadStatus.SUCCESS, DownloadStatus.SUBMITTED}, DownloadStatus.RUNNING},
{new Integer[]{DownloadStatus.SUCCESS, DownloadStatus.BATCH_FAILED}, DownloadStatus.BATCH_FAILED},
{new Integer[]{DownloadStatus.SUBMITTED, DownloadStatus.SUBMITTED, DownloadStatus.SUBMITTED, DownloadStatus.SUCCESS}, DownloadStatus.RUNNING},
{new Integer[]{DownloadStatus.SUCCESS, DownloadStatus.SUCCESS}, DownloadStatus.SUCCESS},
{new Integer[]{DownloadStatus.PENDING, DownloadStatus.PENDING}, DownloadStatus.PENDING},
});
}
private Integer[] statuses;
private int expectedStatus;
public CalculateBatchStatus(Integer[] statuses, int expectedStatus) {
this.statuses = statuses;
this.expectedStatus = expectedStatus;
}
@Test
public void calculateCorrectStatusFromStatuses() throws Exception {
BatchStatusRepository repository = givenABatchWithStatuses(statuses);
int batchStatus = repository.calculateBatchStatusFromDownloads(ANY_BATCH_ID);
assertThat(batchStatus).isEqualTo(expectedStatus);
}
private BatchStatusRepository givenABatchWithStatuses(Integer... statuses) throws Exception {
DownloadsUriProvider downloadsUriProvider = givenDownloadsUriProvider();
MockCursorWithStatuses mockCursorWithStatuses = new MockCursorWithStatuses(statuses);
ContentResolver mockResolver = mock(ContentResolver.class);
when(mockResolver.query(same(downloadsUriProvider.getAllDownloadsUri()), any(String[].class), anyString(), any(String[].class), anyString()))
.thenReturn(mockCursorWithStatuses);
return new BatchStatusRepository(mockResolver, downloadsUriProvider, mock(SystemFacade.class));
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({ContentUris.class, BatchStatusRepository.class})
public static class UpdateBatchStatus {
@Test
public void whenUpdatingABatchStatusThenTheCorrectBatchIsUpdated() throws Exception {
final ContentValues mockContentValues = mock(ContentValues.class);
whenNew(ContentValues.class).withAnyArguments().thenReturn(mockContentValues);
mockStatic(ContentUris.class);
when(ContentUris.withAppendedId(BATCHES_URI, ANY_BATCH_ID)).thenReturn(BATCH_BY_ID_URI);
ContentResolver mockResolver = mock(ContentResolver.class);
BatchStatusRepository batchStatusRepository = givenBatchStatusServiceAtCurrentTime(mockResolver);
batchStatusRepository.updateBatchStatus(ANY_BATCH_ID, ANY_BATCH_STATUS);
verify(mockContentValues).put(Batches.COLUMN_STATUS, ANY_BATCH_STATUS);
verify(mockContentValues).put(Batches.COLUMN_LAST_MODIFICATION, CURRENT_TIME_MILLIS);
verify(mockResolver).update(
BATCH_BY_ID_URI,
mockContentValues,
null,
null
);
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({ContentUris.class, BatchStatusRepository.class})
public static class CancelBatch {
private ContentValues mockContentValues;
private ContentResolver mockResolver;
private BatchStatusRepository batchStatusRepository;
@Before
public void setUp() throws Exception {
mockStatic(ContentUris.class);
when(ContentUris.withAppendedId(BATCHES_URI, ANY_BATCH_ID)).thenReturn(BATCH_BY_ID_URI);
mockContentValues = mock(ContentValues.class);
whenNew(ContentValues.class).withAnyArguments().thenReturn(mockContentValues);
mockResolver = mock(ContentResolver.class);
batchStatusRepository = givenBatchStatusServiceAtCurrentTime(mockResolver);
}
@Test
public void whenCancellingBatchItemsThenCorrectDownloadsAreCancelled() throws Exception {
batchStatusRepository.setBatchItemsCancelled(ANY_BATCH_ID);
InOrder order = inOrder(mockContentValues, mockResolver);
thenDownloadsAreCancelled(ANY_BATCH_ID, order);
}
@Test
public void whenCancellingBatchThenCorrectBatchAndDownloadsAreCancelled() throws Exception {
batchStatusRepository.cancelBatch(ANY_BATCH_ID);
InOrder order = inOrder(mockContentValues, mockResolver);
thenDownloadsAreCancelled(ANY_BATCH_ID, order);
thenStatusIsCancelled(order);
}
private void thenDownloadsAreCancelled(long batchId, InOrder inOrder) {
inOrder.verify(mockContentValues).put(Downloads.COLUMN_STATUS, DownloadStatus.CANCELED);
inOrder.verify(mockResolver).update(
ALL_DOWNLOADS_URI,
mockContentValues,
Downloads.COLUMN_BATCH_ID + " = ?",
new String[]{String.valueOf(batchId)}
);
}
private void thenStatusIsCancelled(InOrder inOrder) {
inOrder.verify(mockContentValues).put(Batches.COLUMN_STATUS, DownloadStatus.CANCELED);
inOrder.verify(mockResolver).update(BATCH_BY_ID_URI, mockContentValues, null, null);
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({BatchStatusRepository.class})
public static class SetBatchItemsFailed {
@Test
public void whenSettingBatchItemsToFailedThenCorrectDownloadsAreUpdated() throws Exception {
final ContentValues mockContentValues = mock(ContentValues.class);
whenNew(ContentValues.class).withAnyArguments().thenReturn(mockContentValues);
ContentResolver mockResolver = mock(ContentResolver.class);
BatchStatusRepository batchStatusRepository = givenBatchStatusServiceAtCurrentTime(mockResolver);
batchStatusRepository.setBatchItemsFailed(ANY_BATCH_ID, ANY_DOWNLOAD_ID);
verify(mockContentValues).put(Downloads.COLUMN_STATUS, DownloadStatus.BATCH_FAILED);
verify(mockResolver).update(
ALL_DOWNLOADS_URI,
mockContentValues,
Downloads.COLUMN_BATCH_ID + " = ? AND " + Downloads._ID + " <> ? ",
new String[]{String.valueOf(ANY_BATCH_ID), String.valueOf(ANY_DOWNLOAD_ID)}
);
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({BatchStatusRepository.class, StringUtils.class})
public static class UpdateBatchToPendingStatus {
@Test
public void whenSettingBatchItemsToFailedThenCorrectDownloadsAreUpdated() throws Exception {
final ContentValues mockContentValues = mock(ContentValues.class);
whenNew(ContentValues.class).withAnyArguments().thenReturn(mockContentValues);
mockStatic(StringUtils.class);
String where = Batches._ID + " = ?";
when(StringUtils.join(anyCollection(), anyString())).thenReturn(where);
ContentResolver mockResolver = mock(ContentResolver.class);
BatchStatusRepository batchStatusRepository = givenBatchStatusServiceAtCurrentTime(mockResolver);
batchStatusRepository.updateBatchToPendingStatus(Collections.singletonList(String.valueOf(ANY_BATCH_ID)));
verify(mockContentValues).put(Batches.COLUMN_STATUS, DownloadStatus.PENDING);
verify(mockResolver).update(
BATCHES_URI,
mockContentValues,
where,
new String[]{String.valueOf(ANY_BATCH_ID)}
);
}
}
@NonNull
private static BatchStatusRepository givenBatchStatusServiceAtCurrentTime(final ContentResolver mockResolver) throws Exception {
DownloadsUriProvider downloadsUriProvider = givenDownloadsUriProvider();
SystemFacade mockSystemFacade = mock(SystemFacade.class);
when(mockSystemFacade.currentTimeMillis()).thenReturn(CURRENT_TIME_MILLIS);
return new BatchStatusRepository(mockResolver, downloadsUriProvider, mockSystemFacade);
}
private static DownloadsUriProvider givenDownloadsUriProvider() {
return new DownloadsUriProvider(
PUBLICLY_ACCESSIBLE_DOWNLOADS_URI,
DOWNLOADS_BY_BATCH_URI,
ALL_DOWNLOADS_URI,
BATCHES_URI,
CONTENT_URI,
DOWNLOADS_WITHOUT_PROGRESS_URI,
BATCHES_WITHOUT_PROGRESS_URI
);
}
}