blob: f798d14a328e395b3c07a05405cf496bfbc0ed83 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.contentpager.content;
import static androidx.contentpager.content.ContentPager.createArgs;
import static androidx.contentpager.content.TestContentProvider.PAGED_URI;
import static androidx.contentpager.content.TestContentProvider.PAGED_WINDOWED_URI;
import static androidx.contentpager.content.TestContentProvider.UNPAGED_URI;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import androidx.annotation.Nullable;
import androidx.contentpager.content.ContentPager.ContentCallback;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class ContentPagerTest {
private ContentResolver mResolver;
private TestQueryRunner mRunner;
private TestContentCallback mCallback;
private ContentPager mPager;
@Rule
public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule(TestActivity.class);
@Before
public void setUp() {
mRunner = new TestQueryRunner();
mResolver = mActivityRule.getActivity().getContentResolver();
mCallback = new TestContentCallback();
mPager = new ContentPager(mResolver, mRunner);
}
@Test
public void testRelaysProviderPagedResults() throws Throwable {
int offset = 0;
int limit = 10;
// NOTE: Paging on Android O is accompolished by way of ContentResolver#query that
// accepts a Bundle. That means on older platforms we either have to cook up
// a special way of paging that doesn't use the bundle (that's what we do here)
// or we simply skip testing how we deal with provider paged results.
Uri uriWithTestPagingData = TestContentProvider.forcePagingSpec(PAGED_URI, offset, limit);
Query query = mPager.query(
uriWithTestPagingData,
null,
createArgs(offset, limit),
null,
mCallback);
mCallback.assertNumPagesLoaded(1);
mCallback.assertPageLoaded(query);
Cursor cursor = mCallback.getCursor(query);
Bundle extras = cursor.getExtras();
assertExpectedRecords(cursor, query.getOffset());
assertEquals(
ContentPager.CURSOR_DISPOSITION_PAGED,
extras.getInt(ContentPager.CURSOR_DISPOSITION, -1));
assertEquals(
TestContentProvider.DEFAULT_RECORD_COUNT,
extras.getInt(ContentResolver.EXTRA_TOTAL_COUNT));
assertHasHonoredArgs(
extras,
ContentResolver.QUERY_ARG_LIMIT,
ContentResolver.QUERY_ARG_OFFSET);
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_PROVIDER_PAGED));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_RESOLVED_QUERIES));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_TOTAL_QUERIES));
}
@Test
public void testLimitsPagedResultsToWindowSize() throws Throwable {
int offset = 0;
int limit = 10;
// NOTE: Paging on Android O is accompolished by way of ContentResolver#query that
// accepts a Bundle. That means on older platforms we either have to cook up
// a special way of paging that doesn't use the bundle (that's what we do here)
// or we simply skip testing how we deal with provider paged results.
Uri uriWithTestPagingData = TestContentProvider.forcePagingSpec(
PAGED_WINDOWED_URI, offset, limit);
Query query = mPager.query(
uriWithTestPagingData,
null,
createArgs(offset, limit),
null,
mCallback);
mCallback.assertNumPagesLoaded(1);
mCallback.assertPageLoaded(query);
Cursor cursor = mCallback.getCursor(query);
Bundle extras = cursor.getExtras();
assertExpectedRecords(cursor, query.getOffset());
assertEquals(
ContentPager.CURSOR_DISPOSITION_REPAGED,
extras.getInt(ContentPager.CURSOR_DISPOSITION, -1));
assertEquals(
TestContentProvider.DEFAULT_RECORD_COUNT,
extras.getInt(ContentResolver.EXTRA_TOTAL_COUNT));
assertEquals(limit, extras.getInt(ContentPager.EXTRA_REQUESTED_LIMIT));
assertEquals(7, extras.getInt(ContentPager.EXTRA_SUGGESTED_LIMIT));
assertHasHonoredArgs(
extras,
ContentResolver.QUERY_ARG_LIMIT,
ContentResolver.QUERY_ARG_OFFSET);
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_PROVIDER_PAGED));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_RESOLVED_QUERIES));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_TOTAL_QUERIES));
}
@Test
public void testAdaptsUnpagedToPaged() throws Throwable {
Query query = mPager.query(
UNPAGED_URI,
null,
createArgs(0, 10),
null,
mCallback);
mCallback.assertNumPagesLoaded(1);
mCallback.assertPageLoaded(query);
Cursor cursor = mCallback.getCursor(query);
Bundle extras = cursor.getExtras();
assertExpectedRecords(cursor, query.getOffset());
assertEquals(
ContentPager.CURSOR_DISPOSITION_COPIED,
extras.getInt(ContentPager.CURSOR_DISPOSITION));
assertEquals(
TestContentProvider.DEFAULT_RECORD_COUNT,
extras.getInt(ContentResolver.EXTRA_TOTAL_COUNT));
assertHasHonoredArgs(
extras,
ContentResolver.QUERY_ARG_LIMIT,
ContentResolver.QUERY_ARG_OFFSET);
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_COMPAT_PAGED));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_RESOLVED_QUERIES));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_TOTAL_QUERIES));
}
@Test
public void testCachesUnpagedCursor() throws Throwable {
mPager.query(
UNPAGED_URI,
null,
createArgs(0, 10),
null,
mCallback);
mPager.query(
UNPAGED_URI,
null,
createArgs(10, 10),
null,
mCallback);
// Rerun the same query as the first...extra exercise to ensure we can return
// to previously loaded results.
Query query = mPager.query(
UNPAGED_URI,
null,
createArgs(0, 10),
null,
mCallback);
mCallback.assertNumPagesLoaded(3);
Cursor cursor = mCallback.getCursor(query);
Bundle extras = cursor.getExtras();
assertEquals(
3,
extras.getInt(ContentPager.Stats.EXTRA_COMPAT_PAGED));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_RESOLVED_QUERIES));
assertEquals(
3,
extras.getInt(ContentPager.Stats.EXTRA_TOTAL_QUERIES));
}
@Test
public void testWrapsCursorsThatJustHappenToFitInPageRange() throws Throwable {
// NOTE: Paging on Android O is accompolished by way of ContentResolver#query that
// accepts a Bundle. That means on older platforms we either have to cook up
// a special way of paging that doesn't use the bundle (that's what we do here)
// or we simply skip testing how we deal with provider paged results.
Uri uri = TestContentProvider.forceRecordCount(UNPAGED_URI, 22);
Query query = mPager.query(
uri,
null,
createArgs(0, 44),
null,
mCallback);
mCallback.assertNumPagesLoaded(1);
// mCallback.assertPageLoaded(pageId);
mCallback.assertPageLoaded(query);
Cursor cursor = mCallback.getCursor(query);
Bundle extras = cursor.getExtras();
assertExpectedRecords(cursor, query.getOffset());
assertEquals(
ContentPager.CURSOR_DISPOSITION_WRAPPED,
extras.getInt(ContentPager.CURSOR_DISPOSITION));
assertEquals(
22,
extras.getInt(ContentResolver.EXTRA_TOTAL_COUNT));
assertHasHonoredArgs(
extras,
ContentResolver.QUERY_ARG_LIMIT,
ContentResolver.QUERY_ARG_OFFSET);
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_COMPAT_PAGED));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_RESOLVED_QUERIES));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_TOTAL_QUERIES));
}
@Test
public void testCorrectlyCopiesRecords_EndOfResults() throws Throwable {
// finally, check the last page.
int limit = 100;
// This will be the size of the last page. Should be 67.
int leftOvers = TestContentProvider.DEFAULT_RECORD_COUNT % limit;
int offset = TestContentProvider.DEFAULT_RECORD_COUNT - leftOvers;
Query query = mPager.query(
UNPAGED_URI,
null,
createArgs(offset, limit),
null,
mCallback);
mCallback.assertNumPagesLoaded(1);
mCallback.assertPageLoaded(query);
Cursor cursor = mCallback.getCursor(query);
assertEquals(leftOvers, cursor.getCount());
Bundle extras = cursor.getExtras();
assertExpectedRecords(cursor, query.getOffset());
assertEquals(
ContentPager.CURSOR_DISPOSITION_COPIED,
extras.getInt(ContentPager.CURSOR_DISPOSITION));
assertHasHonoredArgs(
extras,
ContentResolver.QUERY_ARG_LIMIT,
ContentResolver.QUERY_ARG_OFFSET);
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_COMPAT_PAGED));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_RESOLVED_QUERIES));
assertEquals(
1,
extras.getInt(ContentPager.Stats.EXTRA_TOTAL_QUERIES));
}
@Test
public void testCancelsRunningQueriesOnReset() throws Throwable {
mRunner.runQuery = false;
Query query = mPager.query(
UNPAGED_URI,
null,
createArgs(0, 10),
null,
mCallback);
assertTrue(mRunner.isRunning(query));
mPager.reset();
assertFalse(mRunner.isRunning(query));
}
@Test
public void testRelaysContentChangeNotificationsOnPagedCursors() throws Throwable {
TestContentObserver observer = new TestContentObserver(
new Handler(Looper.getMainLooper()));
observer.expectNotifications(1);
Query query = mPager.query(
UNPAGED_URI,
null,
createArgs(10, 99),
null,
mCallback);
Cursor cursor = mCallback.getCursor(query);
cursor.registerContentObserver(observer);
mResolver.notifyChange(UNPAGED_URI, null);
assertTrue(observer.mNotifiedLatch.await(1000, TimeUnit.MILLISECONDS));
}
private void assertExpectedRecords(Cursor cursor, int offset) {
for (int row = 0; row < cursor.getCount(); row++) {
assertTrue(cursor.moveToPosition(row));
int unpagedRow = offset + row;
for (int column = 0; column < cursor.getColumnCount(); column++) {
TestContentProvider.assertExpectedCellValue(cursor, unpagedRow, column);
}
}
}
private static void assertHasHonoredArgs(Bundle extras, String... expectedArgs) {
List<String> honored = Arrays.asList(
extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS));
for (String arg : expectedArgs) {
assertTrue(honored.contains(arg));
}
}
private static final class TestContentCallback implements ContentCallback {
private int mPagesLoaded;
private Map<Query, Cursor> mCursors = new HashMap<>();
@Override
public void onCursorReady(Query query, Cursor cursor) {
mPagesLoaded++;
mCursors.put(query, cursor);
}
private void assertPageLoaded(Query query) {
assertTrue(mCursors.containsKey(query));
assertNotNull(mCursors.get(query));
}
private void assertNumPagesLoaded(int expected) {
assertEquals(expected, mPagesLoaded);
}
private @Nullable Cursor getCursor(Query query) {
return mCursors.get(query);
}
}
}