• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.database.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.database.CharArrayBuffer;
27 import android.database.CursorWindow;
28 import android.database.CursorWindowAllocationException;
29 import android.database.MatrixCursor;
30 import android.database.sqlite.SQLiteException;
31 import android.os.Parcel;
32 import android.test.suitebuilder.annotation.SmallTest;
33 import android.util.Log;
34 
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Random;
43 
44 @RunWith(AndroidJUnit4.class)
45 @SmallTest
46 public class CursorWindowTest {
47     private static final String TAG = "CursorWindowTest";
48 
49     private static final String TEST_STRING = "Test String";
50 
51     @Test
testWriteCursorToWindow()52     public void testWriteCursorToWindow() throws Exception {
53         // create cursor
54         String[] colNames = new String[]{"_id", "name", "number", "profit"};
55         int colsize = colNames.length;
56         ArrayList<ArrayList<Integer>> list = createTestList(10, colsize);
57         MatrixCursor cursor = new MatrixCursor(colNames, list.size());
58         for (ArrayList<Integer> row : list) {
59             cursor.addRow(row);
60         }
61 
62         // fill window
63         CursorWindow window = new CursorWindow(false);
64         cursor.fillWindow(0, window);
65 
66         // read from cursor window
67         for (int i = 0; i < list.size(); i++) {
68             ArrayList<Integer> col = list.get(i);
69             for (int j = 0; j < colsize; j++) {
70                 String s = window.getString(i, j);
71                 int r2 = col.get(j);
72                 int r1 = Integer.parseInt(s);
73                 assertEquals(r2, r1);
74             }
75         }
76 
77         // test cursor window handle startpos != 0
78         window.clear();
79         cursor.fillWindow(1, window);
80         // read from cursor from window
81         for (int i = 1; i < list.size(); i++) {
82             ArrayList<Integer> col = list.get(i);
83             for (int j = 0; j < colsize; j++) {
84                 String s = window.getString(i, j);
85                 int r2 = col.get(j);
86                 int r1 = Integer.parseInt(s);
87                 assertEquals(r2, r1);
88             }
89         }
90 
91         // Clear the window and make sure it's empty
92         window.clear();
93         assertEquals(0, window.getNumRows());
94     }
95 
96     @Test
testNull()97     public void testNull() {
98         CursorWindow window = getOneByOneWindow();
99 
100         // Put in a null value and read it back as various types
101         assertTrue(window.putNull(0, 0));
102         assertNull(window.getString(0, 0));
103         assertEquals(0, window.getLong(0, 0));
104         assertEquals(0.0, window.getDouble(0, 0), 0.0);
105         assertNull(window.getBlob(0, 0));
106     }
107 
108     @Test
testEmptyString()109     public void testEmptyString() {
110         CursorWindow window = getOneByOneWindow();
111 
112         // put size 0 string and read it back as various types
113         assertTrue(window.putString("", 0, 0));
114         assertEquals("", window.getString(0, 0));
115         assertEquals(0, window.getLong(0, 0));
116         assertEquals(0.0, window.getDouble(0, 0), 0.0);
117     }
118 
119     @Test
testConstructors()120     public void testConstructors() {
121         int TEST_NUMBER = 5;
122         CursorWindow cursorWindow;
123 
124         // Test constructor with 'true' input, and getStartPosition should return 0
125         cursorWindow = new CursorWindow(true);
126         assertEquals(0, cursorWindow.getStartPosition());
127 
128         // Test constructor with 'false' input
129         cursorWindow = new CursorWindow(false);
130         assertEquals(0, cursorWindow.getStartPosition());
131 
132         // Test newFromParcel
133         Parcel parcel = Parcel.obtain();
134         cursorWindow = new CursorWindow(true);
135         cursorWindow.setStartPosition(TEST_NUMBER);
136         cursorWindow.setNumColumns(1);
137         cursorWindow.allocRow();
138         cursorWindow.putString(TEST_STRING, TEST_NUMBER, 0);
139         cursorWindow.writeToParcel(parcel, 0);
140 
141         parcel.setDataPosition(0);
142         cursorWindow = CursorWindow.CREATOR.createFromParcel(parcel);
143         assertEquals(TEST_NUMBER, cursorWindow.getStartPosition());
144         assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0));
145 
146         parcel.recycle();
147     }
148 
149     @Test
testDataStructureOperations()150     public void testDataStructureOperations() {
151         CursorWindow cursorWindow = new CursorWindow(true);
152 
153         // Test with normal values
154         assertTrue(cursorWindow.setNumColumns(0));
155         // If the column has been set to zero, can't put String.
156         assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));
157 
158         // Test allocRow().
159         assertTrue(cursorWindow.allocRow());
160         assertEquals(1, cursorWindow.getNumRows());
161         assertTrue(cursorWindow.allocRow());
162         assertEquals(2, cursorWindow.getNumRows());
163         // Though allocate a row, but the column number is still 0, so can't putString.
164         assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));
165 
166         // Test freeLstRow
167         cursorWindow.freeLastRow();
168         assertEquals(1, cursorWindow.getNumRows());
169         cursorWindow.freeLastRow();
170         assertEquals(0, cursorWindow.getNumRows());
171 
172         cursorWindow = new CursorWindow(true);
173         assertTrue(cursorWindow.setNumColumns(6));
174         assertTrue(cursorWindow.allocRow());
175         // Column number set to negative number, so now can put values.
176         assertTrue(cursorWindow.putString(TEST_STRING, 0, 0));
177         assertEquals(TEST_STRING, cursorWindow.getString(0, 0));
178 
179         // Test with negative value
180         assertFalse(cursorWindow.setNumColumns(-1));
181 
182         // Test with reference limitation
183         cursorWindow.releaseReference();
184         try {
185             cursorWindow.setNumColumns(5);
186             fail("setNumColumns() should throws IllegalStateException here.");
187         } catch (IllegalStateException e) {
188             // expected
189         }
190 
191         // Test close(), close will also minus references, that will lead acquireReference()
192         // related operation failed.
193         cursorWindow.close();
194         try {
195             cursorWindow.acquireReference();
196             fail("setNumColumns() should throws IllegalStateException here.");
197         } catch (IllegalStateException e) {
198             // expected
199         }
200     }
201 
202     @Test
testAccessDataValues()203     public void testAccessDataValues() {
204         final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
205         final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
206         final long NUMBER_SHORT = (short) NUMBER_INTEGER;
207         final float NUMBER_FLOAT_SCIENCE = 7.332952E11f;
208         final double NUMBER_DOUBLE_SCIENCE = 7.33295205887E11;
209         final String NUMBER_FLOAT_SCIENCE_STRING = "7.332952E11";
210         final String NUMBER_DOUBLE_SCIENCE_STRING = "7.33295205887E11";
211         final String NUMBER_FLOAT_SCIENCE_STRING2 = "7.33295e+11";
212 
213         byte[] originalBlob = new byte[Byte.MAX_VALUE];
214         for (int i = 0; i < Byte.MAX_VALUE; i++) {
215             originalBlob[i] = (byte) i;
216         }
217 
218         CursorWindow cursorWindow = new CursorWindow(true);
219         cursorWindow.setNumColumns(5);
220         cursorWindow.allocRow();
221 
222         // Test putString, getString, getLong, getInt, isBlob
223         assertTrue(cursorWindow.putString(Long.toString(NUMBER_LONG_INTEGER), 0, 0));
224         assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 0));
225         assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 0));
226         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 0));
227         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 0));
228         // Converting of Float, there would be some little precision differences. So just compare
229         // first 6 digits.
230         assertEquals(NUMBER_FLOAT_SCIENCE_STRING.substring(0, 6), Float.toString(
231                 cursorWindow.getFloat(0, 0)).substring(0, 6));
232         assertEquals(NUMBER_DOUBLE_SCIENCE_STRING, Double.toString(cursorWindow.getDouble(0, 0)));
233         assertFalse(cursorWindow.isNull(0, 0));
234         assertFalse(cursorWindow.isBlob(0, 0));
235 
236         // Test null String
237         assertTrue(cursorWindow.putString("", 0, 0));
238         assertEquals("", cursorWindow.getString(0, 0));
239         assertEquals(0, cursorWindow.getLong(0, 0));
240         assertEquals(0, cursorWindow.getInt(0, 0));
241         assertEquals(0, cursorWindow.getShort(0, 0));
242         assertEquals(0.0, cursorWindow.getDouble(0, 0), 0.0);
243         assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.0);
244         assertFalse(cursorWindow.isNull(0, 0));
245         assertFalse(cursorWindow.isBlob(0, 0));
246 
247         // Test putNull, getString, getLong, getDouble, getBlob, getInd, getShort, getFloat,
248         // isBlob.
249         assertTrue(cursorWindow.putNull(0, 1));
250         assertNull(cursorWindow.getString(0, 1));
251         assertEquals(0, cursorWindow.getLong(0, 1));
252         assertEquals(0, cursorWindow.getInt(0, 1));
253         assertEquals(0, cursorWindow.getShort(0, 1));
254         assertEquals(0.0, cursorWindow.getDouble(0, 1), 0.0);
255         assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.0);
256         assertNull(cursorWindow.getBlob(0, 1));
257         assertTrue(cursorWindow.isNull(0, 1));
258         // If the field is null, isBlob will return true.
259         assertTrue(cursorWindow.isBlob(0, 1));
260 
261         // Test putLong, getLong, getInt, getString , getShort, getFloat, getDouble, isBlob.
262         assertTrue(cursorWindow.putLong(NUMBER_LONG_INTEGER, 0, 2));
263         assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 2));
264         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2));
265         assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2));
266         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2));
267         assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.0);
268         assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.0);
269         try {
270             cursorWindow.getBlob(0, 2);
271             fail("Can't get Blob from a Integer value.");
272         } catch (SQLiteException e) {
273             // expected
274         }
275         assertFalse(cursorWindow.isNull(0, 2));
276         assertFalse(cursorWindow.isBlob(0, 2));
277 
278         // Test putDouble
279         assertTrue(cursorWindow.putDouble(NUMBER_DOUBLE_SCIENCE, 0, 3));
280         assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 3));
281         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 3));
282         // Converting from Double to String, there would be some little precision differences. So
283         // Just compare first 6 digits.
284         assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3)
285                 .substring(0, 6));
286         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3));
287         assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.0);
288         assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.0);
289         try {
290             cursorWindow.getBlob(0, 3);
291             fail("Can't get Blob from a Double value.");
292         } catch (SQLiteException e) {
293             // expected
294         }
295         assertFalse(cursorWindow.isNull(0, 3));
296         assertFalse(cursorWindow.isBlob(0, 3));
297 
298         // Test putBlob
299         assertTrue(cursorWindow.putBlob(originalBlob, 0, 4));
300         byte[] targetBlob = cursorWindow.getBlob(0, 4);
301         assertTrue(Arrays.equals(originalBlob, targetBlob));
302         assertFalse(cursorWindow.isNull(0, 4));
303         // Test isBlob
304         assertTrue(cursorWindow.isBlob(0, 4));
305     }
306 
307     @Test
testCopyStringToBuffer()308     public void testCopyStringToBuffer() {
309         int DEFAULT_ARRAY_LENGTH = 64;
310         String baseString = "0123456789";
311         String expectedString = "";
312         // Create a 60 characters string.
313         for (int i = 0; i < 6; i++) {
314             expectedString += baseString;
315         }
316         CharArrayBuffer charArrayBuffer = new CharArrayBuffer(null);
317         CursorWindow cursorWindow = new CursorWindow(true);
318         cursorWindow.setNumColumns(2);
319         cursorWindow.allocRow();
320 
321         assertEquals(null, charArrayBuffer.data);
322         cursorWindow.putString(expectedString, 0, 0);
323         cursorWindow.copyStringToBuffer(0, 0, charArrayBuffer);
324         assertNotNull(charArrayBuffer.data);
325         // By default, if the field's string is shorter than 64, array will be allocated as 64.
326         assertEquals(DEFAULT_ARRAY_LENGTH, charArrayBuffer.data.length);
327         assertEquals(expectedString,
328                 new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
329 
330         // Test in case of string is longer than 64,
331         expectedString += baseString;
332         charArrayBuffer = new CharArrayBuffer(null);
333         cursorWindow.putString(expectedString, 0, 1);
334         cursorWindow.copyStringToBuffer(0, 1, charArrayBuffer);
335         assertNotNull(charArrayBuffer.data);
336         // If the string is longer than 64, array will be allocated as needed(longer than 64).
337         assertEquals(expectedString,
338                 new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
339         assertEquals(70, expectedString.length());
340         assertEquals(expectedString.length(), charArrayBuffer.data.length);
341     }
342 
343     @Test
testAccessStartPosition()344     public void testAccessStartPosition() {
345         final int TEST_POSITION_1 = 0;
346         final int TEST_POSITION_2 = 3;
347 
348         CursorWindow cursorWindow = new CursorWindow(true);
349         fillCursorTestContents(cursorWindow, 5);
350 
351         // Test setStartPosition
352         assertEquals(TEST_POSITION_1, cursorWindow.getStartPosition());
353         assertEquals(3, cursorWindow.getInt(3, 0));
354         assertEquals(TEST_STRING + "3", cursorWindow.getString(3, 1));
355         assertEquals(4, cursorWindow.getInt(4, 0));
356         assertEquals(TEST_STRING + "4", cursorWindow.getString(4, 1));
357         cursorWindow.setStartPosition(TEST_POSITION_2);
358 
359         assertEquals(TEST_POSITION_2, cursorWindow.getStartPosition());
360 
361         assertEquals(0, cursorWindow.getInt(3, 0));
362         assertEquals(TEST_STRING + "0", cursorWindow.getString(3, 1));
363         assertEquals(1, cursorWindow.getInt(4, 0));
364         assertEquals(TEST_STRING + "1", cursorWindow.getString(4, 1));
365         try {
366             cursorWindow.getBlob(0, 0);
367             fail("Row number is smaller than startPosition, will cause a IllegalStateException.");
368         } catch (IllegalStateException e) {
369             // expected
370         }
371     }
372 
373     @Test
testClearAndOnAllReferencesReleased()374     public void testClearAndOnAllReferencesReleased() {
375         MockCursorWindow cursorWindow = new MockCursorWindow(true);
376 
377         assertEquals(0, cursorWindow.getNumRows());
378         fillCursorTestContents(cursorWindow, 10);
379         assertEquals(10, cursorWindow.getNumRows());
380         assertEquals(0, cursorWindow.getStartPosition());
381         cursorWindow.setStartPosition(5);
382         assertEquals(5, cursorWindow.getStartPosition());
383 
384         // Test clear(). a complete calling process of cursorWindow has a perfect acquiring and
385         // releasing pair, so the references number will be equal at the begin and the end.
386         assertFalse(cursorWindow.hasReleasedAllReferences());
387         cursorWindow.clear();
388         assertEquals(0, cursorWindow.getNumRows());
389         assertEquals(0, cursorWindow.getStartPosition());
390         assertFalse(cursorWindow.hasReleasedAllReferences());
391 
392         // Test onAllReferencesReleased.
393         // By default, cursorWindow's reference is 1, when it reachs 0, onAllReferencesReleased
394         // be invoked.
395         cursorWindow = new MockCursorWindow(true);
396         cursorWindow.releaseReference();
397         assertTrue(cursorWindow.hasReleasedAllReferences());
398     }
399 
400     @Test
testDescribeContents()401     public void testDescribeContents() {
402         CursorWindow cursorWindow = new CursorWindow(true);
403         assertEquals(0, cursorWindow.describeContents());
404     }
405 
406     @Test
testDefaultCursorWindowSize()407     public void testDefaultCursorWindowSize() {
408         CursorWindow cursorWindow = new CursorWindow("test");
409         cursorWindow.setNumColumns(1);
410         byte[] bytes = new byte[1024];
411         Arrays.fill(bytes, (byte) 1);
412         // Ensure that the default is not too small and it's possible to fill CursorWindow
413         // with ~2Mb of data
414         int testRowCount = 2016;
415         for (int i = 0; i < testRowCount; i++) {
416             assertTrue(cursorWindow.allocRow());
417             assertTrue("Allocation failed for row " + i, cursorWindow.putBlob(bytes, i, 0));
418         }
419         assertTrue(cursorWindow.allocRow());
420         assertFalse("Allocation should fail for row " + testRowCount,
421                 cursorWindow.putBlob(bytes, testRowCount, 0));
422     }
423 
424     @Test
testCustomSize()425     public void testCustomSize() {
426         // Allocate CursorWindow with max size 10KB and test that restriction is enforced
427         CursorWindow cursorWindow = new CursorWindow("test", 10000);
428         cursorWindow.setNumColumns(1);
429         byte[] bytes = new byte[8000];
430         Arrays.fill(bytes, (byte) 1);
431         assertTrue(cursorWindow.allocRow());
432         assertTrue("Allocation of 1 row should succeed", cursorWindow.putBlob(bytes, 0, 0));
433         assertTrue(cursorWindow.allocRow());
434         assertFalse("Allocation of 2nd row should fail", cursorWindow.putBlob(bytes, 1, 0));
435     }
436 
437     @Test
testCreateFailure()438     public void testCreateFailure() {
439         Exception actual = null;
440         try {
441             new CursorWindow("test", -1);
442         } catch (IllegalArgumentException caught) {
443             Log.i(TAG, "Received: " + caught);
444             return;
445         }
446         fail("Didn't catch IllegalArgumentException: actual=" + actual);
447     }
448 
449     @Test
testCreateFromParcelFailure()450     public void testCreateFromParcelFailure() {
451         Exception actual = null;
452         try {
453             CursorWindow.CREATOR.createFromParcel(Parcel.obtain());
454         } catch (CursorWindowAllocationException caught) {
455             Log.i(TAG, "Received: " + caught);
456             return;
457         }
458         fail("Didn't catch CursorWindowAllocationException: actual=" + actual);
459     }
460 
461     @Test
testCursorWindowAllocationException()462     public void testCursorWindowAllocationException() {
463         String exceptionDescription = "description test";
464         CursorWindowAllocationException newException =
465                 new CursorWindowAllocationException(exceptionDescription);
466         assertEquals(exceptionDescription, newException.getMessage());
467         try {
468             throw newException;
469         } catch (CursorWindowAllocationException exception) {
470             assertEquals(exceptionDescription, exception.getMessage());
471         }
472     }
473 
474     private class MockCursorWindow extends CursorWindow {
475         private boolean mHasReleasedAllReferences = false;
476 
MockCursorWindow(boolean localWindow)477         public MockCursorWindow(boolean localWindow) {
478             super(localWindow);
479         }
480 
481         @Override
onAllReferencesReleased()482         protected void onAllReferencesReleased() {
483             super.onAllReferencesReleased();
484             mHasReleasedAllReferences = true;
485         }
486 
hasReleasedAllReferences()487         public boolean hasReleasedAllReferences() {
488             return mHasReleasedAllReferences;
489         }
490 
resetStatus()491         public void resetStatus() {
492             mHasReleasedAllReferences = false;
493         }
494     }
495 
fillCursorTestContents(CursorWindow cursorWindow, int length)496     private void fillCursorTestContents(CursorWindow cursorWindow, int length) {
497         cursorWindow.clear();
498         cursorWindow.setStartPosition(0);
499         cursorWindow.setNumColumns(2);
500         for (int i = 0; i < length; i++) {
501             cursorWindow.allocRow();
502             cursorWindow.putLong(i, i, 0);
503             cursorWindow.putString(TEST_STRING + i, i, 1);
504         }
505     }
506 
createTestList(int rows, int cols)507     private static ArrayList<ArrayList<Integer>> createTestList(int rows, int cols) {
508         ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
509         Random generator = new Random();
510 
511         for (int i = 0; i < rows; i++) {
512             ArrayList<Integer> col = new ArrayList<Integer>();
513             list.add(col);
514             for (int j = 0; j < cols; j++) {
515                 // generate random number
516                 col.add(j == 0 ? i : generator.nextInt());
517             }
518         }
519         return list;
520     }
521 
522     /**
523      * The method comes from unit_test CursorWindowTest.
524      */
getOneByOneWindow()525     private CursorWindow getOneByOneWindow() {
526         CursorWindow window = new CursorWindow(false);
527         assertTrue(window.setNumColumns(1));
528         assertTrue(window.allocRow());
529         return window;
530     }
531 }
532