diff options
| author | 2017-03-24 09:14:14 -0700 | |
|---|---|---|
| committer | 2017-03-25 07:43:37 -0700 | |
| commit | d39da943dad071de289a8a9845b56a3e9dd86f75 (patch) | |
| tree | 146ff7f6e4e6ec58cb2faa8ae9fe95e505067bd3 | |
| parent | 98e4ae9fa52f19f43d937165ac2921b3dbb0f4ca (diff) | |
Add honored args when auto-paging.
Increase test coverage.
Test: Improved!
Change-Id: Icba97c5e821e6cb60157e0c8a5b204d0d2813bc8
| -rw-r--r-- | api/test-current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/database/PageViewCursor.java | 102 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/database/PageViewCursorTest.java | 170 |
3 files changed, 185 insertions, 89 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 8262223c033c..79fe89a38b83 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -11749,7 +11749,7 @@ package android.database { } public final class PageViewCursor extends android.database.CursorWrapper implements android.database.CrossProcessCursor { - ctor public PageViewCursor(android.database.Cursor, int, int); + ctor public PageViewCursor(android.database.Cursor, android.os.Bundle); method public void fillWindow(int, android.database.CursorWindow); method public android.database.CursorWindow getWindow(); method public boolean onMove(int, int); diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java index 44727a0d300e..4569a2765f51 100644 --- a/core/java/android/database/PageViewCursor.java +++ b/core/java/android/database/PageViewCursor.java @@ -15,6 +15,7 @@ */ package android.database; +import static com.android.internal.util.ArrayUtils.contains; import static com.android.internal.util.Preconditions.checkArgument; import android.annotation.Nullable; @@ -25,7 +26,7 @@ import android.os.Bundle; import android.util.Log; import android.util.MathUtils; -import com.android.internal.util.ArrayUtils; +import java.util.Arrays; /** * Cursor wrapper that provides visibility into a subset of a wrapped cursor. @@ -40,11 +41,12 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC /** An extra added to results that are auto-paged using the wrapper. */ public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED"; + private static final String[] EMPTY_ARGS = new String[0]; private static final String TAG = "PageViewCursor"; private static final boolean DEBUG = Build.IS_DEBUGGABLE; private static final boolean VERBOSE = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE); - private final int mOffset; // aka first index + private final int mOffset; // aka first index private final int mCount; private final Bundle mExtras; @@ -55,12 +57,17 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC /** * @see PageViewCursor#wrap(Cursor, Bundle) */ - public PageViewCursor(Cursor cursor, int offset, int limit) { + public PageViewCursor(Cursor cursor, Bundle queryArgs) { super(cursor); + int offset = queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0); + int limit = queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MAX_VALUE); + checkArgument(offset > -1); checkArgument(limit > -1); + int count = mCursor.getCount(); + mOffset = offset; mExtras = new Bundle(); @@ -68,26 +75,47 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC if (extras != null) { mExtras.putAll(extras); } - mExtras.putBoolean(EXTRA_AUTO_PAGED, true); - - // We need a mutable bundle so we can add QUERY_RESULT_SIZE. - // Direct equality check is correct here. Bundle.EMPTY is a specific instance - // of Bundle that is immutable by way of implementation. - // mExtras = (extras == Bundle.EMPTY) ? new Bundle() : extras; // When we're wrapping another cursor, it should not already be "paged". - checkArgument(!mExtras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)); + checkArgument(!hasPagedResponseDetails(mExtras)); - int count = mCursor.getCount(); + mExtras.putBoolean(EXTRA_AUTO_PAGED, true); mExtras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, count); + // Ensure we retain any extra args supplied in cursor extras, and add + // offset and/or limit. + String[] existingArgs = mExtras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS); + existingArgs = existingArgs != null ? existingArgs : EMPTY_ARGS; + + int size = existingArgs.length; + + // copy the array with space for the extra query args we'll be adding. + String[] newArgs = Arrays.copyOf(existingArgs, size + 2); + + if (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET)) { + newArgs[size++] = ContentResolver.QUERY_ARG_OFFSET; + } + if (queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT)) { + newArgs[size++] = ContentResolver.QUERY_ARG_LIMIT; + } + + assert(size > existingArgs.length); // must add at least one arg. + + // At this point there may be a null element at the end of + // the array because our pre-sizing didn't match the actualy + // number of args we added. So we trim. + if (size == newArgs.length - 1) { + newArgs = Arrays.copyOf(newArgs, size); + } + mExtras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, newArgs); + mCount = MathUtils.constrain(count - offset, 0, limit); if (DEBUG) Log.d(TAG, "Wrapped cursor" - + " offset: " + mOffset - + ", limit: " + limit - + ", delegate_size: " + count - + ", paged_count: " + mCount); + + " offset: " + mOffset + + ", limit: " + limit + + ", delegate_size: " + count + + ", paged_count: " + mCount); } @Override @@ -155,9 +183,9 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC public boolean moveToPosition(int position) { if (position >= mCount) { if (VERBOSE) Log.v(TAG, "Invalid Positon: " + position + " >= count: " + mCount - + ". Moving to last record."); + + ". Moving to last record."); mPos = mCount; - super.moveToPosition(mOffset + mPos); // move into "after last" state. + super.moveToPosition(mOffset + mPos); // move into "after last" state. return false; } @@ -198,15 +226,15 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC @Override public boolean getWantsAllOnMoveCalls() { - return false; // we want bulk cursor adapter to lift data into a CursorWindow. + return false; // we want bulk cursor adapter to lift data into a CursorWindow. } @Override public CursorWindow getWindow() { assert(mPos == -1 || mPos == 0); if (mWindow == null) { - mWindow = new CursorWindow("PageViewCursorWindow"); - fillWindow(0, mWindow); + mWindow = new CursorWindow("PageViewCursorWindow"); + fillWindow(0, mWindow); } return mWindow; @@ -224,17 +252,16 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC } /** - * Wraps the cursor such that it will honor paging args (if present), AND if the cursor - * does not report paging size. - * - * <p>No-op if cursor already contains paging or is less than specified page size. + * Wraps the cursor such that it will honor paging args (if present), AND if the cursor does + * not report paging size. + * <p> + * No-op if cursor already contains paging or is less than specified page size. */ public static Cursor wrap(Cursor cursor, @Nullable Bundle queryArgs) { - boolean hasPagingArgs = - queryArgs != null + boolean hasPagingArgs = queryArgs != null && (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET) - || queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT)); + || queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT)); if (!hasPagingArgs) { if (VERBOSE) Log.v(TAG, "No-wrap: No paging args in request."); @@ -253,25 +280,26 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC return cursor; } - return new PageViewCursor( - cursor, - queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0), - queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MAX_VALUE)); + return new PageViewCursor(cursor, queryArgs); } /** - * @return true if the extras contains information indicating the associated - * cursor is paged. + * @return true if the extras contains information indicating the associated cursor is + * paged. */ private static boolean hasPagedResponseDetails(@Nullable Bundle extras) { - if (extras != null && extras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)) { + if (extras == null) { + return false; + } + + if (extras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)) { return true; } String[] honoredArgs = extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS); - if (honoredArgs != null && ( - ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET) - || ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT))) { + if (honoredArgs != null + && (contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET) + || contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT))) { return true; } diff --git a/core/tests/coretests/src/android/database/PageViewCursorTest.java b/core/tests/coretests/src/android/database/PageViewCursorTest.java index 62b54105cf9a..fba6aaf3f406 100644 --- a/core/tests/coretests/src/android/database/PageViewCursorTest.java +++ b/core/tests/coretests/src/android/database/PageViewCursorTest.java @@ -13,20 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.database; +import static com.android.internal.util.ArrayUtils.contains; +import static com.android.internal.util.Preconditions.checkArgument; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.annotation.Nullable; import android.content.ContentResolver; +import android.os.Build; import android.os.Bundle; import android.support.test.runner.AndroidJUnit4; +import android.util.Log; +import android.util.MathUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.Random; @RunWith(AndroidJUnit4.class) @@ -37,32 +45,32 @@ public class PageViewCursorTest { private static final String NAME_COLUMN = "name"; private static final String NUM_COLUMN = "num"; - private static final String[] COLUMNS = new String[]{ - NAME_COLUMN, - NUM_COLUMN + private static final String[] COLUMNS = new String[] { + NAME_COLUMN, + NUM_COLUMN }; private static final String[] NAMES = new String[] { - "000", - "111", - "222", - "333", - "444", - "555", - "666", - "777", - "888", - "999", - "aaa", - "bbb", - "ccc", - "ddd", - "eee", - "fff", - "ggg", - "hhh", - "iii", - "jjj" + "000", + "111", + "222", + "333", + "444", + "555", + "666", + "777", + "888", + "999", + "aaa", + "bbb", + "ccc", + "ddd", + "eee", + "fff", + "ggg", + "hhh", + "iii", + "jjj" }; private MatrixCursor mDelegate; @@ -79,7 +87,7 @@ public class PageViewCursorTest { row.add(NUM_COLUMN, rand.nextInt()); } - mCursor = new PageViewCursor(mDelegate, 10, 5); + mCursor = new PageViewCursor(mDelegate, createArgs(10, 5)); } @Test @@ -94,7 +102,7 @@ public class PageViewCursorTest { @Test public void testPage_OffsetExceedsCursorCount_EffectivelyEmptyCursor() { - mCursor = new PageViewCursor(mDelegate, ITEM_COUNT * 2, 5); + mCursor = new PageViewCursor(mDelegate, createArgs(ITEM_COUNT * 2, 5)); assertEquals(0, mCursor.getCount()); } @@ -155,13 +163,13 @@ public class PageViewCursorTest { @Test public void testCount_ZeroForEmptyCursor() { - mCursor = new PageViewCursor(mDelegate, 0, 0); + mCursor = new PageViewCursor(mDelegate, createArgs(0, 0)); assertEquals(0, mCursor.getCount()); } @Test public void testIsBeforeFirst_TrueForEmptyCursor() { - mCursor = new PageViewCursor(mDelegate, 0, 0); + mCursor = new PageViewCursor(mDelegate, createArgs(0, 0)); assertTrue(mCursor.isBeforeFirst()); } @@ -175,7 +183,7 @@ public class PageViewCursorTest { @Test public void testIsAfterLast_TrueForEmptyCursor() { - mCursor = new PageViewCursor(mDelegate, 0, 0); + mCursor = new PageViewCursor(mDelegate, createArgs(0, 0)); assertTrue(mCursor.isAfterLast()); } @@ -247,71 +255,131 @@ public class PageViewCursorTest { } @Test - public void testOffset_LimitOutOfBounds() { - mCursor = new PageViewCursor(mDelegate, 5, 100); + public void testLimitOutOfBounds() { + mCursor = new PageViewCursor(mDelegate, createArgs(5, 100)); assertEquals(15, mCursor.getCount()); } @Test - public void testAutoPagedExtra() { - mCursor = new PageViewCursor(mDelegate, 5, 100); + public void testOffsetOutOfBounds_EmptyResult() { + mCursor = new PageViewCursor(mDelegate, createArgs(100000, 100)); + assertEquals(0, mCursor.getCount()); + } + + @Test + public void testAddsExtras() { + mCursor = new PageViewCursor(mDelegate, createArgs(5, 100)); assertTrue(mCursor.getExtras().getBoolean(PageViewCursor.EXTRA_AUTO_PAGED)); + String[] honoredArgs = mCursor.getExtras() + .getStringArray(ContentResolver.EXTRA_HONORED_ARGS); + assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)); + assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT)); + } + + @Test + public void testAddsExtras_OnlyOffset() { + Bundle args = new Bundle(); + args.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); + mCursor = new PageViewCursor(mDelegate, args); + String[] honoredArgs = mCursor.getExtras() + .getStringArray(ContentResolver.EXTRA_HONORED_ARGS); + assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)); + assertFalse(contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT)); + } + + @Test + public void testAddsExtras_OnlyLimit() { + Bundle args = new Bundle(); + args.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); + mCursor = new PageViewCursor(mDelegate, args); + String[] honoredArgs = mCursor.getExtras() + .getStringArray(ContentResolver.EXTRA_HONORED_ARGS); + assertFalse(contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)); + assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT)); } @Test public void testGetWindow() { - mCursor = new PageViewCursor(mDelegate, 5, 5); + mCursor = new PageViewCursor(mDelegate, createArgs(5, 5)); CursorWindow window = mCursor.getWindow(); assertEquals(5, window.getNumRows()); } @Test - public void testWrap() { - Bundle queryArgs = new Bundle(); - queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); - queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); - Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs); + public void testWraps() { + Bundle args = createArgs(5, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, args); assertTrue(wrapped instanceof PageViewCursor); assertEquals(5, wrapped.getCount()); } @Test - public void testWrap_NoOpWithoutPagingArgs() { + public void testWraps_NullExtras() { + Bundle args = createArgs(5, 5); + mDelegate.setExtras(null); + Cursor wrapped = PageViewCursor.wrap(mDelegate, args); + assertTrue(wrapped instanceof PageViewCursor); + assertEquals(5, wrapped.getCount()); + } + + @Test + public void testWraps_WithJustOffset() { + Bundle args = new Bundle(); + args.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, args); + assertTrue(wrapped instanceof PageViewCursor); + assertEquals(15, wrapped.getCount()); + } + + @Test + public void testWraps_WithJustLimit() { + Bundle args = new Bundle(); + args.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, args); + assertTrue(wrapped instanceof PageViewCursor); + assertEquals(5, wrapped.getCount()); + } + + @Test + public void testNoWrap_WithoutPagingArgs() { Cursor wrapped = PageViewCursor.wrap(mDelegate, Bundle.EMPTY); assertTrue(mDelegate == wrapped); } @Test - public void testWrap_NoOpCursorsWithExistingPaging_ByTotalSize() { + public void testNoWrap_CursorsHasExistingPaging_ByTotalSize() { Bundle extras = new Bundle(); extras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, 5); mDelegate.setExtras(extras); - Bundle queryArgs = new Bundle(); - queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); - queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); - Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs); + Bundle args = createArgs(5, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, args); assertTrue(mDelegate == wrapped); } @Test - public void testWrap_NoOpCursorsWithExistingPaging_ByHonoredArgs() { + public void testNoWrap_CursorsHasExistingPaging_ByHonoredArgs() { Bundle extras = new Bundle(); extras.putStringArray( ContentResolver.EXTRA_HONORED_ARGS, new String[] { - ContentResolver.QUERY_ARG_OFFSET, - ContentResolver.QUERY_ARG_LIMIT + ContentResolver.QUERY_ARG_OFFSET, + ContentResolver.QUERY_ARG_LIMIT }); mDelegate.setExtras(extras); - Bundle queryArgs = new Bundle(); - queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); - queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); - Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs); + Bundle args = createArgs(5, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, args); assertTrue(mDelegate == wrapped); } + private static Bundle createArgs(int offset, int limit) { + Bundle args = new Bundle(); + args.putInt(ContentResolver.QUERY_ARG_OFFSET, offset); + args.putInt(ContentResolver.QUERY_ARG_LIMIT, limit); + return args; + } + private void assertStringAt(int row, int column, String expected) { mCursor.moveToPosition(row); assertEquals(expected, mCursor.getString(column)); |