1c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay/*
2c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * Copyright (C) 2017 The Android Open Source Project
3c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay *
4c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * Licensed under the Apache License, Version 2.0 (the "License");
5c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * you may not use this file except in compliance with the License.
6c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * You may obtain a copy of the License at
7c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay *
8c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay *      http://www.apache.org/licenses/LICENSE-2.0
9c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay *
10c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * Unless required by applicable law or agreed to in writing, software
11c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * distributed under the License is distributed on an "AS IS" BASIS,
12c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * See the License for the specific language governing permissions and
14c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * limitations under the License.
15c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay */
16c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.contentpager.content;
18c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
19fa39e2bca1b284ad7c931d9194287770b7b507faAurimas Liutikasimport static org.junit.Assert.assertEquals;
20c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
21c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.content.ContentProvider;
22c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.content.ContentResolver;
23c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.content.ContentValues;
24c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.database.AbstractWindowedCursor;
25c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.database.Cursor;
26c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.database.CursorWindow;
27c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.database.MatrixCursor;
28c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.database.MatrixCursor.RowBuilder;
29c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.net.Uri;
30c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.os.Bundle;
31c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKayimport android.os.CancellationSignal;
32fb4b6f71b6a30ea0acc67fce323853e4beb096d7Aurimas Liutikas
33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable;
34ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.VisibleForTesting;
35c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
36c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay/**
37c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * A stub data paging provider used for testing of paging support.
38c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay * Ignores client supplied projections.
39c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay */
40c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKaypublic final class TestContentProvider extends ContentProvider {
41c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
42ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    public static final String AUTHORITY = "androidx.contentpager.content.test.testpagingprovider";
43c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
44c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String UNPAGED_PATH = "/un-paged";
45c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String PAGED_PATH = "/paged";
46c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String PAGED_WINDOWED_PATH = PAGED_PATH + "/windowed";
47c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
48c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final Uri UNPAGED_URI = new Uri.Builder()
49c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .scheme("content")
50c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .authority(AUTHORITY)
51c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .path(UNPAGED_PATH)
52c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .build();
53c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final Uri PAGED_URI = new Uri.Builder()
54c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .scheme("content")
55c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .authority(AUTHORITY)
56c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .path(PAGED_PATH)
57c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .build();
58c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final Uri PAGED_WINDOWED_URI = new Uri.Builder()
59c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .scheme("content")
60c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .authority(AUTHORITY)
61c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .path(PAGED_WINDOWED_PATH)
62c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            .build();
63c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
64c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String COLUMN_POS = "ColumnPos";
65c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String COLUMN_A = "ColumnA";
66c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String COLUMN_B = "ColumnB";
67c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String COLUMN_C = "ColumnC";
68c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String COLUMN_D = "ColumnD";
69c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String[] PROJECTION = {
70c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            COLUMN_POS,
71c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            COLUMN_A,
72c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            COLUMN_B,
73c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            COLUMN_C,
74c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            COLUMN_D
75c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    };
76c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
77c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @VisibleForTesting
78c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final String RECORD_COUNT = "test-record-count";
79c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
80c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @VisibleForTesting
81c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static final int DEFAULT_RECORD_COUNT = 567;
82c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
83c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private static final String TAG = "TestPagingProvider";
84c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
85c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
86c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public boolean onCreate() {
87c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return true;
88c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
89c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
90c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
91c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public Cursor query(
92c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            Uri uri, @Nullable String[] projection, String selection, String[] selectionArgs,
93c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            String sortOrder) {
94c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return query(uri, projection, null, null);
95c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
96c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
97c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
98c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public Cursor query(Uri uri, String[] ignored, Bundle queryArgs,
99c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            CancellationSignal cancellationSignal) {
100c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
101c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;
102c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
103c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int recordCount = getIntValue(RECORD_COUNT, queryArgs, uri, DEFAULT_RECORD_COUNT);
104c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        if (recordCount < 0) {
105c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            throw new RuntimeException("Recordset size must be >= 0");
106c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
107c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
108c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        Cursor cursor = null;
109c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        switch (uri.getPath()) {
110c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case UNPAGED_PATH:
111c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                cursor = buildUnpagedResults(recordCount);
112c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
113c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case PAGED_PATH:
114c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                cursor = buildPagedResults(uri, queryArgs, recordCount);
115c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
116c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case PAGED_WINDOWED_PATH:
117c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                cursor = buildPagedWindowedResults(uri, queryArgs, recordCount);
118c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
119c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            default:
120c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                throw new IllegalArgumentException("Unrecognized path: " + uri.getPath());
121c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
122c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
123c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        cursor.setNotificationUri(getContext().getContentResolver(), uri);
124c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
125c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return cursor;
126c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
127c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
128c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    /**
129c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * Return a int value specified in Bundle key, Uri query arg, or fallback default value.
130c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     */
131c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private static int getIntValue(String key, Bundle queryArgs, Uri uri, int defaultValue) {
132c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int value = queryArgs.getInt(key, Integer.MIN_VALUE);
133c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        if (value != Integer.MIN_VALUE) {
134c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return value;
135c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
136c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
137c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        @Nullable String argValue = uri.getQueryParameter(key);
138c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        if (argValue != null) {
139c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            try {
140c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return Integer.parseInt(argValue);
141c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            } catch (NumberFormatException ignored) {
142c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            }
143c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
144c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
145c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return defaultValue;
146c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
147c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
148c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private MatrixCursor buildPagedResults(Uri uri, Bundle queryArgs, int recordsetSize) {
149c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int offset = getIntValue(ContentResolver.QUERY_ARG_OFFSET, queryArgs, uri, 0);
150c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int limit = getIntValue(ContentResolver.QUERY_ARG_LIMIT, queryArgs, uri, recordsetSize);
151c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
152c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        MatrixCursor c = createInMemoryCursor();
153c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        Bundle extras = c.getExtras();
154c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
155c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        // Calculate the number of items to include in the cursor.
156c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int numItems = constrain(recordsetSize - offset, 0, limit);
157c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
158c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        // Build the paged result set.
159c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        for (int i = offset; i < offset + numItems; i++) {
160c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            fillRow(c.newRow(), i);
161c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
162c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
163c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, new String[] {
164c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                ContentResolver.QUERY_ARG_OFFSET,
165c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                ContentResolver.QUERY_ARG_LIMIT
166c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        });
167c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        extras.putInt(ContentResolver.EXTRA_TOTAL_COUNT, recordsetSize);
168c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return c;
169c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
170c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
171c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private AbstractWindowedCursor buildPagedWindowedResults(
172c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            Uri uri, Bundle queryArgs, int recordsetSize) {
173c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int offset = getIntValue(ContentResolver.QUERY_ARG_OFFSET, queryArgs, uri, 0);
174c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int limit = getIntValue(ContentResolver.QUERY_ARG_LIMIT, queryArgs, uri, recordsetSize);
175c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
176c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int windowSize = limit - 1;
177c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
178c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        TestWindowedCursor c = new TestWindowedCursor(PROJECTION, recordsetSize);
179c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        CursorWindow window = c.getWindow();
180c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        window.setNumColumns(PROJECTION.length);
181c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
182c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        Bundle extras = c.getExtras();
183c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
184c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        // Build the unpaged result set.
185c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        for (int row = 0; row < windowSize; row++) {
186c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            if (!window.allocRow()) {
187c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
188c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            }
189c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            if (!fillRow(window, row)) {
190c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                window.freeLastRow();
191c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
192c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            }
193c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
194c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
195c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, new String[] {
196c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                ContentResolver.QUERY_ARG_OFFSET,
197c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                ContentResolver.QUERY_ARG_LIMIT
198c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        });
199c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        extras.putInt(ContentResolver.EXTRA_TOTAL_COUNT, recordsetSize);
200c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return c;
201c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
202c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
203c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private MatrixCursor buildUnpagedResults(int recordsetSize) {
204c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        MatrixCursor c = createInMemoryCursor();
205c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
206c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        // Build the unpaged result set.
207c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        for (int i = 0; i < recordsetSize; i++) {
208c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            fillRow(c.newRow(), i);
209c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
210c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
211c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return c;
212c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
213c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
214c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    /**
215c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * Returns data type of the given object's value.
216c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *<p>
217c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * Returned values are
218c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * <ul>
219c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *   <li>{@link Cursor#FIELD_TYPE_NULL}</li>
220c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *   <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
221c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *   <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
222c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *   <li>{@link Cursor#FIELD_TYPE_STRING}</li>
223c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *   <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
224c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *</ul>
225c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *</p>
226c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     */
227c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static int getTypeOfObject(Object obj) {
228c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        if (obj == null) {
229c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return Cursor.FIELD_TYPE_NULL;
230c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        } else if (obj instanceof byte[]) {
231c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return Cursor.FIELD_TYPE_BLOB;
232c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        } else if (obj instanceof Float || obj instanceof Double) {
233c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return Cursor.FIELD_TYPE_FLOAT;
234c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        } else if (obj instanceof Long || obj instanceof Integer
235c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                || obj instanceof Short || obj instanceof Byte) {
236c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return Cursor.FIELD_TYPE_INTEGER;
237c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        } else {
238c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return Cursor.FIELD_TYPE_STRING;
239c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
240c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
241c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
242c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private MatrixCursor createInMemoryCursor() {
243c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        MatrixCursor c = new MatrixCursor(PROJECTION);
244c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        Bundle extras = new Bundle();
245c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        c.setExtras(extras);
246c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return c;
247c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
248c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
249c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private void fillRow(RowBuilder row, int rowId) {
250c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        row.add(createCellValue(rowId, 0));
251c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        row.add(createCellValue(rowId, 1));
252c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        row.add(createCellValue(rowId, 2));
253c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        row.add(createCellValue(rowId, 3));
254c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        row.add(createCellValue(rowId, 4));
255c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
256c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
257c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    /**
258c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * @return true if the row was successfully populated. If false, caller should freeLastRow.
259c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     */
260c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private static boolean fillRow(CursorWindow window, int row) {
261c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        if (!window.putLong((int) createCellValue(row, 0), row, 0)) {
262c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return false;
263c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
264c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        for (int i = 1; i < PROJECTION.length; i++) {
265c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            if (!window.putString((String) createCellValue(row, i), row, i)) {
266c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return false;
267c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            }
268c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
269c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return true;
270c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
271c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
272c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private static Object createCellValue(int row, int col) {
273c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        switch(col) {
274c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case 0:
275c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return row;
276c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case 1:
277c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return "--aaa--" + row;
278c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case 2:
279c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return "**bbb**" + row;
280c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case 3:
281c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return ("^^ccc^^" + row);
282c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case 4:
283c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                return "##ddd##" + row;
284c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            default:
285c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                throw new IllegalArgumentException("Unsupported column: " + col);
286c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
287c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
288c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
289c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    /**
290c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * Asserts that the value at the current cursor position x column
291c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * is expected test data for the supplied row.
292c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *
293c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * <p>Cursor must be pre-positioned.
294c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *
295c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * @param cursor must be prepositioned to the row to be tested.
296c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * @param row row value expected to be reflected in cell. This can be different
297c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     *            than the cursor position due to paging.
298c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * @param column
299c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     */
300c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @VisibleForTesting
301c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static void assertExpectedCellValue(Cursor cursor, int row, int column) {
302c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        int type = cursor.getType(column);
303c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        switch(type) {
304c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case Cursor.FIELD_TYPE_NULL:
305c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                throw new UnsupportedOperationException("Not implemented.");
306c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case Cursor.FIELD_TYPE_INTEGER:
307c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                assertEquals(createCellValue(row, column), cursor.getInt(column));
308c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
309c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case Cursor.FIELD_TYPE_FLOAT:
310c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                assertEquals(createCellValue(row, column), cursor.getDouble(column));
311c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
312c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case Cursor.FIELD_TYPE_BLOB:
313c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                assertEquals(createCellValue(row, column), cursor.getBlob(column));
314c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
315c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            case Cursor.FIELD_TYPE_STRING:
316c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                assertEquals(createCellValue(row, column), cursor.getString(column));
317c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                break;
318c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            default:
319c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                throw new UnsupportedOperationException("Unknown column type: " + type);
320c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
321c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
322c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
323c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
324c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public String getType(Uri uri) {
325c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        throw new UnsupportedOperationException();
326c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
327c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
328c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
329c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public Uri insert(Uri uri, ContentValues values) {
330c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        throw new UnsupportedOperationException();
331c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
332c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
333c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
334c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public int delete(Uri uri, String selection, String[] selectionArgs) {
335c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        throw new UnsupportedOperationException();
336c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
337c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
338c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    @Override
339c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
340c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        throw new UnsupportedOperationException();
341c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
342c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
343c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private static int constrain(int amount, int low, int high) {
344c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return amount < low ? low : (amount > high ? high : amount);
345c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
346c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
347c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    /**
348c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * Returns a Uri that includes paging information embedded in the URI.
349c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * This allows a test client to force paged results when running on older SDKs...
350c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * pre Android O SDKs lacking the ContentResolver#query w/ Bundle override
351c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     * necessary for paging.
352c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay     */
353c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static Uri forcePagingSpec(Uri uri, int offset, int limit) {
354c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        assert (uri.getPath().equals(TestContentProvider.PAGED_PATH)
355c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                || uri.getPath().equals(TestContentProvider.PAGED_WINDOWED_PATH));
356c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return uri.buildUpon()
357c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                .appendQueryParameter(ContentResolver.QUERY_ARG_OFFSET, String.valueOf(offset))
358c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                .appendQueryParameter(ContentResolver.QUERY_ARG_LIMIT, String.valueOf(limit))
359c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                .build();
360c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
361c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
362c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    public static Uri forceRecordCount(Uri uri, int recordCount) {
363c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        return uri.buildUpon()
364c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                .appendQueryParameter(RECORD_COUNT, String.valueOf(recordCount))
365c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay                .build();
366c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
367c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
368c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    private static final class TestWindowedCursor extends AbstractWindowedCursor {
369c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
370c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        private final String[] mProjection;
371c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        private final int mCount;
372c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        private final Bundle mExtras;
373c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
374c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        TestWindowedCursor(String[] projection, int count) {
375c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            mProjection = projection;
376c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            mCount = count;
377c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            mExtras = new Bundle();
378c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
379c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            setWindow(new CursorWindow("stevie"));
380c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
381c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
382c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        @Override
383c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        public Bundle getExtras() {
384c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return mExtras;
385c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
386c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
387c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        @Override
388c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        public int getCount() {
389c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return mCount;
390c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
391c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay
392c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        @Override
393c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        public String[] getColumnNames() {
394c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay            return mProjection;
395c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay        }
396c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay    }
397c01ec9664a26a603d90b067d1ca7ca39950eb025Steve McKay}
398