diff --git a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGet.java b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGet.java index b12aa92f3..72b237340 100644 --- a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGet.java +++ b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGet.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import com.pushtorefresh.storio.contentresolver.StorIOContentResolver; +import com.pushtorefresh.storio.contentresolver.queries.Query; import com.pushtorefresh.storio.operations.PreparedOperation; /** @@ -15,8 +16,12 @@ public abstract class PreparedGet implements PreparedOperation { @NonNull protected final StorIOContentResolver storIOContentResolver; - PreparedGet(@NonNull StorIOContentResolver storIOContentResolver) { + @NonNull + protected final Query query; + + PreparedGet(@NonNull StorIOContentResolver storIOContentResolver, @NonNull Query query) { this.storIOContentResolver = storIOContentResolver; + this.query = query; } /** @@ -52,5 +57,15 @@ public PreparedGetCursor.Builder cursor() { public PreparedGetListOfObjects.Builder listOfObjects(@NonNull Class type) { return new PreparedGetListOfObjects.Builder(storIOContentResolver, type); } + + /** + * Returns builder for Get Operation that returns number of results. + * + * @return builder for Get Operation that returns number of results. + */ + @NonNull + public PreparedGetNumberOfResults.Builder numberOfResults() { + return new PreparedGetNumberOfResults.Builder(storIOContentResolver); + } } } diff --git a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetCursor.java b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetCursor.java index 23279423b..78f708797 100644 --- a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetCursor.java +++ b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetCursor.java @@ -25,17 +25,13 @@ */ public final class PreparedGetCursor extends PreparedGet { - @NonNull - private final Query query; - @NonNull private final GetResolver getResolver; PreparedGetCursor(@NonNull StorIOContentResolver storIOContentResolver, @NonNull GetResolver getResolver, @NonNull Query query) { - super(storIOContentResolver); - this.query = query; + super(storIOContentResolver, query); this.getResolver = getResolver; } diff --git a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetListOfObjects.java b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetListOfObjects.java index f639a9c05..297b7081f 100644 --- a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetListOfObjects.java +++ b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetListOfObjects.java @@ -36,9 +36,6 @@ public final class PreparedGetListOfObjects extends PreparedGet> { @NonNull private final Class type; - @NonNull - private final Query query; - @Nullable private final GetResolver explicitGetResolver; @@ -46,9 +43,8 @@ public final class PreparedGetListOfObjects extends PreparedGet> { @NonNull Class type, @NonNull Query query, @Nullable GetResolver explicitGetResolver) { - super(storIOContentResolver); + super(storIOContentResolver, query); this.type = type; - this.query = query; this.explicitGetResolver = explicitGetResolver; } @@ -140,7 +136,7 @@ public Observable> createObservable() { } /** - * Compile-time safe part of builder for {@link PreparedGetListOfObjects}. + * Builder for {@link PreparedGetListOfObjects}. * * @param type of objects for query. */ diff --git a/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetNumberOfResults.java b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetNumberOfResults.java new file mode 100644 index 000000000..a80c516ed --- /dev/null +++ b/storio-content-resolver/src/main/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetNumberOfResults.java @@ -0,0 +1,175 @@ +package com.pushtorefresh.storio.contentresolver.operations.get; + +import android.database.Cursor; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; + +import com.pushtorefresh.storio.StorIOException; +import com.pushtorefresh.storio.contentresolver.StorIOContentResolver; +import com.pushtorefresh.storio.contentresolver.queries.Query; +import com.pushtorefresh.storio.operations.internal.MapSomethingToExecuteAsBlocking; +import com.pushtorefresh.storio.operations.internal.OnSubscribeExecuteAsBlocking; + +import rx.Observable; +import rx.schedulers.Schedulers; + +import static com.pushtorefresh.storio.internal.Checks.checkNotNull; +import static com.pushtorefresh.storio.internal.Environment.throwExceptionIfRxJavaIsNotAvailable; + +public class PreparedGetNumberOfResults extends PreparedGet { + + @NonNull + private final GetResolver getResolver; + + PreparedGetNumberOfResults(@NonNull StorIOContentResolver storIOContentResolver, @NonNull Query query, @NonNull GetResolver getResolver) { + super(storIOContentResolver, query); + this.getResolver = getResolver; + } + + /** + * Executes Get Operation immediately in current thread. + *

+ * Notice: This is blocking I/O operation that should not be executed on the Main Thread, + * it can cause ANR (Activity Not Responding dialog), block the UI and drop animations frames. + * So please, call this method on some background thread. See {@link WorkerThread}. + * + * @return non-null {@link Integer} with number of results of the query. + */ + @WorkerThread + @NonNull + @Override + public Integer executeAsBlocking() { + final Cursor cursor; + + try { + cursor = getResolver.performGet(storIOContentResolver, query); + try { + return getResolver.mapFromCursor(cursor); + } finally { + cursor.close(); + } + } catch (Exception exception) { + throw new StorIOException(exception); + } + } + + /** + * Creates "Hot" {@link Observable} which will be subscribed to changes of tables from query + * and will emit result each time change occurs. + *

+ * First result will be emitted immediately after subscription, + * other emissions will occur only if changes of tables from query will occur during lifetime of + * the {@link Observable}. + *

+ *
Scheduler:
+ *
Operates on {@link Schedulers#io()}.
+ *
+ *

+ * Please don't forget to unsubscribe from this {@link Observable} because + * it's "Hot" and endless. + * + * @return non-null {@link Observable} which will emit non-null + * number of results of the executed query and will be subscribed to changes of tables from query. + */ + @NonNull + @Override + public Observable createObservable() { + throwExceptionIfRxJavaIsNotAvailable("createObservable()"); + + return storIOContentResolver + .observeChangesOfUri(query.uri()) // each change triggers executeAsBlocking + .map(MapSomethingToExecuteAsBlocking.newInstance(this)) + .startWith(Observable.create(OnSubscribeExecuteAsBlocking.newInstance(this))) // start stream with first query result + .subscribeOn(Schedulers.io()); + } + + /** + * Builder for {@link PreparedGetNumberOfResults}. + */ + public static final class Builder { + + @NonNull + private final StorIOContentResolver storIOContentResolver; + + Builder(@NonNull StorIOContentResolver storIOContentResolver) { + this.storIOContentResolver = storIOContentResolver; + } + + /** + * Required: Specifies query which will be passed to {@link StorIOContentResolver} + * to get list of objects. + * + * @param query non-null query. + * @return builder. + * @see Query + */ + @NonNull + public CompleteBuilder withQuery(@NonNull Query query) { + checkNotNull(query, "Please specify query"); + return new CompleteBuilder(storIOContentResolver, query); + } + } + + /** + * Compile-time safe part of builder for {@link PreparedGetNumberOfResults}. + */ + public static final class CompleteBuilder { + + @NonNull + static final GetResolver STANDARD_GET_RESOLVER = new DefaultGetResolver() { + @NonNull + @Override + public Integer mapFromCursor(@NonNull Cursor cursor) { + return cursor.getCount(); + } + }; + + @NonNull + private final StorIOContentResolver storIOContentResolver; + + @NonNull + private final Query query; + + @Nullable + private GetResolver getResolver; + + CompleteBuilder(@NonNull StorIOContentResolver storIOContentResolver, @NonNull Query query) { + this.storIOContentResolver = storIOContentResolver; + this.query = query; + } + + /** + * Optional: Specifies resolver for Get Operation which can be used + * to provide custom behavior of Get Operation. + *

+ * + * @param getResolver nullable resolver for Get Operation. + * @return builder. + */ + @NonNull + public CompleteBuilder withGetResolver(@Nullable GetResolver getResolver) { + this.getResolver = getResolver; + return this; + } + + /** + * Builds new instance of {@link PreparedGetNumberOfResults}. + * + * @return new instance of {@link PreparedGetNumberOfResults}. + */ + @NonNull + public PreparedGetNumberOfResults prepare() { + if (getResolver == null) { + getResolver = STANDARD_GET_RESOLVER; + } + + return new PreparedGetNumberOfResults( + storIOContentResolver, + query, + getResolver + ); + } + } + +} \ No newline at end of file diff --git a/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/GetOperationTest.java b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/GetOperationTest.java index d9f3a6b45..61281015d 100644 --- a/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/GetOperationTest.java +++ b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/GetOperationTest.java @@ -1,5 +1,6 @@ package com.pushtorefresh.storio.contentresolver.integration; +import android.content.ContentValues; import android.database.Cursor; import com.pushtorefresh.storio.contentresolver.BuildConfig; @@ -165,4 +166,38 @@ public void getListOfObjectsAsObservableOnlyInitialValue() { changesTestSubscriber.assertNoErrors(); changesTestSubscriber.assertValues(Changes.newInstance(TestItem.CONTENT_URI)); } + + @Test + public void getNumberOfResults() { + TestSubscriber changesTestSubscriber = new TestSubscriber(); + + storIOContentResolver + .observeChangesOfUri(TestItem.CONTENT_URI) + .take(1) + .subscribe(changesTestSubscriber); + + final int testItemsQuantity = 8; + + ContentValues[] contentValues = new ContentValues[testItemsQuantity]; + for (int i = 0; i < testItemsQuantity; ++i) { + TestItem testItemToInsert = TestItem.create(null, "value"); + contentValues[i] = testItemToInsert.toContentValues(); + } + contentResolver.bulkInsert(TestItem.CONTENT_URI, contentValues); + + Integer numberOfResults = storIOContentResolver + .get() + .numberOfResults() + .withQuery(Query.builder() + .uri(TestItem.CONTENT_URI) + .build()) + .prepare() + .executeAsBlocking(); + + assertThat(numberOfResults).isEqualTo(testItemsQuantity); + + changesTestSubscriber.awaitTerminalEvent(60, SECONDS); + changesTestSubscriber.assertNoErrors(); + changesTestSubscriber.assertValue(Changes.newInstance(TestItem.CONTENT_URI)); + } } diff --git a/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/IntegrationContentProvider.java b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/IntegrationContentProvider.java index 0ac7e1f06..85e1f50c9 100644 --- a/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/IntegrationContentProvider.java +++ b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/integration/IntegrationContentProvider.java @@ -4,6 +4,7 @@ import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.support.annotation.NonNull; @@ -81,6 +82,32 @@ public Uri insert(@NonNull Uri uri, @NonNull ContentValues values) { throw new IllegalArgumentException("Unknown uri = " + uri); } + @Override + public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { + switch (uriMatcher.match(uri)) { + case CODE_TEST_ITEM_MATCH: + SQLiteDatabase sqLiteDatabase = sqLiteOpenHelper.getWritableDatabase(); + try { + sqLiteDatabase.beginTransaction(); + + for (ContentValues value : values) { + sqLiteDatabase.insert( + IntegrationSQLiteOpenHelper.TABLE_TEST_ITEMS, + null, + value); + } + sqLiteDatabase.setTransactionSuccessful(); + } finally { + sqLiteDatabase.endTransaction(); + } + + getContext().getContentResolver().notifyChange(uri, null); + + return values.length; + } + throw new IllegalArgumentException("Unknown uri = " + uri); + } + @Override public int update(@NonNull Uri uri, @NonNull ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { diff --git a/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/operations/get/GetNumberOfResultsStub.java b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/operations/get/GetNumberOfResultsStub.java new file mode 100644 index 000000000..ed2165f26 --- /dev/null +++ b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/operations/get/GetNumberOfResultsStub.java @@ -0,0 +1,100 @@ +package com.pushtorefresh.storio.contentresolver.operations.get; + +import android.database.Cursor; +import android.net.Uri; +import android.support.annotation.NonNull; + +import com.pushtorefresh.storio.contentresolver.Changes; +import com.pushtorefresh.storio.contentresolver.StorIOContentResolver; +import com.pushtorefresh.storio.contentresolver.queries.Query; +import com.pushtorefresh.storio.test.ObservableBehaviorChecker; + +import rx.Observable; +import rx.functions.Action1; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class GetNumberOfResultsStub { + @NonNull + final StorIOContentResolver storIOContentResolver; + + @NonNull + private final StorIOContentResolver.Internal internal; + + @NonNull + final Query query; + + @NonNull + final GetResolver getResolverForNumberOfResults; + + @NonNull + private final Cursor cursor; + + @NonNull + private final Integer numberOfResults; + + private GetNumberOfResultsStub() { + storIOContentResolver = mock(StorIOContentResolver.class); + internal = mock(StorIOContentResolver.Internal.class); + + when(storIOContentResolver.internal()) + .thenReturn(internal); + + query = Query + .builder() + .uri(mock(Uri.class)) + .build(); + + //noinspection unchecked + getResolverForNumberOfResults = mock(GetResolver.class); + cursor = mock(Cursor.class); + + numberOfResults = 129; + + when(storIOContentResolver.get()) + .thenReturn(new PreparedGet.Builder(storIOContentResolver)); + + when(storIOContentResolver.observeChangesOfUri(eq(query.uri()))) + .thenReturn(Observable.empty()); + + when(getResolverForNumberOfResults.performGet(storIOContentResolver, query)) + .thenReturn(cursor); + + when(getResolverForNumberOfResults.mapFromCursor(cursor)) + .thenReturn(numberOfResults); + } + + @NonNull + static GetNumberOfResultsStub newInstance() { + return new GetNumberOfResultsStub(); + } + + void verifyQueryBehaviorForInteger(@NonNull Integer actualNumberOfResults) { + assertThat(actualNumberOfResults).isNotNull(); + verify(storIOContentResolver).get(); + verify(getResolverForNumberOfResults).performGet(storIOContentResolver, query); + assertThat(actualNumberOfResults).isSameAs(numberOfResults); + verify(cursor).close(); + verifyNoMoreInteractions(storIOContentResolver, internal, cursor); + } + + void verifyQueryBehaviorForInteger(@NonNull Observable observable) { + new ObservableBehaviorChecker() + .observable(observable) + .expectedNumberOfEmissions(1) + .testAction(new Action1() { + @Override + public void call(Integer numberOfResults) { + // Get Operation should be subscribed to changes of tables from Query + verify(storIOContentResolver).observeChangesOfUri(eq(query.uri())); + verifyQueryBehaviorForInteger(numberOfResults); + } + }) + .checkBehaviorOfObservable(); + } +} diff --git a/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetNumberOfResultsTest.java b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetNumberOfResultsTest.java new file mode 100644 index 000000000..137c44148 --- /dev/null +++ b/storio-content-resolver/src/test/java/com/pushtorefresh/storio/contentresolver/operations/get/PreparedGetNumberOfResultsTest.java @@ -0,0 +1,125 @@ +package com.pushtorefresh.storio.contentresolver.operations.get; + +import android.database.Cursor; +import android.net.Uri; + +import com.pushtorefresh.storio.StorIOException; +import com.pushtorefresh.storio.contentresolver.Changes; +import com.pushtorefresh.storio.contentresolver.StorIOContentResolver; +import com.pushtorefresh.storio.contentresolver.queries.Query; + +import org.junit.Test; + +import rx.Observable; +import rx.observers.TestSubscriber; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PreparedGetNumberOfResultsTest { + @Test + public void shouldGetNumberOfResultsWithQueryBlocking() { + final GetNumberOfResultsStub getStub = GetNumberOfResultsStub.newInstance(); + + final Integer numberOfResults = getStub.storIOContentResolver + .get() + .numberOfResults() + .withQuery(getStub.query) + .withGetResolver(getStub.getResolverForNumberOfResults) + .prepare() + .executeAsBlocking(); + + getStub.verifyQueryBehaviorForInteger(numberOfResults); + } + + @Test + public void shouldGetNumberOfResultsWithQueryAsObservable() { + final GetNumberOfResultsStub getStub = GetNumberOfResultsStub.newInstance(); + + final Observable numberOfResultsObservable = getStub.storIOContentResolver + .get() + .numberOfResults() + .withQuery(getStub.query) + .withGetResolver(getStub.getResolverForNumberOfResults) + .prepare() + .createObservable() + .take(1); + + getStub.verifyQueryBehaviorForInteger(numberOfResultsObservable); + } + + @Test + public void shouldWrapExceptionIntoStorIOExceptionForBlocking() { + final StorIOContentResolver storIOContentResolver = mock(StorIOContentResolver.class); + + //noinspection unchecked + final GetResolver getResolver = mock(GetResolver.class); + + when(getResolver.performGet(eq(storIOContentResolver), any(Query.class))) + .thenThrow(new IllegalStateException("test exception")); + + try { + new PreparedGetNumberOfResults.Builder(storIOContentResolver) + .withQuery(Query.builder().uri(mock(Uri.class)).build()) + .withGetResolver(getResolver) + .prepare() + .executeAsBlocking(); + + failBecauseExceptionWasNotThrown(StorIOException.class); + } catch (StorIOException expected) { + IllegalStateException cause = (IllegalStateException) expected.getCause(); + assertThat(cause).hasMessage("test exception"); + } + } + + @Test + public void shouldWrapExceptionIntoStorIOExceptionForObservable() { + final StorIOContentResolver storIOContentResolver = mock(StorIOContentResolver.class); + + Uri testUri = mock(Uri.class); + when(storIOContentResolver.observeChangesOfUri(eq(testUri))) + .thenReturn(Observable.empty()); + + //noinspection unchecked + final GetResolver getResolver = mock(GetResolver.class); + + when(getResolver.performGet(eq(storIOContentResolver), any(Query.class))) + .thenThrow(new IllegalStateException("test exception")); + + final TestSubscriber testSubscriber = new TestSubscriber(); + + new PreparedGetNumberOfResults.Builder(storIOContentResolver) + .withQuery(Query.builder().uri(testUri).build()) + .withGetResolver(getResolver) + .prepare() + .createObservable() + .subscribe(testSubscriber); + + testSubscriber.awaitTerminalEvent(60, SECONDS); + testSubscriber.assertError(StorIOException.class); + + assertThat(testSubscriber.getOnErrorEvents()).hasSize(1); + StorIOException storIOException = (StorIOException) testSubscriber.getOnErrorEvents().get(0); + IllegalStateException cause = (IllegalStateException) storIOException.getCause(); + assertThat(cause).hasMessage("test exception"); + + testSubscriber.unsubscribe(); + } + + @Test + public void verifyThatStandardGetResolverJustReturnsCursorGetCount() { + final GetResolver standardGetResolver + = PreparedGetNumberOfResults.CompleteBuilder.STANDARD_GET_RESOLVER; + + final Cursor cursor = mock(Cursor.class); + + when(cursor.getCount()).thenReturn(12314); + + assertThat(standardGetResolver.mapFromCursor(cursor)).isEqualTo(12314); + } +} diff --git a/storio-sqlite/src/main/java/com/pushtorefresh/storio/sqlite/operations/get/PreparedGetNumberOfResults.java b/storio-sqlite/src/main/java/com/pushtorefresh/storio/sqlite/operations/get/PreparedGetNumberOfResults.java index d9f14baf2..e215e1c21 100644 --- a/storio-sqlite/src/main/java/com/pushtorefresh/storio/sqlite/operations/get/PreparedGetNumberOfResults.java +++ b/storio-sqlite/src/main/java/com/pushtorefresh/storio/sqlite/operations/get/PreparedGetNumberOfResults.java @@ -117,6 +117,9 @@ public Observable createObservable() { } } + /** + * Builder for {@link PreparedGetNumberOfResults}. + */ public static final class Builder { @NonNull @@ -155,6 +158,9 @@ public CompleteBuilder withQuery(@NonNull RawQuery rawQuery) { } } + /** + * Compile-time safe part of builder for {@link PreparedGetNumberOfResults}. + */ public static final class CompleteBuilder { @NonNull @@ -205,9 +211,9 @@ public CompleteBuilder withGetResolver(@Nullable GetResolver getResolve } /** - * Builds new instance of {@link PreparedGetListOfObjects}. + * Builds new instance of {@link PreparedGetNumberOfResults}. * - * @return new instance of {@link PreparedGetListOfObjects}. + * @return new instance of {@link PreparedGetNumberOfResults}. */ @NonNull public PreparedGetNumberOfResults prepare() { diff --git a/storio-sqlite/src/test/java/com/pushtorefresh/storio/sqlite/operations/get/GetNumberOfResultsStub.java b/storio-sqlite/src/test/java/com/pushtorefresh/storio/sqlite/operations/get/GetNumberOfResultsStub.java index d9cb273e1..76d285ebc 100644 --- a/storio-sqlite/src/test/java/com/pushtorefresh/storio/sqlite/operations/get/GetNumberOfResultsStub.java +++ b/storio-sqlite/src/test/java/com/pushtorefresh/storio/sqlite/operations/get/GetNumberOfResultsStub.java @@ -43,7 +43,6 @@ class GetNumberOfResultsStub { @NonNull private final Integer numberOfResults; - @SuppressWarnings("unchecked") private GetNumberOfResultsStub() { storIOSQLite = mock(StorIOSQLite.class); internal = mock(StorIOSQLite.Internal.class); @@ -62,6 +61,7 @@ private GetNumberOfResultsStub() { .observesTables("test_table") .build(); + //noinspection unchecked getResolverForNumberOfResults = mock(GetResolver.class); cursor = mock(Cursor.class);